A decentralized asset vault on Solana that supports multi-token deposits, automatic yield compounding, and flexible withdrawal mechanisms.
- 2026-02-24: Security hardening — validate fee receiver token account ownership, validate emergency-withdraw destination mint, and ensure withdrawal-request rent is returned to the requester (with TS regression tests).
- 2026-02-23: Added vault pool enumeration API test (
getProgramAccounts) and Newman example for running only Vault Contract checks.
- Multi-Token Support: Whitelist-based token management with separate vault pools
- Vault Shares Token: ERC-4626 style shares representing user deposits (e.g., vUSDC for USDC)
- Auto-Compounding: Yields are automatically compounded into the vault
- Tiered Access Control: Owner (admin) and Operator (daily operations) roles
- Flexible Withdrawals: Instant withdrawals with liquidity, queue-based when funds are deployed
- Security Features: Pause mechanism, daily withdrawal limits, emergency withdraw
┌─────────────────────────────────────────────────────────────┐
│ Solana DeFi Vault │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ USDC Vault │ │ SOL Vault │ │ USDT Vault │ │
│ │ Pool │ │ Pool │ │ Pool │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ vUSDC Share │ │ vSOL Share │ │ vUSDT Share │ │
│ │ Token │ │ Token │ │ Token │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Roles: Owner (highest privilege) ◄──► Operator (ops) │
├─────────────────────────────────────────────────────────────┤
│ Security: Pause │ Withdrawal Limits │ Emergency Withdraw │
└─────────────────────────────────────────────────────────────┘
solana-defi-vault/
├── programs/vault/src/
│ ├── lib.rs # Program entry point
│ ├── state/ # Account structures
│ │ ├── config.rs # VaultConfig (global settings)
│ │ ├── pool.rs # VaultPool (per-token vault)
│ │ └── withdrawal.rs # WithdrawalRequest (queue)
│ ├── instructions/
│ │ ├── admin/ # Owner-only instructions
│ │ ├── operator/ # Operator instructions
│ │ └── user/ # User instructions
│ ├── errors.rs # Custom error codes
│ └── utils.rs # Helper functions
├── tests/
│ └── vault.ts # Integration tests
├── postman/
│ ├── solana-vault-api-tests.postman_collection.json # API test collection
│ └── solana-vault-devnet.postman_environment.json # Devnet environment
├── .github/workflows/
│ └── ci.yml # CI checks + Postman API tests
└── docs/plans/
└── 2026-01-27-vault-design.md
| Instruction | Description |
|---|---|
initialize |
Initialize global vault configuration |
update_config |
Update fees, operator, fee receiver |
create_pool |
Create a new vault pool for a token |
pause / unpause |
Pause/resume all operations |
set_pool_status |
Activate/deactivate a specific pool |
set_withdrawal_limit |
Set daily withdrawal limit |
emergency_withdraw |
Emergency fund extraction |
transfer_ownership |
Transfer owner role |
close_pool |
Close an empty, inactive pool (reclaim rent) |
close_withdrawal_request |
Close processed withdrawal requests |
| Instruction | Description |
|---|---|
inject_yield |
Inject yield (auto-compounds, takes performance fee) |
withdraw_for_strategy |
Deploy funds to external strategies |
return_from_strategy |
Return funds from strategies |
process_withdrawal_queue |
Process pending withdrawal requests |
| Instruction | Description |
|---|---|
deposit |
Deposit tokens, receive shares |
withdraw |
Burn shares, receive tokens (instant if liquid) |
request_withdrawal |
Request withdrawal (enters queue) |
cancel_withdrawal |
Cancel pending withdrawal request |
| Instruction | Description |
|---|---|
preview_deposit |
Preview shares to receive for a deposit |
preview_withdraw |
Preview assets to receive for a withdrawal |
get_pool_info |
Get pool statistics and share price |
- Performance Fee: Charged on yield injection (default: 20%)
- Withdrawal Fee: Charged on withdrawals (default: 0.5%)
Automated pipeline via GitHub Actions (.github/workflows/ci.yml):
| Stage | What it does |
|---|---|
| Structure Check | Validates critical repo files + Anchor config is present |
| API Tests | Runs Postman collection against Devnet via Newman |
Triggered on every push / pull_request to main (and manual workflow_dispatch).
For on-chain program tests, run locally: anchor test.
The postman/ directory contains a Postman collection for testing Solana JSON-RPC endpoints against the vault contract.
| Category | Request | Validates |
|---|---|---|
| Health Check | Get Cluster Health | RPC node is responsive |
| Health Check | Get Latest Blockhash | Network is producing blocks |
| Wallet & Balance | Get SOL Balance | Wallet balance query, lamport-to-SOL conversion |
| Wallet & Balance | Get Token Accounts by Owner | SPL token holdings for a wallet |
| Transaction | Get Recent Signatures | Wallet transaction history (used to fetch latest signature) |
| Transaction | Get Transaction by Signature | Tx status, fee, balance changes |
| Vault Contract | Get Vault Program Account Info | Contract is deployed and executable |
| Vault Contract | Get Vault Pool Accounts (getProgramAccounts) | Enumerate program-owned accounts for vault pool discovery |
| Coverage Area | Details | Count |
|---|---|---|
| Health Check | getHealth, getLatestBlockhash, getSlot |
3 |
| Wallet & Balance | getBalance, getTokenAccountsByOwner |
2 |
| Transaction | getSignaturesForAddress, getTransaction |
2 |
| Vault Contract | getAccountInfo, getProgramAccounts, getEpochInfo, getVersion |
4 |
| Total | JSON-RPC request scenarios in collection | 11 |
Each request includes automated test scripts (Chai assertions) that validate response structure, data types, and business logic.
# Install Newman (Postman CLI runner)
npm install -g newman
# Run collection against Devnet
newman run postman/solana-vault-api-tests.postman_collection.json \
--environment postman/solana-vault-devnet.postman_environment.json
# Run only vault contract checks (includes getProgramAccounts enumeration)
newman run postman/solana-vault-api-tests.postman_collection.json \
--environment postman/solana-vault-devnet.postman_environment.json \
--folder "Vault Contract"- Open Postman → Import → Upload Files
- Select
postman/solana-vault-api-tests.postman_collection.json - Import
postman/solana-vault-devnet.postman_environment.jsonas environment - Update
wallet_addresswith your value;tx_signatureis optional fallback
- Rust 1.70+
- Solana CLI 1.18+
- Anchor 0.30+
- Node.js 18+
anchor buildanchor test# Devnet
anchor deploy --provider.cluster devnet
# Mainnet
anchor deploy --provider.cluster mainnet- Pause Mechanism: Owner can pause all operations in emergencies
- Daily Limits: Configurable daily withdrawal limits per pool
- Tiered Access: Separation of owner and operator privileges
- Checked Math: All arithmetic uses checked operations to prevent overflow
- Minimum Deposit: Prevents precision/rounding attacks
- Account Validation: Fee receiver token accounts and emergency-withdraw destinations are validated; withdrawal-request rent is returned to the requester
- Automated strategy integration (Marinade, Kamino)
- Multisig support (Squads Protocol)
- DAO governance
- Landing page (project website)
- TypeScript SDK
- React hooks for frontend integration
The project includes a TypeScript SDK for easy frontend integration:
import { VaultClient } from "@solana-defi-vault/sdk";
import { Connection, PublicKey } from "@solana/web3.js";
// Initialize client
const connection = new Connection("https://api.devnet.solana.com");
const client = new VaultClient(connection);
// Get pool info
const poolInfo = await client.getPoolInfo(tokenMint);
console.log("Share Price:", poolInfo.sharePrice);
console.log("TVL:", poolInfo.tvl.toString());
// Get user position
const position = await client.getUserPosition(tokenMint, userPublicKey);
console.log("Shares:", position.sharesBalance.toString());
console.log("Estimated Value:", position.estimatedValue.toString());
// Simulate deposit
const preview = await client.simulateDeposit(tokenMint, depositAmount);
console.log("Shares to receive:", preview.sharesToReceive.toString());
// Build deposit instruction
const instructions = await client.buildDepositInstruction(
userPublicKey,
tokenMint,
depositAmount
);All state-changing instructions emit events for easy tracking:
| Event | Description |
|---|---|
VaultInitialized |
Emitted when vault is initialized |
PoolCreated |
Emitted when a new pool is created |
Deposit |
Emitted on user deposits |
Withdrawal |
Emitted on user withdrawals |
YieldInjected |
Emitted when yield is added |
VaultPaused / VaultUnpaused |
Emitted on pause state changes |
EmergencyWithdrawal |
Emitted on emergency fund extraction |
MIT License - see LICENSE