Search projects, hackathons, sponsors and tech

    ← All projects

    Otter

    First implementation of a provably MEV-resilient AMM — Uniswap v4 hook + off-chain VCG solver.

    Demo

    About

    Otter is the first implementation of "Otter: A Provably MEV-Resilient Automated Market Maker via Surplus Redistribution" (Shi, Zhang, Chung & Li — IACR ePrint 2026/1877), deployed as a Uniswap v4 hook on Ethereum Sepolia. The core problem: every conventional AMM has a queue, and any queue is sellable. A searcher who can see your order and insert before it will front-run you — buying before you drive up the price, then selling after you fill at the worse rate. This is not an edge case. In our harness test, an attacker deploying 5,000 OTB captures 92% of the victim's trade on a plain pool. The victim gets 1.38 OTB instead of 47.6. That loss is the searcher's profit, by construction. Otter removes the queue entirely. Orders are collected into a 60-second batch window as an unordered set — no first, no second, no sequence position to purchase. The mechanism is dominant-strategy truthful: your best move is always to report your real reservation price, regardless of what anyone else does or what the attacker tries. A searcher who joins the batch clears at exactly the same price as every other participant. The Clarke pivot redistributes any surplus back to the LP rather than to the attacker. What makes this design novel over existing batch AMMs (CoW Protocol, Angstrom, SPEEDEX, am-AMM, Penumbra) is that it achieves dominant-strategy truthfulness for both regular users and for a builder-acting-as-user simultaneously, via VCG surplus redistribution. Existing batch AMMs clear at a uniform price and are not incentive compatible in this sense. This is the first implementation of a mechanism with that specific property. The full stack is deployed and verified on Sepolia: OtterOrderBook (escrow and batch commitment), OtterSettlement (VCG outcome verification and v4 pool settlement), and OtterHook (the Uniswap v4 integration). A live demo page walks through a real settled 6-order batch — showing the sandwich comparison, VCG allocation results, individual order fills and refunds, LP surplus redistribution, and Etherscan links to every source-verified contract. Stated limitation: the mechanism's guarantees assume censorship resilience at the consensus layer (Theorem 23 of the paper). A testnet does not provide this. We say this explicitly in the README, in the demo UI, and here — not buried.

    How it's made

    Stack: Foundry (Solidity 0.8.26), TypeScript solver (Node 22), Vite + React + TypeScript frontend, wagmi + RainbowKit for Sepolia wallet connection. No additional backend — the solver runs as a CLI and the web app is fully static. SOLVER (solver/) The two-sided VCG mechanism is implemented in otter.ts. The dominant side is determined by comparing aggregate eligible supply across both directions; ties go to sell-Y (eq. 10 of the paper). The symmetric case reuses the same one-sided code with the token roles swapped and the answer flipped back — one implementation path, no chance of the two branches drifting apart. The dominant-side allocation uses a layer-cake pivot algorithm extracted from the proof of Lemma 16. The closed form for the Clarke pivot integral has a global zero-crossing (the clearing price, shared by the whole batch) and a per-user lower breakpoint. We implement this, verify it against the naïve O(n²) leave-one-out computation across 4,000 random batches (max relative error 3.8e-10), and benchmark both: at n=1,000 the layer-cake solver runs in 8.8ms vs 233ms naïve — a 27× speedup. Current limitation: the pivot loop is still O(n²) with a small constant because it scans pieces linearly; replacing those scans with binary search plus a prefix-summed antiderivative table is the outstanding optimisation. Property tests (solver/test/properties.ts §8) independently grid-search the allocation space for n ≤ 4 and confirm that nothing on the grid beats the solver's own output. This is evidence for small instances, not a proof for arbitrary n — the comment in that test says exactly that. CONTRACTS (contracts/src/) OtterHook implements IHooks directly. BaseHook was removed from both v4-core (v4.0.0) and v4-periphery with no migration note; every tutorial still references it. Implementing IHooks directly is arguably cleaner for a hook that only needs three callbacks, but we found this out by reading source, not from the docs. The hook's CREATE2 address encodes the beforeSwap, beforeAddLiquidity, and beforeRemoveLiquidity permission bits; HookMiner was vendored from v4-periphery/test/ since it lives under test/ despite being a deployment-critical tool. OtterSettlement calls poolManager.unlock() to enter the v4 singleton and runs the residual imbalance swap. Before distributing, it verifies: feasibility (pool conservation), individual rationality (no seller underpaid), budget bounds (no overfill), minority pricing, dominance, and that the pool actually yielded enough to cover all obligations. OtterMath is an internal library compiled directly into OtterSettlement (all functions are internal). Fixed-point arithmetic uses solmate's mulDivDown and mulDivUp exclusively — PRBMath's public API has no mulDivUp, which matters for the one rounding direction that decides whether an invariant flips. The rule throughout OtterMath: amounts a user may receive round down; amounts a user must have earned round up. fTildeDown and fTildeUp are never mixed. Notable finding during integration: Uniswap v4 executes a swap in two stages on a Q64.96 price grid, rounding in the pool's favour at each. It therefore pays strictly less than the continuous curve predicts. A settlement paying the full theoretical ceiling failed with PoolOutputShortfall by 14 wei in testing. We measured the gap across 512 fuzz runs (it scales with price impact, not trade size — roughly 1 wei per 1.2% of impact) and derived a tight allowance function: ceil((y − M) × 200 / y0) + 2. Critically, fTildeDown is left untouched as a faithful port of the paper's curve. The allowance is a separate function used only in verify — the two are kept distinct in kind because one is the mechanism and the other is a fact about executing it on v4. GAS & HARNESS (harness/) Settlement gas is dominated by ERC-20 transfers and ecrecover, not by verification (which is O(n) arithmetic). Measured: ~40,906 gas per order at n=200; block ceiling at 630 orders per block on a 30M gas limit. The sandwich harness runs a vanilla v4 pool vs the Otter pool against the same attacker parameters — the direct swap on the Otter pool reverts at the hook, so the attacker gains nothing. WEB (web/) The demo data is not hardcoded — it is inlined from the real solver fixture generated by solver/src/demo-cli.ts. The Sepolia sandbox reads live batch state directly from the deployed OtterOrderBook contract via wagmi. The GasChart renders the measured harness data as an inline SVG. No public solver/relayer is running; this is clearly disclaimed in the UI.