> For the complete documentation index, see [llms.txt](https://docs.keystonefi.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.keystonefi.xyz/how-it-works/strategy-and-modes/nav-calculation.md).

# NAV & Share Pricing

ksUSD prices itself the way sUSDe does. Your `ksUSD` balance never changes. What changes is the price of each token, which drifts up as carry accrues. So you don't watch for new tokens arriving; you watch the price.

***

## The whole idea, in one line

```
share price = the dollars the vault holds for shareholders ÷ ksUSD in circulation
```

That dollar figure is the vault's **NAV** — net asset value. Divide it by the token count and you have what one token is worth.

Two examples, and they are the only two operations there are:

* **Deposit.** Put in $100 while the price is $1.03 → you get `100 ÷ 1.03 = 97.09` ksUSD.
* **Redeem.** Hand back 97.09 ksUSD while the price is $1.05 → you get `97.09 × 1.05 = $101.94`.

Everything below is that one division, written precisely enough to implement.

***

## Share price

There are two NAV figures, and the difference between them is the accrued performance fee.

```
gross_effective_nav_usdc = cached_nav_usdc − queue_pending_usdc − reserve_fund_usdc
effective_nav_usdc       = gross_effective_nav_usdc − pending_perf_fees_usdc

share_price_1e9       = effective_nav_usdc × 1e9 / total_shares         (when total_shares > 0)
                      = 1_000_000_000 (= $1.00)                         (when total_shares = 0)
gross_share_price_1e9 = gross_effective_nav_usdc × 1e9 / total_shares
```

The **net** price (`share_price_1e9`) is what deposits and withdrawals use, so it's the one that matters to you. The **gross** price is what the high-water mark is measured against. Net is always ≤ gross.

* `cached_nav_usdc` — USDC base units (6 decimals)
* `total_shares` — ksUSD base units (6 decimals)
* Rust helpers: `Vault::share_price_1e9()` and `Vault::gross_share_price_1e9()`

### Reading the `1e9` scale

A Solana account stores integers, not decimals, so the price is carried as dollars × 1,000,000,000. To read one, move the decimal point nine places left:

| Stored `share_price_1e9` | Dollars per ksUSD |
| ------------------------ | ----------------- |
| `1_000_000_000`          | $1.000000         |
| `1_030_000_000`          | $1.030000         |
| `1_002_500_000`          | $1.002500         |

The scale is 1e9 rather than the token's own 1e6 so that a day of carry — a few hundredths of a cent per token — doesn't round away to nothing. It's wide enough to price accurately without pushing the arithmetic past a `u128` intermediate.

### Why three things come out of NAV

The vault holds some money that isn't shareholders'. Each subtraction takes one of those out before the division, so the price reflects only what holders actually own.

| Subtraction              | Reason                                                                                                                                                                                                                                                                        |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `queue_pending_usdc`     | These shares are already burned and the vault owes real USDC against them. Leaving that money in the count would let new depositors mark the price up against a liability that's already locked.                                                                              |
| `reserve_fund_usdc`      | The reserve exists to absorb losses, not to pay holders. Keeping it out means the share price shows only what actually belongs to holders.                                                                                                                                    |
| `pending_perf_fees_usdc` | Performance fees are set aside as they're earned, not at collection time. Carving them out here means the share price is already net of fees owed, so collecting them later doesn't step the price down, and a holder who exits first can't dodge a fee they already accrued. |

***

## What `cached_nav_usdc` includes

The vault recalculates `cached_nav_usdc` any time its state changes. It adds up:

```
NAV = idle USDC in the vault's USDC ATA
    + USDC lent on Kamino (idle-USDC leg)
    + (jitoSOL spot holdings × oracle jitoSOL price)    [normal basis — the unlevered spot leg]
    + perp-venue margin balance (settled funding + PnL) [normal basis — USDC margin on Phoenix]
```

Between updates, funding and profit still sitting open on the perp venue are not counted in `cached_nav_usdc`. Anyone can run `settle` to refresh the margin figures from the venue. For open profit and loss the keeper can also run `attest_nav(new_nav_usdc)`, which is capped per hour by `max_nav_change_bps_per_hour`.

***

## Oracle pricing

jitoSOL and SOL/USD prices come from Pyth. Every single read is checked twice before the vault will use it:

| Check      | Threshold                          | Effect                                               |
| ---------- | ---------------------------------- | ---------------------------------------------------- |
| Staleness  | < 5 minutes since last Pyth update | Stale → revert with `StaleOracle`                    |
| Confidence | conf / price < 2%                  | Out of band → revert with `OracleConfidenceExceeded` |

The thresholds are tight enough to catch an oracle outage or a manipulated feed, but loose enough not to trip on ordinary market activity.

***

## Drawdown peak

`vault.peak_share_price_1e9` holds the highest share price any `settle` has ever seen. The drawdown guard measures how far the price has fallen from that peak:

```
drawdown_bps = (peak - current) × 10_000 / peak
if drawdown_bps >= emergency_close_dd_bps:
    emergency_close is permissionless to crank
```

A single bad reading can't force an emergency exit. The condition has to hold across `consecutive_dd_settles_required` settles in a row, which defaults to 2, and firing early reverts with `DrawdownTriggerLatent`.

Only the admin can reset the peak, using `reset_peak`; collecting fees doesn't touch it. And note that the drawdown watches the live share price, not the post-fee high-water mark used for fees.

***

## Worked example

A vault holding $1M against 1M ksUSD, which then earns $30,000 of carry. The left column is what a holder sees; the right is what's stored on-chain.

|                            | In dollars      | On-chain (base units)                 |
| -------------------------- | --------------- | ------------------------------------- |
| **Start** — NAV            | $1,000,000      | `cached_nav_usdc = 1_000_000_000_000` |
| **Start** — supply         | 1,000,000 ksUSD | `total_shares = 1_000_000_000_000`    |
| **Start** — price          | $1.0000         | `share_price_1e9 = 1_000_000_000`     |
| **After $30k carry** — NAV | $1,030,000      | `cached_nav_usdc = 1_030_000_000_000` |
| **After carry** — supply   | unchanged       | `total_shares = 1_000_000_000_000`    |
| **After carry** — price    | $1.0300         | `share_price_1e9 = 1_030_000_000`     |

Nobody's balance moved when the vault earned. The price did the work.

Now a new depositor sends 100 USDC. They get `$100 ÷ $1.03 = 97.087…` ksUSD:

```
shares_minted = 100_000_000 × 1e9 / 1_030_000_000
              = 97_087_378.6…  →  truncated to 97_087_378 ksUSD base units
```

Vault after: `total_shares ≈ 1_000_097_087_378`, `cached_nav_usdc = 1_030_100_000_000`, and the price is still $1.0300 — $100 of new money bought exactly $100 of shares. The truncation rounds against the depositor by under one base unit, a millionth of a token, which is what stops a mint from nudging the price down for everyone else.

So the new holder has \~97.087 ksUSD worth the same \~$100 they put in: fewer tokens than the earlier depositor got, each one worth more.

> This example ignores fees to keep the arithmetic clear. In the real path, `deposit` calls `accrue_perf_fees` before it prices the mint, so the share price is already net of fees owed and the new depositor is never charged on gains that happened before they arrived.

***

## Related

* [Fee structure](/start-here/fees.md) — HWM math and the hurdle
* [Check position & NAV](/start-here/check-position.md) — TypeScript snippets to query share price live
* [Security model](/reference/security.md) — oracle, slippage, drawdown guard
