solana-vaults — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited solana-vaults (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A vault on Solana is a smart-contract bucket that pools capital from many depositors and routes it through a single strategy — perp market making, lending, LP provision, restaking, basis trades — managed by a delegate or curator. Depositors receive share tokens (or share accounting in a PDA) representing a proportional claim on the underlying pool, which grows or shrinks with strategy P&L net of fees.
Per Mert Mumtaz (Helius CEO), vaults are a top-5 Solana priority for 2026: they let stablecoin holders earn delta-neutral yield without running the strategy themselves, they compose with lending and perp protocols natively, and they're the dominant primitive for "DeFi without an interface" — agents, wallets, and apps embed vault deposits as a single button. Total vault TVL across the major Solana platforms now exceeds $5B (Kamino alone is ~$2.4B per DeFiLlama as of Q2 2026).
The major Solana vault platforms covered here:
This skill ships working deposit scripts for Drift Vaults and Kamino Lend (both have stable, public TypeScript SDKs) and documents the integration pattern for the others.
Not for: spot DEX swaps (see jupiter-swap skill), launching a fungible token (see token-launch), or writing the vault program itself (see anchor-scaffold + read the Drift Vaults source as a reference impl).
Pin these versions — verified against mainnet-beta on 2026-05-21:
{
"dependencies": {
"@solana/web3.js": "1.98.0",
"@solana/spl-token": "0.4.14",
"@coral-xyz/anchor": "0.30.1",
"@drift-labs/sdk": "2.122.0-beta.0",
"@drift-labs/vaults-sdk": "0.3.4",
"@kamino-finance/klend-sdk": "7.3.15",
"bn.js": "5.2.1",
"bs58": "6.0.0",
"dotenv": "16.4.5"
},
"devDependencies": {
"typescript": "5.6.3",
"tsx": "4.19.2",
"@types/node": "22.9.0",
"@types/bn.js": "5.1.5"
}
}Environment:
# .env
RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY # Helius recommended for vault txs (compute, priority fees)
WALLET_SECRET=base58-encoded-keypair-secretYou will need:
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v. Most vaults on this list quote in USDC.simulateTransaction with replaceRecentBlockhash — Helius, Triton, or QuickNode. Free public RPC will rate-limit you mid-deposit.#### Shares vs. underlying
When you deposit X USDC into a vault, the program records your shares, not your USDC. Shares are minted at the current pricePerShare = totalEquity / totalShares. Your underlying balance at any time is yourShares * pricePerShare. If strategy P&L is +10%, pricePerShare rises 10% and your USDC-equivalent balance rises 10% without your share count changing. Never display share counts to users as "your balance" — always convert to underlying first.
Some vaults (Drift, Kamino kvaults, Jito) tokenize shares as transferable SPL tokens; others (older Drift vaults, Marginfi banks) keep shares inside a PDA. Tokenized shares are composable (you can post them as collateral elsewhere); PDA shares are simpler and cheaper.
#### Deposit flow (general shape)
[vault_pubkey, authority_pubkey].initializeVaultDepositor ix (creates the PDA, pays rent).deposit(vaultDepositor, amount) — the program transfers USDC from your ATA, mints shares to the depositor record, and updates totalShares / totalEquity.getVaultDepositor() and convert shares → underlying using current pricePerShare.#### Withdraw flow
Most strategy vaults have a withdraw request → finalization two-step because the manager needs time to unwind positions:
requestWithdraw(vaultDepositor, sharesOrAmount, withdrawUnit) — locks the requested shares at the current price-per-share and starts the redeem_period timer.redeem_period (Drift mainnet default: 7 days for most vaults, 24 hours for short-lockup ones).withdraw(vaultDepositor) — burns the locked shares, transfers USDC back. If pricePerShare dropped during the wait, you receive less than originally quoted (this is intentional — withdrawers absorb their pro-rata of any drawdown).Kamino lending (non-locked) skips the request step — you call withdraw and get USDC immediately if the reserve has liquidity.
#### Lockups and epochs
redeem_period (seconds). Cancel a pending request with cancelRequestWithdraw.Always check the specific vault's parameters before quoting a withdrawal time to a user.
#### Performance fees and high-water marks
Drift Vaults charge fees in two places:
The HWM is per-depositor, not per-vault: if you deposited at HWM=$1.00/share and the vault drew down to $0.95 then rallied to $1.05, you pay profit share on the $0.05 above your HWM, not on the full $0.10 recovery. New depositors who entered at $0.95 pay on the full $0.10. This makes profit share fair across cohorts but means you cannot back out vault APY from price-per-share alone — net APY depends on your entry point.
Kamino lending has no profit share — just a spread between borrow APY and supply APY plus a reserve protocol fee.
#### Drift Vaults
SDK: @drift-labs/vaults-sdk (npm, GitHub).
Pattern (see scripts/deposit-drift-vault.ts for the full runnable version):
import { VaultClient, getVaultDepositorAddressSync, VAULT_PROGRAM_ID } from '@drift-labs/vaults-sdk';
import { DriftClient, BulkAccountLoader } from '@drift-labs/sdk';
const vaultClient = new VaultClient({ driftClient, program });
const vaultPubkey = new PublicKey('...'); // e.g. Turbocharger, Circuit, hJLP
const vaultDepositor = getVaultDepositorAddressSync(VAULT_PROGRAM_ID, vaultPubkey, wallet.publicKey);
// First deposit creates the depositor PDA atomically:
const sig = await vaultClient.deposit(
vaultDepositor,
new BN(100_000_000), // 100 USDC (6 decimals)
{ authority: wallet.publicKey, vault: vaultPubkey } // init on first deposit
);
const vd = await vaultClient.getVaultDepositor(vaultDepositor);
console.log('Shares:', vd.vaultShares.toString());Find live vault pubkeys at https://app.drift.trade/vaults/strategy-vaults (click a vault → copy the pubkey from the URL).
Withdraw: requestWithdraw(vaultDepositor, sharesOrAmount, WithdrawUnit.SHARES | WithdrawUnit.TOKEN) → wait redeem_period → withdraw(vaultDepositor). Cancel with cancelRequestWithdraw(vaultDepositor).
#### Kamino Lend
SDK: @kamino-finance/klend-sdk (npm, GitHub).
Pattern (see scripts/deposit-kamino-vault.ts):
import { KaminoMarket, KaminoAction, VanillaObligation, PROGRAM_ID } from '@kamino-finance/klend-sdk';
import { address } from '@solana/kit';
const MAIN_MARKET = address('7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF');
const market = await KaminoMarket.load(connection, MAIN_MARKET);
const action = await KaminoAction.buildDepositTxns(
market,
'100000000', // 100 USDC raw
'USDC',
new VanillaObligation(PROGRAM_ID)
);
// action.setupIxs, action.lendingIxs, action.cleanupIxs — bundle into a versioned txOther Kamino markets:
DxXdAyU3kCjnyggvHmY5nAwg5cRbbmdyX3npfDMjjMekByYiZxp8QrdN9qbdtaAiePN8AAr3qvTPppNJDpf5DVJ5Kamino has separate kvaults (concentrated-liquidity automated LP vaults) using kliquidity-sdk — different surface area; see the references doc.
#### Jito Vaults
SDK: @jito-foundation/restaking-sdk and @jito-foundation/vault-sdk. Each vault has a Vault Receipt Token (VRT) — an SPL token you receive on deposit and burn on withdraw. Withdrawals are epoch-cooldown-gated.
Docs: https://docs.restaking.jito.network/ Mint a VRT against a supported SPL deposit token → VRT accrues yield from operators serving NCNs (Node Consensus Networks).
#### Marginfi
SDK: @mrgnlabs/marginfi-client-v2. Marginfi treats lending as "banks" not "vaults" but the deposit semantics are identical (shares-vs-underlying via liquidity index).
import { MarginfiClient, getConfig } from '@mrgnlabs/marginfi-client-v2';
const client = await MarginfiClient.fetch(getConfig('production'), wallet, connection);
const account = await client.createMarginfiAccount();
const usdcBank = client.getBankByTokenSymbol('USDC');
await account.deposit(100, usdcBank.address);#### Meteora
@meteora-ag/dlmm-vault): wraps @meteora-ag/dlmm LP positions in a vault accounting layer.@meteora-ag/alpha-vault-sdk): launchpad anti-bot vaults — depositors lock USDC during a window, get pro-rata allocation of the launching token, then vest.Treat Meteora deposits as LP positions with impermanent loss, not lending — APY numbers blend fees and IL.
If a provider doesn't fit your strategy, fork drift-labs/drift-vaults — it's the cleanest open-source reference for share accounting, HWM tracking, and request-based withdrawals on Solana. It's Anchor-based, well-commented, and has a CLI + UI template (drift-labs/vaults-ui-template) you can adapt. Key decisions:
vaultShares: 100000000 does NOT mean the depositor has 100 USDC. Multiply by pricePerShare (often stored as totalEquity / totalShares, sometimes a Q64.64 fixed-point number — check the program's IDL). Drift's VaultDepositor.vaultShares and Vault.totalShares are both i128; divide carefully.initVaultDepositor arg, but if you forget it and the account doesn't exist, the tx will fail with AccountNotInitialized rather than auto-creating. Always check getAccountInfo(vaultDepositor) first or pass the init arg.requestWithdraw does not pay you immediately — it queues a request that finalizes after redeem_period. Calling withdraw before the period expires fails with WithdrawNotAvailable. Users repeatedly try to "withdraw faster" by re-requesting; that just resets the timer in some implementations. Educate users that the timer is one-way.totalEquity / totalShares.delegate field and check what instructions that key can sign in the program. If the manager keypair has any Transfer authority on the vault's USDC ATA, it's a rug-able design.simulateTransaction first, set computeUnitLimit to ~600k, and add a setComputeUnitPrice priority-fee ix.VaultClient uses a BulkAccountLoader cache — if you await deposit(...) then immediately await getVaultDepositor(...), you may read the pre-deposit snapshot. Call await driftClient.accountSubscriber.fetch() between writes and reads, or load with a fresh connection.getAccountInfo and decode manually.scripts/deposit-drift-vault.ts — depositing USDC into a Drift vault, reading share balance.scripts/deposit-kamino-vault.ts — depositing USDC into Kamino main market USDC reserve.references/vault-providers.md — comparison table: strategy type, lockup, fees, SDK, TVL.External:
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.