Skip to content

Search every page of the documentation, including the program reference and error codes.

↑ ↓ to move · Enter to open

Contents · §2 How it works

§2 · The protocol

How it works

Wrap and unwrap in kind, pro-rata against the vault, with every rounding in the pod's favour and no oracle anywhere.

A pod keeps books. For each component it records a tracked_balance: the raw units it holds on behalf of the cohort supply. Every wrap and unwrap is priced against those books and nothing else. There is no price input anywhere in the program.

Wrap: deposit in kind§

To wrap, you name a gross amount of cohorts, pod_amount, and deposit every component in proportion to what the pod already holds:

Text
amount_in[i] = ceil( pod_amount × tracked_balance[i] ÷ supply )

The pod then mints pod_amount cohorts: the wrap fee to its FeeVault and the rest to you. The fee is ceil(pod_amount × wrap_fee_bps ÷ 10,000), so you receive pod_amount − fee. You set a ceiling for each component, max_in, and the wrap fails with SlippageExceededIn if the pod would take more.

Before the first cohort exists the pod has no supply to divide by, so the recipe stands in for it: amount_in[i] = ceil(pod_amount × recipe_units[i] ÷ 10⁹), where recipe_units is the raw amount of each component behind one whole cohort. The creator's genesis wrap inside create_pod works this way. It must mint at least one whole cohort, and it is the only wrap that pays no fee.

Unwrap: redeem in kind§

To unwrap, you return pod_amount cohorts. The unwrap fee, ceil(pod_amount × unwrap_fee_bps ÷ 10,000), moves to the FeeVault; the rest, net, is burned. The pod pays out every component:

Text
amount_out[i] = floor( net × tracked_balance[i] ÷ supply )

supply here is the supply before the burn. You set a floor for each component, min_out, and the unwrap fails with SlippageExceededOut if any payout would fall below it.

An unwrap pays every component or none. If one component can't move because its issuer paused or froze it, a plain unwrap fails. unwrap_partial exists for that case: §6 Partial unwrap and claim tickets.

Books, not balances§

The program never prices against the balance it reads from a vault. Anyone can send tokens straight to a vault; if that changed the exchange rate, a donation could be used to manipulate it, which is a known attack on vault tokens. So a deposit only counts once the program has moved it in through wrap and recorded it.

The vault balance still matters in one direction. If a vault holds less than the books say (tracked_balance + claims_outstanding), an issuer has taken tokens out with its permanent delegate. The program records the gap as shortfall, lowers tracked_balance by it, and every holder of that pod shares the loss pro-rata. If a vault holds more, the surplus is ignored.

A worked example§

Take a pod shaped like cCORE, with three components that each have 8 decimals. It minted 1,000 cohorts at genesis; 0.6 of them have since been burned by fee distributions, so each cohort is backed by slightly more than the recipe. Wrap and unwrap fees are 30 bps.

The pod's books

Cohort supply
999.4 cohorts

999,400,000,000 raw

SPYx tracked
40 SPYx

recipe 0.04 per cohort · backing now 0.04002401

QQQx tracked
30 QQQx

recipe 0.03 per cohort · backing now 0.03001801

GLDx tracked
35 GLDx

recipe 0.035 per cohort · backing now 0.03502101

Wrap and unwrap fee
30 bps each

Wrapping 10 cohorts gross costs each component's current backing, rounded up, and mints 9.97 to you:

Wrap 10 cohorts (gross)

You deposit SPYx
0.40024015

ceil(10,000,000,000 × 4,000,000,000 ÷ 999,400,000,000) = 40,024,015

You deposit QQQx
0.30018011

ceil(10,000,000,000 × 3,000,000,000 ÷ 999,400,000,000) = 30,018,011

You deposit GLDx
0.35021013

ceil(10,000,000,000 × 3,500,000,000 ÷ 999,400,000,000) = 35,021,013

Fee to the FeeVault
0.03 cohorts

ceil(10,000,000,000 × 30 ÷ 10,000) = 30,000,000

Minted to you
9.97 cohorts

Unwrapping 10 cohorts pays out against the same books, rounded down:

Unwrap 10 cohorts

Fee to the FeeVault
0.03 cohorts

ceil(10,000,000,000 × 30 ÷ 10,000)

Burned
9.97 cohorts

net = amount − fee

You receive SPYx
0.39903942

floor(9,970,000,000 × 4,000,000,000 ÷ 999,400,000,000) = 39,903,942

You receive QQQx
0.29927956

floor(9,970,000,000 × 3,000,000,000 ÷ 999,400,000,000) = 29,927,956

You receive GLDx
0.34915949

floor(9,970,000,000 × 3,500,000,000 ÷ 999,400,000,000) = 34,915,949

Wrapping 10 and unwrapping 10 doesn't return what you put in. Two fees of 0.03 cohorts each stayed behind in the FeeVault, and the rounding on each side kept a few raw units in the vaults.

Rounding in the pod's favour§

Every division in the program rounds so that the pod, and so the remaining holders, never lose a raw unit:

QuantityDirectionWho absorbs the remainder
Components in on a wrapupthe depositor
Wrap feeupthe depositor
Unwrap feeupthe redeemer
Components out on an unwrapdownthe redeemer
Creator and protocol fee payoutsdownburned with the holder share

At normal sizes this is invisible. At the edges it decides the outcome:

Rounding at the edges (raw units)

Wrap 1 raw unit of cohort
pays 1 / 1 / 1 raw

SPYx / QQQx / GLDx: each rounds up to at least 1 raw unit, while the fee rounds up to 1 and you receive 0

Unwrap 1,000 raw units
fee 3, SPYx 3

exact share 3.990 raw SPYx; you get 3 and the remainder stays in the vault

Unwrap 1 raw unit
rejected

the fee rounds up to 1 raw unit and leaves nothing to burn, so the program fails it with UnwrapTooSmall

The program's test suite checks that random sequences of wraps and unwraps never lower the backing per cohort.

Why there is no oracle§

An oracle answers the question "what is this basket worth?" A pod never needs to ask it. Wrapping and unwrapping exchange the basket for cohorts at the pod's own book rate, and the book rate is just vault contents divided by supply. A price feed could only be wrong, stale or attacked; leaving it out removes a failure that no amount of monitoring fixes.

The price of a cohort on a DEX still matters to anyone trading it, and nothing in the program sets it. Arbitrage does.

Arbitrage keeps the price near NAV§

The net asset value (NAV) of a cohort is its backing priced at the components' own market prices. Suppose a cohort's pool on a Solana DEX trades above NAV. An arbitrageur buys the components, wraps them, sells the new cohorts into the pool and keeps the difference; selling pushes the pool down. If the pool trades below NAV, they buy cohorts, unwrap them and sell the components; buying pushes the pool up.

Arbitrage keeps the pool near NAVThe pool price drifts around the basket's value. Above the band, arbitrageurs wrap components and sell cohorts, pushing it down. Below the band, they buy cohorts and unwrap, pushing it up.NAVband ≈ pool fee + wrap/unwrap fee + leg costspool pricepremiumwrap components, sell cohortsdiscountbuy cohorts, unwraptime: every session, weekends included
Exhibit BThe pool price wanders around NAV. Inside the band, the fees and leg costs eat any profit and nobody trades. Outside it, wrapping or unwrapping pays, and the trade itself pulls the price back.

The band's width is the round-trip cost of the trade. Cohort's economics simulation (docs/economics.md) measured it from live Solana liquidity for the cMAG7 and cCORE components:

  • With 30 bps wrap and unwrap fees and a $1M cohort/USDC pool, an arbitrage breaks even at about 75 bps of mispricing: the 25 bps pool fee, the 30 bps wrap or unwrap fee, roughly 8–15 bps of component slippage and 2 bps per leg of overhead.
  • In a year of simulated trading the pool sat 38 bps from NAV on average, and within 68 bps 95% of the time.
  • Solana transaction fees and Jito tips came to about $0.02 per arbitrage bundle, which doesn't matter.

Both legs of the trade are on-chain, so arbitrage doesn't wait for the stock market: the arbitrageur prices against the components' own pools, which trade around the clock. That is why a cohort can trade at a sensible price on a Saturday.

Before you continue

Not available to US persons or in restricted regions.

Cohort is experimental software on Solana devnet. Cohort tokens are backed by tokenized stocks issued by third parties, who can freeze, pause or seize them.

You may not use this interface if you are a resident of, or located in:

  • the United States and its territories
  • Canada
  • the United Kingdom
  • Australia
  • any country or region subject to comprehensive sanctions (including Cuba, Iran, North Korea, Syria, and the Crimea, Donetsk and Luhansk regions)
Terms v2026-11-06