Skip to main content
The user-facing entry to Continuum. Holds the Market PDA, mints L+S SPL tokens, and exposes the privileged keeper-only single-sided ixns used inside arb cycles.

Accounts

Market PDA

The central account for one synthetic asset.

UserCollateralPosition PDA

Lazy account for users who opt into multi-collateral mode (where the market accepts more than one collateral type, weighted). Most markets don’t use this.

FeeWaiver PDA

A grant created by donate_to_vault that lets the donor mint/redeem fee-free for some duration.

Fees

Base mint and redeem fees are 10 bps each. The keeper posts vol-informed updates through the admin-signed update_market: fee = clamp(max(10, fee_mult × q90), 10, 100) bps, where q90 is the market’s trailing realized move quantile - 10 bps in calm regimes, up to 100 bps in storms. The fee is the at-NAV venue’s defense against gap risk; it moves with measured volatility, not a forecast. The split: dev_tax_pct (default 10%) of each fee goes to the fee_recipient’s cUSDC account; the remaining ~90% stays in the collateral vault as extra backing. Retained fees are real collateral and count toward total_collateral.

TWAP freshness gate

In any non-Normal risk state (all devnet markets run in ProxyMode), pricing comes from the keeper-relayed market.user_twap_price. Every ratio- or price-dependent instruction - mint_paired, redeem_paired, the keeper single-side paths, donations, excess withdrawals, rebalances - rejects with TwapNotAvailable when twap_updated_at is older than MAX_TWAP_AGE_SECS = 300 (5 minutes). A delayed keeper can’t be quoted against a stale price.

Instructions

User-facing (anyone can call)

mint_paired(collateral_amount: u64)

Deposits collateral_amount cUSDC, mints matched L and S to the user’s ATAs. Fee collateral_amount × mint_fee_bps / 10_000; the dev-tax slice goes to dev_token_account, the rest stays in the vault. Full mint flow

redeem_paired(l_amount: u64, s_amount: u64)

Burns L and S in independent quantities, returns l_amount × L_NAV + s_amount × S_NAV - fee cUSDC. Solvency haircut. The gross payout is nominal × min(1, CR) where CR is the vault’s live collateralization at the same NAV. While the vault is at or above 100% (the normal state) this is a no-op. If a large NAV move ever left it under 100%, every redeemer is scaled by the same factor - no first-mover advantage, no bank-run race, and the ratio is preserved for everyone who stays. Full redeem flow

mint_paired_with_waiver(collateral_amount: u64) / redeem_paired_with_waiver(l_amount: u64, s_amount: u64)

Same as the above but consume an active FeeWaiver to skip the fee. Requires the market vault to be over-collateralized (≥102%) for the waiver to apply. Donate cUSDC into the market’s collateral vault. Creates or extends a FeeWaiver for the donor. Used by power users to earn fee-free mint/redeem in exchange for boosting vault collateralization.

permissionless_burn(is_long: bool, amount: u64)

Burns L or S from the caller’s own token account without any collateral payout, decrementing the supply ledger. Only the token-account owner may burn (delegates are rejected). Per-token collateralization strictly improves. Used for voluntary forfeiture and by the CLP’s clp_burn_excess CPI to retire asymmetric inventory residue.

Keeper-privileged (signer = market.keeper_authority)

keeper_mint_single(is_long: bool, collateral_amount: u64)

Fee-free, single-sided mint at NAV. Deposits collateral_amount cUSDC, mints only the chosen side’s tokens at NAV. Used inside arb cycles. No mint fee, no worst-case quoting markup. The keeper pays full NAV for one side, so the vault grows by exactly the value of the new tokens - per-token collateralization is preserved at any size. (An absolute stress-floor check on this path was tried and removed; the error variant SingleSideMintStressFloor survives in the IDL but is not enforced.)

keeper_redeem_single(is_long: bool, token_amount: u64)

Fee-free, single-sided redeem at NAV. Burns token_amount of the chosen side, returns token_amount × NAV cUSDC. Enforces a post-redeem floor of CR ≥ 100% - peg-keeping operates freely while the vault is solvent, and refuses to make an underwater vault worse.

keeper_burn_single(is_long: bool, token_amount: u64)

Burn only - no cUSDC transfer. Residue cleanup when an arb cycle leaves unsold inventory; the vault keeps the mint-time deposit, the keeper eats the cost.

redeem_single_for_vault(is_long: bool, token_amount: u64) (CLP CPI only)

Mirror of keeper_redeem_single for the CLP’s own inventory: burns from CLP-PDA-owned ATAs, pays cUSDC into the per-market clp_vault. The signer must be the canonical CLP PDA ([b"clp", market] under the CLP program ID), entered via the CLP program’s vault_redeem_single. Same CR ≥ 100% post-redeem floor.

update_risk_state(new_state, user_twap_price, oracle_confidence)

The hot path: mirrors oracle health into risk_state and writes the TWAP + confidence the whole program prices against. Two hard bounds protect against a compromised keeper key: the TWAP may move at most 25% per call, and Stress can only de-escalate through Recovery (where the fee multiplier unwinds over an hour) - never jump straight to Normal.

set_buffer_target(buffer_target_bps: u16)

Posts the market’s dynamic overcollateralization buffer - the per-market solvency target that replaced the static 102%. The keeper sizes it from the volatility regime and live inventory; on-chain it is clamped to [100, 5000] bps over parity (target ratio 101%-150%), and 0/unset falls back to the 200 bps (102%) default. This target gates donations, fee waivers, excess withdrawals, and cross-market rebalances.

set_s_gamma_index(s_gamma_index: u64)

Posts the short-leg volatility-decay scalar γ (1e9 fixed). The keeper ratchets it down by realized per-cycle variance, marking S_NAV down to bank choppiness-convexity as collateral surplus. On-chain it rejects any value above the current γ (ratchet-down only — see GammaMustRatchetDown) and clamps to a 0.5 floor. Gated off-chain by CLMM_GAMMA_DECAY_ENABLED (default off). Does not touch the long leg. See NAV → Volatility decay.

rebalance_collateral(amount: u64)

Cross-market collateral move. With the unified vault this is a scalar-only ledger move — no cUSDC token transfer happens; it shifts total_collateral from an over-collateralized market to an under-collateralized one. The source floor is enforced on-chain: the source market must remain at or above its own dynamic target after the move, and only its liquid share (total_collateral − deployed_to_yield) may move. (The old TWAP-freshness gate was removed — with no token movement the source-CR floor is the real guard, and gating on a closed-market TWAP only produced spurious OraclePriceUnavailable reverts.) Both markets must share the keeper authority.

deploy_collateral_to_yield(amount, protocol) / recall_collateral_from_yield(amount, protocol)

Move idle collateral to an external lending venue and back. Deployment is capped at yield_ceiling_bps of total_collateral (default 60%); the rest stays liquid for redemptions. The keeper composes the actual lending CPI in the same transaction.

harvest_collateral_yield(yield_amount: u64)

Routes net interest from a yield recall to the CLP vault as protocol revenue. Principal returns via recall_collateral_from_yield; this instruction forwards only the earned spread and advances total_yield_harvested.

Admin (signer = market.authority)

initialize_market(asset_symbol, oracle_type, initial_l_price, initial_s_price, mint_fee_bps, redeem_fee_bps, keeper_authority)

Create a new market. Allocates Market PDA, creates L and S SPL mints (mint authority = market PDA), creates the collateral vault.

update_market(mint_fee_bps?, redeem_fee_bps?, is_active?, dev_tax_pct?)

Admin updates to fees, the active flag, and the dev-tax slice (≤ 25%). This is also the channel for the keeper’s vol-informed fee posting - see Fees.

update_keeper_authority(new_keeper) / update_market_oracle(oracle_type) / update_fee_recipient(new_recipient)

Rotate the keeper signer, repoint the oracle, or reroute fee flow. Bound to market.authority.

withdraw_excess_to_clp(amount: u64)

Drain collateral above the market’s dynamic target ratio to the per-market CLP vault - the inverse of donate_to_vault. Guards: fresh TWAP required, only liquid (non-yield-deployed) collateral, and the post-withdrawal ratio must stay at or above target_ratio_bps().

configure_yield_ceiling(ceiling_bps?, enabled?)

Enable collateral-yield management and set the deployable fraction (max 90%).

configure_worst_case_quoting(enabled, normal_mult, proxy_mult, stress_mult)

Configure per-state confidence multipliers used in mint pricing markup.

configure_risk_fees

Per-state fee adjustments (rare; defaults are usually fine).

enable_multi_collateral, add_collateral_vault, update_collateral_vault

Multi-collateral mode (accept multiple stablecoin/equity tokens as collateral with weights). Off for most markets; enabled per-market by admin if needed.

Errors

Full error catalog

TypeScript

Full mint+redeem example

See also

Mint flow

Step-by-step mint_paired walkthrough.

Redeem flow

Step-by-step redeem_paired walkthrough.