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 IDLanchor_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 IDlet 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 setupslet 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_aassert_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 keypairlet payer = ctx.payer();println!("Payer pubkey: {}", payer.pubkey());// ctx.program() — Program instance for instruction buildinglet 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 addresslet 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 TransactionResultctx.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 transactionctx.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 neededlet 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 setuplet 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.svmlet 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 ATAslet 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 PDAslet 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 typeslet 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.svmassert!(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 Takelet 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 assertionsctx.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);}
Is this page helpful?