Quick Start
Learn how to use anchor-litesvm for testing Anchor programs with simplified syntax
Installation
Add the needed dependencies:
cargo add --dev anchor-litesvm litesvm litesvm-utilsWhat is anchor-litesvm?
The anchor-litesvm crate provides a simplified syntax similar to anchor-client but without RPC overhead. It achieves 78% code reduction compared to raw LiteSVM while maintaining type safety with Anchor types.
AnchorContext
- Production-compatible test context
- Same API patterns as anchor-client
- Manages LiteSVM instance, payer, and program
- Execute instructions without RPC overhead
Program API
- Fluent instruction building
- Type-safe account and argument handling
- Familiar anchor-client syntax
Account Deserialization
- Fetch and deserialize Anchor accounts
- Automatic discriminator handling
- Support for PDAs and custom layouts
Event Parsing
- Parse events from transaction logs
- Assert event emission
- Type-safe event deserialization
Quick Example
With Anchor 1.0, use declare_program! to generate client types from your program's IDL. This macro creates client::accounts::* and client::args::* modules for type-safe instruction building:
use anchor_litesvm::AnchorLiteSVM;
use anchor_litesvm::{AssertionHelpers, TestHelpers};
use anchor_lang::system_program;
use solana_sdk::signature::{read_keypair_file, Signer};
// Generate client types from your program's IDL
anchor_lang::declare_program!(my_program);
#[test]
fn test_anchor_program() {
// One-line setup — reads program keypair for the correct ID
let program_keypair = read_keypair_file("target/deploy/my_program-keypair.json").unwrap();
let mut ctx = AnchorLiteSVM::build_with_program(
program_keypair.pubkey(),
include_bytes!("../target/deploy/my_program.so"),
);
// Create a funded account via TestHelpers on ctx.svm
let user = ctx.svm.create_funded_account(10_000_000_000).unwrap();
// Derive PDA
let seed: u64 = 42;
let pda = ctx.svm.get_pda(
&[b"user", user.pubkey().as_ref(), &seed.to_le_bytes()],
&program_keypair.pubkey(),
);
// Build instruction using generated client types
let ix = ctx.program()
.accounts(my_program::client::accounts::Initialize {
user: user.pubkey(),
user_account: pda,
system_program: system_program::ID,
})
.args(my_program::client::args::Initialize {
seed,
name: "test".to_string(),
})
.instruction()
.unwrap();
// Execute and assert in one chain
ctx.execute_instruction(ix, &[&user])
.unwrap()
.assert_success();
// Fetch and deserialize the account
let account: my_program::MyAccount = ctx.get_account(&pda).unwrap();
assert_eq!(account.name, "test");
}declare_program!(my_program) reads the IDL from target/idl/my_program.json at compile time and generates the client::accounts::* and client::args::* modules. This requires building your program first.
Comparison: Raw LiteSVM vs anchor-litesvm
Before (Raw LiteSVM)
use litesvm::LiteSVM;
use solana_keypair::Keypair;
use solana_signer::Signer;
use solana_program::instruction::{AccountMeta, Instruction};
use solana_message::Message;
use solana_transaction::Transaction;
let mut svm = LiteSVM::new();
svm.add_program(program_id, program_bytes).unwrap();
let payer = Keypair::new();
svm.airdrop(&payer.pubkey(), 10_000_000_000).unwrap();
// Manually compute 8-byte discriminator
let discriminator = {
let mut hasher = sha2::Sha256::new();
hasher.update(b"global:initialize");
let result = hasher.finalize();
result[..8].to_vec()
};
// Manually serialize args and build instruction
let mut data = discriminator;
data.extend_from_slice(&borsh::to_vec(&args).unwrap());
let accounts = vec![
AccountMeta::new(user.pubkey(), true),
AccountMeta::new(user_pda, false),
AccountMeta::new_readonly(system_program::id(), false),
];
let ix = Instruction::new_with_bytes(program_id, &data, accounts);
let tx = Transaction::new_signed_with_payer(
&[ix],
Some(&payer.pubkey()),
&[&payer],
svm.latest_blockhash(),
);
svm.send_transaction(tx).unwrap();
// Manually deserialize with discriminator skip
let account_data = svm.get_account(&pda).unwrap().data;
let account: UserAccount = UserAccount::try_deserialize(
&mut &account_data[8..]
).unwrap();After (anchor-litesvm)
use anchor_litesvm::AnchorLiteSVM;
anchor_lang::declare_program!(my_program);
let mut ctx = AnchorLiteSVM::build_with_program(program_id, program_bytes);
let user = ctx.svm.create_funded_account(10_000_000_000).unwrap();
let ix = ctx.program()
.accounts(my_program::client::accounts::Initialize {
user: user.pubkey(),
user_account: user_pda,
system_program: anchor_lang::system_program::ID,
})
.args(my_program::client::args::Initialize { name: "test".to_string() })
.instruction()
.unwrap();
ctx.execute_instruction(ix, &[&user]).unwrap().assert_success();
let account: my_program::UserAccount = ctx.get_account(&user_pda).unwrap();Key Components
AnchorLiteSVM Builder
| Method | Description |
|---|---|
new() | Creates a new builder instance |
with_payer(keypair) | Sets a custom payer keypair |
deploy_program(id, bytes) | Adds a program to deploy |
build() | Builds the AnchorContext |
build_with_program(id, bytes) | Convenience for single program |
build_with_programs(programs) | Deploy multiple programs |
AnchorContext
| Method | Description |
|---|---|
svm | Direct access to the underlying LiteSVM instance (public field) |
program_id | The program ID (public field) |
program() | Returns Program for instruction building |
payer() | Get the payer Keypair |
execute_instruction(ix, signers) | Execute a single instruction |
execute_instructions(ixs, signers) | Execute multiple instructions in one tx |
send_and_confirm_transaction(&tx) | Send a raw transaction |
get_account<T>(pubkey) | Fetch and deserialize an Anchor account |
get_account_unchecked<T>(pubkey) | Fetch without discriminator check |
create_funded_account(lamports) | Create and fund a new keypair |
airdrop(pubkey, lamports) | Airdrop SOL to an address |
latest_blockhash() | Get the current blockhash |
account_exists(pubkey) | Check if an account exists |
deploy_program(id, bytes) | Deploy an additional program (via ProgramTestExt) |
Program
| Method | Description |
|---|---|
accounts(accounts) | Set instruction accounts (any ToAccountMetas type) |
args(args) | Set instruction arguments (any InstructionData type) |
instruction() | Build the final Instruction |
id() | Get the program ID |
ctx.svm — TestHelpers & AssertionHelpers
ctx.svm is a public LiteSVM field with TestHelpers and AssertionHelpers traits available via litesvm-utils:
| Method | Description |
|---|---|
ctx.svm.create_funded_account(lamports) | Create and fund a keypair |
ctx.svm.create_token_mint(authority, decimals) | Create an SPL token mint |
ctx.svm.create_associated_token_account(mint, owner) | Create an ATA |
ctx.svm.mint_to(mint, token_account, authority, amount) | Mint tokens |
ctx.svm.get_pda(seeds, program_id) | Derive a PDA address |
ctx.svm.get_pda_with_bump(seeds, program_id) | Derive PDA with bump seed |
ctx.svm.assert_token_balance(token_account, expected) | Assert token balance |
ctx.svm.assert_account_closed(pubkey) | Assert account was closed |
ctx.svm.assert_sol_balance(pubkey, expected) | Assert SOL balance |
Troubleshooting
Common Errors
| Error | Cause | Solution |
|---|---|---|
AccountNotFound | Account doesn't exist | Ensure account is created before fetching |
DiscriminatorMismatch | Wrong account type | Verify you're using the correct account struct |
DeserializationError | Invalid account data | Check account was initialized correctly |
| No programs added | Called build() without deploy_program() | Add at least one program before building |
| Missing client types | declare_program! not called | Run anchor build first to generate IDL, then call declare_program! |