LiteSVM Docs
Additional Cratesanchor-litesvm

Program & Context

Setting up test environments and building instructions with AnchorContext and Program

Generating Client Types with declare_program!

Anchor 1.0 introduces declare_program!, which reads your program's IDL at compile time and generates type-safe client::accounts::* and client::args::* modules. This is the recommended way to build instructions in tests.

// At the top of your test file, generate client modules from the IDL
anchor_lang::declare_program!(my_program);

// Now use the generated types:
// my_program::client::accounts::Initialize { ... }
// my_program::client::args::Initialize { ... }
// my_program::MyAccount (for account deserialization)

Run anchor build first to generate target/idl/my_program.json. declare_program! reads this file at compile time. The macro name must match your program's crate name (snake_case).

Setting Up the Test Environment

The AnchorLiteSVM builder provides a fluent API for configuring your test environment.

Basic Setup

use anchor_litesvm::AnchorLiteSVM;
use solana_sdk::signature::{read_keypair_file, Signer};

anchor_lang::declare_program!(my_program);

#[test]
fn test_basic_setup() {
    // Read the program keypair to get the correct program ID
    let program_keypair = read_keypair_file("target/deploy/my_program-keypair.json").unwrap();

    let mut ctx = AnchorLiteSVM::new()
        .deploy_program(
            program_keypair.pubkey(),
            include_bytes!("../target/deploy/my_program.so"),
        )
        .build();

    println!("Program deployed: {}", ctx.program().id());
}

Single Program Shorthand

use anchor_litesvm::AnchorLiteSVM;
use solana_sdk::signature::{read_keypair_file, Signer};

#[test]
fn test_single_program() {
    let program_keypair = read_keypair_file("target/deploy/my_program-keypair.json").unwrap();

    // Convenience method for single-program setups
    let mut ctx = AnchorLiteSVM::build_with_program(
        program_keypair.pubkey(),
        include_bytes!("../target/deploy/my_program.so"),
    );
}

Multiple Programs

use anchor_litesvm::AnchorLiteSVM;
use solana_sdk::signature::{read_keypair_file, Signer};

#[test]
fn test_multiple_programs() {
    let program_a_kp = read_keypair_file("target/deploy/program_a-keypair.json").unwrap();
    let program_b_kp = read_keypair_file("target/deploy/program_b-keypair.json").unwrap();

    // Deploy multiple programs — first one becomes the primary (ctx.program_id)
    let mut ctx = AnchorLiteSVM::build_with_programs(&[
        (program_a_kp.pubkey(), include_bytes!("../target/deploy/program_a.so") as &[u8]),
        (program_b_kp.pubkey(), include_bytes!("../target/deploy/program_b.so") as &[u8]),
    ]);

    // Primary program is program_a
    assert_eq!(ctx.program().id(), program_a_kp.pubkey());
}

Custom Payer

use anchor_litesvm::AnchorLiteSVM;
use solana_keypair::Keypair;
use solana_signer::Signer;

#[test]
fn test_custom_payer() {
    let custom_payer = Keypair::new();

    let mut ctx = AnchorLiteSVM::new()
        .with_payer(custom_payer)
        .deploy_program(PROGRAM_ID, include_bytes!("../target/deploy/your_program.so"))
        .build();

    println!("Payer: {}", ctx.payer().pubkey());
}

If you don't specify a payer, AnchorLiteSVM automatically creates and funds one for you.

The AnchorContext

AnchorContext is the main interface for interacting with your test environment.

Core Properties

use anchor_litesvm::AnchorLiteSVM;
use solana_signer::Signer;

#[test]
fn test_context_properties() {
    let mut ctx = AnchorLiteSVM::build_with_program(
        PROGRAM_ID,
        include_bytes!("../target/deploy/your_program.so"),
    );

    // ctx.svm — direct access to the underlying LiteSVM instance (public field)
    let balance = ctx.svm.get_balance(&some_pubkey);

    // ctx.payer() — the payer keypair
    let payer = ctx.payer();
    println!("Payer pubkey: {}", payer.pubkey());

    // ctx.program() — Program instance for instruction building
    let program = ctx.program();
    println!("Program ID: {}", program.id());

    // ctx.latest_blockhash()
    let blockhash = ctx.latest_blockhash();
}

Creating Funded Accounts

use anchor_litesvm::AnchorLiteSVM;
use anchor_litesvm::TestHelpers;
use solana_keypair::Keypair;
use solana_signer::Signer;

#[test]
fn test_create_accounts() {
    let mut ctx = AnchorLiteSVM::build_with_program(
        PROGRAM_ID,
        include_bytes!("../target/deploy/your_program.so"),
    );

    // Option 1: via AnchorContext (returns Result<Keypair>)
    let user = ctx.create_funded_account(10_000_000_000).unwrap();

    // Option 2: via ctx.svm with TestHelpers (same result)
    let user2 = ctx.svm.create_funded_account(10_000_000_000).unwrap();

    // Airdrop to an existing address
    let another_user = Keypair::new();
    ctx.airdrop(&another_user.pubkey(), 5_000_000_000).unwrap();
}

Checking Account Existence

use anchor_litesvm::AnchorLiteSVM;

#[test]
fn test_account_exists() {
    let mut ctx = AnchorLiteSVM::build_with_program(
        PROGRAM_ID,
        include_bytes!("../target/deploy/your_program.so"),
    );

    let some_pda = ctx.svm.get_pda(&[b"vault"], &PROGRAM_ID);

    if ctx.account_exists(&some_pda) {
        println!("Account exists!");
    } else {
        println!("Account not found");
    }
}

Building Instructions with Program

The Program struct provides a fluent API for building instructions that mirrors anchor-client. Use the types generated by declare_program! for type safety.

Basic Instruction Building

use anchor_litesvm::AnchorLiteSVM;
use anchor_lang::system_program;
use solana_signer::Signer;

anchor_lang::declare_program!(my_program);

#[test]
fn test_build_instruction() {
    let mut ctx = AnchorLiteSVM::build_with_program(
        PROGRAM_ID,
        include_bytes!("../target/deploy/my_program.so"),
    );

    let user = ctx.svm.create_funded_account(10_000_000_000).unwrap();
    let user_pda = ctx.svm.get_pda(&[b"user", user.pubkey().as_ref()], &PROGRAM_ID);

    // Build instruction using types generated by declare_program!
    let ix = ctx.program()
        .accounts(my_program::client::accounts::Initialize {
            user: user.pubkey(),
            user_account: user_pda,
            system_program: system_program::ID,
        })
        .args(my_program::client::args::Initialize {
            name: "test".to_string(),
        })
        .instruction()
        .unwrap();

    println!("Instruction built successfully!");
}

The .accounts() method accepts any type that implements ToAccountMetas, which all Anchor account structs do automatically. Similarly, .args() accepts any type implementing InstructionData.

Executing Instructions

use anchor_litesvm::AnchorLiteSVM;
use anchor_lang::system_program;
use solana_signer::Signer;

anchor_lang::declare_program!(my_program);

#[test]
fn test_execute_instruction() {
    let mut ctx = AnchorLiteSVM::build_with_program(
        PROGRAM_ID,
        include_bytes!("../target/deploy/my_program.so"),
    );

    let user = ctx.svm.create_funded_account(10_000_000_000).unwrap();
    let user_pda = ctx.svm.get_pda(&[b"user", user.pubkey().as_ref()], &PROGRAM_ID);

    let ix = ctx.program()
        .accounts(my_program::client::accounts::Initialize {
            user: user.pubkey(),
            user_account: user_pda,
            system_program: system_program::ID,
        })
        .args(my_program::client::args::Initialize { name: "test".to_string() })
        .instruction()
        .unwrap();

    // Execute — returns TransactionResult
    ctx.execute_instruction(ix, &[&user])
        .unwrap()
        .assert_success();
}

Executing Multiple Instructions

use anchor_litesvm::AnchorLiteSVM;
use solana_signer::Signer;

anchor_lang::declare_program!(my_program);

#[test]
fn test_execute_multiple_instructions() {
    let mut ctx = AnchorLiteSVM::build_with_program(
        PROGRAM_ID,
        include_bytes!("../target/deploy/my_program.so"),
    );

    let user = ctx.svm.create_funded_account(10_000_000_000).unwrap();

    let ix1 = ctx.program()
        .accounts(my_program::client::accounts::Initialize { /* ... */ })
        .args(my_program::client::args::Initialize { name: "first".to_string() })
        .instruction()
        .unwrap();

    let ix2 = ctx.program()
        .accounts(my_program::client::accounts::Update { /* ... */ })
        .args(my_program::client::args::Update { name: "second".to_string() })
        .instruction()
        .unwrap();

    // Execute multiple instructions in one transaction
    ctx.execute_instructions(vec![ix1, ix2], &[&user])
        .unwrap()
        .assert_success();
}

Using send_and_confirm_transaction

use anchor_litesvm::AnchorLiteSVM;
use solana_signer::Signer;
use solana_transaction::Transaction;

#[test]
fn test_send_transaction() {
    let mut ctx = AnchorLiteSVM::build_with_program(
        PROGRAM_ID,
        include_bytes!("../target/deploy/your_program.so"),
    );

    let user = ctx.svm.create_funded_account(10_000_000_000).unwrap();

    let ix = ctx.program()
        .accounts(/* ... */)
        .args(/* ... */)
        .instruction()
        .unwrap();

    // Build transaction manually if needed
    let tx = Transaction::new_signed_with_payer(
        &[ix],
        Some(&ctx.payer().pubkey()),
        &[ctx.payer(), &user],
        ctx.latest_blockhash(),
    );

    // Note: takes &Transaction (reference)
    ctx.send_and_confirm_transaction(&tx).unwrap();
}

Complete Example

Here's a comprehensive example using the escrow pattern with declare_program!:

use anchor_litesvm::AnchorLiteSVM;
use anchor_litesvm::{AssertionHelpers, TestHelpers};
use anchor_lang::system_program;
use solana_sdk::signature::{read_keypair_file, Signer};
use spl_associated_token_account::get_associated_token_address;
use litesvm_token::spl_token;

anchor_lang::declare_program!(anchor_escrow);

#[test]
fn test_make_and_take() {
    // 1. One-line setup
    let program_keypair = read_keypair_file("target/deploy/anchor_escrow-keypair.json").unwrap();
    let program_id = program_keypair.pubkey();

    let mut ctx = AnchorLiteSVM::build_with_program(
        program_id,
        include_bytes!("../target/deploy/anchor_escrow.so"),
    );

    // 2. Create test accounts using TestHelpers on ctx.svm
    let maker = ctx.svm.create_funded_account(10_000_000_000).unwrap();
    let taker = ctx.svm.create_funded_account(10_000_000_000).unwrap();

    // 3. Create token mints and funded ATAs
    let mint_a = ctx.svm.create_token_mint(&maker, 9).unwrap();
    let mint_b = ctx.svm.create_token_mint(&maker, 9).unwrap();

    let maker_ata_a = ctx.svm
        .create_associated_token_account(&mint_a.pubkey(), &maker)
        .unwrap();
    ctx.svm.mint_to(&mint_a.pubkey(), &maker_ata_a, &maker, 1_000_000_000).unwrap();

    let taker_ata_b = ctx.svm
        .create_associated_token_account(&mint_b.pubkey(), &taker)
        .unwrap();
    ctx.svm.mint_to(&mint_b.pubkey(), &taker_ata_b, &maker, 500_000_000).unwrap();

    // 4. Derive PDAs
    let seed: u64 = 42;
    let escrow_pda = ctx.svm.get_pda(
        &[b"escrow", maker.pubkey().as_ref(), &seed.to_le_bytes()],
        &program_id,
    );
    let vault = get_associated_token_address(&escrow_pda, &mint_a.pubkey());

    // 5. Build and execute Make instruction using generated client types
    let make_ix = ctx.program()
        .accounts(anchor_escrow::client::accounts::Make {
            maker: maker.pubkey(),
            escrow: escrow_pda,
            mint_a: mint_a.pubkey(),
            mint_b: mint_b.pubkey(),
            maker_ata_a,
            vault,
            associated_token_program: spl_associated_token_account::id(),
            token_program: spl_token::id(),
            system_program: system_program::ID,
        })
        .args(anchor_escrow::client::args::Make {
            seed,
            receive: 500_000_000,
            amount: 1_000_000_000,
        })
        .instruction()
        .unwrap();

    ctx.execute_instruction(make_ix, &[&maker])
        .unwrap()
        .assert_success();

    // 6. Assert state using AssertionHelpers on ctx.svm
    assert!(ctx.account_exists(&escrow_pda));
    ctx.svm.assert_token_balance(&vault, 1_000_000_000);
    ctx.svm.assert_token_balance(&maker_ata_a, 0);

    // 7. Execute Take
    let taker_ata_a = get_associated_token_address(&taker.pubkey(), &mint_a.pubkey());
    let maker_ata_b = get_associated_token_address(&maker.pubkey(), &mint_b.pubkey());

    let take_ix = ctx.program()
        .accounts(anchor_escrow::client::accounts::Take {
            taker: taker.pubkey(),
            maker: maker.pubkey(),
            escrow: escrow_pda,
            mint_a: mint_a.pubkey(),
            mint_b: mint_b.pubkey(),
            vault,
            taker_ata_a,
            taker_ata_b,
            maker_ata_b,
            associated_token_program: spl_associated_token_account::id(),
            token_program: spl_token::id(),
            system_program: system_program::ID,
        })
        .args(anchor_escrow::client::args::Take {})
        .instruction()
        .unwrap();

    ctx.execute_instruction(take_ix, &[&taker])
        .unwrap()
        .assert_success();

    // 8. Final assertions
    ctx.svm.assert_account_closed(&escrow_pda);
    ctx.svm.assert_account_closed(&vault);
    ctx.svm.assert_token_balance(&taker_ata_a, 1_000_000_000);
    ctx.svm.assert_token_balance(&maker_ata_b, 500_000_000);
}