Venue 1: native bin books
Each market has onePool account per side, a long pool and a short pool (PDA seeds [b"pool", market, side]), so there are 26 native pools across the 13 live markets.
- Up to 16 price bins live inline in the
Poolaccount itself (live books use 12, and XAU’s long book uses 6). There are no separate bin-array accounts and no rent churn on a reposition. - The geometry has two zones: fine core steps near the centre and wide wings further out. QQQ uses three 20 bps core steps, then 300 bps wings.
configure_pool_geom(wing_step_bps, core_bins)sets it. - The keeper posts the shape.
set_pool_shape(base_price_1e6, asks, bids)writes the centre and the per-bin sizes in one call, and the program checks the sums against the pool’s vault balances. The keeper centres each book on NAV plus a small inventory shift, and repositions it when NAV drifts past a gate of at most 20 bps.
pool_swap(params: PoolSwapParams { direction, amount_in, min_out }) walks the bins with real slippage and charges no swap fee. It is pure inventory trading: nothing is minted or burned. It trades only while the market floats: its risk state isn’t Normal and its last price push is more than 300 s old, the same condition that freezes mint and redeem. While prices are pushing it fails with PoolClosedWhileLive, and trades go through committed orders. Nothing in the program can set pool.frozen or disable a pool, so there is no pool-level kill switch. pool_swap is held to a rolling notional cap: 1,000,000 cUSDC per hour on QQQ, SPY and XAU, and unset (uncapped) on the other markets, as of 11 Oct 2026.
What the book actually is
The keeper’s env vars carry aCLMM_ prefix and its pricing module is clmm.rs. Those are legacy names. The venue is a bin book, and it works differently from a Uniswap v3 pool:
Mechanically it is a discrete-bin liquidity book, the Liquidity Book family, where price moves in discrete bins. Ask bin
i sits i + 1 steps above the centre and bid bin i sits i + 1 steps below. Each step multiplies the price by 1 + step/1e4, using the core step for the first core_bins steps and the wing step beyond. A bin holds a reserve and fills at its own price until it runs out.
It differs from those too, by being less of an AMM. The whole book lives inline in one Pool account with no per-bin accounts, and the keeper posts the ladder; deposits don’t shape it. There is no bonding curve. The most accurate description is a keeper-quoted bin book: an on-chain market maker whose quotes are settled by an AMM-style swap instruction.
We keep the CLMM_* env-var prefix and the clmm.rs filename for compatibility with deployed configuration; the docs say “book” or “bin book”.
Lean by design
Book depth is sized to demand, never to TVL:- Loss to toxic arbitrage grows with deployed depth, while organic revenue is capped by demand, so depth beyond demand is pure downside.
- Bid cUSDC sitting in bins earns nothing, so each side’s bids are held at the depth its next free-float book will post (at least $500). The excess is swept to the CLP vault, where it provisions committed orders.
- Asks trim to at most 1.5× bids via
pool_sweep_synth.
Venue 2: committed orders
The large-order path uses three instructions and one account:commit_swap needs the market’s swap guards (swap_guards) enabled and not frozen.
How the venue behaves:
-
There is no size cap of its own, but the market’s exposure cap limits how far traders can go net long or short: the order fills up to the room left and the rest is refunded. The fill price is set after the commitment, so latency arbitrage is impossible by construction. Besides the cap, the limits are oracle health, a 100% collateral floor and keeper-provisioned inventory. A fill that fails any of them reverts, and the order stays pending for a later push. A
min_outmiss refunds the order instead. -
COMMIT_SPREAD_BPS = 10is only a floor. The keeper postscommit_spread_bps = clamp(half-spread, 10, 500)from the books’ vol model: 18 bps for QQQ, 15 for SPY and 18 for XAU as of 10 Oct 2026. The post-commit price removes latency arbitrage, and the spread prices the protocol’s convexity exposure on large fills. -
Funds are always recoverable. After the 180 s TTL (
COMMIT_TTL_SECS), anyone can sendcancel_swap, which returns the escrow to the owner whatever the oracle, keeper or admin state. The keeper sends it about 5 s after expiry, and the app offers a Refund now button 60 s after. Before expiry nobody can cancel. Every terminal path (settle, refund, cancel) closes theOrderaccount and returns its rent. -
Only the keeper can call
settle_swap: the signer must beclp.authorityormarket.keeper_authority. It settles at the first push it sees after the commit, so the owner can’t pick the price. The keeper cranks it every 20 s, or every 5 s while orders are pending. -
If a payout or refund can’t reach the owner’s recorded account (it is closed, re-owned, frozen or the wrong mint), the settle or cancel still succeeds and the order is held. The funds wait in a claim account or the escrow until
claim, which the keeper cranks after creating the owner’s token account. The keeper fronts the claim account’s rent and gets it back.
Order account layout supports getProgramAccounts filters: user at offset 8, market at 40, status at 74.
Retired: the instant oracle swap
clp_swap(side, direction, amount_in, min_out) filled instantly from CLP inventory at NAV ± spread_bps, on QQQ, SPY and XAU. It filled at the last pushed price, which is a stale quote between pushes, and it was retired on 11 Oct 2026. The instruction keeps its accounts and arguments, so existing clients still decode it, but every call fails with SwapDisabled.
Its guards, set by configure_swap, now serve committed orders. On the 13 markets with guards on, settle_swap needs a price at most 600 s old and a market at least 100% collateralized at that price. The confidence and deviation limits (5,000 and 10,000 bps) are off in practice, and the price write itself refuses a move of more than 25% per push.
Vaults and capital flow
vault_mint_pairs, vault_redeem_pairs and vault_redeem_single are CPIs into mint-redeem signed by the CLP PDA. The paired ones go through the ordinary mint_paired and redeem_paired, at NAV and with the same fee and price checks as any user. vault_redeem_pairs burns exactly the amounts the CLP asks for, without the balanced-pair trim that user redemptions get. Each one must name the CLP’s own market, and its cUSDC, L and S accounts must belong to the CLP PDA. Inventory can’t be minted against another market or paid to a wallet.
Each market keeps its collateral in its own vault, owned by its Market PDA, and keeps its own total_collateral ledger. Every outflow is limited to that market’s liquid share, total_collateral − deployed_to_yield. QQQ, SPY and XAU shared a unified vault until 11 Oct 2026. After they moved out, every vault’s balance equaled its market’s ledger, and the program upgrade that day removed the unified vault. A cross-market rebalancer moves only collateral above the source market’s own posted buffer target. rebalance_collateral on mint-redeem caps each move at that amount on-chain, so a larger request moves less and never takes the source below target.
The treasury
TheGlobalClp vault at the top of the diagram is the protocol treasury. It held about 40.0M cUSDC late on 12 Oct 2026, after the keeper returned idle CLP cash on 11 Oct and the CLP’s own holdings were cut to working size on 12 Oct. On devnet the keeper is now burning it down, and it held 36.1M cUSDC on 13 Oct 2026. Besides allocate_to_market, four instructions draw on it:
backstop_marketmoves cUSDC straight into a market’s collateral vault and credits itstotal_collateral. It has no rate limit, because the funds stay inside the protocol. The keeper calls it when a market falls below 100% or stays short of its posted target; see Solvency → Treasury backstop.admin_withdrawpays cUSDC out of the protocol, to one named account only. Its allowance refills at a rate the keeper keeps in proportion to the balance, so a full exit takes 24 h whatever the treasury holds. Loosening the limits waits 24 h. A guardian key can freeze withdrawals and veto a loosening, but it can’t move funds.treasury_lendsends idle cUSDC to a lending venue andtreasury_recallbrings it back. A lend spends the same allowance asadmin_withdrawand stops while the treasury is frozen; a recall restores the allowance and always works. Nothing lends the treasury on a schedule yet, and devnet has no venues.advance_to_marketlends cUSDC to a market’s collateral vault so that a queued redemption can be paid, within the market’s advance cap. It is not charged to the withdrawal allowance, because the cash stays inside the protocol, and the market repays it from its own next recall. See mint-redeem → MarketAdvance.
Where protocol revenue accumulates
- Trading-spread profits build up in the pool vaults and CLP custody as the books buy below NAV and sell above it. They reach a market’s collateral only when someone adds them. Anyone can call
donate_to_vault, and the market authority can callfund_collateral. Profits deposited into the treasury reach a market throughbackstop_market, which the keeper calls when a market falls below 100% or stays short of its target. The keeper calls neitherdonate_to_vaultnorfund_collateral. - Of each mint and redeem fee, 90% stays in the market’s collateral vault and 10% (
dev_tax_pct) goes to the market’s fee recipient, which has been the treasury on every live market since 12 Oct 2026. - Harvested yield goes into the market’s own vault and is added to
total_collateral, so it raises that market’s collateral ratio. Collateral above the target can still move to the CLP withwithdraw_excess_to_clp. Nothing is lent on devnet: the keeper’s emulator harvests the yield its mainnet plan would earn, and every live market hasyield_enabledset as of 11 Oct 2026.
Reading pool state
Read more
CLP program reference
All instructions, account fields, errors.
Keeper
The spread engine and book-management loop behind set_pool_shape.
Solvency
How lean books and depth caps defend the collateral ratio.
Trading flows
Choosing a venue and executing trades end to end.

