Reference
Accounts
Vault and WithdrawalRequest: seeds, fields, byte sizes.
Two account types. Both are Anchor accounts, so each begins with an 8-byte discriminator. Byte sizes below are the Borsh encoding that follows it.
Vault
What it does not hold is the thing worth noticing: there is no authority key on the withdrawal path. owner_hint exists so a wallet can find its own vaults, and the program never checks a signature against it for anything that moves value.
seeds = [b"vault", owner_hint.as_ref(), label.as_ref()]| Field | Type | Bytes | Meaning |
|---|---|---|---|
| commitment | [u8; 32] | 32 | Where the lock is. The only thing between the balance and the world. |
| nonce | u64 | 8 | Increments on every completed withdrawal. Part of the signed digest. |
| owner_hint | Pubkey | 32 | Lets a wallet find its vaults. Never consulted when authorising anything. |
| label | [u8; 16] | 16 | Seed so one owner can hold several vaults. |
| created_at | i64 | 8 | Unix time the vault was opened. |
| rotations | u64 | 8 | How many times the lock has rotated. |
| bump | u8 | 1 | PDA bump. |
Total: 8 + 105 = 113 bytes. The vault stays rent exempt; finalize_withdrawal never pays below the minimum for 113 bytes.
WithdrawalRequest
An in-flight withdrawal. The intent is written here before any signature material is revealed, and the chain endpoints land here as they are verified.
seeds = [b"request", vault.key().as_ref(), &vault.nonce.to_le_bytes()]| Field | Type | Bytes | Meaning |
|---|---|---|---|
| vault | Pubkey | 32 | The vault being withdrawn from. |
| nonce | u64 | 8 | The vault nonce the request was opened against. |
| amount | u64 | 8 | Lamports to pay. |
| destination | Pubkey | 32 | Who is paid. May be a wallet, a PDA or a program-owned account. |
| next_commitment | [u8; 32] | 32 | What the lock becomes once this withdrawal lands. Signed over. |
| chain_endpoints | Vec<[u8; 32]> | 4 + 34 × 32 = 1,092 | Up to 34 endpoints, filled as chunks arrive. |
| chains_filled | u8 | 1 | How many chains have been accepted. Doubles as the cursor. |
| opened_at | i64 | 8 | Unix time the request was opened. Expiry is measured from it. |
| payer | Pubkey | 32 | Who paid the rent, and who gets it back. |
| bump | u8 | 1 | PDA bump. |
Total: 8 + 1,246 = 1,254 bytes. Closed on finalise or cancel, with the rent returned to payer.
Constants
| Constant | Value | Meaning |
|---|---|---|
| WINTERNITZ_W | 256 | One chain per byte of the digest. |
| MESSAGE_CHAINS | 32 | Chains covering the 32-byte digest. |
| CHECKSUM_CHAINS | 2 | Chains covering the checksum, maximum 8,160. |
| TOTAL_CHAINS | 34 | MESSAGE_CHAINS + CHECKSUM_CHAINS. |
| CHAIN_LENGTH | 255 | Steps in a full chain. |
| SIGNATURE_BYTES | 1,088 | TOTAL_CHAINS × 32. |
| CHAINS_PER_PUSH | 8 | Most chains one push_signature may carry. |
| REQUEST_EXPIRY_SECONDS | 86,400 | After which anyone may cancel a live request. |
| VAULT_SEED | "vault" | PDA seed prefix. |
| REQUEST_SEED | "request" | PDA seed prefix. |