LiteSVM Docs
Additional Cratesanchor-litesvm

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-utils

What 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

MethodDescription
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

MethodDescription
svmDirect access to the underlying LiteSVM instance (public field)
program_idThe 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

MethodDescription
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:

MethodDescription
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

ErrorCauseSolution
AccountNotFoundAccount doesn't existEnsure account is created before fetching
DiscriminatorMismatchWrong account typeVerify you're using the correct account struct
DeserializationErrorInvalid account dataCheck account was initialized correctly
No programs addedCalled build() without deploy_program()Add at least one program before building
Missing client typesdeclare_program! not calledRun anchor build first to generate IDL, then call declare_program!