Inventory management

AIMM books every asset separately: each leg carries its own reserves, its own LP liabilities and its own risk parameters, with no pool-wide pricing invariant tying them together. The ratio of the two, coverage, is the single state variable the pricing stack reads. LP settlement is the one pool-wide quantity: every mint and exit settles at the pool rate C (§5).


0. Units and notation

Every quantity below is an on-chain integer. Fixed-point bases (ConstantsLib.sol):

SymbolBaseValueUsed for
WAD1e181018coverage, the pool rate C, prices
BPS1e41040.01% units: kappa, spline x-domain
PBPS1e61060.0001% units: dispersion, spread

All division is Solidity integer division, truncating toward zero. Truncation is called out where it changes an economic outcome. Symbol collision: κ denotes dispersion in Liquidity Shaping and Spread & Fees; this document writes the convex coverage-wall strength as κcov (kappaCovBps) to keep the two apart.

Per-parameter tables: Parametrization. Deployed values: pool composition.


1. Overview

AIMM keeps a reserve and a liability ledger per asset (asset-liability management):

  • Reserves R (Asset.reserves, uint128, token units): tokens the pool holds.
  • Liabilities L (Asset.liabilities, uint128, token units): LP claims on that asset.
  • Coverage c=R/L.

There is no pool-wide pricing invariant. Each asset carries its own (R,L) and its own risk parameters, and the price is anchored to an external oracle mark rather than to a reserve ratio. Coverage enters pricing at exactly two points, and LP settlement at one:

MechanismFunctionEffect
Inventory skew ψPricingLib.computeInventorySkewShifts the quote center along the spline
Coverage toll tPricingLib._covTollConvex charge on draining an under-covered leg
In-kind exit boundPoolLiquidityLib.exitMuCaps a same-asset exit at min(c_leg, C) (§5)

It does not enter the depth axis: the denominator converting trade size into spline domain is the leg’s raw reserves (§4). Coverage moves the quote center and charges the drain. It does not add depth the leg does not hold.

1.1. Beyond pool-wise invariants

Traditional AMMs enforce a pool-wise invariant: x·y=k; Curve StableSwap’s Ann∑ixi+D=ADnn+Dn+1/(nn∏ixi); Balancer’s ∏ixiwi=k; Orbital/CCMM’s spherical ∑i(r-xi)2=r2. Each of those is simultaneously the pricing rule and the solvency rule, so N-asset generalization requires new geometry. AIMM separates them:

  • Pricing comes from the oracle mark, the inventory skew, and a chosen density profile.
  • Solvency is per-asset (R,L) bookkeeping, settled for LPs at one pool-level rate C (§5).

Adding an asset therefore adds a row, not a dimension to an invariant.


2. Coverage ratio

2.1. Definition

c=R·WADL

PricingLib.calculateCoverage returns exactly this, in WAD, so c=1018 is 100% coverage. Two edge behaviors:

  • L=0 returns type(uint256).max, not a revert. Every consumer treats that as “infinitely covered”.
  • The division truncates, so the stored c is a floor. A leg is never reported as better covered than it is.

R is the economic reserve, the full dual-ledger book R=Rliq+Rinv when a yield hook is invested. Pricing and coverage always use full R; executable cash for swap/flash/push is Rliq only, checked separately at settlement (PoolIOLib.settle, PoolIOLib.sol). See Pool Hooks.

2.2. Key thresholds

The saturation points are protocol constants, not configuration: the skew law hard-codes c=1/2 and c=2 (§3), identically on every leg of every pool.

CoverageStateSkewBehavior
c≥2Overcollateralized-100Max discount on the asset
c=1Equilibrium0Quote centered on the mark
c≤1/2Undercollateralized+100Max premium on the asset

Both are constants, so no write can move the slope or reprice an open book by shifting where the arms saturate. Why those two points are the only admissible ones: §3.2.1.


3. Inventory skew (coverage → skew)

ψ maps to a position on the spline in Liquidity Shaping §5.

3.1. Exact formula

PricingLib.computeInventorySkew, in order:

ψ=-100L=0+100c≤1/2-100c≥2+⌊200(1-c)⌋1/2<c<1(drainingarm)-⌊100(c-1)⌋1≤c<2(fillingarm)

with c=R·WAD/L from §2.1. Four properties of that law:

  • No inputs beyond coverage: no per-asset parameter, no oracle read.
  • Return type is int8, so ψ∈[-100,+100] by both clamp and type.
  • The magnitude truncates toward zero: a computed 14.62 becomes 14, never 15.
  • Positive ψ means undercovered: buyers of the asset pay a premium over the mark, and sellers are paid at most the mark.

The empty-liability sentinel is not a rounding case. L=0 returns -100, the maximum discount, not 0: a leg with reserves and no LP claims is surplus, so the pool quotes it at the maximum discount.

The formula prices imbalance; the roster is what makes it net out. The law charges a premium on the side that drains and a discount on the side that refills. If the discount is never taken, the leg sits at its clamp holding the asset that was sold into it. Inside a core of correlated legs the two sides usually meet: the flow that refills one leg drains another, so a single trade pays skew on the leg it takes and restores coverage on the leg it delivers. Inventory mean-reverts instead of accumulating. Correlation makes that likely, not certain: legs that move together can leave the whole core displaced the same way at once. Why cores are cut by asset class: Pool Composition.

3.2. The two arms are not symmetric

The draining arm has slope 200, the filling arm slope 100. The asymmetry is a conservation bound. Merging the two into one 200(1-c) reopens a round-trip refund.

Both slopes come from the same requirement: a closed round trip must not refund more impact than the outbound leg charged. The skew step and the volume traverse advance the same spline x-coordinate, so the skew slope is pinned against the traverse’s step. On the draining side the traverse already out-steps and 200 is admissible. On the filling side it under-steps, and a 200 slope there would put the return leg of a ping-pong ahead of the outbound one by exactly the gap, which a trader extracts from LPs. Asserted by ImpactConservation.t.sol.

CoverageψNote
≤50%+100saturated
75%+50draining arm
90%+20draining arm
100%0centered on the mark
120%-20filling arm
150%-50filling arm
≥200%-100saturated

The arms meet their clamps exactly, so the whole [0.5,2.0] range carries information and neither arm has a dead zone.

3.2.1. Why saturation sits at 1/2 and 2

Write the saturation points as [1/k,k]. Two independent studies evaluated k as a per-asset dial. Both rejected it.

k cannot reach the draining arm at all. The round-trip conservation bound of §3.2 pins the draining slope at 200 for every k. All k could ever do is soften the filling arm, whose slope would be 100/(k-1).

k=2 is the unique admissible value, pinned from both sides:

  • Continuity. The draining arm reaches ψ=+100 at c=1/2. Saturating at 1/k>1/2 (that is, k<2) clamps the arm early, at 200(1-1/k)<100, so ψ jumps at the saturation point. Measured leak across such a discontinuity: up to +583 pbps.
  • Conservation. A filling slope 100/(k-1) above 100 lets a ping-pong round trip refund more impact than it charged (§3.2), which requires k≥2.

k<2 is discontinuous and k>2 only weakens inventory control. k=2 is the single point satisfying both, which is why 200, 100 and the clamps at c≤1/2 and c≥2 are constants rather than writable fields.

Empirical support. k=2 is also the LVR minimum on all three real tapes (ETH, BTC, SOL). The coverage floor is fee-determined: a drain stops where the skew’s mid displacement equals the spread, which puts cfloor≈0.85 on a volatile leg and 0.82 on a stable one independently of shock size. The [0.5,2] clamp region is therefore unreachable in practice. Real-tape dwell below c=0.5: 0.14% to 0.70% of the time for an unwalled volatile leg, and 0.000% of 3.5 years for a walled stable. A per-asset saturation point would be a dial on a region the pool does not visit.

3.3. Asymmetric book

The book is asymmetric in normal operation. Legs rarely sit at ψ=0, so the tradeable spline domain is split unevenly about the center before any other mechanism acts (see Liquidity Shaping §5). Each filling-arm row displaces half as far as the same distance from par displaces on the draining arm, which is the conservation bound of §3.2.

3.4. Relation to Avellaneda-Stoikov

Avellaneda-Stoikov displace the quote by δAS=γqσ2, linear in inventory q and scaled by their risk-aversion coefficient γ and variance σ2. AIMM keeps the linearity, replaces q with the coverage ratio c, and moves both the risk-aversion multiplier and σ out of the skew entirely. What remains is an inventory-driven mid shift, first instantiated on-chain by DODO PMM’s oracle-anchored proactive maker, driven by a coverage metric rather than by a slippage-on-invariant.

Risk aversion is not a dial on the skew: the round-trip conservation bound of §3.2 determines both slopes uniquely. AIMM expresses it instead through vegaBps (vega), which widens the quoting band against σ, and through minFeePbps, which floors the spread.

σ enters two other places: the dispersion that scales the density curve (Liquidity Shaping §6) and the volatility term of the spread (Spread & Fees). The separation lets the level shift and the width be tuned independently, and keeps computeInventorySkew free of any oracle read.

See Foundations §2.4 for the AS reference.


4. The depth denominator

Canonical. Liquidity Shaping §7, Spread & Fees §6.5 and Slippage & Price Impact §2.1 restate the result only.

4.1. What it is

The denominator converting a trade size into a fraction of the spline domain is the leg’s raw reserves, with a zero-guard and nothing else:

uint256 depth = reserves == 0 ? 1 : uint256(reserves); // PricingLib.quoteSwap, PricingLib._priceEdgeHop volumeFraction = amountIn * BPS / depth;

It is expressed in the profile asset’s own token units, so the x-axis is anchor-free: it depends neither on which asset the leg is anchored to nor on any price. The guard exists because _traverseCurve divides by it; 1 wei is not a liquidity claim, it is a division floor.

4.2. Properties

PointValue
R=0D=1 wei (division guard)
R>0, any coverageD=R

Monotone in reserves, independent of L, independent of c, and constant per swap. The real depth lever is the reserve balance itself.

Coverage does not reach this axis: an under-covered leg gets no virtual depth. Subsidizing the denominator on a leg an informed flow is draining would shrink the traversed spline interval and so reduce the impact charged on the drain, paying a trader to cycle the leg that needs reserves. Coverage is priced by the two mechanisms that charge instead: the skew shifts the mid against the deficit (§3) and the convex wall tolls the drain (§6).


5. LP settlement at the pool rate

Coverage is per leg; LP solvency is not. Every mint, exit and hook write-down settles at one pool-level rate. The legs share one balance sheet: a cross exit burns the full face from the source leg’s liabilities and pays out of another leg’s reserves, so a rate read off one leg is a rate the exiter moves by exiting. The only rate an exit cannot move is one whose numerator and denominator fall by the same factor.

5.1. Definition

PoolSolvencyLib.solvency:

C=∑kRk^mk∑kLkmk,Rk^=min(Rk,investedk+balancek)

summed over the pool’s leg roster (Pool.getLegs()), with mk = PricingLib.markToBaseWad (the leg’s mark in base units) and every amount rescaled from the leg’s decimals to the base’s. WAD, uncapped: an over-solvent pool reads C>1 and pays it.

  • Physical backing cap. Rk^ caps the stored book at what the pool holds, so a negative rebase, an on-top transfer tax or an issuer clawback deflates C instead of minting and paying against units that are not there (test_backing_cap_deflates_the_rate_on_an_unsynced_slash). invested is counted, so a hooked leg’s deployed tranche does not read as missing.
  • Gateable, not in band. The rate is usable only if every roster leg with a non-empty book passes FeedMathLib.gate (TTL, non-zero mark, confidence at or below MAX_CONFIDENCE_HALT_BPS); one unusable mark returns ok = false and no rate. It does not require every leg inside its reference band: one leg out of band leaves deposits and cross exits live (test_an_out_of_band_leg_does_not_freeze_the_pool). The band still guards the legs a payout prices through, at the sites that already arm it. A leg at reserves == liabilities == 0 is skipped before its oracle read, so a dead feed on an emptied leg freezes nothing.
  • Edges. An empty roster reads C=1 with no oracle call (§5.4). A zero claim book is a rate only over zero value: A > 0, B == 0 is a stranded book, reachable only by a pre-seed legacy leg’s full same-asset exit at μ<1, and it reads (1, ok = false) so no depositor can mint face amt and claim A + amt. The owner’s own deposit re-seeds it at par and holds the residue (test_stranded_surplus_is_not_a_mint_rate_until_the_owner_reseeds). donate refuses a leg with no liabilities, so it can never produce that state (testFuzz_B_zero_implies_A_zero).
  • Never on the swap or flash path. Quoting reads the leg’s own c exactly as §2 to §4 and §6 describe. C is read only by LP entrypoints and hookWriteDown, and the per-leg wall floor c*=κcov/(κcov+BPS) is untouched.
  • Reads. Pool.poolSolvencyWad() returns (cWad, ok). Every LP entrypoint on an armed pool emits SolvencyUpdated(cWad, navBase, claimBase), numerator and denominator in base units.

5.2. Where the rate is applied

EntrypointRateWith a roster mark unusable
depositmints face amt · 1e18 / Creverts FeedUnavailable
withdraw (same asset)pays face · min(c_leg, C), less the exit tollpays face · min(c_leg, C_lower), less the exit toll
withdrawTo (cross asset)converts face · C along the anchor pathreverts FeedUnavailable
swapLiabilityconverts liabIn · C along the anchor path; the token output is credited as face amountOut · 1e18 / Creverts FeedUnavailable
donatecredits amt to reserves and face amt · 1e18 / C to liabilities, raises the indexreverts FeedUnavailable
hookCreditYieldcredits the realised amount and face amount · 1e18 / C, rate-bucketedreverts FeedUnavailable
hookWriteDowncuts liabilities by ceil(loss · 1e18 / C)reverts FeedUnavailable

Exit toll. A same-asset exit pays half of (minFeePbps + staleness premium) on the payout, rounded up and split with the protocol at protoSharePct (PoolLiquidityLib._exitFee, PricingLib.exitSpreadPbps). A leg whose own feed is unusable pays the staleness premium saturated at its TTL, so going dark never cheapens the exit. A swap pays half of the full path spread, which adds the σ·ν and confidence terms (Spread & Fees §3.1), so exiting ahead of a predictable push and re-depositing after the cooldown costs less than a swap by half of those two terms. A cross exit and swapLiability pay the path spread inside their quote instead. previewWithdraw returns the net payout.

Every credit is booked at C for the same reason the mint is: booked at par, a credit at C>1 buys face worth C per unit paid and moves C against every other leg; at C it is neutral, C′=(A+am)/(B+am/C)=C. The outage column is the whole outage policy: no path degrades to par, and the only settlement that survives an unusable mark is the same-asset exit at the lower bound Clower.

A deposit leaves C unchanged. Mint-at-C adds a of value to the numerator and a of value to the denominator at rate C, so the ratio holds, with floor rounding toward the pool (testFuzz_deposit_preserves_the_rate). At C>1 the depositor buys less face than it pays; at C<1, more.

min(c_leg, C) is a delivery bound, not a fence. A leg cannot hand out more of its own token than it holds. Paying face · C out of a drained leg until it empties is a run; allocating cleg pro-rata is race-free. When cleg<C the pool is short of that token, not of value, and the rest of the claim is reachable through withdrawTo, which sources another token and pays that conversion’s spread. At μ=C the exit leaves C unchanged; at μ=cleg<C it leaves value behind and raises it (testFuzz_same_asset_exit_never_lowers_the_rate). μ exceeds 1 when both the pool and the leg are over-covered.

The degraded rate never pays more than the healthy one. With a roster mark unusable the same-asset exit still settles, at min(c_leg, C_lower). Clower is the lesser of the rate over the usable legs and each unusable leg’s token-unit coverage ck: C is a mediant of those numbers, so their minimum bounds it whatever the unknown marks are. A bare 1 would pay more for an outage than for a healthy pool whenever C<1, and stale feeds correlate with the events that push C below 1 (test_degraded_never_pays_more_than_the_good_path). A stale stamp would not do either: one taken before a dark leg’s loss lets first movers exit ahead of it. Deposits and cross conversions have no fallback: both credit claims against a pool whose value is unknown.

A cross exit cannot be sliced. PoolHooksLib.requireBacked runs on the source leg first. The payout is priced off face · C, never off the source leg’s own coverage, which rises with every slice because the cross branch burns Lfrom and never touches Rfrom (test_cross_withdraw_50_slices_no_longer_beats_one_shot). The conversion is capped at the oracle mark path, min(amountOut, fair · markPrice · 10^(d_to - d_from)) (PoolLiquidityLib._markCap): the inventory skew is one-sided on an under-covered leg and a one-way LP conversion would otherwise monetise it. The output leg’s coverage is not charged again. One term can still move C: the output leg’s LP fee is credited as face lpFee · 1e18 / g at the cached lastGoodC g (PoolLiquidityLib._credit; undivided when g≤1, so a stale g<1 never over-books). It is neutral to C when g=C and drifts only by the gap between the cache and the live rate. swapLiability is the same conversion stopping short of a payout: the quote output is a token amount and liabilities is a face book, so the credit is amountOut · 1e18 / C, which re-denominates to faceinmin/mout with no second haircut.

A write-down is fenced to its leg. hookWriteDown cuts liabilities by ceil(cut · 1e18 / C), capped at the book, and scales the index with them, so

C′=A-cut·mB-cut·m/C=C

The leg whose venue lost pays and no other leg’s claim moves (testFuzz_write_down_leaves_the_rate_unmoved). The remaining liability is floored at ceil(liabBefore / idx), so the index stays at or above 1 and a leg survives losing its whole book (test_total_loss_leaves_the_leg_live_and_depositable); the sliver that floor keeps lands in C. The write-down reverts FeedUnavailable while a roster mark is unusable, and the hook keeper retries: a write-down at an unknown rate is a write-down of the wrong size.

5.3. What pooling costs, and what bounds it

A genuine NAV loss lands in C and every LP in the pool pays pro-rata, stable-leg LPs included. The trade-off is a certain pro-rata loss instead of a first-exit advantage. Premium routes the same way. The coverage toll is retained in the output reserve with no liability credit (§6), so it raises C for every LP, and each leg’s κcov sizes the toll to that leg’s risk.

Two RiskConfig fields bound what one leg can put into C, both checked after the liability write at deposit, donate and the credited leg of swapLiability, and both soft: they bind when a claim is credited and a leg’s notional drifts with its mark afterwards (test_a_mark_move_carries_a_leg_past_its_cap_without_reverting).

  • maxLiabWeightBps caps the leg’s share of the claim book (WeightCapExceeded); 0 or 10,000 disables it (a leg’s notional is part of the claim book, so its weight never exceeds 10,000).
  • depositCapCode caps the leg’s notional in whole base tokens, decoded as m·10^e from m<<4|e (Err.ExcessiveAmount). Every writer refuses m == 0, so 0 survives only on a pre-upgrade leg and means uncapped. hookCreditYield is not capped: its per-day bucket bounds the drift and a cap read would revert syncs on a stale mark.

Both are written through RiskConfig: instantly when they tighten (setRiskConfigTighten), on the queued UPDATE_RISK lane otherwise, with a cap raise bounded at 10× per op (Admin §5.3). A capped leg whose own mark is unusable refuses the credit (test_stale_mark_fails_closed_on_a_capped_leg).

Pooling also shrinks a self-drainer’s share of its own loss, because the denominator is the whole pool. test_self_drain_then_cross_exit_never_profits, test_mixed_self_drain_then_cross_exit_never_profits and test_fuzz_roundtrip_never_profits are that defence.

5.4. Arming

A pool created on the current implementation is armed at initialize, and every initAsset appends its leg, so the roster is always complete (test_roster_is_complete_by_construction). A clone that predates the roster reads C=1 until the owner’s BACKFILL_LEGS op executes (Admin.executeBackfillLegs, GOVERNANCE tier, one-shot, emits LegsBackfilled(legs)). The roster it writes must name only listed assets, each once, and include the base. Unarmed, a same-asset exit pays face · min(c_leg, 1), and cross exits and liability swaps convert full face.


6. Convex coverage wall

Canonical derivation. Spread & Fees §6 owns the settlement order and the charge semantics; this section owns the potential and the clamps.

PricingLib._covToll is the second coverage mechanism. Unlike the skew it is a charge, not a level shift. It mirrors the reference simulator’s cov_q.

6.1. Potential and toll

Q(c)=lnc-c+1(≤0,max0atc=1,concave,→-∞asc→0)

c0=min(1,R·WADL),c1=min(1,(R-g)·WADL),t=min(g,⌈(Q(c0)-Q(c1))·κcov·LBPS·WAD⌉)

where g is the gross output before fees, in output-token units, and κcov = RiskConfig.kappaCovBps. Charged only when Q(c0)-Q(c1)>0; the toll is withheld from the gross output and retained in the output reserve, so it accrues to LPs and is never a mark shift.

6.2. Why the clamps are there

  • min(c,1) on both endpoints. Q is non-monotonic: it decreases on both sides of c=1. A raw endpoint difference lets a drain that starts over-covered cross the peg and land below it with ΔQ≤0, paying zero toll. Clamping restricts Q to its increasing branch so the toll prices exactly the below-peg deficit and the over-peg portion stays free.
  • Gross is capped at the argmax. Net output g-t(g) peaks where the leg lands on its floor c*=κcov/(κcov+BPS), at G*=R-κcovL/(κcov+BPS) (PricingLib._grossCap). _settleQuote clamps a gross up to 2% of L past G* (COV_OVERSHOOT_BPS) to G*, and reverts ThresholdViolation(gross, G*) beyond that or when G*=0. Swap, cross exit and swapLiability share this quote, so getSwapQuote refuses the same sizes. With κ ≥ 50, G*<R: the g≥R arm of _covToll is reached only on a dust book.
  • Charge-only, no rebate ledger. A coverage-restoring trade drains the healthy leg, where c≈1 and Q≈0, so it pays approximately nothing. A round trip therefore strictly loses, which is the LP-safety property.

6.3. Round-trip behavior

Finite differences of Q telescope to zero over any closed reserve loop at constant L. Since LP fees accrue into liabilities mid-loop (§7), the telescoping is only approximate. The residual is pool-favorable: higher L gives lower c gives more toll.

The wall is output-only: a same-pool spoke→spoke swap never outputs the hub, while a cross-pool hop that takes the hub out pays hub κcov.

Every listed asset, the hub included, carries 50≤κcov≤BPS; PoolConfigLib.requireNeverDepletable enforces it at every writer. The per-class ladder in Parametrization §6.2 is sized so the resting coverage ceq=κ/(κ+e) holds against each class’s on-chain maxDevBps as the worst-case single-push mark error e. Values differ by deployment (pool composition); read the live value with getAsset, never off a params file.


7. Settlement: what actually moves

PoolIOLib.settle is the only place reserves and liabilities change on a swap (there is no separate exec: a split was rejected because it would only thread need across the boundary as an argument):

aIn.reserves += amountIn aOut.reserves -= amountOut // both fee slices stay in reserves aOut.liabilities += lpCredit // LP slice, liquidity index raised aOut.liabilities += treasuryFace // protocol slice, booked as pending treasury shares

Two consequences:

  • Settlement is endpoint-only. Interior hops of a multi-leg path do not move any reserve. Only the two endpoint legs settle. This is what makes per-leg impact charging on interior hops incorrect (see Anchor Path Pricing).
  • Liabilities are NOT unchanged on a swap. PoolLiquidityLib._credit and _raiseIndex raise aOut.liabilities by the LP fee and raise the liquidity index to match; PoolLiquidityLib.accrueTreasury books the protocol slice as pending treasury shares at the raised index, claimed through Admin.claimTreasury (Protocol Fee Collection). Both slices are output the reserve debit did not pay out, so Rout and Lout both rise by the fee (divided by C when C>1) relative to a fee-free settlement, and cout moves accordingly.

The executable-liquidity check reads Rliq=R-invested fresh at settlement and reverts InsufficientAmount if it cannot cover amountOut + minLiquidity.


8. ALM flows

8.1. Deposit

  1. Read C (PoolSolvencyLib.mintRate, which reverts FeedUnavailable on an unusable roster mark), then pull x.
  2. Face f=x·WAD/C and lpAmt = f * WAD / mintIndex(asset), less the dead-share seed. Reject if lpAmt rounds to zero: a deposit too small to mint a share would otherwise donate reserves to existing LPs at no cost.
  3. R+=x, L+=f. C is unchanged (§5.2).

Deposits are single-sided: no pairing requirement, no forced ratio. Denomination, price exposure and fee yield stay on the leg. Solvency does not: the legs share one balance sheet on every cross exit, so LPs hold a pro-rata claim on the pool’s NAV, paid in the token they chose (§5).

8.2. Withdrawal

  1. Compute face x from LP shares and the liquidity index.
  2. Same asset: y=x·min(cleg,C). Cross asset: x·C converted along the anchor path and mark-capped (§5.2).
  3. R-=y, L-=x (full face). A cross exit debits y from the destination leg instead, so the source leg’s c rises while C moves only by the output leg’s fee credit.

8.3. Swap

  1. Skews ψin,ψout per §3, depth denominators per §4.
  2. Spline traversal per Liquidity Shaping §8.
  3. Coverage toll on the output leg per §6, then the spread fee.
  4. Settlement per §7.
  5. cin↑, cout↓ (net of the LP-fee accrual on the output leg).

Pool State

User Actions

+x, +x

+x, +x

-y

-x face

+in, -out

+fees on out

Deposit

Withdraw

Swap

Reserves

Liabilities


9. Coverage classification

9.1. Per asset

CoverageState
c> 200%OVER
200% ≥c> 100%ADEQUATE
100% ≥c> 50%UNDER
c≤ 50%CRITICAL

Only deposits, withdrawals and swaps move a leg between these states; nothing runs on a clock (§5).

withdrawals

deposits

withdrawals

deposits

coverage drops

deposit, cross exit, or coverage toll

OVER

ADEQUATE

UNDER

CRITICAL

9.2. Pool level

C (§5.1) is on chain and settles every LP entrypoint. It gates no swap: every swap-path check is per-asset.


10. Parameter reference

10.1. Per-asset RiskConfig (IPool.sol)

Four fields. It is an ABI and memory type only: every field is stored in Asset slot 2, not in a mapping of their own, which is why a risk write and an asset write touch the same word.

FieldTypeUnitPurpose
flagsuint16bitfieldcapability, gating and halt bits (layout)
kappaCovBpsuint16bpsConvex coverage wall (§6). Range [50, 10000] enforced by requireNeverDepletable at every writer; each pool’s hub leg at the maximum of its own spokes. Ladder and its derivation: Parametrization §6.2
depositCapCodeuint16m<<4|e base tokensSoft per-leg notional cap on credited claims (§5.3)
maxLiabWeightBpsuint16bps of the claim bookSoft per-leg weight cap on credited claims (§5.3)

The inventory skew carries no per-asset field: it is the fixed law of §3, identical on every leg. Neither does the depth denominator, which is the leg’s own reserves (§4).


11. Traditional AMM vs AIMM ALM

AspectTraditional AMMAIMM ALM
Price sourceReserve ratio via invariantOracle mark, shifted by inventory skew
LP accountingShare of the whole poolPer-asset liability + liquidity index
DepositBoth sides requiredSingle-sided
WithdrawalPro-rata across assetsNamed asset at min(c_leg, C), or another asset at face · C
Rebalancing incentiveArbitrage on the invariantSkew (level) plus coverage toll (charge)
UndercollateralizationImplicit as impermanent lossPer leg, c<1 priced by skew and toll; pool-wide, C<1 shared pro-rata by every LP

12. Security considerations

12.1. Bank-run resistance

  1. One settlement rate. Every exit is priced against C and a same-asset exit is allocated min(c_leg, C) pro-rata, so an earlier exit gets nothing a later exit cannot, and slicing a cross exit gains nothing (§5.2).
  2. Mid skew gives a coverage-restoring trade the favorable side of the same shift a coverage-worsening trade pays (§3), so flow is priced toward equilibrium continuously.
  3. The convex wall (§6) makes the marginal cost of draining an under-covered leg diverge as c→0.
  4. minLiquidity floors the executable reserve as the last outflow backstop, but it is inert unless a deploy config raises it: initAsset writes 0.

12.2. Manipulation resistance

  1. External-mark pricing. Quotes center on the keeper-pushed mark (MarkWordLib.mark). No quote input is derived from the pool’s own reserves or its own trade history, so a flash-loan-and-swap cannot move the quote center within a block. Only realized inventory skew moves, and skew is a level: it cancels exactly on a round trip.
  2. The skew law takes no configuration. A sign-inverted or over-steep skew is not a rejected write, it is an unrepresentable state: both slopes and both saturation points are constants (§3.2.1).
  3. Skew bounds ±100 hard-cap the inventory-driven price displacement at one wall of the spline.
  4. Flow Guard locks freshly minted LP receipt shares for flowCooldownSecs, against transfer and against burn alike (LPToken._beforeTokenTransfer), so JIT liquidity around a single block is impractical. There is no staking surface. See Flow Guards.

12.3. Circuit breakers

  • HALT_MASK (0x0005: HALT_BIT and HALT_ANCHOR_BIT, layout) halts the asset, checked on every hop of every path.
  • FeedMathLib.gate fails closed on stale, dead, or over-confident feeds.
  • The base reverts BaseDepegged on every path through it once its mark leaves parity by more than its band: the per-asset depeg code times 250 bps (RiskConfig.flags bits 12 to 15), 2,500 bps at code 0.
  • The per-asset depeg band is OracleConfig.refBandBps, armed at settlement by PoolIOLib.priceBandGuardPath, halting the leg when its primary mark leaves refBandBps of the same lane’s reference (R) tier. On a USD-attested mark the leg’s depeg code adds a parity test against 1.0. A discretionary “stop quoting this leg” call is the guardian halt.