# Admin Operations

Admin-only instructions on the ksUSD vault. All signed by the `vault.admin` keypair (or successor after `transfer_admin` + `accept_admin`).

> Source: [`programs/keystone-finance/src/instructions/admin.rs`](https://github.com/kamwithak/keystone-contracts/blob/main/programs/keystone-finance/src/instructions/admin.rs) · [`init_wind_down.rs`](https://github.com/kamwithak/keystone-contracts/blob/main/programs/keystone-finance/src/instructions/init_wind_down.rs) · [`enable_lending.rs`](https://github.com/kamwithak/keystone-contracts/blob/main/programs/keystone-finance/src/instructions/enable_lending.rs) · [`set_oracles.rs`](https://github.com/kamwithak/keystone-contracts/blob/main/programs/keystone-finance/src/instructions/set_oracles.rs)

***

## One-time setup

| Instruction                          | When                                                                                                      |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------- |
| `initialize(params)`                 | First deploy. Creates the vault PDA, ksUSD mint, reserve USDC ATA.                                        |
| `enable_drift(sub_account_id, name)` | After init, before opening any perp position.                                                             |
| `enable_reverse(tag, id)`            | Optional — needed only if reverse basis will be used.                                                     |
| `enable_lending()`                   | After init, before any `lend_idle_usdc` / `lend_reserve`. Pins Kamino USDC reserve + the two cToken ATAs. |
| `set_oracles()`                      | After init, before strategy paths that read SOL/USD or jitoSOL/USD.                                       |

***

## `set_pause(paused: bool)`

* Halts new deposits and new positions
* Instant withdrawals and `claim_wind_down` stay open by design — depositors should always be able to exit
* Cannot un-pause while `position_mode == WindDown` (reverts with `WindDownActive`)

**When to use:**

* Pre-audit incident response
* Investigating an oracle / venue anomaly
* During an admin handover (briefly, to flush in-flight state)

```ts
await program.methods.setPause(true).accountsStrict({
  vault: vaultPda,
  admin: adminWallet.publicKey,
}).rpc();
```

Emits `PauseToggled`.

***

## `update_params(args)`

* Adjust risk parameters in place
* Each field on `UpdateParamsArgs` is `Option<T>` — only the fields you pass get written

| Field                             | Bounds                                                                                                                             |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `liquidity_buffer_bps`            | 0–5\_000 (≤ 50% of NAV)                                                                                                            |
| `funding_threshold_normal_bps`    | i32; expected positive                                                                                                             |
| `funding_threshold_reverse_bps`   | i32; expected negative                                                                                                             |
| `perf_fee_bps`                    | 0–5\_000 (≤ 50%)                                                                                                                   |
| `reserve_skim_bps`                | 0–10\_000 (of perf fee)                                                                                                            |
| `min_dwell_seconds`               | 0–`u32::MAX`                                                                                                                       |
| `max_swap_slippage_bps`           | 0–1\_000 (≤ 10%)                                                                                                                   |
| `emergency_close_dd_bps`          | 0–10\_000                                                                                                                          |
| `deposit_cap_usdc`                | `u64::MAX` = uncapped, `0` = pause new deposits, else USDC base units                                                              |
| `authorized_keeper`               | Pubkey; **exact match required**. `Pubkey::default()` (the post-init state) blocks all keeper-gated ix until admin sets a real key |
| `min_request_shares`              | u64                                                                                                                                |
| `max_pending_queue_usdc`          | `u64::MAX` = disabled                                                                                                              |
| `max_nav_change_bps_per_hour`     | 0–10\_000                                                                                                                          |
| `funding_max_staleness_seconds`   | u32                                                                                                                                |
| `consecutive_dd_settles_required` | ≥ 1                                                                                                                                |

* Invalid bounds → `InvalidParams`
* Mints, admin, oracles, Drift / Kamino refs are **immutable** through this instruction — set once at `initialize` / `enable_*` / `set_oracles`

```ts
await program.methods.updateParams({
  liquidityBufferBps: 800,            // tighten buffer to 8%
  emergencyCloseDdBps: 400,           // tighten drawdown guard to 4%
  // omit other fields to leave them unchanged
}).accountsStrict({
  vault: vaultPda,
  admin: adminWallet.publicKey,
}).rpc();
```

***

## `transfer_admin(new_admin)` + `accept_admin()`

Two-step admin handover:

* **Step 1** — records `new_admin` as a pending successor (signed by current admin)
* **Step 2** — finalizes (signed by the new admin)
* Prevents accidentally transferring to a dead key

**Target end-state:** a multisig (Squads / similar) once mainnet is live.

```ts
// Step 1 — current admin proposes
await program.methods.transferAdmin(squadsMultisig).accountsStrict({
  vault: vaultPda,
  admin: currentAdmin.publicKey,
}).rpc();

// Step 2 — new admin accepts
await program.methods.acceptAdmin().accountsStrict({
  vault: vaultPda,
  newAdmin: squadsMultisig,
}).rpc();
```

Errors: `NoPendingAdmin` (step 2 called without a pending), `NotPendingAdmin` (step 2 signer mismatch).

***

## `collect_fees()`

* Pays the performance fee in USDC (no dilutive share mint)
* Only callable when `position_mode == Idle` — fees crystallize on realized gains
* See [Fees](/reference/fees.md) for the math
* Admin share → `admin_usdc_account`
* Reserve skim → `reserve_ata`
* HWM bumps to the post-fee share price (monotonic)

**When to call:**

* On a regular cadence (monthly is conventional; weekly is fine)
* Before any planned parameter change that affects share-price computation
* Before mainnet upgrade events (clean accounting boundary)

```ts
await program.methods.collectFees().accountsStrict({
  vault: vaultPda,
  vaultUsdcAccount: vaultUsdcAta,
  reserveUsdcAccount: vault.reserveAta,
  adminUsdcAccount: adminUsdcAta,
  admin: adminWallet.publicKey,
  tokenProgram: TOKEN_PROGRAM_ID,
}).rpc();
```

Idempotent when share price ≤ HWM (no transfer, no state change). Emits `FeesCollected`.

***

## `reset_peak()`

* Clears `peak_share_price_1e9` and `consecutive_dd_settles_observed` to current share price
* Use after legitimate recovery — prevents a stale peak from keeping the drawdown guard armed against routine volatility

```ts
await program.methods.resetPeak().accountsStrict({
  vault: vaultPda,
  admin: adminWallet.publicKey,
}).rpc();
```

Emits `PeakReset`.

***

## `pay_from_reserve(amount)`

* Moves USDC out of `reserve_ata` into the vault's main USDC ATA → socializes a reserve draw into NAV
* Caller must invoke `unlend_reserve` first if part of the reserve is currently lent at Kamino

```ts
await program.methods.payFromReserve(new BN(amount)).accountsStrict({
  vault: vaultPda,
  reserveUsdcAccount: vault.reserveAta,
  vaultUsdcAccount: vaultUsdcAta,
  admin: adminWallet.publicKey,
  tokenProgram: TOKEN_PROGRAM_ID,
}).rpc();
```

Emits `ReservePaidOut`.

***

## `init_wind_down()`

Terminal mode switch. Sets `position_mode = WindDown` and pauses the vault. After this:

* `deposit` reverts (`WindDownActive`)
* Strategy `open_*` reverts (`WindDownActive`)
* Existing positions must be closed via `close_position` / `close_reverse` / `emergency_close`
* Users redeem pro-rata via `claim_wind_down(shares)` against the vault's idle USDC

Cannot be undone via `set_pause(false)`.

```ts
await program.methods.initWindDown().accountsStrict({
  vault: vaultPda,
  admin: adminWallet.publicKey,
}).rpc();
```

Emits `WindDownInitiated`.

***

## Operational checklist

| Cadence                      | Action                                                                                                           |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| **Daily**                    | Verify `cached_nav_usdc` against on-chain Drift / Kamino reads (off-chain script).                               |
| **Weekly**                   | `collect_fees` if share price has crossed HWM. Refresh keeper bot keys if rotated.                               |
| **Monthly**                  | Review `peak_share_price_1e9` vs. live share price; `reset_peak` if a stale peak is dragging the drawdown guard. |
| **On incident**              | `set_pause(true)`, investigate, then either resolve and unpause or invoke `emergency_close`.                     |
| **On admin rotation**        | `transfer_admin` + `accept_admin` (two transactions).                                                            |
| **On legitimate retirement** | `init_wind_down`, close any open position, broadcast `claim_wind_down` instructions to depositors.               |

***

## Related

* [Keeper bot](/for-operators/keeper-bot.md) — non-admin strategy operations
* [Monitoring](/for-operators/monitoring.md) — what to watch
* [Errors](/for-developers/errors.md) — what reverts look like
* [Fees](/reference/fees.md) — `collect_fees` math


---

# 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-operators/admin-ops.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.
