AIMM Overview
BTR pools implement an AIMM: a multi-asset AMM quoting off a keeper-pushed external mark instead of a bonding curve, with mid and spread adapting to per-asset inventory and realized volatility. This page is the map: each mechanism gets one line and a link to the page that states it in full. Rosters per chain: Deployments.
1. Core components
| Component | What it is | Full statement |
|---|---|---|
| Single-sided deposits | LP deposits one token, receives fungible LP tokens | Providing Liquidity |
| N-asset pooling | Anchor-tree topology, any-to-any pricing along the unique tree path; rosters grouped by correlated asset class so inventory nets across legs | Pool Composition |
| Inventory-based pricing | Avellaneda-Stoikov mid shift off coverage | Inventory Management |
| Coverage-aware ALM | Reserves / liabilities separation | §7 |
| Clamped quartic I-spline depth curves | C2 density, monotone (nondecreasing control weights); five curves in a shared per-pool table, assets point in via curveId | §8, Liquidity Shaping |
| External-mark feed | Keeper pushes a fresh per-asset mark (θ + heartbeat); Pool quotes off it, no on-chain price EMA | §6, Price feed reads |
| Singleton contracts + ERC-1967 pool proxies | Each pool is a PoolProxy holding its implementation in its own slot; the factory upgrades the whole fleet; Admin and Flash are standalone singletons shared across pools | §2.1 |
| Optional per-asset hooks (dual ledger) | Physical rehypothecation of idle liquidity, ; pricing and coverage use full , a liquid buffer keeps typical swaps at 0 hook CALL. | Pool Hooks |
Call recipes (swap / LP / deploy / oracles) live in Developer Guides; this section stays conceptual.
2. Contract Architecture
2.1. Standalone Singletons + Pool Proxies
Poolis the whole pool-side entry surface and answers every call directly. It DELEGATECALLsexternallibrary functions (PricingLib,PoolConfigLib,PoolLiquidityLib) through normal Solidity linking, andPoolConfigLiblinksNUQuarticLibfor curve writes. The targets are fixed at compile time, an EIP-170 code-size measure rather than a runtime module registry.Admindoes the same withAdminParamsLib.- 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 aGOVERNANCE-tier timelocked swap atPoolFactory(7 days in production) that re-points every live pool at once while their storage persists. Largest trust assumption in the protocol: Admin. - Singletons (
Admin,Flash) are key-by-(pool, …), so one 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 one shared
AccessControlsingleton.
2.2. Core Contracts
| Contract | File | Purpose |
|---|---|---|
| Pool | Pool.sol | Swap, deposit, withdraw, donate, liability swap. Serves the chain’s mark store (MarkStoreP8) from its own storage and reads it per leg. |
| Admin | Admin.sol | Per-pool timelocked configuration. UUPS behind an ERC-1967 proxy. |
| Flash | Flash.sol | ERC-3156-style (onFlashLoan variant) flash loans. UUPS behind an ERC-1967 proxy. |
| PoolFactory | PoolFactory.sol | Deploys PoolProxy contracts (ERC-1967, one implementation slot each). It alone may call upgradeTo on them: executeReferenceUpgrade moves every proxy it minted to the new implementation, behind the impl-swap timelock. |
| AccessControl | AccessControl.sol | Single owner ref consumed by all singletons. |
| Mark store | MarkStoreP8.sol + MarkStoreBase.sol (run by the Pool implementation) | k-of-n signed keeper marks, primary and reference tier per lane; 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. Full field reference: Parametrization.
Key per-asset fields:
- Reserves and liabilities, for coverage tracking
- Anchor pointer (parent in the anchor tree)
vegaBps, this leg’s σ-sensitivity slope (BPS = 1x), andminDispersionPbps, the quiet-tape band floor. σ scales the band up from that floor; the ceiling is structural:MAX_DISPERSION_PBPS, and on an interior-capable leg also the curve’sPricingLib.dispersionCap, derived from the interior swing cap and bound at the write path. Inventory skew is a fixed protocol law with no per-asset dialminFeePbps, bounded two-sidedly atMIN_FEE_PBPS(1) andONE_PCT_PBPS(1%) at every write. It floors the spread, not the fee (§11.3), and it is the only fee rate the asset carriescurveId, the pointer into the pool’s shared curve table (0 refused at config)depositCapCode, the leg’s notional cap in whole base tokens (m·10^e), andmaxLiabWeightBps, a soft cap on the leg’s share of the pool claim book (0 = off). Both bind at credit time only
4. Pricing System
4.1. Pipeline
4.2. Key Concepts
| Term | Definition | Owner page |
|---|---|---|
| Coverage ratio | Inventory Management | |
| Inventory skew | A-S mid shift, piecewise-linear in and saturating at : draining, filling. The arms are asymmetric: the filling slope is the round-trip impact-conservation bound. No per-asset dial | Inventory Management |
| Spread | Symmetric round-trip fee width: volatility band + confidence + keeper-staleness surcharges, no directional surcharge. A swap pays half of it, once, on the output | Spread & Fees |
| 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 | Liquidity Shaping |
Canonical vocabulary (mark, mid, skew, impact, spread, fee, toll) and the term-by-term cost decomposition: Slippage & Price Impact §1.1.
5. Anchor Tree Paths
5.1. Topology
Every asset anchors to one parent and every chain terminates at the base token, the root. The parent need not be the base: the contracts ship the general anchor tree, so any asset may anchor to a non-base parent up to MAX_DEPTH = 4 and a curator can give a correlated pair its own edge, feed, σ and fee floor instead of forcing it through the base mark. A pool’s actual shape is the anchor column of its risk-param file; an empty column anchors every leg to the base. Activating a deep edge needs the cross pair’s mark on the off-chain mark pipeline, a configuration and data step rather than 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.
[stETH, WETH, WBTC, USDC, USDT] is 4 legs: stETH→WETH, WETH→WBTC, WBTC→USDC, USDC→USDT. A pair sharing 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)
This intra-pool path resolves inside a single Pool.swap_qe call, with no router involved; aggregators call Pool.swap_qe directly. Cross-pool routing (best pool for a pair, splitting a large trade to minimize price impact) is selected off-chain by the frontend router POST /v1/route, then 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, Composability §2 and Anchor Path Pricing.
6. Oracle System
6.1. External-Mark Feed
Price discovery is external: no internal TWAP, no write-on-swap. The off-chain mark pipeline signs a fresh per-asset mark with sigmaPbps (realized vol) and confidenceBps (a 1σ band); a listed relayer lands the signed segments via publishMarks (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 (Mark Relayer).
Feed contract, encodings and failure modes: Mark Relayer. Pool-side read path: Price feed reads.
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
ALM tracks reserves against LP claims per asset. Formulas and edges: Inventory Management.
- Coverage ratio (100% = equilibrium)
- Pool rate over the roster; every LP mint and exit settles at it
- Undercollateralized (
c < 100%): positive skew, and a same-asset exit is capped in kind atc - Overcollateralized (
c > 100%): skew discounts; surplus raisesCand is LP-redeemable through it
Two safety mechanisms, neither on a clock, neither reducing liabilities on its own:
- One LP settlement rate: exits price off
C, which no exit moves by exiting, so there is no first-mover advantage - Convex coverage toll (
kappaCovBps): any swap draining a walled leg further pays a cost rising superlinearly ascfalls
8. Liquidity Curves
Clamped quartic I-spline curves define liquidity distribution across the depth axis. Curve design and selection: Liquidity Shaping.
- C2 density, monotone (nondecreasing control weights; 1-14 segments)
- Exact O(1) range integration via stored prefix integrals; cost flat in trade size
- Curve codebook in a shared per-pool table, written once per pool and addressed by
curveId(Curve Presets). Presets differ in shape and reference scaledispRefPbps; quotes y-scale bydispersion / dispRefPbps - Distinct
wQpresets are independent fits, not rescalings, off exact multiples of each other by 1-2 ULP. Installed vectors are canonical on-chain state, so a rescaled regeneration fails parity: never synthesize one curve 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
curveIdpoints at the nearest entry, while 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
9. Storage Layout
9.1. Per-Contract Layouts
Each singleton uses default Solidity storage (no ERC-7201 namespacing). Cross-pool keying is mapping(address pool => ...) at the storage root.
| Contract | Layout | Purpose |
|---|---|---|
Pool (PoolProxy) | 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)
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 GOVERNANCE (7 d) / LISTING (1 d) / TUNING (1 h) timelock tiers. Full duration table with on-chain constant names: Access Control.
10.2. Two-Phase Execution
admin.requestOp(pool, opType, subject, payload)→ stores pending, emitsTimelockRequested(pool, id, opType, executableAt)- Wait for the tier delay (the deployment’s
GOV_DELAYSschedule) 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 cancelOp serves them all (owner or any guardian on an unseated pool, the seat alone on a seated one). Only the execute side is per-op named (executeAddAsset, executeUpdateCurve, executeUpdateAnchor), because each applies its own typed payload. See Admin §4.
11. Key Invariants
Properties for fuzzing and formal verification: Invariants.
11.1. Coverage Bounds
c = 1.0→ equilibriumc < 1.0→ undercollateralized (positive skew; same-asset exits capped in kind)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; the slope is bound there by round-trip impact conservation on both arms. Derivation: Liquidity Shaping §5.1.
11.3. Spread Bounds
has no upper bound beyond its uint16 field width, and minFeePbps floors the path spread per leg, not the fee. Why, and what a swap pays: Spread & Fees §3.1 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 |
|---|---|
| Transient oracle caching | ~2,100 gas/hit |
| 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) | O(1) eval and range integral (gas) |
Per-operation swap figures: Pool §12.
13. Error Handling
Consolidated error set (ErrLib.sol plus PoolSolvencyLib.sol for FeedUnavailable and WeightCapExceeded); the rest per contract in GET /v1/abis/<ContractName>:
| Error | Usage |
|---|---|
ZeroValue() | Zero address/amount/price |
NotOwner() / NotAuthorized() | Caller is not the owner / lacks the lane or role |
InvalidConfig() | Config outside its bounds |
Expired() | Deadline passed |
CooldownActive() | Flow cooldown not elapsed |
FeedUnavailable() | Leg has no usable mark |
PriceOutsideRefBand(mark, ref) | Spoke mark left its reference band |
BaseDepegged(basePrice, deviationBps) | Base left its depeg band |
WeightCapExceeded(weightBps, capBps) | Leg’s claim-book share cap hit |
OperationFailed() | Hook or external call failed |
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) |
14. Contract Deployment
14.1. Immutable Components
- All AIMM libraries. Five are deployed and linked (Deployer §4); the rest (
FeedMathLib,AnchorTreeLib,PoolIOLib,PoolHooksLib,PoolSolvencyLib,YieldHookLib,TransientCacheLib,PoolConstantsLib) areinternaland inline into their callers. AccessControl(shared singleton, non-upgradeable:Ownable, no proxy)
14.2. Replaceable via Factory Timelock
Pool implementation, swappable via a 7-day timelock at PoolFactory. The swap upgrades every live pool, third-party pools included; there is no opt-out. Cancellable by owner or guardian, expires 7 days after maturity (Admin).
14.3. Upgradeable via UUPS
Admin and Flash, each a UUPS implementation behind its own ERC-1967 proxy, gated by UpgradeGate at the GOVERNANCE tier (7 days in production). Admin holds per-pool state (bootstrapSealed, riskFences, pendingOps), so a redeploy-and-repoint is not equivalent to an upgrade.
15. Related Documentation
| Page | Covers |
|---|---|
| Inventory Management | Coverage, skew, pool rate |
| Liquidity Shaping | Curve depth curves |
| Anchor Path Pricing | Path composition, reference band |
| Spread & Fees | σ, confidence, staleness, the fee |
| Slippage & Price Impact | Execution costs, minAmountOut, deadline |
| Toxic Flow Mitigation | Flow guards |
| Parametrization | Parameter reference |
| Invariants | Fuzzing and formal-verification properties |
| Curve presets | Installed depth-curve presets, charts and selection |
| Pool · Price feed reads · Admin · Flash | Module references |
| Pool Hooks | Dual ledger, buffer, CompoundV2YieldHook |