# Account Structure

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, Drift sub-account, Kamino obligation, 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 collateral leg)                                             |
| `ksusd_mint`    | `Pubkey` | ksUSD share mint — vault PDA is mint + freeze authority                       |

### Drift integration

| Field                          | Type     | Meaning                                                                         |
| ------------------------------ | -------- | ------------------------------------------------------------------------------- |
| `drift_user`                   | `Pubkey` | Drift User PDA (vault's sub-account) — set by `enable_drift`                    |
| `drift_user_stats`             | `Pubkey` | Drift UserStats PDA — set by `enable_drift`                                     |
| `drift_state`                  | `Pubkey` | Drift global state PDA — constrained on every Drift CPI                         |
| `drift_perp_market`            | `Pubkey` | SOL-PERP market account — constrained on `settle` (and any place-order callers) |
| `drift_usdc_spot_market_vault` | `Pubkey` | Drift's USDC spot-market vault — constrained on `settle_pnl` / withdraw paths   |
| `drift_subaccount_id`          | `u16`    | Sub-account index (default 0)                                                   |
| `drift_perp_market_index`      | `u16`    | Drift market — `0 = SOL-PERP` on mainnet                                        |
| `drift_initialized`            | `bool`   | `true` after `enable_drift` succeeds                                            |

### Kamino integration (reverse basis)

| Field                | Type     | Meaning                                         |
| -------------------- | -------- | ----------------------------------------------- |
| `kamino_obligation`  | `Pubkey` | Kamino obligation PDA — set by `enable_reverse` |
| `kamino_market`      | `Pubkey` | Kamino market (main market on mainnet)          |
| `kamino_initialized` | `bool`   | `true` after `enable_reverse` 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` / `Reverse` / `WindDown`                                           |
| `position_base_amount`    | `i64`          | Drift perp base size (signed: + long, − short). Units: SOL × `BASE_PRECISION` (1e9). |
| `usdc_collateral_kamino`  | `u64`          | USDC posted to Kamino (reverse only)                                                 |
| `jitosol_borrowed_kamino` | `u64`          | jitoSOL borrowed from Kamino (reverse only)                                          |
| `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                                                              |
| `funding_threshold_reverse_bps`   | `i32` | −1\_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 Drift 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     | No active perp position. Capital in vault USDC (possibly lent on Kamino).               |
| `Normal`   | 1     | Long jitoSOL on Drift + short SOL-PERP at 1× notional.                                  |
| `Reverse`  | 2     | Kamino USDC + borrowed jitoSOL sold to USDC + long SOL-PERP.                            |
| `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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.keystonefi.xyz/for-developers/accounts.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
