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 the trade size:
| Term | Definition | Depends on size | Owned by |
|---|---|---|---|
| , mid over mark | Inventory skew offset, evaluated at zero volume | No | Inventory Management §3, Liquidity Shaping §5 |
| Curve traversal, the VWAP over the interval the trade sweeps | Yes | Liquidity Shaping §8 | |
| Convex coverage toll on the drained output leg | Yes | Inventory Management §6 | |
= SwapQuote.spreadPbps | Path spread; the realized fee is of the pre-fee output | No | Spread & Fees |
Price impact in the AIMM sense is alone, together with 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
, the extractable value, from ,
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.
| Term | Is | Kind | Symmetric in direction? | Site |
|---|---|---|---|---|
| mark | the oracle price, as attested by the keeper in the child’s anchor unit | level | yes | MarkWordLib.mark, consumed at PricingLib.sol |
| skew | the signed dimensionless inventory index, int8 in , a function of coverage alone. An input, never a price | index | n/a | computeInventorySkew, PricingLib.sol |
| skew offset | the price displacement that index produces, in bps | level | at , 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 |
| mid | the mark carrying that offset, evaluated at zero volume | level | yes | _legMid, PricingLib.sol |
| price impact (= curve traversal) | the cost of traversing the liquidity shape from the skew anchor, | one-directional cost | no, see §1.2 | _traverseCurve, PricingLib.sol |
| spread | the round-trip fee width, SwapQuote.spreadPbps, symmetric about the mid | width | yes (Spread & Fees §4) | _pathSpread, PricingLib.sol |
| fee | what a swap pays: half the spread, once, on the output | charge | yes | PricingLib.sol |
| coverage toll | the convex charge on a drained output leg | one-directional, charge-only | no | _covToll, PricingLib.sol |
| slippage | the stochastic gap between the quote and the fill (§3), not a pool term at all | risk | n/a | minAmountOut, §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.
minFeePbpsis 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.
skewis 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
refBandBpsreference 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, 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 in bps. The skew column is the fixed law,
draining and filling, saturating at and ; and
are where the law clamps, not a configurable band, and no per-asset dial enters these rows.
Measurement note: the row reads / from the fixture. The shipped law gives exactly and at . 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 and rows predate the sell-arm mark cap (2026-09-11). At a sell now executes at , so those sell cells understate the cost by up to the skew offset. The rows are unaffected.
| skew | skew offset | notional | slip sell | slip buy | gap (buy sell) | ||
|---|---|---|---|---|---|---|---|
| 0.70 | 8000 | bps | 1% | 0.290 | 0.280 | ||
| 0.70 | 10% | 2.770 | 3.050 | ||||
| 0.70 | 25% | 6.600 | 4.420 | (buy clipped) | |||
| 0.85 | 6500 | bps | 1% | 0.210 | 0.210 | ||
| 0.85 | 10% | 2.070 | 2.160 | ||||
| 0.85 | 25% | 5.100 | 5.710 | ||||
| 1.00 | 5000 | 1% | 0.170 | 0.170 | |||
| 1.00 | 10% | 1.710 | 1.710 | ||||
| 1.00 | 25% | 4.360 | 4.360 | ||||
| 1.15 | 4300 | bps | 25% | 3.890 | 3.720 | ||
| 1.30 | 3500 | bps | 25% | 3.610 | 3.350 |
- At the gap is exactly zero at every size. The preset
wQvectors (Curve Presets) are antisymmetric about , so their density median is , the traverse starts there at , 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 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 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 and the buy side is now the healing direction and pays less.
- The row is not a discount, it is the buy-side traverse hitting the domain clip at . Past that point marginal impact is zero. See §2.5.
- The level dominates. At 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.
(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:- the off-center start , where
is the curve’s density median from the header and equals 5000 on every installed preset
(
_skewToDepth,PricingLib.sol); - the convexity of the shape either side of that start, since a sell integrates and a buy integrates over a curve that is not locally linear;
- domain clipping at and (
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 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
with ( on every installed preset), , and , all as defined in Liquidity Shaping §8.
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 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 , with the curve’s half-swing at its reference dispersion. No trade of any size can move the price beyond that edge, because clamps at the domain edge and clamps at BPS.
- Impact is zero below the quantum. is an integer count of bps, so any trade smaller than executes exactly at the mid.
- Impact is asymmetric. 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 , executing against reserve gives , so impact is and grows without bound. AIMM’s impact is bounded by the book: it spans (§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._legScaleOutclamps 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 () a full-reserve output overshoots the cap by , which exceeds the 2% of slack, so the quote reverts instead of clamping once bps (,COV_OVERSHOOT_BPS= 200).PoolIOLib.settle(PoolIOLib.sol) revertsInsufficientAmountwhen cannot coveramountOut + 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 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 reaches BPS the averaged offset over 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 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.
| Coverage | Clips at (% of reserves) |
|---|---|
| 50% | |
| 35% | |
| 20% |
Draining the entire reserve costs, in traverse plus half-spread, at most the book’s edge
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 , and its marginal cost diverges as
at any listed (§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
minLiquidityfloor (§2.3), whichinitAssetwrites as 0 (PoolConfigLib.sol).
The wall dominates everything else on a drain: the toll is linear in 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 :
PricingLib._calculateDispersion (PricingLib.sol).
= 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 this reduces to 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
| Source | Mechanism | Direction | AIMM-specific note |
|---|---|---|---|
| Mark update | A keeper push lands between quote and inclusion | Either | The pool quotes off the keeper mark (MarkWordLib.mark), so a push moves the whole quote, not just the tail |
| Inventory drift | Another swap changes , hence and | Either | Discrete: moves in steps of 50 bps of the domain |
| Coverage drift | Another swap changes on the output leg | Negative if worsened | Changes convexly |
| Staleness ramp | Feed ages past the keeper grace, widening | Negative | Deterministic given the age; see §3.2 |
| Front-running | Searcher trades ahead | Negative | Bounded by the book: a searcher cannot push the quote past |
| Sandwich | Bot trades both sides | Negative | Round trips lose (Liquidity Shaping §8.3), so the sandwicher pays too |
| Reserve clamp | Output reserve drained by an earlier tx | Negative | §2.3 |
| Halt | Feed goes stale, confident, or depegged | Revert | Fails closed, never degrades silently |
3.2. The staleness ramp is not random
The spread carries a keeper-lag term (PricingLib._staleTerm, PricingLib.sol):
with the feed’s age in seconds, its ttl, and = 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 payoutA 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 quote | Use |
|---|---|
| 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
| Strategy | Effect | Trade-off |
|---|---|---|
| Trade shortly after a keeper push | Minimizes and mark drift | Requires watching the feed |
| Keep size below | Exactly zero curve impact | Only useful for small orders |
| Split across blocks | Lets and recover between clips | Higher gas; each clip pays again |
| Trade the coverage-restoring direction | Favorable side of , near-zero | Direction is dictated by pool state, not preference |
| Avoid draining a walled leg below parity | Avoids the convex toll entirely | Check on the output asset first |
Splitting usually costs more: the spread is charged per swap and is size-independent, while the curve impact is the small term. On a stable leg, splitting a trade into clips multiplies the dominant cost by and only saves the minor one.
5. Practical guidance
5.1. Takers
- Set
minAmountOutfrom the quote, not from the mark. The gap between them is the skew plus the fee and is not slippage. - Set a real
deadline.type(uint256).maxopts out of the only staleness bound the caller controls. - Check the output leg’s coverage before a large trade. The convex toll is the term that grows without bound.
- On a large size, a
ThresholdViolationfromgetSwapQuoteis the output leg’s coverage cap (§2.4), not slippage. - Use a MEV-protected RPC wherever the chain has a public mempool.
5.2. Integrators
- Quote at display time with
Pool.getSwapQuote; it is aviewand takes no cache writes. - Show the decomposition, not one number: skew, curve, toll and fee are four different things with
four different risk profiles.
SwapQuoteexposesmarkPrice,midPrice,covToll,spreadPbps,protoFeeandlpFeeso this is possible without re-deriving anything. - Credit 100% of the fee to
tokenOut. There is no input-leg fee (IPool.sol). - The realized fee is
spreadPbps/2of the pre-fee output, notspreadPbps. - Handle
Err.Expired,Err.ThresholdViolation,Err.ZeroValue,Err.InsufficientAmount,Err.BaseDepeggedandErr.FeatureDisableddistinctly. They mean different things and only two of them are worth retrying.
5.3. Large traders
- Intent-based systems (CoW Protocol, UniswapX) for execution above the reserve scale.
- Private mempools or direct builder connections.
- 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).
- Monitor markout against
midPrice, not againstmarkPrice. The mid is the book’s center at quote time.
6. Research and further reading
- Flashbots: MEV in Eth2: MEV taxonomy and measurement
- Private MEV Protection RPCs: effectiveness analysis
- MEV on Polygon
- Cross-chain Sandwich Attacks
- High-Frequency Trading on DEXes
- UniswapX Price Improvement
- Protected Order Flow Relay
- Flashbots Docs, Jito Documentation
7. Related documentation
- 1.1.1. Inventory Management: and the coverage toll
- 1.1.2. Liquidity Shaping: , the curve and dispersion
- 1.1.3. Anchor Path Pricing: how the terms compose over multiple legs
- 1.1.4. Spread & Fees: and its components
- 1.1.6. Toxic Flow Mitigation
- 1.1.7. Parametrization
- Flow Guards
- Glossary: Slippage, Price Impact, MEV-Protected RPC