AIMM Overview
BTR pools implement an Adaptive Inventory Market Maker (AIMM): a multi-asset AMM whose quotes come from a keeper-pushed external mark rather than from a bonding curve, and whose mid and spread adapt to per-asset inventory and realized volatility. This page is the architectural map. Every mechanism is summarised here and stated in full on the page that owns it. AIMM pools are live; per-chain rosters, including the chains still being stood up, are in Deployments.
1. Core components
AIMM is a multi-asset AMM designed for passive liquidity providers.
- Single-sided deposits: LPs deposit one token, receive fungible LP tokens
- N-asset pooling: multi-anchor tree topology, any-to-any pricing along the unique tree path. Rosters are grouped by correlated asset class, which is what lets inventory net across legs instead of accumulating on one side (Pool Composition)
- Inventory-based pricing: Avellaneda-Stoikov inspired mid-price adjustment
- Coverage-aware ALM: Reserves/liabilities separation (Wombat-style)
- Clamped quartic I-spline depth curves: C2 density, monotone by construction (nondecreasing control weights). Five presets live in a shared per-pool table; assets point in via
presetId(§8) - External-mark price feed: a keeper pushes a fresh per-asset mark (deviation-θ + heartbeat) and
Poolquotes off it, with no on-chain price EMA. Residual push-latency LVR and OEV are covered in Oracles §8.4 - Singleton contracts + ERC-1967 beacon proxies: Each pool is a beacon proxy reading
PoolFactory.implementation()(the factory is the beacon), so all pools execute the same implementation and a single timelocked swap re-points all of them (Deployment & Upgrades §4.1).AdminandFlashare standalone singletons shared across all pools. - Optional per-asset hooks (dual ledger): Physical rehypothecation of idle liquidity (). Pricing / coverage use full ; a liquid buffer keeps typical swaps at 0 hook CALL. Example:
CompoundV2YieldHookagainst Venus. See Pool Hooks.
Builders (swap / LP / deploy / oracles): Developer Guides. Concepts stay in this section; call recipes live there.
2. Contract Architecture
2.1. Standalone Singletons + Pool Proxies
The protocol is a small set of standalone singletons plus per-pool ERC-1967 beacon proxies:
Properties:
Poolis the whole pool-side entry surface and answers every call directly. Internally it DELEGATECALLsexternallibrary functions (Pricing,PoolConfig,PoolLiquidity,NUQuartic) through normal Solidity linking, and those four targets are fixed at compile time - an EIP-170 code-size measure, not a runtime module registry.- Each contract uses its own default storage layout.
- Each
Poolholds its ownPoolStorageat slot 0 (set once viainitialize). Storage is per-instance; code is not. The implementation is replaceable via a timelocked swap atPoolFactory(UPGRADEtier, 7 days in production), which re-points every live pool at once while their storage persists. This is the protocol’s largest trust assumption: see Deployment & Upgrades §4.1. - Singletons (
Admin,Flash) are key-by-(pool, …) so a single deployment serves every pool. Both are UUPS implementations behind ERC-1967 proxies;Poolbakes each proxy address as an immutable andPoolFactory._validateImplementationpins them on every fleet upgrade. - Owner authority for every singleton routes through a single shared
AccessControlsingleton (one source of truth).
2.2. Core Contracts
| Contract | File | Purpose |
|---|---|---|
| Pool | Pool.sol | Swap, deposit, withdraw, donate, liability swap. Reads the external-mark feed (ExternalOracle). |
| Admin | Admin.sol | Per-pool timelocked configuration. UUPS behind an ERC-1967 proxy. |
| Flash | Flash.sol | ERC-3156-style (postFlashLoan variant) flash loans. UUPS behind an ERC-1967 proxy. |
| PoolFactory | PoolFactory.sol | Deploys ERC-1967 beacon proxies. It is the beacon: it holds the fleet implementation slot and the impl-swap timelock. |
| AccessControl | AccessControl.sol | Single owner ref consumed by all singletons. |
| ExternalOracle | ExternalOracle.sol | k-of-n signed keeper mark feed; the quote source every pool reads. |
| LPToken | LPToken.sol | Per-leg ERC-20 share receipt, one EIP-1167 clone per (pool, leg), minted and burned by the owning pool only. |
3. Core Data Structures
Struct definitions: IPool.sol.
Key per-asset fields:
- Reserves & liabilities for coverage tracking
- Anchor pointer (parent in the anchor tree)
- Sensitivity params:
vegaBps(this leg’s σ-sensitivity slope, BPS = 1x) andminDispersionPbps(the quiet-tape band floor). σ scales the band up from that floor; its ceiling is structural, derived from the leg’s preset curve (Pricing.dispersionCap) off the interior swing cap, and bound at the write path. Inventory skew is a fixed protocol law with no per-asset dial - Spread floor per leg:
minFeePbps, bounded two-sidedly atMIN_FEE_PBPS(1) andONE_PCT_PBPS(1%) at every write. It floors the spread, not the fee (see §11.3), and it is the only fee rate the asset carries - Preset pointer:
presetIdinto the pool’s shared curve table (0 refused at config), plushaircutSuppressorBps, the share of an under-covered leg’s deficit a same-asset exit is spared
See: Parametrization for full field reference.
4. Pricing System
4.1. Pipeline
4.2. Key Concepts
- Coverage Ratio: , measures asset health
- Inventory Skew: Avellaneda-Stoikov mid shift, piecewise-linear in and saturating at : draining, filling. The two arms are asymmetric on purpose (the filling arm’s slope is the round-trip impact-conservation bound) and there is no per-asset dial
- Spread: the symmetric round-trip fee width: volatility band + confidence + keeper-staleness surcharges (no directional surcharge). A swap pays half of it, once, on the output
- Curve Traversal: price impact via O(1) quartic I-spline integration. Direction-asymmetric, unlike the spread, because depth is per-asset and the traverse starts at an off-center skew anchor
See:
- Slippage & Price Impact §1.1: the canonical vocabulary (mark, mid, skew, impact, spread, fee, toll) and the term-by-term cost decomposition
- Inventory Management: Coverage, skew, withdrawal haircuts
- Spread & Fees: Fee calculation
- Liquidity Shaping: Preset depth curves
5. Anchor Tree Paths
Every asset anchors to one parent. The parent need not be the base: assets anchor to whichever asset they correlate with, and every chain terminates at the base token, the root.
5.1. Topology
The contracts ship the general anchor tree: any asset may be anchored to a non-base parent up to MAX_DEPTH = 4, so a pool prices each asset against whatever reference its curator chooses - correlated pairs get their own edge, feed, σ and fee floor instead of being forced through the base mark. Whether a given pool uses that depth is configuration, not a contract limit: the anchor column of its risk-param file is the topology, and an empty column means every leg prices off the base. Activating a deep edge needs the cross pair’s mark to exist on NX Rates - a configuration and data step, never a contract change. See Anchor Path Pricing §1.1.
5.2. Swap Path (unique, via the LCA)
The path between two assets is the unique tree path: up to the lowest common ancestor, then down.
Path: [stETH, WETH, WBTC, USDC, USDT]
Legs: [stETH→WETH, WETH→WBTC, WBTC→USDC, USDC→USDT]
A pair that shares a low ancestor stays short: USDT → USDC → DAI is 2 legs and never touches the WBTC mark.
Constraints:
MAX_DEPTH = 4bounds one walk; a path is two walks meeting at the LCA, soMAX_PATH_LENGTH = 2 * MAX_DEPTH + 1 = 9nodes and 8 legs- Endpoint legs: full impact, reserves settle. Interior legs: mid with skew, no impact, no settlement
- Path is unique, so there is nothing to search and no decomposition ambiguity
- Cycles rejected by
validateAnchor(explicit self-check plus an unconditional step cap)
On-chain vs off-chain routing. This intra-pool path is resolved inside a single Pool.swap
call, with no router of any kind involved. Aggregators call Pool.swap directly. Cross-pool
routing (choosing the best pool for a pair, and splitting a large trade across several pools to
minimize price impact) is selected off-chain, by the frontend router POST /v1/route, then
either settled leg by leg through @btr-protocol/sdk buildSwapCalls (EIP-5792 or sequential) or
handed whole to the Router singleton for one all-or-nothing transaction. See
Quotes & Routing and
Composability §2.
See: Anchor Path Pricing
6. Oracle System
6.1. External-Mark Feed
Price discovery is external: there is no internal TWAP and no write-on-swap. NX Rates signs a fresh per-asset mark together with sigmaPbps (realized vol) and confidenceBps (a 1σ band), and any keeper lands the batch via ExternalOracle.batchPushSigned (k-of-n) once the price moves past a deviation band θ or a heartbeat elapses; every quote (mid, spread, depeg band) reads that mark, and nothing is smoothed or averaged on-chain. Quoting off a fresh mark rather than a lagging average removes classical curve LVR, but not push-latency LVR or OEV.
See Oracles for the feed contract, encodings and failure modes, and Feed Oracle for the pool-side read path.
6.2. Gas Optimization
Oracle reads are cached in transient storage (EIP-1153), so a repeat read inside one transaction is a
TLOAD rather than an external call (§12).
7. Coverage-Aware ALM
Asset-Liability Management (ALM) tracks reserves vs LP claims per asset:
- Coverage Ratio: (100% = equilibrium)
- Undercollateralized (
c < 100%): Withdrawal haircuts apply - Overcollateralized (
c > 100%): Surplus retained (haircut headroom + skew discounts; not LP-redeemable)
Safety Mechanisms: two pricing mechanisms restore coverage, neither on a clock, neither reducing liabilities on its own.
- Withdrawal haircuts: every exit from an under-covered leg pays a deficit-proportional penalty, so leaving raises
cfor those who stay and there is no first-mover advantage - Convex coverage toll (
kappaCovBps): any swap that drains a walled leg further pays a cost rising superlinearly ascfalls
See: Inventory Management for formulas and details.
8. Liquidity Curves
Clamped quartic I-spline preset curves define liquidity distribution across the depth axis:
- C2 density, monotone by construction (nondecreasing control weights; 1-14 segments)
- Exact O(1) range integration via stored prefix integrals (cost flat in trade size)
- Five-preset codebook in a shared per-pool table - the reference roster assigns all five across 28 legs; the live Arc fleet ships four of them (preset 3 unused) across 26 symbols - all on interior knots
[1314, 8686](m = 3 spans). They carry three distinctwQcontrol polygons, whose half-swing at the reference dispersion is 100, 200 and 500 pbps (1, 2 and 5 bp). Live quotes y-scale bydispersion / dispRefPbps - The three
wQvectors are independent fits, not rescalings, off exact multiples of each other by 1-2 ULP. The shipped vectors are canonical on-chain state, so a rescaled regeneration fails parity against them: never synthesize one preset from another, re-fit (Liquidity Shaping §4.3) - The table is a quantized density codebook. An asset’s observed depth density is fitted off-chain and its
presetIdpoints at the nearest entry; the continuousdispersion / dispRefPbpsscale absorbs the scale part of the residual, so the codebook only has to span shape (Liquidity Shaping §2.2) - The traverse is anchored on each curve’s density median, stored in the header, so zero inventory skew quotes the mark for any shape
See: Liquidity Shaping for preset design and selection.
9. Storage Layout
9.1. Per-Contract Layouts
Each singleton uses default Solidity storage (no ERC-7201 namespacing). Cross-pool keying is handled via mapping(address pool => ...) at the storage root.
| Contract | Layout | Purpose |
|---|---|---|
Pool (beacon proxy) | PoolStorage at slot 0 | Per-pool assets, reserves, config, set once via initialize. Append-only field order, because live pools keep this storage across an implementation swap. |
Admin | pendingOps[keccak256(pool, opId, subject)], pendingData[...] | Per-pool timelock queue, keyed by op and subject (Admin §3). |
Flash | none (reads pool state) | Stateless. |
9.2. Transient Storage (EIP-1153)
Used for:
- Reentrancy guards
- Oracle price caching (§6.2)
- Flash loan state
10. Admin timelocks
10.1. Operation Types
Every governed operation is gated by one of the LOW/BASE/HIGH/CRITICAL/UPGRADE/ROTATION/FACTORY timelock tiers; see Access Control & Roles for the full duration table with real on-chain constant names.
10.2. Two-Phase Execution
admin.requestOp(pool, opType, subject, payload)→ stores pending, emitsTimelockRequested(pool, id, opType, executableAt)- Wait for the tier delay (set by the deployment’s
GOV_DELAYSschedule, see Access Control & Roles) admin.execute<Op>(pool, ...)→ applies within the grace period, emits the operation’s own event
The request side is one generic entry point: every operation type queues through Admin.requestOp with an opType discriminator, and one cancelTimelock serves them all (open to the owner or any guardian). Only the execute side is per-op named - executeAddAsset, executeSetCurve, executeAnchorUpdate, etc. - because each applies its own typed payload. See Admin §4.
11. Key Invariants
11.1. Coverage Bounds
c = 1.0→ equilibriumc < 1.0→ undercollateralized (haircuts apply)c > 1.0→ overcollateralized
11.2. Skew Bounds
The skew index displaces the spline coordinate off the curve’s stored density median , not off the domain midpoint: . Anchoring on the stored median is what makes zero skew quote the mark for any shape, and the slope is bound there by round-trip impact conservation on both arms. Derivation: Liquidity Shaping §5.1.
11.3. Spread Bounds
is bounded below by the summed per-leg floors and above only by its uint16 field width: a spread widened by a σ, confidence or staleness term is the price of that risk, and a per-leg ceiling would break path additivity. Trader protection is minAmountOut.
minFeePbps floors the path spread , per leg, not the fee. The fee a swap pays is half the spread, charged once on the output. A round trip pays ; a single swap pays .
Full statement: Spread & Fees §3 and §5.
11.4. Anchor Tree
MAX_DEPTH = 4: every anchor chain reaches the base within 4 stepsMAX_PATH_LENGTH = 9:2*MAX_DEPTH + 1nodes (8 legs: up to the LCA, then down)noCycles = true: explicit self-check + unconditional step cap
12. Gas Optimizations
| Optimization | Savings |
|---|---|
| Single-slot FeedData packing | ~2,100 gas/read |
| Transient oracle caching | ~2,100 gas/hit |
| Packed timelocks | 66% slot reduction |
| Anchor-tree path walk (depth ≤ 4, ≤ 8 legs, no storage) | Storage-free path resolution |
| Bitmask hooks | 32 bits vs N mappings |
| Packed quartic curve (header directory + prefix integrals) | Eval 5.4k / O(1) range integral 11.2k cold |
13. Error Handling
Minimal consolidated error set:
| Error | Usage |
|---|---|
ZeroValue() | Zero address/amount/price |
InsufficientAmount(available, required) | Balance checks |
ExcessiveAmount(amount, limit) | Limit exceeded |
InvalidState() | Not initialized/paused |
FeatureDisabled(resource) | Swap/flash disabled |
NotConfigured(resource, target) | Missing config |
ThresholdViolation(value, threshold) | Slippage/coverage |
StaleData(age, maxAge) | Oracle staleness |
Reentrancy() | Guard triggered (Solady ReentrancyGuardTransient) |
See: Errors.sol (Err library).
14. Contract Deployment
14.1. Immutable Components
- All AIMM libraries. Four are DEPLOYED and linked (
Pricing,PoolConfig,PoolLiquidity,NUQuartic); the*Libones (FeedMathLib,AnchorTreeLib,PoolIOLib,PoolHooksLib,TransientCacheLib,PoolConstantsLib) areinternaland inline into their callers. AccessControl(shared singleton, non-upgradeable:Ownable, no proxy)
14.2. Replaceable via Factory Timelock
Poolimplementation, swappable via a 7-day timelock atPoolFactory. The swap re-points every live pool, third-party pools included; there is no opt-out. Cancellable by owner or guardian, expires 7 days after maturity (§4.1).
14.3. Upgradeable via UUPS
AdminandFlash, each a UUPS implementation behind its own ERC-1967 proxy, gated byUpgradeGateat theUPGRADEtier (7 days in production).Adminholds per-pool state (bootstrapSealed,riskFences,pendingOps), so a redeploy-and-repoint is not equivalent to an upgrade.
15. Related Documentation
- Inventory Management: Pricing mechanics
- Pool Contract: Module documentation
- Pool Hooks: Dual ledger, buffer,
CompoundV2YieldHook - Parametrization: Parameter reference
- Slippage & Price Impact: Execution costs, minAmountOut, deadline
- Invariants: Properties for fuzzing and formal verification