Admin Operations
Admin-only instructions on the ksUSD vault. All signed by the vault.admin keypair (or successor after transfer_admin + accept_admin).
Source: the admin instructions · wind-down · lending setup · oracle setup
One-time setup
initialize(params)
First deploy. Creates the vault PDA, ksUSD mint, reserve USDC ATA.
enable_phoenix(subaccount_id, name)
After init, before opening any perp position.
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_downstay open by design — depositors should always be able to exitCannot un-pause while
position_mode == WindDown(reverts withWindDownActive)
When to use:
Pre-audit incident response
Investigating an oracle / venue anomaly
During an admin handover (briefly, to flush in-flight state)
Emits PauseToggled.
update_params(args)
Adjust risk parameters in place
Each field on
UpdateParamsArgsisOption<T>— only the fields you pass get written
liquidity_buffer_bps
0–5_000 (≤ 50% of NAV)
funding_threshold_normal_bps
i32; expected positive
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 →
InvalidParamsMints, admin, oracles, Phoenix / Kamino refs are immutable through this instruction — set once at
initialize/enable_*/set_oracles
transfer_admin(new_admin) + accept_admin()
Two-step admin handover:
Step 1 — records
new_adminas 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.
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(Parked) — fees crystallize on realized gainsSee Fees for the math
Admin share →
admin_usdc_accountReserve skim →
reserve_ataHWM 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)
Idempotent when share price ≤ HWM (no transfer, no state change). Emits FeesCollected.
reset_peak()
Clears
peak_share_price_1e9andconsecutive_dd_settles_observedto current share priceUse after legitimate recovery — prevents a stale peak from keeping the drawdown guard armed against routine volatility
Emits PeakReset.
pay_from_reserve(amount)
Moves USDC out of
reserve_atainto the vault's main USDC ATA → socializes a reserve draw into NAVCaller must invoke
unlend_reservefirst if part of the reserve is currently lent at Kamino
Emits ReservePaidOut.
init_wind_down()
Terminal mode switch. Sets position_mode = WindDown and pauses the vault. After this:
depositreverts (WindDownActive)Strategy
open_*reverts (WindDownActive)Existing positions must be closed via
close_position/emergency_closeUsers redeem pro-rata via
claim_wind_down(shares)against the vault's idle USDC
Cannot be undone via set_pause(false).
Both preconditions, and why they are enforced
init_wind_down requires position_mode == Idle and usdc_lent_kamino == 0 && reserve_lent_kamino == 0 (WindDownRequiresUnlent). The lending one matters because claim_wind_down pays out of the vault's USDC ATA but sizes each payout off effective_nav_usdc(), which counts the Kamino basis — so entering with USDC lent would make the lent fraction permanently unclaimable. unlend_usdc and unlend_reserve are keeper-only, and WindDown is exactly when the keeper stands down, so nothing would be left that could recover it.
So run the drain first, and wait for it to finish:
Then close any position, then init_wind_down.
Emits WindDownInitiated.
Operational checklist
Daily
Verify cached_nav_usdc against on-chain Phoenix / 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
KEEPER_DRAIN_LENDING=1 until both lent figures read 0, close any open position, init_wind_down, broadcast claim_wind_down instructions to depositors.
Related
Keeper bot — non-admin strategy operations
Monitoring — what to watch
Errors — what reverts look like
Fees —
collect_feesmath
Last updated