Slippage & Price Impact

The price a taker receives differs from the oracle mark by four separate quantities; only one of them is what most traders mean by price impact.


1. The decomposition

Every AIMM quote is a product of four factors applied to the oracle mark, for a single leg with q the trade size:

execmark=Pskew⏟inventorylevel·Γ(q)⏟curvetraversal·(1-t(q))⏟coveragetoll·(1-s2)⏟halfpathspread

TermDefinitionDepends on sizeOwned by
Pskew=r/m, mid over markInventory skew offset, evaluated at zero volumeNoInventory Management §3, Liquidity Shaping §5
Γ(q)=p¯/midCurve traversal, the VWAP over the interval the trade sweepsYesLiquidity Shaping §8
t(q)=covToll/grossConvex coverage toll on the drained output legYesInventory Management §6
s = SwapQuote.spreadPbpsPath spread; the realized fee is s/2 of the pre-fee outputNoSpread & Fees

Price impact in the AIMM sense is Γ(q) alone, together with t(q) when the output leg is under-covered. Everything else is a level or a fee and is present at zero size.

The pool publishes both the mark and the mid (SwapQuote.markPrice, midPrice, both exact WAD (1e18), tokenOut per tokenIn, chained across every leg). This lets an indexer split (exec-mid), the extractable value, from (mid-mark), the inventory skew, instead of conflating them into one execution-versus-oracle number. See IPool.sol.

1.1. What each term is, and what it is not

Canonical vocabulary. Each word names exactly one object; none is a synonym for another.

TermIsKindSymmetric in direction?Site
markthe oracle price, as attested by the keeper in the child’s anchor unitlevelyesMarkWordLib.mark, consumed at PricingLib.sol
skewthe signed dimensionless inventory index, int8 in [-100,+100], a function of coverage alone. An input, never a priceindexn/acomputeInventorySkew, PricingLib.sol
skew offsetthe price displacement that index produces, (mid-mark)/mark in bpslevelat c≥1, yes. Below parity a sell into the leg is capped at the mark (Liquidity Shaping §8.5), so the offset prices buys only and does not cancel on a round trip_legMid, PricingLib.sol; surfaced at IPool.sol and by @btr-protocol/sdk
midthe mark carrying that offset, evaluated at zero volumelevelyes_legMid, PricingLib.sol
price impact (= curve traversal)the cost of traversing the liquidity shape from the skew anchor, Γ(q)one-directional costno, see §1.2_traverseCurve, PricingLib.sol
spreadthe round-trip fee width, SwapQuote.spreadPbps, symmetric about the midwidthyes (Spread & Fees §4)_pathSpread, PricingLib.sol
feewhat a swap pays: half the spread, once, on the outputchargeyesPricingLib.sol
coverage tollthe convex charge on a drained output legone-directional, charge-onlyno_covToll, PricingLib.sol
slippagethe stochastic gap between the quote and the fill (§3), not a pool term at allriskn/aminAmountOut, §4.1

Four usages to reject:

  • “spread” for total cost. The spread is one of four terms and is usually not the largest. Total cost is the whole product above.
  • “fee” for the spread. minFeePbps is a floor on the spread, not on the fee; the fee is half the spread. Nothing caps the spread from above (Spread & Fees §3.1).
  • “premium”, for anything. The word carries three objects at once: the mid displacement, a generic price-versus-mark ratio, and a component of the spread, which conflates level with width. The mid displacement is the skew offset (a level); the spread’s parts are bands and surcharges (widths). “Premium” and “discount” are admissible only as informal words for the sign of a skew offset, never as a quantity and never as a parameter.
  • “skew” for a price. skew is the dimensionless index; the price effect is the skew offset.

Naming, settled: the Avellaneda-Stoikov reservation price is this codebase’s mid, and “mid” is the only name used for it. The depeg gate is the refBandBps reference band, a per-asset depeg code on USD-attested marks, and the base parity halt (Anchor Path Pricing §7.1).

1.2. Total cost is direction-asymmetric; the width and the level are not

The spread is direction-invariant: both sides quote the same width about the same mid (Spread & Fees §4). The skew offset is a level: the same number for both directions, and it cancels on a round trip. Price impact is neither, so total cost is not.

Measured on an exact integer port of PricingLib.sol + NUQuarticLib.sol, bit-verified against forge on the bell_500 preset: dispRefPbps 500, σ=0 so dispersion is minDispersionPbps = 2034 PBPS, spread 1032 PBPS (half-spread 5.16 bps), mark 1. The impact and skew columns, which is what the table is for, do not depend on the spread. Notional is a fixed percentage of liabilities, held constant across coverage states, so the rows are comparable. slip is |p_-mid| in bps. The skew column is the fixed law, +200(1-c) draining and -100(c-1) filling, saturating at c≤0.5 and c≥2; 50% and 200% are where the law clamps, not a configurable band, and no per-asset dial enters these rows.

Measurement note: the c=1.15 row reads -14 / x0=4300 from the fixture. The shipped law gives exactly -15 and x0=4250 at c=1.15. One skew unit of fixture drift, worth 50 bps of the spline domain; the slippage figures either side of it are unaffected at this resolution.

The c=0.70 and 0.85 rows predate the sell-arm mark cap (2026-09-11). At c<1 a sell now executes at min(p¯,m), so those sell cells understate the cost by up to the skew offset. The c≥1 rows are unaffected.

cskewx0skew offsetnotionalslip sellslip buygap (buy - sell)
0.70+608000+10.880 bps1%0.2900.280-0.010
0.7010%2.7703.050+0.280
0.7025%6.6004.420-2.180 (buy clipped)
0.85+306500+5.200 bps1%0.2100.2100.000
0.8510%2.0702.160+0.090
0.8525%5.1005.710+0.610
1.00050000.0001%0.1700.1700.000
1.0010%1.7101.7100.000
1.0025%4.3604.3600.000
1.15-144300-2.390 bps25%3.8903.720-0.170
1.30-303500-5.200 bps25%3.6103.350-0.260
  • At c=1 the gap is exactly zero at every size. The preset wQ vectors (Curve Presets) are antisymmetric about x=5000, so their density median is x*=5000, the traverse starts there at ψ=0, and a symmetric inventory state gives symmetric slippage. The asymmetry is a property of the inventory state, not of the curve. On an asymmetric curve x* moves and the zero-gap point moves with it, since the anchor is the median and not the domain midpoint (Liquidity Shaping §5.1).
  • Off-center and unclipped, the gap is nonzero and correctly signed. At c=0.85 and 25% of liabilities, the direction that worsens inventory pays 5.710 bps against 5.100 bps for the direction that heals it. The 0.610 bps gap is about 12% of the 5.16 bps half-spread.
  • Above parity the sign flips: at c=1.15 and 1.30 the buy side is now the healing direction and pays less.
  • The -2.180 row is not a discount, it is the buy-side traverse hitting the domain clip at x=BPS. Past that point marginal impact is zero. See §2.5.
  • The level dominates. At c=0.70 and 1% of liabilities the skew offset is 10.880 bps against 0.29 bps of traverse, so the mid, not the curve, is what moves the executed price off the mark.
  • Above parity a round trip costs the same in either order: the skew offset cancels, and what remains is the spread plus two traverse terms. Below parity the sell side is capped at the mark, so a round trip also pays the offset once.
The mechanism. The intuitive version (“the depleted side traverses further”) is wrong for a single leg, because depth is per-asset, not per-side: `profileAsset = isUpward ? fromto (PricingLib.sol`), so both directions of one edge use the same child, the same curve and the same depth. Single-leg asymmetry comes from three things instead:
  1. the off-center start x0=clamp(x*+50ψ,0,BPS), where x* is the curve’s density median from the header and equals 5000 on every installed preset (_skewToDepth, PricingLib.sol);
  2. the convexity of the shape either side of that start, since a sell integrates [x0-v,x0] and a buy integrates [x0,x0+v] over a curve that is not locally linear;
  3. domain clipping at x=0 and x=BPS (PricingLib.sol).

The “depleted side traverses further” intuition does hold one level up, on a two-leg spoke-to-spoke path, where the two directions traverse different assets’ curves at different depths.

A round trip pays the full spread S once (Spread & Fees §5.5) plus two traverse terms that differ off par, plus the toll on whichever direction drains a walled leg.


2. Price impact: deterministic

Impact is knowable before execution: a pure function of on-chain state the quote already reads.

2.1. AIMM

Γ(q)=PBPS+y¯PBPS+y(x0),y¯=1x1-x0∫x0x1yscaled(x)dx

with x0=clamp(x*+50ψ,0,BPS) (x*=5000 on every installed preset), x1=x0∓vf, and vf=min(BPS,⌊q·BPS/D⌋), all as defined in Liquidity Shaping §8.

D is the leg’s raw reserves. uint256 depth = reserves == 0 ? 1 : uint256(reserves) in PricingLib.quoteSwap and _priceEdgeHop: the zero-guard exists only because the traverse divides by it. There is no coverage-dependent adjustment on top, and no “effective” or “virtual” depth anywhere in the decomposition. Impact therefore scales with 1/R and nothing else: at a given size, a thinner leg is a deeper traverse, with no state-dependent discount in either direction. A denominator that grew as coverage fell would shorten the traverse on the leg an informed flow is draining, which is how a two-block round trip becomes trader-positive: an LP leak, not a depth benefit.

Three structural consequences:

  • Impact is bounded. The whole book spans ±Hκ/dispRef, with H the curve’s half-swing at its reference dispersion. No trade of any size can move the price beyond that edge, because x clamps at the domain edge and vf clamps at BPS.
  • Impact is zero below the quantum. vf is an integer count of bps, so any trade smaller than D/BPS executes exactly at the mid.
  • Impact is asymmetric. x0≠x* whenever the leg is off parity, so the sell side and the buy side of the same leg have different remaining room. See Liquidity Shaping §5.2.

2.2. Against a constant-product pool

For x·y=k, executing q against reserve R gives exec/spot=1/(1+q/R), so impact is q/(R+q) and grows without bound. AIMM’s impact is bounded by the book: it spans ±Hκ/dispRef (§2.1) end to end and cannot charge more, while a constant-product pool charges 99 bp at 1% of depth, 909 bp at 10% and 1,667 bp at 20%. The defense against adverse selection sits in the spread, the coverage toll and the oracle push policy rather than in curvature. A constant-product pool prices size. AIMM prices information; size is cheap until the reserve binds.

2.3. When the reserve binds

Beyond the curve, two hard limits apply and neither is a smooth impact term:

  • PricingLib._legScaleOut clamps the delivering leg’s output to its reserves. A size past the coverage cap (§2.4) reverts instead of filling short. On a fully covered leg (R=L) a full-reserve output overshoots the cap by κcovL/(κcov+BPS), which exceeds the 2% of L slack, so the quote reverts instead of clamping once κcov>204 bps (BPS·200/(BPS-200), COV_OVERSHOOT_BPS = 200).
  • PoolIOLib.settle (PoolIOLib.sol) reverts InsufficientAmount when Rliq=R-invested cannot cover amountOut + minLiquidity. This is the failure that governs at settlement.

2.4. The coverage toll

When the output leg is coverage-walled (every listed leg, hub included) a drain below parity pays a convex charge that diverges as coverage goes to 0, so the marginal cost of the last of a reserve is unbounded even though the curve impact is bounded. This, not the spline, is what stops a drain. The quote also clamps gross output at the toll’s argmax and refuses a size more than 2% of L past it, in getSwapQuote and swap_qe alike. Formula, clamps and size cap: Inventory Management §6.

2.5. The buy-side traverse saturates, and depth cannot stop a drain

A consequence of the bounded book, not a defect.

PricingLib._traverseCurve clamps the volume fraction at BPS and the buy-side end depth at BPS. Once x1 reaches BPS the averaged offset over [x0,BPS] stops changing with size, so marginal price impact past that clip is exactly zero, and the saturated value falls as the pool depletes, because a higher x0 leaves a shorter interval to average over. The clip is on the spline domain, not on liquidity, so the clip percentages below are percentages of raw reserves (§2.1), the only depth quantity there is.

CoverageClips at (% of reserves)
c=1.0050%
c=0.8535%
c=0.7020%

Draining the entire reserve costs, in traverse plus half-spread, at most the book’s edge Hκ/dispRef plus half the spread, and those two terms alone can never exceed that at any size. That is a bound on the bounded terms, not on the price. The coverage toll sits on top of it and is unbounded: PricingLib._covToll charges the drained output leg once c1<1, and its marginal cost diverges as c→0 at any listed κcov (§2.4). A drain that stays above parity pays at most that ceiling; one that crosses the peg also pays the toll, up to the size cap of §2.4.

The ceiling is correct for an oracle AMM. A pool that quotes the external mark must not price-discover; if size could move its quote without bound, the quote would stop being the mark. The consequence:

Depth cannot stop a drain. The curve is a bounded offset book, so no amount of size exhausts it. What stops one is the coverage toll and its size cap (§2.4), plus the minLiquidity floor (§2.3), which initAsset writes as 0 (PoolConfigLib.sol).

The wall dominates everything else on a drain: the toll is linear in κcov and unbounded in depletion, while the spread is a few bps (Parametrization §6.2).

The saturation offers no atomic profit: what bounds a drain is the coverage toll of §2.4 and its size cap, plus minLiquidity wherever a deploy config raises it above the initAsset zero.

2.6. Dispersion

Dispersion κ sets the vertical scale of the curve and therefore the magnitude of Γ:

κ=min(MAX_DISPERSION_PBPS,κmin+⌊σνBPS⌋)

PricingLib._calculateDispersion (PricingLib.sol). κmin = minDispersionPbps is the additive base, not a post-hoc clamp; only the protocol-wide MAX_DISPERSION_PBPS = 900000 ceiling clamps - there is no per-asset ceiling field.

At ν=10000 this reduces to κ=κmin+⌊σ⌋ pbps: impact IS volatility-adaptive, one-for-one with the feed’s σ above the floor. A ν below 1.0x makes the slope that fraction of σ, which keeps the book inside the interior swing cap (Parametrization §4.2). Full argument with numbers: Liquidity Shaping §6.4.


3. Slippage: stochastic

Slippage is the difference between the quote a taker saw and the price they got: the part that is not a function of the state the quote read.

3.1. Sources

SourceMechanismDirectionAIMM-specific note
Mark updateA keeper push lands between quote and inclusionEitherThe pool quotes off the keeper mark (MarkWordLib.mark), so a push moves the whole quote, not just the tail
Inventory driftAnother swap changes R, hence ψ and x0EitherDiscrete: x0 moves in steps of 50 bps of the domain
Coverage driftAnother swap changes c on the output legNegative if worsenedChanges t(q) convexly
Staleness rampFeed ages past the keeper grace, widening sNegativeDeterministic given the age; see §3.2
Front-runningSearcher trades aheadNegativeBounded by the book: a searcher cannot push the quote past ±Hκ/dispRef
SandwichBot trades both sidesNegativeRound trips lose (Liquidity Shaping §8.3), so the sandwicher pays too
Reserve clampOutput reserve drained by an earlier txNegative§2.3
HaltFeed goes stale, confident, or depeggedRevertFails closed, never degrades silently

3.2. The staleness ramp is not random

The spread carries a keeper-lag term (PricingLib._staleTerm, PricingLib.sol):

Ustale=zστBPS,z=472,τ=max(0,a-min(T/2,30))

with a the feed’s age in seconds, T its ttl, and z = STALE_Z (PricingLib.sol). τ is 0 inside the grace and the surcharge grows as τ beyond it. A one-leg path pays that leg’s own floor alone; a two-leg cross sums both spokes’ floors (Spread & Fees §9). A taker who quotes and then waits sees the spread widen, predictably.

Past the hard TTL the feed does not widen further: FeedMathLib.gate reverts, and the leg does not quote at all.

3.3. Positive slippage

Slippage can be favorable if the market moves your way between quote and inclusion. On a public mempool it rarely reaches the trader: searchers monitor pending transactions, favorable movements are captured by backrunners, and the unfavorable ones pass through. That asymmetry is the case for private order flow.


4. Protections

4.1. minAmountOut

function swap_qe( address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, // slippage bound address recipient, uint256 deadline // staleness bound; type(uint256).max opts out ) external payable returns (uint256 out);

Pool.sol. The single state-mutating swap entry is Pool.swap_qe, which calls PricingLib.swapExactIn. After the quote it checks, in order:

if (out == 0) revert Err.ZeroValue(); if (out < minAmountOut) revert Err.ThresholdViolation(out, minAmountOut); _settleAndPay(...); // PoolIOLib.settle, then the reference-band checks, then the payout

A violated bound reverts before any ledger write and before the reference-band reads, so it costs quote gas, not settlement gas. A size past the output leg’s coverage cap fails earlier, inside the quote (§2.4).

Sizing the bound, given §1.1: the fee and skew are visible in the quote at zero size, so a bound set from the quote rather than from the mark can be tight.

Tolerance below quoteUse
0.05% to 0.1%Stable legs, fresh feed, private order flow
0.1% to 0.5%Stable legs, public mempool
0.5% to 1%Volatile legs, normal conditions
1% to 3%Volatile legs, stressed tape; expect the spread term to move, not the curve

4.2. deadline

Pool.beforeDeadline (Pool.sol) reverts Err.Expired when block.timestamp > deadline. The deadline is inclusive (equality succeeds), there is no zero sentinel, and the opt-out is type(uint256).max. Every value-moving entry point carries it: swap_qe, withdraw, withdrawTo, swapLiability. deposit and donate do not, because they mint at the current index against no minimum output.

4.3. MEV-protected RPCs

A private RPC hides the transaction from the public mempool, which is where front-running and sandwiching start. Providers and endpoints per chain: MEV protection.

4.4. Timing and sizing

StrategyEffectTrade-off
Trade shortly after a keeper pushMinimizes Ustale and mark driftRequires watching the feed
Keep size below D/BPSExactly zero curve impactOnly useful for small orders
Split across blocksLets ψ and c recover between clipsHigher gas; each clip pays s/2 again
Trade the coverage-restoring directionFavorable side of Pskew, near-zero tDirection is dictated by pool state, not preference
Avoid draining a walled leg below parityAvoids the convex toll entirelyCheck c on the output asset first

Splitting usually costs more: the spread s is charged per swap and is size-independent, while the curve impact is the small term. On a stable leg, splitting a trade into n clips multiplies the dominant cost by n and only saves the minor one.


5. Practical guidance

5.1. Takers

  1. Set minAmountOut from the quote, not from the mark. The gap between them is the skew plus the fee and is not slippage.
  2. Set a real deadline. type(uint256).max opts out of the only staleness bound the caller controls.
  3. Check the output leg’s coverage before a large trade. The convex toll is the term that grows without bound.
  4. On a large size, a ThresholdViolation from getSwapQuote is the output leg’s coverage cap (§2.4), not slippage.
  5. Use a MEV-protected RPC wherever the chain has a public mempool.

5.2. Integrators

  1. Quote at display time with Pool.getSwapQuote; it is a view and takes no cache writes.
  2. Show the decomposition, not one number: skew, curve, toll and fee are four different things with four different risk profiles. SwapQuote exposes markPrice, midPrice, covToll, spreadPbps, protoFee and lpFee so this is possible without re-deriving anything.
  3. Credit 100% of the fee to tokenOut. There is no input-leg fee (IPool.sol).
  4. The realized fee is spreadPbps/2 of the pre-fee output, not spreadPbps.
  5. Handle Err.Expired, Err.ThresholdViolation, Err.ZeroValue, Err.InsufficientAmount, Err.BaseDepegged and Err.FeatureDisabled distinctly. They mean different things and only two of them are worth retrying.

5.3. Large traders

  1. Intent-based systems (CoW Protocol, UniswapX) for execution above the reserve scale.
  2. Private mempools or direct builder connections.
  3. Time-weight across blocks only if the spread cost of extra clips is smaller than the impact saved, which on a stable leg it usually is not (§4.4).
  4. Monitor markout against midPrice, not against markPrice. The mid is the book’s center at quote time.

6. Research and further reading