The two formulas
user_twap_price is the on-chain TWAP from the oracle program, updated by the keeper roughly every 15 seconds. initial_l_price and initial_s_price are set at market initialization and never change. γ is the volatility-decay scalar (s_gamma_index / 1e9), 1.0 unless the keeper has marked the short leg down — see Volatility decay. While γ = 1.0 the trailing factor is a no-op and these are the plain constant-product NAVs.
For QQQ at launch: initial_l_price = 480, initial_s_price = 1. So if L_NAV = 528 (QQQ moved +10%), then S_NAV = 480 × 1 / 528 = 0.909.
Why constant-product
The relationshipL_NAV × S_NAV = initial_l × initial_s is invariant by construction. This produces several useful properties:
- Pair value is bounded.
L_NAV + S_NAVis minimized atL_NAV = S_NAV = √(initial_l × initial_s)(the geometric mean) and grows as either side moves away. There is no scenario where the pair value collapses to zero. - No funding rate needed. In a perp DEX, funding redistributes from longs to shorts (or vice versa) when one side is over-popular. Continuum doesn’t need this - the constant-product NAV does the redistribution automatically through reciprocal NAV movement. If everyone holds L, S becomes cheap, and any new mint on the popular side is dilutive in NAV terms.
- Short side can never go negative. As
L_NAV → ∞,S_NAV → 0. A holder of S can lose 100% of their position but never owe more. - Long side has unbounded upside. No cliff, no liquidation; if the underlying 100xs, the L side 100xs.
Computing paired position value
For a position holdingn_L long tokens and n_S short tokens:
mint_amount cUSDC at the time of minting:
mint_amount × (1 - mint_fee_bps / 10_000) - i.e., your deposit minus fees. Trivially.
How position value moves with the underlying
Take a 100 cUSDC mint at QQQ = 480 (initial_l = 480, initial_s = 1, mint fee = 10 bps):P. New NAVs:
Note: a paired position has near-zero delta locally - it’s a curved zero-coupon. The pair becomes valuable only at large moves in either direction. To take a directional view, sell one leg.
Volatility decay
The short-leg γ-index. All of the pair’s convexity lives in the short leg:L_NAV = P is linear (d²L/dP² = 0), while S_NAV = K/P is convex (d²S/dP² = 2K/P³ > 0). The protocol is the counterparty to that convexity, so it is structurally short volatility on the short side.
To charge for that, the keeper can post a decay scalar γ = s_gamma_index / 1e9 that marks the short NAV down:
- Ratchet-down only. The on-chain
set_s_gamma_indexinstruction rejects any value above the current γ, and clamps to a floor of0.5(at most 50% cumulative decay). γ can never be raised to retroactively restore short claims. - It charges realized variance, not direction. The keeper decays γ by the realized per-cycle variance (
Δγ/γ ≈ σ²), so a choppy path costs the decay while a smooth directional move barely does — the directional payoff lives in the untouchable long leg. This banks the choppiness-convexity the protocol is short as collateral surplus. - It is not a funding rate. There is no periodic cash transfer between holders. γ is a structural mark on the short token’s value; an instant mint→redeem round-trip is unaffected at a fixed γ, and long positions never pay it. The frontend surfaces it on the short side as “volatility decay,” deliberately not “funding.”
- Default 1.0 (off). Until the keeper posts a value (
CLMM_GAMMA_DECAY_ENABLED, off by default),s_gamma_indexreads as1e9and every formula above reduces to the plain constant-product NAV.
NAV in the on-chain Market account
The relevant fields on theMarket PDA:
lNav.toNumber() / 1e6 gives a human-readable price.
TWAP and risk-state effects on NAV
The TWAP is what the user-facing flow uses. There’s also a keeper TWAP (shorter window) used internally for arb decisions. They derive from the same observation buffer in the oracle program but with different windows. When the oracle’s risk state is notNormal, the mint-redeem program applies a worst-case quoting markup to mint pricing. This is bounded by oracle_confidence (also stored on the market) and a state-specific multiplier:
Redeem prices are not marked up - only mint. (Otherwise users would be stranded when the oracle goes shaky.)
→ Risk states explained
When TWAP is zero
On a freshly initialized market,user_twap_price = 0 until the keeper has pushed enough observations to populate the TWAP window (default 5 observations / ~75 seconds). During this window:
Edge: oracle stale or paused
Iflast_oracle_update is past the staleness threshold, the oracle’s OracleHealth flips to Passive, and the mint-redeem program rejects new mints (OraclePriceUnavailable). Existing redeems still work, but at the last-fresh NAV - see Risk states.
NAV vs pool price
NAV is what the protocol mints/redeems at. Book price is what the native per-side CLMM book trades at between repositions - the two are kept close by structure, not by arbitrage. The keeper re-pins each book’s base to NAV whenever drift exceeds the per-market gate (20-40bps), and the standing half-spread means the book never quotes inside NAV ± spread anyway. In steady state,book_price ≈ NAV ± half_spread. During closed market hours the oracle withholds pushes and the books float - the gap can widen, and the first fresh print re-anchors everything. A user choosing a venue picks by size and urgency:
- Small size, urgent: hit the book (
pool_swap) - instant, pays the bin ladder’s real spread. - Large size: commit an order - fills at the next oracle print at NAV ± 10bps, any size.
- Want the pair, not a direction: mint or redeem at NAV (pays the mint/redeem fee, no spread).

