> 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/keystone-finance/deposit.md).

# Deposit

Send USDC to the ksUSD vault and get `ksUSD` back at the current share price. How many tokens you get depends on that price, so a deposit made later buys fewer tokens, each worth more.

> **New here?** Read [What is ksUSD?](/keystone-finance/reserve-asset.md) first.

***

## The instruction

```
deposit(usdc_amount: u64)
```

One transaction does all three steps, or none of them:

* Moves your USDC into the vault's USDC account
* Mints `ksUSD` to you at the current share price
* Updates the vault's record of what it holds

The accounts below use two Solana conventions. A **PDA** is an account the program controls directly, with no private key behind it. An **ATA** is the standard token account a wallet holds a given token in.

| Account              | Mutability | Purpose                                   |
| -------------------- | ---------- | ----------------------------------------- |
| `vault`              | mut        | Vault PDA (seeds: `[b"vault"]`)           |
| `vault_usdc_account` | mut        | Vault's USDC ATA — receives the deposit   |
| `ksusd_mint`         | mut        | ksUSD share mint — vault PDA is authority |
| `user_usdc_account`  | mut        | Depositor's USDC source ATA               |
| `user_ksusd_account` | mut        | Depositor's ksUSD destination ATA         |
| `user`               | signer     | Depositor                                 |
| `token_program`      | —          | SPL token program                         |

***

## Share-price math

```
effective_nav_usdc = cached_nav_usdc − queue_pending_usdc − reserve_fund_usdc
                                     − pending_perf_fees_usdc
share_price_1e9    = effective_nav_usdc × 1e9 / max(total_shares, 1)
shares_to_mint     = usdc_amount × 1e9 / share_price_1e9    // both sides 6 decimals
```

`deposit` calls `accrue_perf_fees` before it prices your mint. That advances the high-water mark to the current price first, so you're never charged a performance fee on gains the vault made before you arrived.

The first deposit into an empty vault mints shares 1:1 at $1.00, since the starting `share_price_1e9` is `1_000_000_000`. Every deposit after that mints fewer shares, because the share price has drifted up with the carry earned so far.

***

## Capacity

The vault ships with a **$500k deposit cap**. That isn't a demand estimate — it's roughly the largest short Phoenix's book can absorb, and it re-sizes as the venue grows.

It's a hard cap rather than a soft target because the vault only earns while its dollars are *in* the hedge. Whatever it can't short sits in USDC lending instead. A cap set above what the venue can hold doesn't add yield, then — it dilutes the part that does, until ksUSD is a Kamino deposit wearing a wrapper, carrying all of the operational risk for none of the carry.

So the cap tracks the venue:

```
max short = min( 15% of Phoenix SOL-PERP OI , 25% of the 30-day MEDIAN daily volume )
```

On current Phoenix numbers the volume limb binds, by roughly a factor of two, because exit is harder than entry — a clean unwind is about a quarter of a day's flow.

The word *median* is load-bearing. The 30-day mean runs well above the median, since a few spike days sit inside any window, and sizing off the mean would imply a short the venue couldn't absorb on an ordinary day.

### The ramp

| Phase        | Cap        | Opens when Phoenix has                                         |
| ------------ | ---------- | -------------------------------------------------------------- |
| **Launch**   | **$500k**  | roughly today's book                                           |
| Private beta | $1M        | roughly twice today's open interest                            |
| Public       | $5M → $25M | a far deeper book, or a second venue to split the short across |

Tiers open against the live book, not on a date. Drift went down in January 2025, so there's no second Solana perp venue wired into v1 to split across yet.

### Reading the cap live

`deposit_cap_usdc` is a `u64` on the `Vault` account. `0` pauses deposits outright, `u64::MAX` means uncapped, anything else is a hard ceiling.

```ts
const vault = await program.account.vault.fetch(vaultPda);

// Headroom is measured against CACHED nav, not effective nav: queued
// withdrawals and the reserve fund are netted out of TVL but still occupy
// cap room, so effective NAV would overstate what you can actually deposit.
const remaining = BN.max(
  vault.depositCapUsdc.sub(vault.cachedNavUsdc),
  new BN(0)
);
```

Admins move the cap with `update_params`. Recompute it against the live book first — `npm run phoenix:capacity` — rather than trusting a constant, since both inputs move week to week.

***

## Guardrails

A deposit can be blocked by any of three things:

* **The vault is paused.** The admin can pause it with `set_pause` in an emergency.
* **The deposit cap is hit.** A deposit reverts with `DepositCapExceeded` if `cached_nav_usdc + usdc_amount > deposit_cap_usdc`. See [Capacity](#capacity) above.
* **The amount is zero.** `usdc_amount` must be greater than 0.

***

## TypeScript example

```ts
import { Program, BN } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";
import { TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from "@solana/spl-token";

const PROGRAM_ID = new PublicKey("E7tpCcxtvuTXLAckBGWb1AsndpLQ1Y9hQA3iGYSXz2vJ");

const [vaultPda] = PublicKey.findProgramAddressSync(
  [Buffer.from("vault")],
  PROGRAM_ID
);

const vault = await program.account.vault.fetch(vaultPda);
const userUsdcAta  = getAssociatedTokenAddressSync(vault.usdcMint,  user.publicKey);
const userKsusdAta = getAssociatedTokenAddressSync(vault.ksusdMint, user.publicKey);
const vaultUsdcAta = getAssociatedTokenAddressSync(vault.usdcMint,  vaultPda, true);

const tx = await program.methods
  .deposit(new BN("100_000_000"))         // 100 USDC (6 decimals)
  .accountsStrict({
    vault: vaultPda,
    vaultUsdcAccount: vaultUsdcAta,
    ksusdMint: vault.ksusdMint,
    userUsdcAccount: userUsdcAta,
    userKsusdAccount: userKsusdAta,
    user: user.publicKey,
    tokenProgram: TOKEN_PROGRAM_ID,
  })
  .rpc();
```

***

## Events

`DepositEvent` is emitted on success:

```rust
pub struct DepositEvent {
    pub user: Pubkey,
    pub usdc_amount: u64,
    pub shares_minted: u64,
    pub share_price_1e9: u64,
    pub new_total_shares: u64,
    pub timestamp: i64,
}
```

***

## Related

* [Withdraw](/keystone-finance/withdraw.md) — instant vs. queued paths
* [Check position & NAV](/keystone-finance/check-position.md)
* [NAV & share pricing](/reference/nav-calculation.md)
* [Errors](/for-developers/errors.md)
