Market PDA, mints L+S SPL tokens, and exposes the privileged keeper-only single-sided ixns used inside arb cycles.
Accounts
Market PDA
UserCollateralPosition PDA
FeeWaiver PDA
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-signedupdate_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 inProxyMode), 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_to_vault(amount: u64)
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
See also
Mint flow
Step-by-step
mint_paired walkthrough.Redeem flow
Step-by-step
redeem_paired walkthrough.
