Toxic flow mitigation
AIMM prices toxic flow rather than banning it. Each defense below is a quantity: what it bounds, what it costs, and where the shipped parameters do not yet cover the bound.
1. The framing
An external-mark AMM leaks value through three channels, each with a different clock and defense:
| Channel | Clock | Bound | Defense | Priced by |
|---|---|---|---|---|
| Push-latency LVR | between pushes | (the deviation trigger) | keeper cadence + minFee floor | |
| OEV | around the push tx | mempool visibility of the push tx | minFee, private relay | |
| Keeper delinquency | past the grace | staleness surcharge |
Classical curve LVR (the reserve-ratio kind, where the pool’s own invariant is the stale price) does not apply: the quote center is a fresh external mark, never a reserve- or trade-derived accumulator. That does not remove all LVR.
A fourth channel is not oracle-timing at all: inventory-direction toxicity, flow that persistently moves coverage one way. It is priced by the mid skew and the coverage toll, not by the spread. See §5.
Coverage ratio definition: Inventory Management §2. Spread and fee mechanics: Spread & Fees.
2. The 2\theta fence: the one number that matters
2.1. The mechanism
The keeper pushes a new mark when either trigger fires (deviation trigger evaluated in the keeper’s push loop):
- deviation: bps
- heartbeat:
heartbeat_selapsed
Between pushes the mark is frozen and the true price moves. The deviation trigger bounds that move: while the keeper is live, the gap never exceeds before a push fires. This is the primary LVR defense, and the only one independent of volatility and elapsed time.
2.2. The derivation
An informed trader observing has an edge of at most bps in whichever direction the mark is wrong. Extracting it requires a round trip: buy at the stale mark, sell back at truth. That crosses the quoted width twice and pays in total ( per swap, on the output, Spread & Fees §5.1). The pick-off is unprofitable when
The floor must satisfy it, not the runtime spread: , confidence and staleness all add to the quote, so a fence built on them would vanish in the calm conditions where the arb is cheapest to run.
2.3. The enforcement
The floor predicate is one comparison:
minfee_covers_theta(minfee_pbps, theta_bps) = minfee_pbps >= 200 * theta_bpsIt is checked at four points, all off chain - the keeper’s startup gate, the pre-submit coupling gate, parameter generation and the deploy manifest - enumerated in Parametrization §11.2.
exists only in the keeper: not an on-chain parameter, not in Asset, not in OracleConfig. The chain carries maxDevBps and ttl, the acceptance band: maxDevBps bounds what a signer may push, bounds when the keeper chooses to push.
Every leg must clear the fence, which is what the startup gate checks; read getAsset(token).minFeePbps for a leg’s floor. A cross needs more, hence the summed path floor (§3).
3. The path floor sums over legs, it does not reduce by max
The pick-off edge on a cross is the sum of the per-leg thetas. Both marks are independently allowed to sit from truth, and an adversary selects the joint worst realization, so a max would fund one leg and leave the other unfunded.
Under a max rule the extraction is repeatable and size-unbounded: a per-swap rate, not a one-off, with nothing in the pool capping how often it runs. It is guaranteed, and it is largest when the rest of the spread is thinnest: the surcharge terms all vanish when the keeper is healthy (Spread & Fees §8).
The shipped rule sums, and nothing caps the composed spread from above (Spread & Fees §3.1).
4. Staleness: what happens when the keeper misses
funds the pool only while the keeper meets its push contract. Past the grace it has not, and the guarantee is void; the premium then ramps with the unobserved drift , per leg and summed (Spread & Fees §3.4: formula, constants, worked values). The hard revert sits above it at age > ttl (FeedMathLib.gate).
Two properties:
- The grace is capped at 30 s regardless of
ttl(PricingLib.MAX_STALE_GRACE_SECS). The push side separately refuses a source second more than 120 s old (MarkStoreP8). Without the cap attl = 600feed bought a 300 s premium-free window. - It is defense-in-depth, not the defense. Under a per-block keeper (block times of a few seconds or less) every is zero and the term never engages. It exists so a late or censored keeper degrades gracefully instead of being picked off at the frozen mark.
During a stale window the pool quotes wider and receives less flow. That is the intended trade-off; it is not a substitute for a fresh keeper.
Operational requirement: the keeper heartbeat must be strictly below ttl/2 on every feed (heartbeat_le_half_ttl, reported per feed at GET /thresholds on the keeper’s status endpoint).
5. Inventory toxicity: skew and the coverage toll
5.1. Skew is a Glosten-Milgrom soft fee
Coverage imbalance shifts the mid, not the spread. The spread is direction-invariant (Spread & Fees §4), so a coverage-worsening trade pays a worse price at the same width.
Inventory-moving (informed) flow walks up the skew and pays the displacement on top of the floor; that slippage stays in reserves. Round-trip (noise) flow restores coverage, the displacement nets to zero, and it pays only the floor. Because the mark is frozen between pushes, the quote moves only through realized inventory. The construction, its formula and the flash-loan guardrail are at Spread & Fees §7.2.
Which of the two dominates is a composition property rather than a pricing one. Inside a core of correlated legs, flow that drains one leg tends to arrive against another, so inventory mean-reverts and one-sided accumulation is the exception; an uncorrelated roster makes it the default and the skew becomes a one-way charge with no offsetting flow. It is a tendency, not a guarantee: correlated legs can move together and leave every leg in the core displaced the same way at once (Pool Composition).
Consequence: defensiveness is tuned with the shape, not with . A pool can quote a tight floor for the benign majority and still charge the toxic minority, because the charge is a slope on inventory rather than a level on volume.
The levers. The skew slope is not one of them. PricingLib.computeInventorySkew is a fixed protocol law with no per-asset field behind it, and its two arms are asymmetric because the filling arm’s slope is the round-trip impact-conservation bound read from its other side; merging them into one slope reopens a ping-pong extraction. The law is held in constants, so no write can move it. Formula and derivation: Inventory Management §3.
Tunable per asset is the magnitude that fixed skew index produces: the dispersion (per-asset fitted floor minDispersionPbps and sensitivity vegaBps; σ scales κ 1:1 above the floor at the shipped , capped only by the protocol-wide MAX_DISPERSION_PBPS), the curve the asset points at, and for the coverage wall.
5.2. The coverage toll is a level shift, and it is asymmetric
The toll is charged once, on the terminal output asset, before the fee (PricingLib._settleQuote, _covToll), and is charge-only. Definition: Spread & Fees §6.1.
Its marginal rate at zero size is strictly positive whenever the leg is under-covered (§6.3), with sized in Parametrization §6.2. That is the correct anti-drain shape: it charges the first unit that worsens an already-deficient leg, not only the block trade.
_covToll is output-only: a sell into the hub pays hub κ, and a same-pool spoke→spoke trade ends on the destination spoke, so the toll asymmetry is . Proof: Spread & Fees §6.4.
Every listed asset, the hub included, carries (requireNeverDepletable, Invariants §I-9).
6. Mark integrity
Every defense above reads the mark. Four independent bounds sit on it:
| Bound | Mechanism | Constant |
|---|---|---|
| Per-push move | volatility-adaptive deviation band: bps, σ in PBPS, so any push is bounded at maxDevBps | MarkStoreP8._lane |
| Uncertainty | revert past a 1σ CI of 1000 bps | MAX_CONFIDENCE_HALT_BPS, PoolConstantsLib.sol |
| Age | revert past ttl; priced below it | FeedMathLib.gate |
| Depeg | reference band (refBandBps): the asset’s primary mark must sit within refBandBps of the same lane’s reference (R) tier, checked on both endpoints and every interior node. A USD-attested mark also carries a per-asset depeg code (band = code x 250 bps) against 1.0, and the base halts past its own band (2,500 bps at code 0). An anchor-attested mark has no fixed unit to test against; that policy call is the guardian’s | PoolConstantsLib.DEPEG_STEP_BPS; PoolIOLib.priceBandGuard / priceBandGuardPath |
Two details that matter for toxicity:
- The in the deviation band is the stored prior , never the incoming push’s own. Using the incoming value would let a signer inflate its own band (
MarkStoreP8._lane). - The observation clock is the segment’s signed source second: a segment more than 120 s old or 5 s in the future reverts, and a lane moves only on an advancing source second (
MarkStoreP8._segment), so a relay cannot stamp a withheld signed quote fresh and avoid the staleness surcharge.
Authority is a k-of-n signer quorum on a committed roster, not a single feed’s uptime. A mark landed this block is not age-gated: that would hand every allowlisted relayer a pool-wide outage switch (Mark Relayer §5.3).
7. JIT liquidity
Attack: add liquidity immediately before a large swap, remove immediately after, capturing fee share without carrying inventory risk.
flowCooldownSecs enforces a mandatory delay on freshly minted receipt shares, against transfer and burn alike, making the atomic version impossible. Default 15 s (DEFAULT_FLOW_COOLDOWN_SECS, PoolConstantsLib.sol), hard ceiling 300 s (MAX_FLOW_COOLDOWN_SECS, PoolConstantsLib.sol). The ceiling exists because the receipt is transferable and the raise lane is untimelocked: it bounds the worst-case unavailability one compromised admin key can impose. There is no disable: MIN_FLOW_COOLDOWN_SECS = 1 and both writers reject 0.
Detail: Flow Guards.
Second reason JIT is unattractive here: the LP fee is credited as a liability via liquidityIndexWad (PoolLiquidityLib._raiseIndex), pro rata across the whole leg. A JIT depositor’s share of one swap’s fee is its share of the leg, so capture is proportional to real capital committed, not to timing.
8. What is not defended
| Residual | Size | Why it is accepted |
|---|---|---|
| OEV at the push | deterministic once the push is pending | needs a private relay or a jump auction; neither is shipped. minFee is the only shipped lever |
| Push-latency LVR inside | per event, funded by | the fence makes it unprofitable, not impossible |
| Cross-leg basis on an anchored pair | unmeasured | if a leg’s ratio is not its own push trigger, both legs can sit inside their own bands while the ratio drifts to the sum, and no push fires. Applies to multi-anchor paths; the mitigation is a pinned synthetic feed with its own and its own trigger |
| Interior hop clamped against a hub balance | 0 | PricingLib._legScaleOut caps against destination reserves only on the delivering leg (if (!delivering) return), so an interior hop is never clamped against a balance the swap does not spend. The decimal rescale runs per leg regardless. Listed so a regression is recognizable |
Auction-shaped recapture of the push OEV is out of scope. Ordering rent accrues to whoever owns the ordering rule, not to the venue whose flow creates it, and a pool that does not control sequencing has no ordering right to sell. The lever is the adverse-selection fee already in the curve: the floor.
The reopen threshold measures LP harm, not the ability to monetize it: a sustained residual above 1% APR of pool TVL under a healthy oracle. Value leaks from LPs whether the taker is a searcher, a block assembler or a latency racer, and whether or not anyone auctions the right to be it. Most of the LVR is between-push latency loss, which no mempool policy, auction or ordering rule touches. If the threshold trips, the response is chain-specific, bounded by the ordering primitives that chain exposes and whether the venue controls sequencing at all.
9. Why not the alternatives
| Approach | Portability | Centralization | Oracle dependency | Permissionless |
|---|---|---|---|---|
| McAMM (first-trade auction) | low (needs PBS) | medium (pro MEV shops) | none | yes |
| am-AMM (Harberger manager lease) | high | high (single manager) | practical (manager needs CEX) | yes |
| UAMM / DODO / Swaap (oracle-first) | high | very high (oracle control) | total | yes |
| AIMM (external mark + adverse-selection fee) | high | medium (k-of-n signer quorum) | external mark, hardened | yes |
- McAMM auctions the right to trade first in each block, sending the bid to LPs. Strongly LVR-reducing, but requires PBS and builders who respect the auction, realistically Ethereum mainnet only. It also concentrates capture in builder-integrated shops, and “first trade of block” does not translate to a multi-asset pool.
- am-AMM auctions a manager lease; the winner pays continuous rent and captures internal arb. Chain-agnostic, but a single manager controls all fees and trading revenue at any time, and a competitive manager needs off-chain CEX feeds anyway.
- UAMM and the oracle-first family quote at oracle price and add slippage only for inventory. The strongest LVR answer if the oracle is perfect, which is the failure mode: a wrong, manipulated or lagging oracle produces unbounded LP loss, and AMM safety collapses to oracle safety.
AIMM is external-mark-primary, so it inherits the oracle-first LVR result. The difference: the fragile parts are fenced individually.
- A volatility-adaptive per-push band bounds a compromised key.
- A confidence halt rejects an uncertain mark.
- A staleness surcharge degrades on keeper lag.
- Per-asset depeg bands gate tail risk.
- Trust rests on a k-of-n quorum with immediate revocation rather than one feed’s uptime.
Residual push-latency LVR and OEV remain (Mark Relayer).
10. Monitoring
On a stressed feed most of the quoted spread is oracle health rather than pricing (Spread & Fees §8), so the spread decomposition is the monitoring dashboard:
| Metric | Source | Alarm |
|---|---|---|
confidence per feed | rising share means the source, not the market, is degrading | |
| mark age vs grace | non-zero at all means a push was missed | |
| vs | per cross | any negative value is a live pick-off exposure |
covToll / amountOut | SwapQuote.covToll | rising means a leg is draining |
(exec - mid)/mid vs (mid - mark)/mark | SwapQuote.midPrice, markPrice | splits extractable value from skew premium exactly |
That last row makes OEV measurable rather than inferred: the book is centered on the mid, not the mark, so the two components sum to the naive exec-vs-mark number and can be attributed separately (IPool.sol). Both are exact WAD, so the split carries no encoding error into the attribution.
Off-chain: LP-versus-arbitrageur PnL segregation, adverse-selection cost estimation, CEX-DEX price correlation.
11. Related documentation
- Spread & Fees: the complete fee model and both theorems
- Inventory Management: coverage and the pool rate
- Parametrization: every parameter, its fence, its live value
- Price feed reads: mark, , confidence, push API
- Mark Relayer: signer quorum, deviation band, LVR/OEV risk register
- Flow Guards: JIT cooldown
https://ethresear.ch/t/mev-capturing-amm-mcamm/13336
https://www.gate.com/learn/articles/-introduction-to-mev-mitigation/5121
https://www.pyth.network/blog/beyond-the-dark-forest-experiments-in-mitigating-mev
https://arxiv.org/html/2403.03367v1
https://fc25.ifca.ai/preproceedings/183.pdf
https://arxiv.org/abs/2403.03367
https://fenbushi.vc/2024/01/20/ending-lps-losing-game-exploring-the-loss-versus-rebalancing-lvr-problem-and-its-solutions/
https://arxiv.org/html/2308.06375v2
https://arxiv.org/abs/2308.06375