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.

SectionPage
Who may do what, and how long each action takesAccess Control
Transaction-time guards: reentrancy, flash, JIT, depeg halt, reference bandsGuards
What is emitted, where to read it, what alertsObservability
Internal audits, perpetual auditing, disclosure intake, third-party reports as they landAudits

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

LayerFocusSpecified in
EconomicExternal mark, inventory skew bounds, coverage toll§3
Access controlTimelocked governance, asymmetric guardian haltAccess Control
OperationalAsset halt, depeg halt, flow guardsGuards
CodeTests, invariant proofs, perpetual internal auditing; third-party reports linked as they land§4, Audits

2. Threat model

2.1. Adversary capabilities

AdversaryCapabilitiesMitigations
Flash Loan AttackerUnlimited capital for single transactionNo write-on-swap (external mark); volatility-adaptive per-push deviation band
Whale TraderLarge positions, multi-block attacksExternal keeper mark; inventory skew bounds
MEV SearcherOrdering, sandwich; oracle-push OEVSlippage protection; symmetric spread + skewed mid; minFee; private keeper relay preferred
Oracle ManipulatorCompromised keeper; stale pushk-of-n quorum over a committed roster; per-lane deviation band; lane halt; confidence halt; depeg bands
Governance AttackerMajority controlTimelocks + grace windows (Access Control)
Smart Contract ExploiterCode bugsGuardian 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.

RoleTrustBreaks contractsProtocol lossUser lossBound
OwnerTrustedyesyesyesUpgrades timelocked, guardian-cancellable
GuardianLimitednononoHalt, tighten and cancel only
TreasuryUntrustednoyesnoAccrued fee shares only
Mark signersTrustednoyesyesk-of-n quorum, per-push deviation band
Oracle relayerUntrustednononoNo signing key, TTL fails closed
Pool seat holderUntrustednonoyesIts own pool only, never official ones
UsersUntrustednononoEvery 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 LISTING delay. 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.claimTreasury and 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 k of n registered 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 initdata it passes to the owner-only PoolFactory.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.computeInventorySkew returns a dimensionless int8 clamped to [-100,100], saturating at coverage c≤12 and c≥2. One skew unit maps to 104/200=50 bps of curve-x displacement, so the clamp caps the coverage-driven mid shift at ±5000 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 minLiquidity is the hard floor on every outflow. A swap, withdrawal or flash loan reverts InsufficientAmount if it would leave Rliq below it (PoolIOLib.settle; there is no exec function). Quoted outflows (swap, cross exit, liability swap) also stop at the leg’s coverage floor c*=κcov/(κcov+BPS): 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 converts face · C and a same-asset exit is allocated min(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 cap maxLiabWeightBps (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 Pool is a PoolProxy: an ERC-1967 proxy holding its implementation in its own slot, whose upgradeTo answers PoolFactory only. Its PoolStorage sits 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 STATICCALL into Pool.fallback, and pushes and store governance DELEGATECALL the store logic (MarkStoreP8) from the implementation. It is one store per chain, not per pool.
  • The Admin and Flash singletons 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 Pool impl follows an append-only rule on PoolStorage: existing fields’ offsets and types are frozen across upgrades (new fields appended only). ArtifactGuards.t.sol pins it at build time, asserting Pool declares 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

AttackDefense
Flash loanNo write-on-swap (quote source is an external mark, not pool state)
Multi-blockVolatility-adaptive per-push deviation band + reference-feed halt, independent where the reference carries a disjoint signer set (Mark Relayer)
Low liquidityThin 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
StalenessTTL-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:

ConditionEffect
age past a grace of min(ttl/2, 30 s)staleness surcharge widens the spread
age past the TTLrevert 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 referencerevert 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

LimitationBoundImplication
Max skew±100 (dimensionless)the skew offset saturates at the band edge; sign only, no separate premium parameter; see §3 for the mapping to curve bps
Anchor depthMAX_DEPTH = 4 (enforced, general)Depth is whatever the anchor column says, no feature flag; tree and re-anchoring: Pool §7
Max pathLmax=2Dmax+1=9 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 costNot 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 tradeAn 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 k-of-n 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.

SurfaceAttacker must controlWhat the contracts doResidual
1. The source. The aggregate is computed faithfully; its inputs are notA majority of the volume-weighted venues behind one signed composite, simultaneously, for at least one push intervalNothing 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 aggregationThe largest of the three. Bounded economically and off-chain, never cryptographically
2. The price provider. A signed price that existed on no venuek of n roster attester keys, or coercion of the replicas holding themMarkStoreBase._quorum: k 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 GOVERNANCEThe 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 selectivelyThe 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 secondIt 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 closedWithholding produces a halt, which still serves a griefer. Delay inside the premium-free grace, min(ttlSecs/2,30s), 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 max(2·heartbeat,ttlSecs/2), 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.

ConditionWhat happens
Low keeper cadence, stale markPriced first, then refused: the spread carries a staleness premium growing with στ past a grace of min(ttlSecs/2,30s), and past the feed’s TTL FeedMathLib.gate reverts StaleData. Shorter TTLs for faster feeds
Fast price movementWider spreads via σ. The per-push band widens with the attested Δt, so a genuine gap move ladders in rather than wedging the feed; past the 10dmax 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 outageFail-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 parkedEvery 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 exitsExits 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)

PageRole
Access ControlPrincipals, guardian surface, halt authority, timelock table (SSoT)
DeployerDeploy order, CREATE3 addresses, handover
AdminOwner multisig: upgrades, listings, treasury rotation, emergency lanes
Risk StewardFenced param lane: clamps, fences, what fails closed
GuardianGuardian key: levers, argument traps, escalation
Mark RelayerExternal mark, push triggers, signer quorum, TTL and bands, reference tier
TreasuryFee claims, untrusted, bounds
GuardsHalt bits, per-asset risk thresholds
Flow GuardsReentrancy, flash, JIT cooldown, base depeg halt, spoke reference bands
Guardian RoutinesOperational runbooks and release delays
ObservabilityWhat is emitted, where to read it, what alerts
AuditsInternal audits, perpetual auditing, bug bounty, third-party reports as they land
Internal Audit 2026-09Pre-launch internal audit report
Admin modulePer-pool admin ops
Risk DisclaimerLegal risk surface