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 Pool quotes 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). Admin and Flash are standalone singletons shared across all pools.
  • Optional per-asset hooks (dual ledger): Physical rehypothecation of idle liquidity (R=Rliq+Rinv). Pricing / coverage use full R; a liquid buffer keeps typical swaps at 0 hook CALL. Example: CompoundV2YieldHook against 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:

external call

external call

deploys

reads impl

owner ref

owner ref

User

Pool beacon proxy

Admin

Flash

PoolFactory

AccessControl

Properties:

  • Pool is the whole pool-side entry surface and answers every call directly. Internally it DELEGATECALLs external library 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 Pool holds its own PoolStorage at slot 0 (set once via initialize). Storage is per-instance; code is not. The implementation is replaceable via a timelocked swap at PoolFactory (UPGRADE tier, 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; Pool bakes each proxy address as an immutable and PoolFactory._validateImplementation pins them on every fleet upgrade.
  • Owner authority for every singleton routes through a single shared AccessControl singleton (one source of truth).

2.2. Core Contracts

ContractFilePurpose
PoolPool.solSwap, deposit, withdraw, donate, liability swap. Reads the external-mark feed (ExternalOracle).
AdminAdmin.solPer-pool timelocked configuration. UUPS behind an ERC-1967 proxy.
FlashFlash.solERC-3156-style (postFlashLoan variant) flash loans. UUPS behind an ERC-1967 proxy.
PoolFactoryPoolFactory.solDeploys ERC-1967 beacon proxies. It is the beacon: it holds the fleet implementation slot and the impl-swap timelock.
AccessControlAccessControl.solSingle owner ref consumed by all singletons.
ExternalOracleExternalOracle.solk-of-n signed keeper mark feed; the quote source every pool reads.
LPTokenLPToken.solPer-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) and minDispersionPbps (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 at MIN_FEE_PBPS (1) and ONE_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: presetId into the pool’s shared curve table (0 refused at config), plus haircutSuppressorBps, 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

swap request

Calculate Coverage

Compute Inventory Skew

Get Oracle Price

Calculate Volatility

Traverse Preset Curve

Apply Spread and Fees

amountOut

4.2. Key Concepts

  • Coverage Ratio: c=R/L, measures asset health
  • Inventory Skew: Avellaneda-Stoikov mid shift, piecewise-linear in c=R/L and saturating at ±100: +200(1-c) draining, -100(c-1) 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:


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

WBTC base

WETH

USDC

stETH

USDT

DAI

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.

stETH

WETH

WBTC

USDC

USDT

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 = 4 bounds one walk; a path is two walks meeting at the LCA, so MAX_PATH_LENGTH = 2 * MAX_DEPTH + 1 = 9 nodes 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: c=R/L (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 c for 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 as c falls

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 distinct wQ control polygons, whose half-swing at the reference dispersion is 100, 200 and 500 pbps (1, 2 and 5 bp). Live quotes y-scale by dispersion / dispRefPbps
  • The three wQ vectors 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 presetId points at the nearest entry; the continuous dispersion / dispRefPbps scale 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.

ContractLayoutPurpose
Pool (beacon proxy)PoolStorage at slot 0Per-pool assets, reserves, config, set once via initialize. Append-only field order, because live pools keep this storage across an implementation swap.
AdminpendingOps[keccak256(pool, opId, subject)], pendingData[...]Per-pool timelock queue, keyed by op and subject (Admin §3).
Flashnone (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

  1. admin.requestOp(pool, opType, subject, payload) → stores pending, emits TimelockRequested(pool, id, opType, executableAt)
  2. Wait for the tier delay (set by the deployment’s GOV_DELAYS schedule, see Access Control & Roles)
  3. 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[0,)

  • c = 1.0 → equilibrium
  • c < 1.0 → undercollateralized (haircuts apply)
  • c > 1.0 → overcollateralized

11.2. Skew Bounds

-100ψ+100

The skew index ψ displaces the spline coordinate off the curve’s stored density median x*, not off the domain midpoint: x0=clamp(x*+ψ·BPS/200,0,BPS). Anchoring on the stored median is what makes zero skew quote the mark for any shape, and the BPS/200 slope is bound there by round-trip impact conservation on both arms. Derivation: Liquidity Shaping §5.1.

11.3. Spread Bounds

Simax(fmin,i,Fi)

S 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 S, per leg, not the fee. The fee a swap pays is half the spread, charged once on the output. A round trip pays S; a single swap pays S/2.

Full statement: Spread & Fees §3 and §5.

11.4. Anchor Tree

  • MAX_DEPTH = 4: every anchor chain reaches the base within 4 steps
  • MAX_PATH_LENGTH = 9: 2*MAX_DEPTH + 1 nodes (8 legs: up to the LCA, then down)
  • noCycles = true: explicit self-check + unconditional step cap

12. Gas Optimizations

OptimizationSavings
Single-slot FeedData packing~2,100 gas/read
Transient oracle caching~2,100 gas/hit
Packed timelocks66% slot reduction
Anchor-tree path walk (depth ≤ 4, ≤ 8 legs, no storage)Storage-free path resolution
Bitmask hooks32 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:

ErrorUsage
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 *Lib ones (FeedMathLib, AnchorTreeLib, PoolIOLib, PoolHooksLib, TransientCacheLib, PoolConstantsLib) are internal and 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 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

  • Admin and Flash, each a UUPS implementation behind its own ERC-1967 proxy, gated by UpgradeGate at the UPGRADE tier (7 days in production). Admin holds per-pool state (bootstrapSealed, riskFences, pendingOps), so a redeploy-and-repoint is not equivalent to an upgrade.