---
title: "AIMM Overview"
description: "Technical overview of BTR pools (Adaptive Inventory Market Maker)"
audience: tech
type: explanation
status: live
lang: en
updated: "2026-09-02"
publish: true
---
# 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](/docs/2-overview).

---

## 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](/docs/2-3-pool-composition#why-several-cores-rather-than-one))
- **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](/docs/3-4-oracles#84-known-risks-lvr--oev)
- **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](/docs/3-2-deployment-upgrades#41-pool-beacon-upgrade-poolfactory-7-day-timelock)). `Admin` and `Flash` are standalone singletons shared across all pools.
- **Optional per-asset hooks (dual ledger)**: Physical rehypothecation of idle liquidity ($R = R_{\mathrm{liq}} + R_{\mathrm{inv}}$). Pricing / coverage use full $R$; a liquid buffer keeps typical swaps at 0 hook CALL. Example: `CompoundV2YieldHook` against Venus. See [Pool Hooks](/docs/5-1-3-hooks).

**Builders** (swap / LP / deploy / oracles): [Developer Guides](/docs/5-overview). 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:

```mermaid
graph LR
    User --> Pool[Pool beacon proxy]
    User --> Admin
    User --> Flash
    Admin -. external call .-> Pool
    Flash -. external call .-> Pool
    Factory[PoolFactory] -. deploys .-> Pool
    Pool -. reads impl .-> Factory
    AC[AccessControl] -. owner ref .-> Admin
    AC -. owner ref .-> Factory
```

**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](/docs/3-2-deployment-upgrades#41-pool-beacon-upgrade-poolfactory-7-day-timelock).
- 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

| Contract | File | Purpose |
|---|---|---|
| **[Pool](/docs/1-2-1-pool)** | `Pool.sol` | Swap, deposit, withdraw, donate, liability swap. Reads the external-mark feed (`ExternalOracle`). |
| **[Admin](/docs/1-2-3-admin)** | `Admin.sol` | Per-pool timelocked configuration. UUPS behind an ERC-1967 proxy. |
| **[Flash](/docs/1-2-4-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) 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](/docs/1-1-7-parametrization) for full field reference.

---

## 4. Pricing System

### 4.1. Pipeline

```mermaid
graph TB
    Start[swap request] --> Step1[Calculate Coverage]
    Step1 --> Step2[Compute Inventory Skew]
    Step2 --> Step3[Get Oracle Price]
    Step3 --> Step4[Calculate Volatility]
    Step4 --> Step5[Traverse Preset Curve]
    Step5 --> Step6[Apply Spread and Fees]
    Step6 --> End[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 $\pm 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:
- [Slippage & Price Impact §1.1](/docs/1-1-5-slippage-price-impact): the canonical vocabulary (mark, mid, skew, impact, spread, fee, toll) and the term-by-term cost decomposition
- [Inventory Management](/docs/1-1-1-inventory-management): Coverage, skew, withdrawal haircuts
- [Spread & Fees](/docs/1-1-4-spread-fees): Fee calculation
- [Liquidity Shaping](/docs/1-1-2-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

```mermaid
graph TD
    WBTC[WBTC base] --> WETH
    WBTC --> USDC
    WETH --> stETH
    USDC --> USDT
    USDC --> 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](/docs/1-1-3-anchor-path-pricing#11-what-the-anchor-column-configures).

### 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.

```mermaid
graph LR
    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](/docs/5-2-2-quotes-routing) and
[Composability §2](/docs/5-1-4-composability#2-routing-through-the-router).

See: [Anchor Path Pricing](/docs/1-1-3-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](/docs/glossary#oev-oracle-extractable-value).

See [Oracles](/docs/3-4-oracles) for the feed contract, encodings and failure modes, and [Feed Oracle](/docs/1-2-2-internal-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](/docs/1-1-1-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](/docs/1-1-2-liquidity-shaping#43-shipped-presets-5-rows))
- **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](/docs/1-1-2-liquidity-shaping#22-preset-table-and-asset-pointer))
- **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](/docs/1-1-2-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](/docs/1-2-3-admin#3-timelock-delays)). |
| `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](/docs/3-1-access-control-roles-emergency-powers) 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](/docs/3-1-access-control-roles-emergency-powers))
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](/docs/1-2-3-admin).

---

## 11. Key Invariants

### 11.1. Coverage Bounds

$c \in [0, \infty)$

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

### 11.2. Skew Bounds

$-100 \le \psi \le +100$

The skew index $\psi$ displaces the spline coordinate off the curve's stored **density median** $x^*$, not off the domain midpoint: $x_0 = \mathrm{clamp}(x^* + \psi\cdot\text{BPS}/200,\ 0,\ \text{BPS})$. Anchoring on the stored median is what makes zero skew quote the mark for any shape, and the $\text{BPS}/200$ slope is bound there by round-trip impact conservation on both arms. Derivation: [Liquidity Shaping §5.1](/docs/1-1-2-liquidity-shaping#51-the-center).

### 11.3. Spread Bounds

$$S \;\ge\; \sum_i \max\big(f_{\min,i},\ F_i\big)$$

$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](/docs/1-1-4-spread-fees#3-the-spread) and [§5](/docs/1-1-4-spread-fees#5-the-fee).

### 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

| 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 `*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](/docs/3-2-deployment-upgrades#41-pool-beacon-upgrade-poolfactory-7-day-timelock)).

### 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.

---

## 15. Related Documentation

- [Inventory Management](/docs/1-1-1-inventory-management): Pricing mechanics
- [Pool Contract](/docs/1-2-1-pool): Module documentation
- [Pool Hooks](/docs/5-1-3-hooks): Dual ledger, buffer, `CompoundV2YieldHook`
- [Parametrization](/docs/1-1-7-parametrization): Parameter reference
- [Slippage & Price Impact](/docs/1-1-5-slippage-price-impact): Execution costs, minAmountOut, deadline
- [Invariants](/docs/1-1-8-invariants): Properties for fuzzing and formal verification
