Security Overview
The contract-level view of AIMM security: the adversaries the design targets, the four layers that address them, and which limitations are intended rather than pending. Every layer is specified on its own page; this one is the threat model and the routing table.
| Section | Page |
|---|---|
| Who may do what, and how long each action takes | Access Control |
| Transaction-time guards: reentrancy, flash, JIT, depeg halt, reference bands | Guards |
| What is emitted, where to read it, what alerts | Observability |
| Internal audits, perpetual auditing, disclosure intake, third-party reports as they land | Audits |
Operational checklists live with their audience: Pool Deployment & Curation §7 for deployers and curators, Providing Liquidity §10 for LPs, Slippage & Price Impact §5.1 for takers. On the swap path the contracts enforce the TTL staleness gate and the coverage toll; the caller supplies minAmountOut and deadline.
1. Defense layers
| Layer | Focus | Specified in |
|---|---|---|
| Economic | External mark, inventory skew bounds, coverage toll | §3 |
| Access control | Timelocked governance, asymmetric guardian halt | Access Control |
| Operational | Asset halt, depeg halt, flow guards | Guards |
| Code | Tests, invariant proofs, perpetual internal auditing; third-party reports linked as they land | §4, Audits |
2. Threat model
2.1. Adversary capabilities
| Adversary | Capabilities | Mitigations |
|---|---|---|
| Flash Loan Attacker | Unlimited capital for single transaction | No write-on-swap (external mark); volatility-adaptive per-push deviation band |
| Whale Trader | Large positions, multi-block attacks | External keeper mark; inventory skew bounds |
| MEV Searcher | Ordering, sandwich; oracle-push OEV | Slippage protection; symmetric spread + skewed mid; minFee; private keeper relay preferred |
| Oracle Manipulator | Compromised keeper; stale push | k-of-n quorum over a committed roster; per-lane deviation band; lane halt; confidence halt; depeg bands |
| Governance Attacker | Majority control | Timelocks + grace windows (Access Control) |
| Smart Contract Exploiter | Code bugs | Guardian halt; the fleet upgrade (PoolFactory.executeReferenceUpgrade) lands after the GOVERNANCE tier delay within a 7-day grace, guardian-cancellable (Admin) |
2.2. Trust assumptions
Only the Owner can act destructively protocol-wide. Every other role is bounded by what the contracts
let it call, not by an expectation that it behaves. Loss columns read as the worst case with that
principal fully compromised. The treasury is a pointer the Owner rotates on the GOVERNANCE tier, so
the Owner row covers fee custody too.
| Role | Trust | Breaks contracts | Protocol loss | User loss | Bound |
|---|---|---|---|---|---|
| Owner | Trusted | yes | yes | yes | Upgrades timelocked, guardian-cancellable |
| Guardian | Limited | no | no | no | Halt, tighten and cancel only |
| Treasury | Untrusted | no | yes | no | Accrued fee shares only |
| Mark signers | Trusted | no | yes | yes | k-of-n quorum, per-push deviation band |
| Oracle relayer | Untrusted | no | no | no | No signing key, TTL fails closed |
| Pool seat holder | Untrusted | no | no | yes | Its own pool only, never official ones |
| Users | Untrusted | no | no | no | Every input validated at the boundary |
- Owner writes risk parameters, curves, oracle config and hooks, upgrades the fleet, and un-halts. On an official pool it is the only principal that can un-halt; a seated third-party pool admin clears its own risk bit immediately and the guardian bit after the
LISTINGdelay. Upgrades are timelocked, risk parameters are not (Risk Steward). - Guardian cannot un-halt, upgrade or move value; compromising it stops the protocol; it cannot drain it. A halt shuts deposits and exits as well as swaps, so its cost to users is a freeze, not a loss (Guardian).
- Treasury calls
Admin.claimTreasuryand nothing else, minting the protocol’s accrued fee share, held as pending LP shares, to itself. Reserves, liabilities, LP positions and parameters are out of reach (Treasury). - Mark signers are the mark’s authority. A price is accepted because
kofnregistered signers signed that exact blob, never because of who submitted it (Mark Relayer). - Oracle relayer holds no signing key: its surface is delay, withhold and reorder inside the 120 s segment window, a liveness actor, and the chain gates on staleness. List, window and roster commitment: Mark Relayer §3.4.
- Pool admin is a seat the protocol owner names in the
initdatait passes to the owner-onlyPoolFactory.createPool: authority over that pool only, none over official pools, other pools or protocol state. Treat an uncurated pool as operated by whoever holds its seat (Pool Deployment & Curation §2).
3. Layer 1: economic security
Manipulation resistance:
- External keeper mark: quote source is an off-venue signed aggregate, not pool reserve state; reserve moves cannot move the quote.
- No write-on-swap: a swap never mutates a feed, so there is no accumulator to manipulate.
- Inventory skew bounds:
PricingLib.computeInventorySkewreturns a dimensionlessint8clamped to , saturating at coverage and . One skew unit maps to bps of curve-x displacement, so the clamp caps the coverage-driven mid shift at bps of the curve, then clamps into range. Spline depth and spread are separate terms, so this is not a cap on total price impact. The bounds are fixed in code, not operator-set. - Reserve floor: the per-asset
minLiquidityis the hard floor on every outflow. A swap, withdrawal or flash loan revertsInsufficientAmountif it would leave below it (PoolIOLib.settle; there is noexecfunction). Quoted outflows (swap, cross exit, liability swap) also stop at the leg’s coverage floor : the quote clamps gross output there and refuses a larger size (Inventory Management §6.2).
Incentive alignment:
- Coverage-aware pricing: the symmetric spread plus inventory-skew mid shift and convex coverage toll make coverage-worsening flow pay more (no directional fee term in the spread itself).
- One LP settlement rate: every mint and exit settles at the pool-level solvency rate
C, which no exit can move by exiting. A cross exit convertsface · Cand a same-asset exit is allocatedmin(c_leg, C)pro-rata, less the exit toll (Inventory Management §5.2), so there is no first-mover advantage and nothing to gain by slicing. A real loss on any leg is shared pro-rata by every LP in the pool, except a yield-venue loss, which is written down on that leg alone (Hooks §5.2). Each leg’s notional is bounded by its deposit cap and its claim-book share by the weight capmaxLiabWeightBps(Inventory Management §5.3; values per pool: deployed instances). Coverage is never rewritten by a background process.
Layers 2 and 3 are specified elsewhere: Access Control for principals, timelock tiers and halt authority, Guards for the per-asset flag bits and risk thresholds.
4. Layer 4: code security
Solidity =0.8.36 (exact pragma), custom error types, and the build-time artifact guards that pin storage layout (ArtifactGuards.t.sol, AdminFlashUUPS.t.sol). The test suite covers unit, integration, fuzz and invariant cases; specific proofs are cited at the property they establish rather than claimed in aggregate here.
Assurance is internal, run as perpetual auditing, with its methodology public in the audits repository; campaign counts, findings and third-party reports: Audits.
5. Storage security
5.1. Standalone-contract storage isolation
Every contract is standalone with its own default storage layout, and cross-contract calls between distinct singletons (Admin↔Pool, Flash↔Pool) are standard external calls with no shared storage. Slot collisions between distinct contracts cannot occur.
Pool’s own DELEGATECALL targets are a separate case: PoolConfigLib, PoolLiquidityLib and PricingLib (with NUQuarticLib behind PoolConfigLib), plus AdminParamsLib on Admin’s side (AIMM Overview §2.1). Each takes its caller’s storage as a storage parameter, so the compiler resolves the slots and there is no hand-mirrored layout to drift.
- Each
Poolis aPoolProxy: an ERC-1967 proxy holding its implementation in its own slot, whoseupgradeToanswersPoolFactoryonly. ItsPoolStoragesits at slot 0. Cross-pool storage isolation is automatic; code is shared and the factory moves the whole fleet. - The mark store lives in the Pool implementation’s own storage: pools read it by a 1-3 byte
STATICCALLintoPool.fallback, and pushes and store governanceDELEGATECALLthe store logic (MarkStoreP8) from the implementation. It is one store per chain, not per pool. - The
AdminandFlashsingletons are UUPS contracts behind ERC-1967 proxies (UpgradeGate, placed first so its 50 reserved slots lead the layout); their state is keyed by(pool, ...). - The reference
Poolimpl follows an append-only rule onPoolStorage: existing fields’ offsets and types are frozen across upgrades (new fields appended only).ArtifactGuards.t.solpins it at build time, assertingPooldeclares exactly one storage entry ($at slot 0). - ERC-7201 namespaced storage is not used anywhere: plain default storage at slot 0 is sufficient since DELEGATECALL targets are fixed at compile time (no Diamond/module-registry pattern).
5.2. Transient storage (EIP-1153)
Transaction-scoped only, cleared automatically at the end of the transaction: reentrancy guards and flash-loan state. Marks are not cached: one pool call loads each leg’s mark word once and reads it from memory. The guards built on it, the exploit they close and their gas: Flow Guards.
6. Oracle security
| Attack | Defense |
|---|---|
| Flash loan | No write-on-swap (quote source is an external mark, not pool state) |
| Multi-block | Volatility-adaptive per-push deviation band + reference-feed halt, independent where the reference carries a disjoint signer set (Mark Relayer) |
| Low liquidity | Thin books do not move the mark directly. The oracle is a discretionary contract: it decides its own sources and may include pool liquidity among them. Thin-book risk is carried by coverage and toll pricing, not by the mark |
| Staleness | TTL-based freshness check (fail-closed) + staleness surcharge |
Push-path validation (source-time window, per-lane band, k-of-n quorum), the formula and the quorum ceremony: Mark Relayer §3.3. Multi-source aggregation happens off-chain; the chain sees a single mark.
There is one mode and no fallback switch. Degradation escalates in this order:
| Condition | Effect |
|---|---|
age past a grace of min(ttl/2, 30 s) | staleness surcharge widens the spread |
| age past the TTL | revert StaleData (fail-closed) |
confidence above MAX_CONFIDENCE_HALT_BPS (1,000 bps, strict) | revert ThresholdViolation |
| base-token USD mark past its depeg band (code·250 bps, code 0 = 2,500 bps) | revert BaseDepegged (hub halt) |
spoke mark outside refBandBps of its reference | revert PriceOutsideRefBand |
Thresholds and readers: Mark Relayer, Flow Guards.
7. Upgrade security
Pools are not per-instance immutable: one GOVERNANCE-tier fleet upgrade replaces the code of every pool, third-party pools included, with no opt-out. Admin and Flash upgrade separately as UUPS singletons under the same tier, and PoolFactory, the LPToken implementation and the linked libraries have no upgrade path at all. The mark store moves only with the Pool implementation. Procedure, grace window, cancel authority and storage-layout obligation: Admin.
8. Emergency procedures
Runbook and authority: Access Control for the lever list and delays, Guardian Routines for the procedure. Disclosure intake: Audits.
9. Known limitations
9.1. Economic bounds
| Limitation | Bound | Implication |
|---|---|---|
| Max skew | (dimensionless) | the skew offset saturates at the band edge; sign only, no separate premium parameter; see §3 for the mapping to curve bps |
| Anchor depth | MAX_DEPTH = 4 (enforced, general) | Depth is whatever the anchor column says, no feature flag; tree and re-anchoring: Pool §7 |
| Max path | nodes (8 legs) | Unique tree path via the LCA; only the two endpoints settle. Depth bounds one walk, a path is two |
| Worst-case trade cost | Not bounded by config. A spread widened by , confidence or staleness prices that risk; a cap would sell an underpriced quote when the pool most needs the width. The bound is the caller’s minAmountOut, per trade | An integrator must quote and enforce, not read a ceiling off config. The uint16 saturation of SwapQuote.spreadPbps is a field width, not a policy |
9.2. Oracle limitations
The mark is an off-chain aggregate, signed by a -of- attester set and relayed by an allowlisted relayer. Three attack surfaces. The on-chain guards cover the second and third; they barely cover the first. Push-path mechanics: Mark Relayer.
| Surface | Attacker must control | What the contracts do | Residual |
|---|---|---|---|
| 1. The source. The aggregate is computed faithfully; its inputs are not | A majority of the volume-weighted venues behind one signed composite, simultaneously, for at least one push interval | Nothing that can detect it. The signature covers a correctly computed aggregate of manipulated inputs, so every check passes. The lane band in MarkStoreP8 bounds how fast the mark may move; PoolIOLib’s ref-band guard compares it against a reference built from the same aggregation | The largest of the three. Bounded economically and off-chain, never cryptographically |
| 2. The price provider. A signed price that existed on no venue | of roster attester keys, or coercion of the replicas holding them | MarkStoreBase._quorum: strictly ascending signatures from the tier’s committed roster over one verifyingContract-bound EIP-712 digest. Magnitude is then clamped per segment by the lane band, is floored at the class minimum, confidenceBps > MAX_CONFIDENCE_HALT_BPS (1,000) fails closed, srcSecs must strictly advance per lane, and a srcSecs more than 5 s ahead is rejected. Lane halt (guardian, risk steward or owner) is immediate; roster rotation is setAuth at GOVERNANCE | The band caps the step per segment, not per transaction. The cumulative bound is the reference band, and it collapses wherever the reference roster shares the primary’s signers |
| 3. The keeper. Withhold, delay, reorder, submit selectively | The keeper host and its funded EOA. No signing key: price authority comes from the signatures; msg.sender must be on the tier’s relayer list, and a segment lands only within 120 s of its source second | It cannot forge or edit: the digest commits to the whole segment. Reorder and replay fail the per-lane monotonic source-second check: a stale lane is skipped silently, and a band failure darkens the lane. The push emits no event; lane state is a storage read. Withholding hits the TTL and fails closed | Withholding produces a halt, which still serves a griefer. Delay inside the premium-free grace, , is free |
Surface 1 has no cryptographic answer: the pipeline’s pre-signing refusals (Mark Relayer §1.2) catch a minority venue moving, not a coordinated majority, and the bands bound the rate of change, not correctness. What is left is the capital cost of moving a majority of the weighted book, which is low for a thin asset, so listing is a curation decision (Pool Deployment & Curation §7). Where the reference tier shares the primary’s signer set, surface 2’s cumulative bound is gone (Mark Relayer §4.4).
The keeper controls timing, not content; the on-chain bound is the relayer list and the 120 s window, the rest is operational: leader election with failover across a configured keeper set, a per-feed staleness alarm at , and a periodic manifest-parity check against the mark pipeline’s roster. Cadence and outage bound how well the pool tracks the market and when it stops trading; they are not paths to a wrong price.
| Condition | What happens |
|---|---|
| Low keeper cadence, stale mark | Priced first, then refused: the spread carries a staleness premium growing with past a grace of , and past the feed’s TTL FeedMathLib.gate reverts StaleData. Shorter TTLs for faster feeds |
| Fast price movement | Wider spreads via . The per-push band widens with the attested , so a genuine gap move ladders in rather than wedging the feed; past the ceiling it does wedge, and the release is the owner’s reanchor(lane, tierMask) at TUNING: the next push lands unbanded (Mark Relayer) |
| Keeper or feed outage | Fail-closed: swaps revert past TTL (a halt, not a bleed). Multi-venue aggregation removes the single-venue outage, not the single-provider one |
| Reference feed parked | Every armed spoke band fails closed once the reference passes its own TTL, so the reference must be relayed on the same discipline as the primary |
| Leg dark or halted, LP exits | Exits stay open at a bounded rate: a same-asset exit pays min(c_leg, C_lower), where C_lower counts a dark leg at its backed reserves only, so no LP escapes a loss the dark mark hides (Inventory Management §5.2) |
10. Related documentation
| Page | Role |
|---|---|
| Access Control | Principals, guardian surface, halt authority, timelock table (SSoT) |
| Deployer | Deploy order, CREATE3 addresses, handover |
| Admin | Owner multisig: upgrades, listings, treasury rotation, emergency lanes |
| Risk Steward | Fenced param lane: clamps, fences, what fails closed |
| Guardian | Guardian key: levers, argument traps, escalation |
| Mark Relayer | External mark, push triggers, signer quorum, TTL and bands, reference tier |
| Treasury | Fee claims, untrusted, bounds |
| Guards | Halt bits, per-asset risk thresholds |
| Flow Guards | Reentrancy, flash, JIT cooldown, base depeg halt, spoke reference bands |
| Guardian Routines | Operational runbooks and release delays |
| Observability | What is emitted, where to read it, what alerts |
| Audits | Internal audits, perpetual auditing, bug bounty, third-party reports as they land |
| Internal Audit 2026-09 | Pre-launch internal audit report |
| Admin module | Per-pool admin ops |
| Risk Disclaimer | Legal risk surface |