> 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/for-developers/accounts.md).

# Account Structure

> v1 hedges on **Phoenix Perps** (Ellipsis Labs) with USDC margin via the **Ember** program; jitoSOL is held unlevered as the spot leg. The perp-venue account set below follows the Rise SDK (TraderHeader, PermissionAccount, GlobalConfig, PerpAssetMap) and is finalized with the integration.

The ksUSD vault uses two on-chain account types: a single `Vault` PDA and one `WithdrawalRequest` PDA per queued withdrawal.

> Source: [`programs/keystone-finance/src/state/vault.rs`](https://github.com/kamwithak/keystone-contracts/blob/main/programs/keystone-finance/src/state/vault.rs)
>
> Anchor client name: `program.account.vault.fetch(vaultPda)` and `program.account.withdrawalRequest.fetch(requestPda)`.

***

## `Vault` (singleton)

PDA seed: `[b"vault"]` · authority for ksUSD mint, vault USDC ATA, vault jitoSOL ATA, reserve USDC ATA, Phoenix trader account, and lend / reserve cToken ATAs.

### Identity

| Field           | Type     | Meaning                                                                       |
| --------------- | -------- | ----------------------------------------------------------------------------- |
| `admin`         | `Pubkey` | Admin authority (transferable via two-step `transfer_admin` + `accept_admin`) |
| `pending_admin` | `Pubkey` | Pending admin during a transfer; `Pubkey::default()` = none pending           |
| `bump`          | `u8`     | Vault PDA bump                                                                |
| `usdc_mint`     | `Pubkey` | USDC mint (depositor token)                                                   |
| `jitosol_mint`  | `Pubkey` | jitoSOL mint (LST spot leg — held unlevered for staking)                      |
| `ksusd_mint`    | `Pubkey` | ksUSD share mint — vault PDA is mint + freeze authority                       |

### Phoenix integration

| Field                   | Type     | Meaning                                                                             |
| ----------------------- | -------- | ----------------------------------------------------------------------------------- |
| `phoenix_trader`        | `Pubkey` | Phoenix trader account (vault's subaccount) — set by `enable_phoenix`               |
| `phoenix_permission`    | `Pubkey` | Phoenix PermissionAccount (trader onboarding / allowlist) — set by `enable_phoenix` |
| `phoenix_config`        | `Pubkey` | Phoenix GlobalConfig — constrained on every Phoenix CPI                             |
| `phoenix_market`        | `Pubkey` | SOL-PERP market (PerpAssetMap) — constrained on `settle` and place-order paths      |
| `ember_margin_vault`    | `Pubkey` | Ember USDC margin vault — constrained on margin post / withdraw                     |
| `phoenix_subaccount_id` | `u16`    | Subaccount index (default 0)                                                        |
| `phoenix_market_index`  | `u16`    | SOL-PERP market index                                                               |
| `phoenix_initialized`   | `bool`   | `true` after `enable_phoenix` succeeds                                              |

### Lending venues

| Field                          | Type     | Meaning                                                                 |
| ------------------------------ | -------- | ----------------------------------------------------------------------- |
| `kamino_lend_reserve`          | `Pubkey` | Pinned Kamino USDC reserve (set by `enable_lending`)                    |
| `marginfi_lend_bank`           | `Pubkey` | Marginfi USDC bank (v1.1 placeholder — not yet wired)                   |
| `vault_lend_collateral_ata`    | `Pubkey` | Vault's cToken ATA for Kamino lending                                   |
| `reserve_lend_collateral_ata`  | `Pubkey` | Reserve fund's cToken ATA — distinct so reserve yield tracks separately |
| `kamino_lending_initialized`   | `bool`   | `true` after `enable_lending` succeeds                                  |
| `marginfi_lending_initialized` | `bool`   | Reserved for v1.1                                                       |
| `usdc_lent_kamino`             | `u64`    | USDC base units deposited at Kamino from vault main ATA                 |
| `reserve_lent_kamino`          | `u64`    | USDC base units deposited at Kamino from reserve ATA                    |
| `usdc_lent_marginfi`           | `u64`    | Reserved for v1.1                                                       |
| `reserve_ata`                  | `Pubkey` | Reserve fund USDC ATA (vault-PDA-owned). Pinned at init.                |

### Oracles (set by `set_oracles`)

| Field                 | Type     | Meaning                              |
| --------------------- | -------- | ------------------------------------ |
| `sol_oracle`          | `Pubkey` | Pyth SOL/USD pull-oracle account     |
| `jitosol_oracle`      | `Pubkey` | Pyth jitoSOL/USD pull-oracle account |
| `oracles_initialized` | `bool`   | `true` after `set_oracles` succeeds  |

### NAV & share accounting (USDC base units, 6 decimals)

| Field                | Type  | Meaning                                                       |
| -------------------- | ----- | ------------------------------------------------------------- |
| `total_shares`       | `u64` | Total ksUSD outstanding (6 decimals)                          |
| `cached_nav_usdc`    | `u64` | Last computed NAV in USDC base units                          |
| `last_settle_ts`     | `i64` | Last `settle` crank timestamp                                 |
| `last_nav_attest_ts` | `i64` | Last `attest_nav` timestamp — anchors the per-hour change cap |

### Performance fee & reserve

| Field                    | Type  | Meaning                                                                        |
| ------------------------ | ----- | ------------------------------------------------------------------------------ |
| `hwm_share_price_1e9`    | `u64` | High-water-mark share price (1e9 scale)                                        |
| `peak_share_price_1e9`   | `u64` | Rolling peak share price (drawdown anchor)                                     |
| `pending_perf_fees_usdc` | `u64` | Reserved for v1.5 per-settle accrual (currently always 0)                      |
| `reserve_fund_usdc`      | `u64` | Reserve fund balance in USDC base units (`reserve_ata` balance + lent portion) |

### Position state

| Field                  | Type           | Meaning                                                                                |
| ---------------------- | -------------- | -------------------------------------------------------------------------------------- |
| `position_mode`        | `PositionMode` | `Idle` / `Normal` / `WindDown`                                                         |
| `position_base_amount` | `i64`          | Phoenix perp base size (signed: + long, − short). Units: SOL × `BASE_PRECISION` (1e9). |
| `last_mode_change_ts`  | `i64`          | Anchors the `min_dwell_seconds` guard                                                  |

### Withdrawal queue cursors

| Field                     | Type  | Meaning                                                     |
| ------------------------- | ----- | ----------------------------------------------------------- |
| `queue_next_id`           | `u64` | Next request\_id to assign                                  |
| `queue_processed_through` | `u64` | Smallest request\_id not yet processed (strict FIFO cursor) |
| `queue_pending_usdc`      | `u64` | Total USDC owed across pending requests                     |

### Configurable parameters

| Field                             | Type  | Default                                                           |
| --------------------------------- | ----- | ----------------------------------------------------------------- |
| `liquidity_buffer_bps`            | `u16` | 1\_000 (10%)                                                      |
| `funding_threshold_normal_bps`    | `i32` | +200                                                              |
| `perf_fee_bps`                    | `u16` | 2\_000                                                            |
| `reserve_skim_bps`                | `u16` | 500                                                               |
| `min_dwell_seconds`               | `u32` | 43\_200 (12 h)                                                    |
| `max_swap_slippage_bps`           | `u16` | 50                                                                |
| `emergency_close_dd_bps`          | `u16` | 500                                                               |
| `deposit_cap_usdc`                | `u64` | 1\_000\_000\_000\_000 ($1M). `u64::MAX` = uncapped; `0` = paused. |
| `min_request_shares`              | `u64` | 1\_000\_000 (1 ksUSD)                                             |
| `max_pending_queue_usdc`          | `u64` | `u64::MAX` (disabled)                                             |
| `max_nav_change_bps_per_hour`     | `u16` | 5\_000                                                            |
| `funding_max_staleness_seconds`   | `u32` | 21\_600 (6 h)                                                     |
| `consecutive_dd_settles_required` | `u8`  | 2                                                                 |
| `consecutive_dd_settles_observed` | `u8`  | Running counter                                                   |
| `lst_depeg_bps`                   | `u16` | 500 (5%)                                                          |

### Risk flags & keeper allowlist

| Field               | Type     | Meaning                                                                                                                                                                                                                    |
| ------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `paused`            | `bool`   | Halts deposits + new positions; instant withdraw stays open                                                                                                                                                                |
| `authorized_keeper` | `Pubkey` | Gates `settle` / strategy opens / closes / lending cranks. **Exact match required** — `Pubkey::default()` (the post-init state) blocks every keeper-gated ix until admin runs `update_params { authorized_keeper: <key> }` |

### Smoothed funding signal

| Field                      | Type  | Meaning                                                                |
| -------------------------- | ----- | ---------------------------------------------------------------------- |
| `funding_apr_smoothed_bps` | `i32` | EMA of SOL-PERP funding, updated on every `settle`                     |
| `funding_smooth_last_ts`   | `i64` | Last EMA update timestamp. `0` sentinel = first settle hasn't run yet. |

### Reserved

| Field       | Type       | Meaning               |
| ----------- | ---------- | --------------------- |
| `_reserved` | `[u8; 32]` | Forward compatibility |

***

## `PositionMode` (enum)

| Variant    | Value | Meaning                                                                                 |
| ---------- | ----- | --------------------------------------------------------------------------------------- |
| `Idle`     | 0     | **Parked** (v1 name). No active perp position; capital in vault USDC (lent on Kamino).  |
| `Normal`   | 1     | Long jitoSOL spot (unlevered) + short SOL-PERP on Phoenix at 1× notional, USDC margin.  |
| `WindDown` | 3     | Terminal. New positions / deposits blocked; users claim pro-rata via `claim_wind_down`. |

***

## `WithdrawalRequest`

PDA seed: `[b"withdrawal_request", vault.key(), request_id_le_bytes]` · created by `request_withdrawal`, closed by `process_withdrawal` (rent refunded to the original requester).

| Field               | Type     | Meaning                                                          |
| ------------------- | -------- | ---------------------------------------------------------------- |
| `vault`             | `Pubkey` | Vault this request belongs to                                    |
| `user`              | `Pubkey` | Original requester (rent refund recipient)                       |
| `user_usdc_account` | `Pubkey` | USDC destination — must match at process time                    |
| `usdc_owed`         | `u64`    | Upper bound USDC owed at burn time (FIFO at request)             |
| `shares_burned`     | `u64`    | Shares burned at request — used for the price-at-process haircut |
| `request_id`        | `u64`    | Sequential ID assigned at enqueue                                |
| `requested_ts`      | `i64`    | Timestamp of request                                             |
| `bump`              | `u8`     | PDA bump                                                         |

`process_withdrawal` price-at-process haircut:

* Re-prices `usdc_owed` against the current share price
* Takes the **lower** of locked vs. live
* Depositors can never extract more than their pro-rata share if NAV has fallen since they queued

***

## Derivation helpers (TypeScript)

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

const [requestPda] = PublicKey.findProgramAddressSync(
  [
    Buffer.from("withdrawal_request"),
    vaultPda.toBuffer(),
    new BN(requestId).toArrayLike(Buffer, "le", 8),
  ],
  PROGRAM_ID
);

// Vault ATAs (PDA-owned)
const vaultUsdcAta    = getAssociatedTokenAddressSync(vault.usdcMint,    vaultPda, true);
const vaultJitosolAta = getAssociatedTokenAddressSync(vault.jitosolMint, vaultPda, true);
const reserveUsdcAta  = vault.reserveAta;             // pinned at init
```

***

## Related

* [Instructions](/for-developers/instructions.md) · [Events](/for-developers/events.md) · [Errors](/for-developers/errors.md)
* [Quick start](/for-developers/quick-start.md) — getting a Program instance set up
