> ## Documentation Index
> Fetch the complete documentation index at: https://docs.continuum.markets/llms.txt
> Use this file to discover all available pages before exploring further.

# NAV math

> How long and short NAVs are derived, why the constant-product invariant matters, and how to compute paired position value.

NAV (Net Asset Value) is what each long and short token is worth in cUSDC, **at the protocol layer**. It is what you pay to mint, what you receive to redeem, and what the keeper anchors pool prices to.

## The two formulas

```
L_NAV = market.user_twap_price                                (or initial_l_price if TWAP == 0)
S_NAV = (initial_l_price × initial_s_price) / L_NAV × γ        (γ = s_gamma_index / 1e9, default 1.0)
```

`user_twap_price` is the on-chain TWAP from the [oracle program](/programs/oracle), 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](#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 relationship `L_NAV × S_NAV = initial_l × initial_s` is invariant by construction. This produces several useful properties:

1. **Pair value is bounded.** `L_NAV + S_NAV` is minimized at `L_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.
2. **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. This removes *positioning-driven* funding specifically; the short leg still carries a [volatility decay](#volatility-decay) for the pair's convexity.
3. **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.
4. **Long side has unbounded upside.** No cliff, no liquidation; if the underlying 100xs, the L side 100xs.

## Computing paired position value

For a position holding `n_L` long tokens and `n_S` short tokens:

```
position_value_cUSDC = n_L × L_NAV + n_S × S_NAV
```

For a position minted from `mint_amount` cUSDC at the time of minting:

```
mint_value_per_side = (mint_amount × (1 - mint_fee_bps / 10_000)) / 2
n_L                 = mint_value_per_side / L_NAV_at_mint
n_S                 = mint_value_per_side / S_NAV_at_mint
```

So at mint time, the position value equals `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):

```
mint_value_per_side = (100 × 0.999) / 2 = 49.95 cUSDC
n_L = 49.95 / 480     = 0.10406 QQQL
n_S = 49.95 / 1       = 49.95   QQQS
```

Now QQQ moves to some new price `P`. New NAVs:

```
L_NAV = P
S_NAV = (480 × 1) / P = 480 / P
```

Position value:

```
value = 0.10406 × P + 49.95 × (480 / P)
      = 0.10406 × P + 23976 / P
```

Some sample values:

| QQQ price | Position value | Note |
| - | - | - |
| 240 (-50%) | 124.85 cUSDC | gain on short side dominates |
| 384 (-20%) | 102.39 cUSDC | small gain - convexity |
| 432 (-10%) | 100.43 cUSDC | small gain |
| **480** (mint) | **100.00 cUSDC** | break-even (modulo fee) |
| 528 (+10%) | 100.30 cUSDC | small gain |
| 576 (+20%) | 101.55 cUSDC | small gain |
| 720 (+50%) | 108.20 cUSDC | gain on long side dominates |
| 1440 (+200%) | 166.50 cUSDC | long appreciation |

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:

```
S_NAV = (initial_l_price × initial_s_price) / L_NAV × γ
```

* **Ratchet-down only.** The on-chain `set_s_gamma_index` instruction rejects any value above the current γ, and clamps to a floor of `0.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; decay on.** `CLMM_GAMMA_DECAY_ENABLED` defaults **on** as of June 2026 (solvency-critical; set `=false` to disable). Until the keeper has posted a value `s_gamma_index` reads as `1e9` and every formula above reduces to the plain constant-product NAV.

See [Solvency](/concepts/solvency) for why this only covers the variance regime and not a directional run.

## NAV in the on-chain Market account

The relevant fields on the [`Market` PDA](/programs/mint-redeem#market-account):

```rust theme={null}
pub struct Market {
    pub initial_l_price: u64,    // 6 decimals (lamports of cUSDC per L token)
    pub initial_s_price: u64,    // 6 decimals
    pub user_twap_price: u64,    // 6 decimals - the on-chain TWAP source for L_NAV
    pub twap_updated_at: i64,    // unix seconds
    // ... fees, mints, oracle, yield (deployed_to_yield), buffer_target_bps ...
    pub s_gamma_index: u64,      // 1e9 fixed; 0/unset → 1.0 (no decay). Short-leg vol decay.
}
```

To read NAV from a client:

```typescript theme={null}
import { BN } from "@coral-xyz/anchor";

const market = await mintRedeem.account.market.fetch(marketPDA);
const lNav = market.userTwapPrice.gt(new BN(0))
  ? market.userTwapPrice
  : market.initialLPrice;
// Short NAV includes the γ decay scalar (1e9 = 1.0). 0/unset → no decay.
const GAMMA_ONE = new BN(1_000_000_000);
const gamma = market.sGammaIndex && market.sGammaIndex.gt(new BN(0))
  ? market.sGammaIndex
  : GAMMA_ONE;
const sNav = market.initialLPrice.mul(market.initialSPrice).div(lNav).mul(gamma).div(GAMMA_ONE);
```

The 6-decimal scaling means `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 **not** `Normal`, the mint-redeem program applies **worst-case quoting** to **both mint and redeem**, quoted **per leg against the user**. It is bounded by `oracle_confidence` (stored on the market) and a state-specific multiplier `c = oracle_confidence × mult`:

| Risk state | Multiplier (default) | Confidence half-width `c` |
| - | - | - |
| `Normal` | 1× | NAV directly (no markup) |
| `ProxyMode` | 1.5× | `1.5 × confidence_bps` |
| `Stress` | 3× | mint **and** redeem disabled by default (free-float; `MintNotAllowedInStress` / `RedeemNotAllowedInStress`) — the 3× markup applies only if an operator re-enables trading via `allow_mint_in_stress` / `allow_redeem_in_stress` |
| `Recovery` | decays 3× → 1× over 1h | shrinks with recovery |

Because the long NAV is linear in price (`L_NAV = P`) but the short NAV is **inverse** (`S_NAV = K/P`), a single widened price can't protect both legs — so each leg is quoted at the price that moves *against* the user:

| | Long leg (`L_NAV`) | Short leg (`S_NAV = K/P`) |
| - | - | - |
| **Mint** (user buys) | `P + c` → fewer L | priced at `P − c` → `K/(P−c)` is higher → fewer S |
| **Redeem** (user sells) | `P − c` → less out | priced at `P + c` → `K/(P+c)` is lower → less out |

This makes a paired mint→redeem round-trip return **less** than the deposit at any confidence (the spread is protective on both sides). An earlier version widened one shared price into both legs, which inverted the short leg and let a round-trip return *more* than deposited — that's fixed.

→ [Risk states explained](/concepts/risk-states)

## 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:

```
L_NAV = initial_l_price   (fallback)
S_NAV = initial_s_price   (since L_NAV × S_NAV = initial_l × initial_s holds at boot)
```

Mints during the boot window use the initial prices. Once TWAP populates, NAV becomes price-responsive.

## Edge: oracle stale or paused

If `last_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](/concepts/risk-states).

## NAV vs pool price

NAV is what the protocol mints/redeems at. **Book price** is what the native per-side bin 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).

→ [Trading venues](/flows/trade)

## NAV in TradingView and analytics

Continuum's frontend overlays NAV on a TradingView chart. The long-side book price is shown directly; the short-side is **derived from the long side via the constant-product invariant** for visual coherence (otherwise two charts that should be inverse appear unrelated). When you read the S book programmatically, you get its actual base price.
