Skip to content

Repository files navigation

Vault Program

A PDA-based SOL vault system on Solana. Manage the full lifecycle: create vaults, deposit SOL, assign claim amounts, process claims, withdraw leftovers, and clean up accounts.

Built with Anchor framework.

Warning

This software has not undergone a professional security audit. Use it at your own risk. See the security policy for vulnerability reporting.

Features

  • Upgrade-authority-controlled vault creation
  • PDA-based vault and claim-record accounts
  • SOL deposits, authority-managed payouts, and withdrawals
  • Explicit open and closed lifecycle states
  • Rent recovery through vault and claim-record cleanup
  • Anchor client example and @solana/kit PDA helpers

Getting Started

Requirements

  • Anchor CLI 1.1.2
  • Solana CLI 1.18 or newer
  • Rust and Cargo
  • Bun (for the integration test)

Windows is supported through WSL 2. Install the requirements and run all build, test, and deployment commands from a WSL terminal.

Documentation

Testing

The integration test runs against a disposable local validator. It generates a temporary program ID and wallets, leaving the placeholders in this repository unchanged.

cd tests
bun install
bun run test

The test requires Anchor CLI, Solana CLI, solana-test-validator, and Bun. Compiled dependencies are cached under the ignored target/local-test-cache/ directory; temporary IDs, keypairs, and validator data are still deleted after every run.

How It Works

Accounts

Vault — Holds deposited SOL for a single vault instance.

FieldTypeDescription
authorityPubkeyWallet that controls the vault
vault_id[u8; 32]Unique fixed-size vault identifier
statusVaultStatusCurrent lifecycle state
total_depositedu64Cumulative lamports deposited
total_claimedu64Cumulative lamports claimed
total_withdrawnu64Cumulative lamports withdrawn
bumpu8PDA bump seed

Space: 98 bytes (8 + 32 + 32 + 1 + 8 + 8 + 8 + 1)

ClaimRecord — Tracks a payout that the vault authority may process for a claimant. It does not reserve or escrow SOL.

FieldTypeDescription
vaultPubkeyAssociated vault address
authorityPubkeyVault authority at time of creation
claimantPubkeyWallet that gets the payout
amountu64Authority-managed payout (lamports)
claimed_ati64Unix timestamp of claim (0 if unclaimed)
bumpu8PDA bump seed

Space: 121 bytes (8 + 32 + 32 + 32 + 8 + 8 + 1)

PDA Seeds

AccountSeeds
Vault["vault", vault_id]
ClaimRecord["claim", vault_pubkey, claimant_pubkey]

Vault Lifecycle

Open ──deposit──► Open ──close_vault──► Closed
│
set_claimable / claim
withdraw / withdraw_all
│
cleanup_vault

VaultStatus

VariantDescription
OpenVault is active — deposits and close are allowed
ClosedLocked — claims, withdrawals, and cleanup are now enabled

Instructions

init_vault(vault_id: [u8; 32])

Creates a new vault PDA. Only the program's upgrade authority can call this — only the deployer can create vaults.

  • Signer:authority (must be program upgrade authority)
  • Accounts: vault (init), authority, program, program_data, system_program
  • Validations:vault_id cannot be all zeroes; authority must match program_data.upgrade_authority_address
  • Status after:Open

deposit(amount: u64)

Transfers SOL from the authority into the vault via CPI to the system program.

  • Signer:authority (vault authority)
  • Accounts: vault (mut), authority (mut), system_program
  • Validations:amount > 0; vault must be Open

close_vault()

Locks the vault, preventing further deposits. Enables claims, withdrawals, and cleanup.

  • Signer:authority (vault authority)
  • Accounts: vault (mut), authority
  • Validations: vault status must be Open
  • Status after:Closed

set_claimable(amount: u64)

Creates a ClaimRecord PDA for a claimant, recording an intended payout. No SOL is transferred or reserved. The authority can still withdraw funds before the payout is processed, so a claim record is not a funding guarantee.

  • Signer:authority (vault authority)
  • Accounts: vault (mut), claimant (unchecked), claim_record (init), authority (mut), system_program
  • Validations:amount > 0; vault must be Closed

claim()

Pays out a claimant. The authority—not the claimant—must sign, and SOL transfers directly from the vault to the recipient. Claimants cannot redeem records independently.

  • Signer:authority (vault authority)
  • Accounts: vault (mut), claim_record (mut), authority, recipient (mut)
  • Validations: vault Closed; claim_record matches vault + recipient; not already claimed; sufficient funds

withdraw(amount: u64)

Authority withdraws a specific amount of SOL from the vault.

  • Signer:authority (vault authority)
  • Accounts: vault (mut), authority, destination (mut)
  • Validations:amount > 0; vault Closed; amount ≤ available lamports (above rent-exempt minimum)

withdraw_all()

Authority withdraws all available SOL (above rent-exempt minimum) from the vault.

  • Signer:authority (vault authority)
  • Accounts: vault (mut), authority, destination (mut)
  • Validations: vault Closed; available > 0

cleanup_vault()

Closes the vault account and gets rent back. All payouts need to be settled first (total_deposited == total_claimed + total_withdrawn). Any leftover dust is drained before closing.

  • Signer:authority (vault authority)
  • Accounts: vault (mut, close → destination), authority, destination (mut)
  • Validations: vault Closed; accounting needs to balance

cleanup_claim_record()

Closes a claim record and gets rent back. Two ways:

  • Claimed records: The claimant signs to get their rent back
  • Unclaimed records: The authority signs to clean up abandoned records

Vault-independent — works even after the vault is closed.

  • Signer:signer (claimant or authority)
  • Accounts: claim_record (mut, close → signer), signer (mut)

Error Codes

CodeMessage
InvalidVaultIdInvalid vault ID
InvalidAmountInvalid amount
InvalidVaultStatusVault is not in the correct status for this operation
InsufficientFundsInsufficient funds in vault
OverflowArithmetic overflow
AlreadyClaimedPayout already claimed
InvalidClaimRecordClaim record does not match expected claimant
NothingToClaimNothing to claim
NothingToWithdrawNothing to withdraw
UnauthorizedUnauthorized
VaultAccountingMismatchVault accounting mismatch

Security

Please report vulnerabilities privately by following the security policy.

License

Licensed under the MIT License.

About

PDA-based SOL vault program built with Anchor

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages