Instructions
An instruction is one call to a Solana program. A transaction can contain several. This page lists every instruction of akashi_vault, groups them by who may call them, and walks through the three flows that matter most: depositing, withdrawing and investing.
This page is reference material for developers and for readers who want to check exactly what the program allows. Instruction, argument and account names are exactly as they appear in the program's public interface. Amounts are in USDC base units, where 1 USDC = 1,000,000 base units, because USDC and the share mints have 6 decimals. Argument types such as u8, u16 and u64 are unsigned whole numbers of 1, 2 and 8 bytes; i64 is a signed one; bps means basis points, where 10,000 is 100%.
Who can call what
| Caller | Instructions |
|---|---|
| Holder (signs for their own funds) | deposit, request_withdrawal, cancel_withdrawal, settle_shares |
| Anyone (permissionless) | refresh_leg, sync_vault, fund_withdrawal, claim_withdrawal, settle_async, refresh_pending, execute_change, execute_adapter_approval, distribute_settlement_cash, sweep_leg_transit, seed_junior (Draft only) |
| Executor | allocate, deallocate, allocate_v2, deallocate_v2, begin_async, cancel_async, begin_trade, end_trade |
| Guardian or admin | pause, lower_leg_limits, set_leg_close_only, lower_vault_cap, remove_executor, mark_leg_impaired, suspend_leg, unsuspend_leg, cancel_change, lower_cost_budget |
| Admin only | unpause, set_executor and set_guardian (Draft only), propose_admin, configure_vault (Draft), add_leg, configure_v2_leg, init_token_leg, init_cost_budget (Draft), activate_vault, retire_leg, resume, begin_settlement, queue_change, set_share_metadata, close_draft_vault |
| Proposed admin | accept_admin |
| Allowed vault creator | create_vault, init_vault_accounts |
| Global authority | init_global, resize_global, set_adapter_approval, set_vault_creator, set_registry_delay, propose_global_authority, cancel_global_authority |
| Proposed global authority | accept_global_authority |
Table: the roles that may sign each instruction; guardian-or-admin actions can only reduce risk, so they take effect at once.
The rule behind the table: anything that can only make a vault safer is instant, and anything that can make it riskier waits for the timelock (see Governance changes).
Holder instructions
| Instruction | Arguments | What it does |
|---|---|---|
deposit | tranche: Tranche, amount: u64, min_shares: u64 | Moves amount USDC from your wallet into idle and mints shares of the chosen tranche. Fails if you would receive fewer than min_shares. |
request_withdrawal | tranche: Tranche, shares: u64, min_usdc_out: u64 | Moves your shares into escrow and creates a WithdrawalRequest. It becomes eligible after the tranche's notice period: the Senior delay or the Junior notice. |
cancel_withdrawal | none | Returns the shares not yet funded to you. A partly funded request keeps its funded part for claiming. |
settle_shares | tranche: Tranche, shares: u64 | In Settlement only: converts shares into a claim on the vault's cash and pays what is due. Call again with 0 shares to collect later cash. |
Table: the four instructions a holder signs.
Tranche is an enum: Senior (0) or Junior (1).
Deposit: accounts
| # | Account | Writable | Signer | Check |
|---|---|---|---|---|
| 1 | user | Yes | Your wallet | |
| 2 | vault | Yes | Must have this usdc_mint | |
| 3 | vault_authority | PDA ["auth", vault] | ||
| 4 | usdc_mint | |||
| 5 | user_usdc | Yes | Your USDC token account | |
| 6 | idle_account | Yes | Must equal vault.idle_account | |
| 7 | share_mint | Yes | Must be the chosen tranche's mint (InvalidMint) | |
| 8 | user_shares | Yes | Your token account for that share mint | |
| 9 | token_program | SPL Token |
Table: the accounts of deposit, in the order the instruction expects them.
Deposit: checks, in order
- No trade bracket is open (
TradeOpen). - The vault is Active (
VaultNotActive). - Every pending queued action has been re-valued (
PendingClaim). - Deposits are not paused (
Paused). amount > 0andamountis at least the vault's minimum deposit (AmountTooSmall).- Every leg is fresh (
StaleValuation). The vault then books gains and losses since the last update (the same step assync_vault) and checks it is still Active. - NAV plus
amountstays within the vault's deposit cap (VaultCapExceeded). - Shares to mint are above 0 (
ZeroShares) and at leastmin_shares(MinSharesNotMet). - Coverage after the deposit is valid (
CoverageViolation): a Senior deposit must keep Junior at least the vault's minimum coverage of the total; a Junior deposit must keep it at most the vault's maximum coverage, where 0 means no maximum.
Then the USDC moves into idle, the shares are minted, and the Deposited event is emitted.
sequenceDiagram
participant W as Holder wallet
participant C as Akashi app (client)
participant K as Akashi API
participant V as akashi_vault
participant T as SPL Token
C->>K: refresh request (warm-up; the app sends at most one every 20 seconds)
K->>V: refresh_leg for each leg
C->>W: ask to sign deposit(tranche, amount, min_shares)
W->>V: deposit
V->>V: checks, legs fresh, book gains and losses, price at the high band
V->>T: CPI transfer amount: user_usdc to idle_account (signed by user)
V->>T: CPI mint_to shares: share_mint to user_shares (signed by vault authority)
V-->>W: Deposited event
Figure: the app first makes sure every leg is fresh, then one deposit instruction moves the USDC and mints the shares.
The takeaway: the app's warm-up call is a convenience, not a requirement. If a leg is stale, the deposit fails with StaleValuation and can simply be retried after a refresh.
Withdrawal flow
A withdrawal has three steps, and only the first needs your signature.
sequenceDiagram
participant W as Holder wallet
participant V as akashi_vault
participant T as SPL Token
participant K as Anyone (executor, app or you)
W->>V: request_withdrawal(tranche, shares, min_usdc_out)
V->>T: CPI transfer shares: owner_shares to escrow
V->>V: create WithdrawalRequest, eligible_at = now + delay
Note over V: wait until eligible_at
K->>V: fund_withdrawal
V->>V: checks, legs fresh, book gains and losses, price at the low band
V->>T: CPI burn shares from escrow
V->>T: CPI transfer USDC: idle_account to payout_account
K->>V: claim_withdrawal
V->>T: CPI transfer USDC: payout_account to owner_usdc
V->>V: close the request when fully funded and paid
Figure: shares are escrowed when you request, burned and priced when the request is funded, and the USDC is paid to you when anyone claims it.
Request states
stateDiagram-v2
[*] --> Waiting: request_withdrawal
Waiting --> Eligible: eligible_at passes
Eligible --> PartlyFunded: fund_withdrawal (not enough idle, brake or coverage)
PartlyFunded --> PartlyFunded: fund_withdrawal again
Eligible --> Funded: fund_withdrawal (all shares)
PartlyFunded --> Funded: fund_withdrawal (rest)
Waiting --> Closed: cancel_withdrawal
Eligible --> Closed: cancel_withdrawal
PartlyFunded --> FundedPart: cancel_withdrawal returns unfunded shares
FundedPart --> Closed: claim_withdrawal
Funded --> Closed: claim_withdrawal pays everything
Figure: a request can be funded in several parts and cancelled at any time before it is fully funded; USDC already funded always stays claimable.
Fund: checks, in order
- No trade bracket is open, and withdrawals are open: the vault is Active or Recovery and not frozen (
VaultFrozen,VaultNotActive). - Pending queued actions are re-valued (
PendingClaim), and funding is not paused (Paused). - The request is eligible (
NotEligibleYet) and not already funded (AlreadyFunded). - Every leg is fresh, gains and losses are booked, and withdrawals are still open.
- The price is the tranche's value at the cautious low band.
- The amount is limited by the vault's idle cash. For Senior, also by the outflow brake when it is on (
OutflowLimitReached). For Junior, also by the USDC that waiting Senior requests need (InsufficientIdle) and by the coverage floor (CoverageViolation). - If not everything fits, the request is funded in part. The part must still meet
min_usdc_outpro rata (BelowMinUsdcOut).
Then the shares are burned, the USDC moves from idle to the payout account, and the vault's reserved payouts grow by that amount.
Withdraw: accounts
| Instruction | Accounts (in order) |
|---|---|
request_withdrawal | owner (signer, writable), vault, request (new PDA ["request", vault, next_request_id]), share_mint, owner_shares, escrow, token_program, system_program |
fund_withdrawal | vault, vault_authority, request, usdc_mint, share_mint, escrow, idle_account, payout_account, senior_mint, token_program |
claim_withdrawal | vault, vault_authority, usdc_mint, request (must have unclaimed funded USDC, else NotFunded), owner, owner_usdc, payout_account, token_program |
cancel_withdrawal | owner (signer), vault, vault_authority, request (not fully funded, else AlreadyFunded), share_mint, escrow, owner_shares, token_program |
Table: the accounts of each withdrawal instruction; fund_withdrawal and claim_withdrawal need no signer from the vault's side, so anyone can send them.
Investing flow (executor)
| Instruction | Arguments | What it does |
|---|---|---|
allocate / allocate_v2 | amount: u64, min_value_increase: u64 | Moves USDC from idle into a leg's protocol through its adapter. |
deallocate / deallocate_v2 | usdc_target: u64, min_usdc_out: u64 | Takes USDC out of a leg back into idle. |
begin_async | direction, amount: u64, min_claim_value: u64, deadline: i64 | Starts a queued deposit or exit on an AAI v2 leg. |
settle_async | min_return_units: u64 | Completes a queued action once the adapter proves the protocol paid. Anyone may call. |
cancel_async | none | Takes back a queued action. |
begin_trade / end_trade | direction, amount: u64, min_out: u64 / none | Brackets a swap on a token leg, in the same transaction. |
Table: the executor's instructions; the _v2 versions are for legs configured with configure_v2_leg.
sequenceDiagram
participant E as Executor
participant V as akashi_vault
participant T as SPL Token
participant A as Adapter
participant P as Protocol
E->>V: allocate(amount, min_value_increase) + bound accounts
V->>V: Active, not paused, leg Active, legs fresh, book gains and losses
V->>T: CPI transfer amount: idle_account to leg_transit (signed by vault authority)
V->>A: CPI aai_deposit(amount) (signed by leg authority)
A->>P: CPI deposit into the protocol
A-->>V: ValuationReport
V->>V: leg_transit back to its old balance? (TransitBalanceMismatch)
V->>V: value grew by amount minus the leg's slippage allowance? (ValueIncreaseTooLow)
V->>V: leg cap, leg weight, idle minimum (LegCapExceeded, LegWeightExceeded, IdleBelowMinimum)
V->>V: book gains and losses
Figure: an allocation passes USDC through the leg's own transit account, and the vault checks afterwards that exactly that amount left and the leg's value grew by it.
The takeaway: the executor chooses when and how much to invest, but it cannot choose where the money goes. It can only reach the leg's adapter and the accounts bound to it, and every allocation is checked against the leg's limits.
Token flows
flowchart LR
U[Holder USDC account] -->|deposit| I[Idle account PDA]
I -->|allocate| TR[Leg transit account]
TR -->|aai_deposit| P[Protocol position]
P -->|aai_withdraw| TR
TR -->|deallocate| I
I -->|fund_withdrawal| PO[Payout account PDA]
PO -->|claim_withdrawal| U
PO -->|settle_shares| U
S[Holder share account] -->|request_withdrawal| ES[Escrow PDA]
ES -->|fund_withdrawal: burn| X[Burned]
ES -->|cancel_withdrawal| S
M[Share mint PDA] -->|deposit: mint| S
Figure: USDC moves from your wallet to idle, out to legs and back, then through the payout account to you; shares move from your wallet to escrow and are burned when funded.
The key point is the payout account. Once a request is funded, its USDC is moved out of idle into the payout account and recorded as a reserved payout. From then on it cannot be invested or used to absorb losses. It belongs to the request owner.
Maintenance (anyone)
| Instruction | Arguments | What it does |
|---|---|---|
refresh_leg | none, plus the leg's bound accounts | Asks the adapter for a ValuationReport and records the leg's value. |
sync_vault | none | Books gains and losses since the last sync into Senior and Junior. |
refresh_pending | none | Re-values a queued action's claim. It may only go down. |
sweep_leg_transit | none | Moves USDC that arrived in a leg's transit account into idle, for example a protocol's compensation. |
distribute_settlement_cash | none | In Settlement: adds new idle cash to the settlement book. |
execute_change | none | Applies a queued change after its delay. |
Table: instructions anyone can send to keep a vault's numbers current.
Guardian and admin actions
| Instruction | Arguments | Effect |
|---|---|---|
pause / unpause | scopes: u8 | Stops or restarts deposits (1), funding (2) and allocation (4). The numbers can be added to combine scopes, so 3 means deposits and funding and 7 means all three. Anything else is InvalidPauseScope. Only the admin can unpause. |
lower_leg_limits | cap: u64, max_bps: u16 | Lowers a leg's cap and maximum weight. A higher value fails with LimitNotLower. |
set_leg_close_only | none | The leg takes no new money; it can still be emptied. |
lower_vault_cap | vault_cap: u64 | Lowers the vault's deposit cap. |
remove_executor | none | Removes the executor at once. |
mark_leg_impaired | mark: u64 | Caps a leg's value and starts Recovery (details). |
suspend_leg / unsuspend_leg | none | Sets a leg aside while it cannot be valued (details). |
cancel_change | none | Cancels a queued change. |
lower_cost_budget | budget_bps: u16 | Lowers the token-leg trading cost budget. |
retire_leg | none | Admin: closes an empty leg for good (LegNotEmpty otherwise). |
resume | none | Admin: Recovery back to Active (RecoveryNotFinished if a leg is still impaired or Senior is not repaid). |
begin_settlement | none | Admin: starts the final wind-down. Cannot be undone. |
Table: the safety and incident instructions.
Governance changes
Changes that could increase risk on a live vault go through queue_change(change: ConfigChange). Each creates a PendingChange that anyone can execute with execute_change once its delay has passed, within a 7-day window set by the program. During the delay, the guardian or admin can cancel it.
ConfigChange variant | Fields | Delay |
|---|---|---|
UpdateLeg | leg_index: u8, params (the leg's limits and settings) | Governance delay |
SetIdleTargets | idle_target_bps: u16, idle_min_bps: u16 | Governance delay |
SetVaultCap | vault_cap: u64 | Governance delay |
SetMinDeposit | min_deposit: u64 | Governance delay |
SetMaxValuationAge | slots: u64 | Governance delay |
SetRiskSettings | max_leg_growth_apr_bps: u16, senior_outflow_limit_bps: u16, incident_loss_bps: u16 | Governance delay |
RaiseMark | leg_index: u8, mark: u64 | Governance delay |
SetExecutor | executor: Pubkey | Governance delay |
SetGuardian | guardian: Pubkey | Governance delay |
RepinAdapter | leg_index: u8, adapter_slot: u64 | Add-leg delay |
AddLeg | adapter_program: Pubkey, params (the leg's limits and settings), accounts_hash: [u8; 32] | Add-leg delay |
Table: every timelocked change; by default the governance delay is 86,400 seconds (1 day) and the add-leg delay is 172,800 seconds (2 days).
The program caps both delays at 30 days. AddLeg is not applied by execute_change; it authorises the admin's add_leg for exactly that adapter, those parameters and that account list.
In Recovery and Settlement, only role changes (SetExecutor, SetGuardian), RepinAdapter and, in Recovery only, RaiseMark can be executed. Anything else fails with VaultNotActive.
Parameters and limits
| Setting | Limit |
|---|---|
| Minimum coverage | 500 to 9,000 basis points (5% to 90%) |
| Maximum coverage | 0 (no maximum), or above the minimum coverage and at most 10,000 basis points |
| Premium curve | up to 4 points with increasing coverage; each premium at most 9,000 basis points (90%) |
| Senior delay | up to 3,600 seconds (1 hour) |
| Junior notice | up to 2,592,000 seconds (30 days) |
| Maximum valuation age | up to 150 slots (about one minute) |
| A leg's slippage allowance | up to 1,000 basis points (10%) |
| Token-leg cost budget | up to 500 basis points (5%) over 7 days |
| A leg's maximum growth rate | up to 60,000 basis points (600% a year); default 3,000 (30%) |
| Profit unlock period | up to 7 days; default 0 |
| Senior outflow brake | up to 10,000 basis points; default 0 (off) |
| Incident threshold | up to 10,000 basis points; default 25 (0.25%) |
| Governance delay, add-leg delay | up to 30 days; defaults 1 and 2 days |
| Legs per vault | 8 |
| Bound accounts per leg | 64 |
Table: the limits the program enforces on every setting, and the program's defaults where it has them.
The program has no fee setting: no vault setting is a fee, and no instruction pays a fee to the curator or to Akashi. See Fees and execution costs.
Advanced details
- The program adds a tolerance of 2 base units to the slippage allowance on allocations, to absorb protocol rounding.
- Share math uses virtual offsets of 1,000,000 shares and 1,000,000 base units, which stops the first depositor from manipulating the share price. See NAV and share pricing.
- An instruction that would leave a trade bracket open fails:
begin_trademust be followed byend_tradein the same transaction (MissingEndTrade), and must be a top-level instruction (NotTopLevel).