LiteSVM
LiteSVM 0.6.x runs an in-process Solana VM for sub-second program tests without solana-test-validator. Use it for unit tests, security negative cases, and tight Rust feedback loops.
Recipe
Quick-reference recipe card - copy-paste ready.
use litesvm::LiteSVM;
use solana_sdk::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
signature::Keypair,
signer::Signer,
transaction::Transaction,
};
let mut svm = LiteSVM::new();
let payer = Keypair::new();
svm.airdrop(&payer.pubkey(), 10_000_000_000).unwrap();
svm.add_program(program_id, include_bytes!("../target/deploy/my_program.so"));When to reach for this:
- Testing instruction handlers in pure Rust
#[test]. - Adversarial account meta fuzzing quickly.
- CI unit test layer before slower
anchor test. - Debugging program errors without RPC noise.
Working Example
use litesvm::LiteSVM;
use solana_sdk::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
signature::Keypair,
signer::Signer,
system_program,
transaction::Transaction,
};
#[test]
fn withdraw_requires_signer() {
let program_id = Pubkey::new_unique();
let mut svm = LiteSVM::new();
svm.add_program(program_id, include_bytes!("../target/deploy/my_program.so"));
let payer = Keypair::new();
svm.airdrop(&payer.pubkey(), 2_000_000_000).unwrap();
let vault = Pubkey::new_unique();
let ix = Instruction {
program_id,
accounts: vec![
AccountMeta::new(vault, false),
AccountMeta::new_readonly(payer.pubkey(), false), // missing signer
],
data: vec![1, 0, 0, 0], // withdraw discriminator stub
};
let tx = Transaction::new_signed_with_payer(
&[ix],
Some(&payer.pubkey()),
&[&payer],
svm.latest_blockhash(),
);
let result = svm.send_transaction(tx);
assert!(result.is_err());
}What this demonstrates:
- Load compiled
.sodirectly into LiteSVM. - Build transactions with explicit account metas for attack cases.
- Assert failure without starting validator or TypeScript harness.
Deep Dive
How It Works
- LiteSVM embeds SVM execution in your test process.
- Sysvars, rent, and BPF loader behavior approximate Agave 4.1.1 semantics.
- Blockhash and clock can be advanced manually for time-dependent tests.
- No RPC - tests run entirely in memory.
LiteSVM vs Other Harnesses
| Tool | Speed | Fidelity | Language |
|---|---|---|---|
| LiteSVM 0.6.x | Fastest | High for ix logic | Rust |
| Mollusk | Fast | Instruction-focused | Rust |
| Bankrun | Fast | TS ergonomics | TypeScript |
| test-validator | Slow | Full RPC | Any |
Rust Notes
// Advance clock for staleness tests
svm.set_sysvar(&solana_sdk::clock::Clock {
unix_timestamp: 1_700_000_000,
..Default::default()
});Gotchas
- Stale .so artifact - Test runs old binary after code change. Fix:
anchor buildin CI beforecargo test. - Missing programs in svm - CPI to SPL Token fails without loading token program. Fix:
svm.add_programfor dependencies. - Account data setup - Uninitialized accounts need manual data packing. Fix: Helper to write Borsh/Anchor layouts into svm accounts.
- Blockhash expired - Long test sequences need
svm.expire_blockhash()or refresh. Fix: Fetchlatest_blockhash()per tx. - Feature mismatch - Build features differ from deploy. Fix: Same
Cargo.tomlfeatures as release build. - Not full RPC - RPC-specific quirks need validator tests. Fix: Layer E2E tests separately.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Mollusk | Instruction-level Rust only | You want full tx simulation ergonomics |
| Bankrun | TypeScript team | Pure Rust workflow |
| anchor test | Full integration | Inner-loop speed |
FAQs
Which LiteSVM version matches Agave 4.1.1?
Pin LiteSVM 0.6.x per manifest; verify release notes for syscall compatibility.
Can LiteSVM test CPIs?
Yes - load callee programs (Token, your vault) and supply correct account graph.
How do I fund accounts?
svm.airdrop for SOL; manually set token account data for SPL balances.
Does Anchor work with LiteSVM?
Use raw .so + instruction data, or community helpers to build Anchor discriminators.
CI integration?
cargo test after anchor build - no validator service required for LiteSVM layer.
How do I test PDA signers?
Include correct PDA account with is_signer: false; program invokes signed CPI internally.
Logs available?
Failed txs return logs in error - assert log substrings for custom msg! debugging.
Parallel tests?
Each test should own LiteSVM::new() - avoid shared mutable svm across threads.
Devnet program IDs?
Use same program IDs as target cluster when testing CPI allowlists.
When to graduate to validator tests?
When depending on RPC methods, ALTs, or multi-tx landing behavior.
Related
- Testing Basics - pyramid context
- Mollusk - alternative Rust harness
- Testing CPIs & PDAs - CPI test patterns
- Security Best Practices - negative tests
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.