A Solana vault program built with Anchor that allows users to deposit tokens, receive IOU tokens representing their share, and withdraw with a two-step process that includes an epoch-based delay.
The Solana Vault is a DeFi protocol that implements a tokenized vault system where:
- Users deposit tokens and receive IOU tokens based on the current exchange rate
- IOU tokens represent a share of the vault's underlying assets
- Withdrawals require a two-step process: request (burn IOUs) → wait (epoch delay) → claim (receive tokens)
- The exchange rate can be increased by the admin to simulate yield growth
- Users benefit from exchange rate increases that occur between withdrawal request and claim
- Deposit - Deposit tokens into the vault and receive IOU tokens
- Request Withdraw - Burn IOU tokens and create a withdrawal ticket (unlocks next epoch)
- Claim Withdraw - Claim withdrawal after unlock epoch using the current exchange rate
- Increase Rate - Admin-only function to update exchange rate and increment epoch
admin: Admin authority that can update exchange ratedeposit_mint: The mint of tokens that can be depositediou_mint: The mint of IOU tokens representing sharesexchange_rate: Exchange rate scaled byEXCHANGE_RATE_SCALE(1,000,000)current_epoch: Current epoch number (incremented by admin)
PDA Seeds: [b"vault_state", deposit_mint]
user: The user who requested the withdrawaliou_amount: Amount of IOU tokens burned for this withdrawalunlock_epoch: Epoch when withdrawal can be claimed (current_epoch + 1 when created)claimed: Boolean flag indicating if withdrawal has been claimed
PDA Seeds: [b"withdrawal_ticket", user.key(), vault_state.key()]
- Deposit:
iou_amount = (deposit_amount * exchange_rate) / EXCHANGE_RATE_SCALE - Withdraw:
deposit_amount = (iou_amount * EXCHANGE_RATE_SCALE) / exchange_rate
The exchange rate is scaled by 1_000_000 for precision (6 decimal places).
- Rust (latest stable)
- Solana CLI (v1.18+)
- Anchor (v0.31+)
- Yarn or npm
-
Clone the repository:
git clone <repository-url> cd vault-program
-
Install dependencies:
yarn install
-
Build the program:
anchor build
This generates TypeScript types in
target/types/vault_program.tsused by tests.
Run the test suite:
anchor testOr use the npm script:
yarn testNote: anchor test automatically starts a local validator. If you see "port 8899 is already in use", either use the existing validator with anchor test --skip-local-validator or stop it first with pkill solana-test-validator.
The frontend is a React + Vite application that provides a web interface for interacting with the vault.
-
Navigate to the frontend directory:
cd frontend -
Install dependencies:
npm install
-
Run the development server:
npm run dev
-
Build for production:
npm run build
-
Preview the production build:
npm run preview
-
Deploy the
dist/folder to any static hosting service (Vercel, Netlify, GitHub Pages, etc.).
Note: Make sure the program IDL is copied to frontend/idl/ before building. The IDL is generated in target/idl/vault_program.json after running anchor build.
The scripts/ directory contains utility scripts for managing the vault on devnet:
Creates deposit and IOU mints, transfers IOU mint authority to the vault PDA, and initializes the vault state.
Usage:
npx ts-node scripts/initialize-vault.tsMints tokens to a recipient's token account. Useful for testing and providing tokens to users.
Usage:
npx ts-node scripts/mint-tokens.ts <DEPOSIT_MINT_ADDRESS> [AMOUNT] [RECIPIENT_ADDRESS]Example:
npx ts-node scripts/mint-tokens.ts 3mJFZXLudQF1YgyoWJ5gB6Q97kRoVyE1C6UCihNc9xVN 100Mints tokens to the admin wallet and transfers them directly to the vault's token account. Requires mint authority.
Usage:
npx ts-node scripts/mint-to-vault.ts <DEPOSIT_MINT_ADDRESS> <AMOUNT>Example:
npx ts-node scripts/mint-to-vault.ts 3mJFZXLudQF1YgyoWJ5gB6Q97kRoVyE1C6UCihNc9xVN 100Admin-only script to increase the exchange rate. Simulates yield growth.
Usage:
npx ts-node scripts/increase-rate.ts <DEPOSIT_MINT_ADDRESS> <NEW_EXCHANGE_RATE>Example:
npx ts-node scripts/increase-rate.ts 3mJFZXLudQF1YgyoWJ5gB6Q97kRoVyE1C6UCihNc9xVN 1.1Admin-only script to increment the epoch without changing the exchange rate. Useful for testing withdrawal delays.
Usage:
npx ts-node scripts/increase-epoch.ts <DEPOSIT_MINT_ADDRESS>Example:
npx ts-node scripts/increase-epoch.ts 3mJFZXLudQF1YgyoWJ5gB6Q97kRoVyE1C6UCihNc9xVNCreates the VaultState PDA and sets up the vault with admin authority, deposit mint, IOU mint, initial exchange rate (1:1), and initial epoch (0).
Transfers deposit tokens from user to vault, calculates IOU amount based on current exchange rate, and mints IOU tokens to user.
Burns IOU tokens from user's token account and creates a WithdrawalTicket PDA with unlock_epoch = current_epoch + 1. Enforces one active withdrawal ticket per user per vault.
Validates withdrawal ticket ownership, checks that current_epoch >= unlock_epoch, calculates deposit token amount using current exchange rate, transfers deposit tokens from vault to user, and marks withdrawal ticket as claimed.
Note: Users benefit from exchange rate increases that occur between request and claim.
Admin-only function to update the exchange rate (simulating yield growth) and increment the current epoch.
InvalidExchangeRate- Exchange rate must be greater than zeroInvalidAmount- Calculated IOU amount must be greater than zeroMathOverflow- Arithmetic operation resulted in overflowTicketAlreadyClaimed- Unclaimed ticket exists or ticket already claimedInvalidTicketOwner- Withdrawal ticket belongs to different userWithdrawalNotReady- Attempted to claim before unlock epoch
Error: "Cannot find module '../target/types/vault_program'"
- Run
anchor buildfirst to generate types
Error: "Your configured rpc port: 8899 is already in use"
- A validator is already running. Either use it:
anchor test --skip-local-validatoror stop it:pkill solana-test-validator
Error: "Account not found"
- Make sure the program is deployed (Anchor will do this automatically with
anchor test)
Error: "Insufficient funds"
- The test automatically airdrops SOL, but if it fails, manually airdrop:
solana airdrop 2 <your-wallet-address>
- Anchor Version: 0.31+
- Token Compatibility: Supports both Token Program and Token Extension Program via
InterfaceAccount - Patterns: Modern Anchor style with
#[account]structs and#[derive(Accounts)]for instruction contexts - Security: Proper PDA derivation and signing for secure token minting, burning, and transfers
- Error Handling: Comprehensive validation and error handling throughout
vault-program/
├── programs/
│ └── vault-program/
│ └── src/
│ └── lib.rs # Main program logic
├── tests/
│ └── vault-program.ts # Integration tests
├── scripts/ # Utility scripts for devnet
│ ├── initialize-vault.ts
│ ├── mint-tokens.ts
│ ├── mint-to-vault.ts
│ ├── increase-rate.ts
│ └── increase-epoch.ts
├── frontend/ # React + Vite frontend
│ ├── src/
│ ├── idl/ # Program IDL
│ └── package.json
├── Anchor.toml # Anchor configuration
├── Cargo.toml # Rust dependencies
└── package.json # Node dependencies
MIT