Accounts and PDAs
This page is reference material for developers who read Akashi vault data directly from the chain. You do not need it to deposit or withdraw.
It lists the on-chain accounts of a vault, how to find their addresses, and how their data is laid out. Investors can also use it to check where their money sits.
Terms used on this page
- PDA (program-derived address). An address computed from the program ID and a list of seeds. It has no private key, so only the program can sign for it. Given the seeds, anyone can compute the address, for example with
PublicKey.findProgramAddressSyncin the Solana web3 libraries. - Seed. One input to a PDA. Text seeds are UTF-8 bytes, such as
"vault". Numbers are little-endian bytes, written "LE" below:vault_idas au64is 8 bytes, lowest byte first. - Discriminator. The first 8 bytes of every Anchor account. They identify the account type.
- ATA (associated token account). The standard token account address for a given owner and mint.
- Integer types.
u8,u16,u32andu64are unsigned whole numbers of 1, 2, 4 and 8 bytes;i64is a signed whole number of 8 bytes. APubkey(an account address) is 32 bytes. Amounts are in USDC base units, where 1 USDC is 1,000,000 base units, and "bps" fields are basis points, where 10,000 is 100%.
Why addresses are derived
Every account of a vault is a PDA of the akashi_vault program. This has two benefits:
- Nobody holds a key to the money. The vault's USDC sits in token accounts owned by the vault authority PDA. Only the program can move it, and only by following its rules.
- Anyone can find every account. You do not need an index or an API. Given a vault ID, the seeds below give every address.
The account map
classDiagram
class GlobalConfig {
PDA seeds: "global"
authority
approved_adapters (max 16)
vault_creators (max 32)
registry_delay_secs
pending_adapters (max 8)
}
class Vault {
PDA seeds: "vault", vault_id LE u64
admin, guardian, params.executor
state
senior_value, junior_value
idle_ledger, reserved_payouts
leg_values[8], leg_marks[8]
}
class VaultAuthority {
PDA seeds: "auth", vault
no data, signs for the vault
}
class SeniorMint {
PDA seeds: "senior_mint", vault
}
class JuniorMint {
PDA seeds: "junior_mint", vault
}
class IdleAccount {
PDA seeds: "idle", vault
USDC token account
}
class PayoutAccount {
PDA seeds: "payout", vault
USDC token account
}
class Escrow {
PDA seeds: "escrow", vault, tranche byte
share token account
}
class Leg {
PDA seeds: "leg", vault, index byte
adapter_program, bound_accounts
params, mode
}
class LegAuthority {
PDA seeds: "leg_auth", vault, index byte
signs adapter calls
}
class LegTransit {
USDC ATA of LegAuthority
}
class V2LegConfig {
PDA seeds: "leg_v2", leg
}
class PendingAction {
PDA seeds: "pending_action", leg
}
class TokenLeg {
PDA seeds: "token_leg", leg
}
class WithdrawalRequest {
PDA seeds: "request", vault, request_id LE u64
}
class PendingChange {
PDA seeds: "change", vault, change_id LE u64
}
class CostBudget {
PDA seeds: "cost_budget", vault
}
class SettlementClaim {
PDA seeds: "settlement_claim", vault, owner, tranche byte
}
GlobalConfig "1" --> "many" Vault : approves adapters and creators
Vault "1" --> "1" VaultAuthority
VaultAuthority --> SeniorMint : mint authority
VaultAuthority --> JuniorMint : mint authority
VaultAuthority --> IdleAccount : owner
VaultAuthority --> PayoutAccount : owner
VaultAuthority --> Escrow : owner
Vault "1" --> "0..8" Leg
Leg "1" --> "1" LegAuthority
LegAuthority --> LegTransit : owner
Leg "1" --> "0..1" V2LegConfig
Leg "1" --> "0..1" PendingAction
Leg "1" --> "0..1" TokenLeg
Vault "1" --> "many" WithdrawalRequest
Vault "1" --> "many" PendingChange
Vault "1" --> "0..1" CostBudget
Vault "1" --> "many" SettlementClaim
Figure: every account hangs off one Vault PDA, and every token account is owned by one of two PDA signers, the vault authority or a leg authority.
The takeaway: there are only two signers that can move a vault's tokens. The vault authority signs for idle cash, payouts, escrowed shares and share minting. Each leg authority signs only for its own leg's transit account and protocol position.
Seeds table
| Account | Seeds | Created by |
|---|---|---|
GlobalConfig | "global" | init_global |
GlobalAuthorityProposal | "global_authority" | propose_global_authority |
Vault | "vault", vault_id (u64 LE) | create_vault |
| Vault authority | "auth", vault | No account data; derived only |
| Senior share mint | "senior_mint", vault | init_vault_accounts |
| Junior share mint | "junior_mint", vault | init_vault_accounts |
| Idle USDC account | "idle", vault | init_vault_accounts |
| Payout USDC account | "payout", vault | init_vault_accounts |
| Share escrow | "escrow", vault, [0] for Senior or [1] for Junior | init_vault_accounts |
Leg | "leg", vault, [index] | add_leg |
| Leg authority | "leg_auth", vault, [index] | No account data; derived only |
| Leg transit | The leg authority's USDC associated token account | add_leg |
V2LegConfig | "leg_v2", leg | configure_v2_leg |
PendingAction | "pending_action", leg | begin_async |
TokenLeg | "token_leg", leg | init_token_leg |
WithdrawalRequest | "request", vault, request_id (u64 LE) | request_withdrawal |
PendingChange | "change", vault, change_id (u64 LE) | queue_change |
CostBudget | "cost_budget", vault | init_cost_budget |
SettlementClaim | "settlement_claim", vault, owner, [tranche] | settle_shares |
Table: the seeds of every vault account and the instruction that creates it.
Example. To find the Senior escrow of vault 7: compute the vault PDA from ["vault", 07 00 00 00 00 00 00 00], then the escrow PDA from ["escrow", <vault address>, 00].
The next request ID is Vault.next_request_id, so the address of a new withdrawal request is known before it is created.
Share mints
- Senior and Junior share mints have the same number of decimals as USDC, which is 6, so one share starts at 1 USDC.
- Their mint authority is the vault authority PDA. There is no freeze authority, so nobody can freeze your shares.
set_share_metadata(admin) sets each tranche's name, symbol and URI.
Byte layouts
All layouts are Borsh, the encoding Anchor uses: integers are little-endian, Pubkey is 32 bytes, bool and enums are 1 byte, and a Vec is a 4-byte length followed by its items. Offsets are in bytes from the start of the account. The maximum list lengths shown below (16 approved adapters, 32 vault creators, 8 pending adapters, 64 bound accounts per leg, 4 premium curve points, 8 legs) are fixed by the program.
Vault (1,312 bytes)
The size assumes a premium curve with the maximum of 4 points, which is how the account is allocated. With fewer points, the curve is shorter and every field after it moves 4 bytes earlier per missing point. Always decode with the program's IDL (its published interface description) rather than fixed offsets.
offset size field
0 8 discriminator
8 8 vault_id u64
16 32 admin Pubkey
48 32 params.executor Pubkey
80 2 params.min_coverage_bps u16
82 2 params.max_coverage_bps u16
84 20 params.premium_curve Vec<PremiumPoint>: u32 len + 4 x (u16 coverage_bps, u16 premium_bps)
104 4 params.senior_delay_secs u32
108 4 params.junior_notice_secs u32
112 8 params.min_deposit u64
120 8 params.vault_cap u64
128 2 params.idle_target_bps u16
130 2 params.idle_min_bps u16
132 8 params.max_valuation_age_slots u64
140 8 params.junior_seed u64
148 1 state 0 Draft, 1 Active, 2 Recovery, 3 Settlement
149 32 usdc_mint
181 32 senior_mint
213 32 junior_mint
245 32 idle_account
277 32 payout_account
309 32 senior_escrow
341 32 junior_escrow
373 1 accounts_initialized bool
374 1 leg_count u8
375 64 leg_values [u64; 8]
439 64 leg_value_slots [u64; 8]
503 8 senior_value u64
511 8 junior_value u64
519 8 idle_ledger u64
527 8 reserved_payouts u64
535 8 pending_seed_amount u64
543 32 pending_seeder Pubkey
575 8 next_request_id u64
583 1 bump
584 1 authority_bump
585 1 v2_leg_mask bit i = leg i
586 1 pending_leg_mask
587 8 next_action_nonce u64
595 1 pending_valued_mask
596 32 guardian
628 32 pending_admin
660 1 paused bits: 1 DEPOSITS, 2 FUNDING, 4 ALLOCATION
661 18 settings risk settings (see below)
679 64 leg_value_times [i64; 8]
743 8 senior_pending_shares u64
751 1 impaired_leg_mask
752 64 leg_marks [u64; 8]
816 8 senior_impairment u64
824 56 settlement settlement record: 7 x u64
880 8 next_change_id u64
888 64 leg_values_low [u64; 8]
952 64 leg_values_high [u64; 8]
1016 8 locked_profit u64
1024 8 locked_profit_at i64
1032 8 locked_profit_end i64
1040 8 senior_outflow_window_start i64
1048 8 senior_outflow_in_window u64
1056 64 leg_positions [u64; 8]
1120 64 leg_cost_basis [u64; 8]
1184 8 junior_impairment u64
1192 8 recovery_value u64
1200 43 open_trade open trade (see below)
1243 1 token_leg_mask
1244 1 suspended_leg_mask
1245 67 reserved
----
1312Figure: the Vault account field by field; a 4-point premium curve is assumed.
settings, the risk settings (18 bytes): max_leg_growth_apr_bps u16, profit_unlock_secs u32, senior_outflow_limit_bps u16, incident_loss_bps u16, governance_delay_secs u32, add_leg_delay_secs u32.
settlement, the settlement record (56 bytes): entitled_senior, entitled_junior, supply_senior, supply_junior, cash_total, cum_senior, cum_junior, all u64.
open_trade, the open trade (43 bytes): active bool, leg_index u8, direction u8 (0 Enter, 1 Exit), then amount, min_out, value_before, units_before, mark_before, all u64.
Leg (294 bytes plus 32 per bound account)
offset size field
0 8 discriminator
8 32 vault
40 1 index u8
41 32 adapter_program
73 4+32*n bound_accounts Vec<Pubkey>, n up to 64
then, with n = 0:
77 64 account_config 8 x u64 masks (init, deposit, withdraw, value; then the writable masks)
141 32 transit_account
173 14 params target_bps u16, max_bps u16, cap u64, max_slippage_bps u16
187 1 authority_bump
188 1 bump
189 1 mode 0 Active, 1 CloseOnly, 2 Impaired, 3 Retired, 4 Suspended
190 32 adapter_program_data
222 8 adapter_slot u64
230 1 mode_before_suspension
231 63 reservedFigure: the Leg account; every field after bound_accounts moves 32 bytes later for each bound account.
WithdrawalRequest (139 bytes)
offset size field
0 8 discriminator
8 32 vault
40 32 owner
72 8 request_id u64
80 1 tranche 0 Senior, 1 Junior
81 8 shares u64, shares escrowed
89 8 min_usdc_out u64
97 8 requested_at i64, unix seconds
105 8 eligible_at i64, unix seconds
113 1 funded bool, true once every share is funded
114 8 usdc_reserved u64, USDC set aside in the payout account so far
122 1 bump
123 8 funded_shares u64
131 8 claimed_usdc u64Figure: the WithdrawalRequest account; a request can be funded in parts, tracked by funded_shares and usdc_reserved.
ValuationReport (53 bytes, adapter return data)
offset size field
0 1 version u8, the adapter interface version
1 8 value_low u64, cautious exit value in USDC base units
9 8 value_mid u64, book value
17 8 value_high u64, upper value
25 8 liquid_usdc u64, USDC withdrawable now
33 8 receipt_units u64, protocol units the leg holds
41 8 protocol_slot u64
49 4 flags u32: 1 PROTOCOL_PAUSED, 2 WITHDRAW_DISABLED, 4 DEBT_PRESENT,
8 PRICE_UNCERTAIN, 16 MARKET_STRESSFigure: the report every adapter returns from its aai_value, aai_deposit and aai_withdraw instructions.
GlobalConfig (up to 1,913 bytes)
offset size field
0 8 discriminator
8 32 authority
40 516 approved_adapters Vec<Pubkey>, up to 16
556 1028 vault_creators Vec<Pubkey>, up to 32
1584 1 bump
1585 4 registry_delay_secs u32
1589 324 pending_adapters Vec, up to 8 entries of (program Pubkey, approvable_at i64)Figure: the protocol-wide registry at its maximum size; offsets shift when the lists are shorter.
Other accounts
| Account | Fields |
|---|---|
SettlementClaim | vault, owner, tranche, units (shares converted), collected (USDC paid so far), bump |
PendingChange | vault, change_id, change (a ConfigChange), queued_at, executable_at, expires_at, payer, bump |
PendingAction | vault, leg, payer, direction, amount, input_units_settled, nonce, external_message_id, expected_return_mint, expected_return_account, observed_return_balance, returned_units, claim_value_low, claim_value_mid, claim_value_high, started_slot, deadline, bump |
V2LegConfig | vault, leg, capabilities, eight account masks for begin and settle of deposits and withdrawals, deposit_return_mint, deposit_return_account, bump |
TokenLeg | leg, mint, token_account, max_trade_usdc, bump |
CostBudget | vault, budget_bps, days and spent (7 entries each, one per day), bump |
GlobalAuthorityProposal | proposer, proposed, bump |
Table: the remaining account types and their fields, in order.
Advanced details
Vault.idle_ledgeris the vault's own count of idle USDC. It changes only through the program's own flows, never by reading the token balance. USDC sent to the idle account directly is not counted, so nobody can change the share price by donating to it. NAV isidle_ledgerplus the recognised value of every leg.Vault.reserved_payoutsis the USDC in the payout account that funded requests are still owed.- A
WithdrawalRequestis closed when it is fully funded and fully claimed, and its rent returns to the owner.