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

# Redeem paired tokens

> Burn long and short tokens (independently), receive cUSDC at NAV.

`redeem_paired(l_amount, s_amount)` burns L and S in **independently chosen quantities** and returns the value at NAV in cUSDC. Unlike mint, redeem amounts are not quantity-paired - they are NAV-weighted.

## Math

In the normal **over-collateralized** state (the protocol targets a solvency buffer above 100%), redeem returns full NAV value:

```
gross  = l_amount × L_NAV + s_amount × S_NAV
fee    = gross × redeem_fee_bps / 10_000
return = gross - fee
```

You burn your L + S, you receive `return` cUSDC.

If the vault is ever **under-collateralized** (`CR < 100%`), the payout is scaled by the collateralization ratio - see [Under-collateralization](#under-collateralization-pro-rata-redemption) below. In the normal case `min(1, CR) = 1`, so the haircut is a no-op. The fee is taken on the post-haircut gross, so it scales with what is actually paid out.

If you redeem only one side (the other amount is zero), the program just burns that side and returns its value in cUSDC. No paired requirement.

## Fees

Same structure as [mint](/flows/mint#fees): base **10bps**, of which 90% stays in the collateral vault as backing and 10% (the dev tax) goes to the fee recipient. On high-vol markets the keeper posts a vol-informed fee - `max(10, fee_mult × q90)` bps, capped at 100 - because the mint/redeem fee is the hurdle that gates paired round-trip extraction. Broad indices in calm regimes sit at the static 10bps.

## Oracle staleness gate

Like mint, redeem outside `Normal` risk state (always, on devnet) requires the TWAP to be fresher than **300 seconds**. When the underlying market is closed the keeper withholds oracle pushes, the TWAP ages out, and redeem freezes with `TwapNotAvailable` until reopen - paying out at a stale pre-gap price would misprice against everyone who stays in. The pool books continue floating off-hours if you need to exit at a live two-sided quote.

## Accounts

Same as mint. The user signs, ATAs source/sink tokens, vault transfers cUSDC out.

| Account                   | Notes                        |
| ------------------------- | ---------------------------- |
| `market`                  | Market PDA                   |
| `user` (signer)           | Receives cUSDC, gives up L+S |
| `user_collateral`         | Receives cUSDC               |
| `user_long`               | Source of L burn             |
| `user_short`              | Source of S burn             |
| `long_mint`, `short_mint` | From market                  |
| `collateral_vault`        | Source of cUSDC              |
| `dev_token_account`       | Fee recipient                |
| `oracle_address`          | NAV source                   |
| `token_program`           | SPL Token                    |

## TypeScript

```typescript theme={null}
import { Program, BN } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";

const lAmt = new BN(100_000); // 0.1 QQQL (6 decimals)
const sAmt = new BN(50_000_000); // 50 QQQS

await program.methods
  .redeemPaired(lAmt, sAmt)
  .accounts({
    market: marketPDA,
    user: wallet.publicKey,
    userCollateral: userCusdc,
    userLong,
    userShort,
    longMint: market.longMint,
    shortMint: market.shortMint,
    collateralVault: market.collateralVault,
    devTokenAccount: devCusdc,
    oracleAddress: market.oracleAddress,
    tokenProgram: TOKEN_PROGRAM_ID,
  })
  .rpc();
```

## Asymmetric redemption

L and S have very different unit prices (e.g. QQQ: `L_NAV ≈ 480`, `S_NAV ≈ 0.002`). Burning equal **quantities** doesn't burn equal **value**. The program is fine with this:

| Redeem          | Burn             | NAV value              |
| --------------- | ---------------- | ---------------------- |
| `(0.1 QQQL, 0)` | 0.1 long only    | `0.1 × 480 = 48` cUSDC |
| `(0, 50 QQQS)`  | 50 short only    | `50 × 1 ≈ 50` cUSDC    |
| `(0.1, 50)`     | both             | `48 + 50 = 98` cUSDC   |
| `(0.1, 25)`     | both, asymmetric | `48 + 25 = 73` cUSDC   |

There's no penalty for asymmetric redeems. They're how you implement directional position closing - sell one leg, redeem the other.

## Maximum redeem

There's no protocol-imposed maximum. You can redeem the entire vault if you hold the corresponding tokens. The program checks:

```
require!(user_long_balance >= l_amount, InsufficientFunds);
require!(user_short_balance >= s_amount, InsufficientFunds);
require!(return_value <= total_collateral, InsufficientCollateral);
require!(vault_balance >= return_value, InsufficientLiquidCollateral);
```

The pro-rata haircut (below) caps `return_value` at the vault's collateralization, so `InsufficientCollateral` is unreachable in practice. `InsufficientLiquidCollateral` can fire only if collateral is deployed to yield (off by default) and must be recalled first.

## Under-collateralization: pro-rata redemption

Continuum targets an over-collateralized vault, but the [constant-product NAV](/concepts/nav) means a large move on an imbalanced book can, in the tail, push collateralization below 100%. Redeem stays open in that case — it just pays **pro-rata**:

```
CR     = total_collateral / (total_l_supply × L_NAV + total_s_supply × S_NAV)
payout = gross × min(1, CR)        // floored
return = payout - fee
```

Two guarantees hold by construction:

* **Never overdraws.** A single redeem can never pay out more than the vault holds (`payout ≤ total_collateral`), so the vault can't be pushed negative.
* **No first-mover advantage.** Every redeemer takes the same `min(1, CR)` factor and the ratio is preserved for everyone who remains — there's no race to exit first. This removes the bank-run dynamic that a "full NAV for early redeemers" rule would create when the vault is thin.

In the normal over-collateralized state this is invisible: `CR ≥ 100%` → `min(1, CR) = 1` → you receive full NAV value.

## Behavior across risk states

| Risk state  | Behavior                                           |
| ----------- | -------------------------------------------------- |
| `Normal`    | Redeem at NAV, no markup.                          |
| `ProxyMode` | Redeem at NAV, no markup (only mint is throttled). |
| `Stress`    | Redeem **still works** at last fresh NAV.          |
| `Recovery`  | Redeem at NAV.                                     |

Redeem is **never blocked** by risk state - users can always exit at the last fresh NAV (pro-rata if the vault is under-collateralized; see [above](#under-collateralization-pro-rata-redemption)). The only thing that pauses it is the [staleness gate](#oracle-staleness-gate) while the underlying market is closed.

## Errors

| Error                    | Cause                                                              |
| ------------------------ | ------------------------------------------------------------------ |
| `MarketNotActive`        | `market.is_active = false`                                         |
| `InvalidAmount`          | Both `l_amount` and `s_amount` are zero                            |
| `InsufficientFunds`      | User's L or S balance below request                                |
| `InsufficientCollateral` | Vault balance can't cover NAV value (should be unreachable)        |
| `TwapNotAvailable`       | TWAP older than 300s - typically the underlying market is closed   |
| `OraclePriceUnavailable` | Oracle paused (extreme case - only emergency\_pause triggers this) |
| `MathOverflow`           | Should be unreachable                                              |

→ [Full error catalog](/reference/errors)

## Optional: `redeem_paired_with_waiver`

Same as mint's fee-waiver variant. Skips the redeem fee if the user has an active fee waiver against this market. Used by power users; ignore for typical integrations.

## What you receive

```
User cUSDC ATA:  +return_value
Vault cUSDC:     -gross
Dev cUSDC ATA:   +fee
User L ATA:      -l_amount (burned)
User S ATA:      -s_amount (burned)

market.total_l_supply: -= l_amount
market.total_s_supply: -= s_amount
market.total_collateral: -= gross
```

## Closing a directional position

If you minted paired and sold one side to express direction, your wallet now holds only one side. Redeem proceeds normally:

```typescript theme={null}
// You hold 0.1 QQQL and 0 QQQS.
await program.methods
  .redeemPaired(new BN(100_000), new BN(0))
  .accounts({ ... })
  .rpc();
// Receive 0.1 × L_NAV cUSDC.
```

Your final P\&L is the redeem cUSDC plus whatever cUSDC you got from selling the other side, minus your initial mint cost. If `L_NAV` rose since mint, you profit.

## Redeem vs sell

To exit one leg, compare the redeem fee (10bps) against the book's half-spread (15-40bps) - redeeming at NAV is usually the cheaper exit for a single side, and a committed order (NAV - 10bps) matches it for size. Selling on the book wins only when you want instant execution and the spread is at its floor.

## See also

<CardGroup cols={2}>
  <Card title="Mint" icon="circle-plus" href="/flows/mint">
    The inverse - deposit cUSDC, receive paired tokens.
  </Card>

  <Card title="Trade" icon="chart-mixed" href="/flows/trade">
    The three venues - books, committed orders, instant swap - and when to use each.
  </Card>
</CardGroup>
