LiteSVM Docs
Additional Cratesanchor-litesvm

Account Management

Fetching and deserializing Anchor accounts with automatic discriminator handling

Understanding Anchor Account Discriminators

Anchor programs prepend an 8-byte discriminator to all account data. This discriminator is a hash of the account type name and is used to verify that account data matches the expected type.

┌─────────────────────────────────────────────────┐
│ Account Data Layout                             │
├────────────────┬────────────────────────────────┤
│ Discriminator  │ Account Fields                 │
│ (8 bytes)      │ (variable size)                │
└────────────────┴────────────────────────────────┘

The anchor-litesvm account utilities handle this discriminator automatically.

Fetching Anchor Accounts

Basic Account Fetching

use anchor_litesvm::AnchorLiteSVM;
use anchor_lang::prelude::*;
use solana_sdk::{native_token::LAMPORTS_PER_SOL, signature::Signer, pubkey::Pubkey};

#[account]
pub struct UserAccount {
    pub name: String,
    pub balance: u64,
    pub authority: Pubkey,
}

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

    let user = ctx.create_funded_account(10 * LAMPORTS_PER_SOL);

    // ... initialize the account ...

    let (user_pda, _) = Pubkey::find_program_address(
        &[b"user", user.pubkey().as_ref()],
        &PROGRAM_ID,
    );

    // Fetch and deserialize the account
    let account: UserAccount = ctx.get_account(&user_pda).unwrap();

    assert_eq!(account.name, "Alice");
    assert_eq!(account.authority, user.pubkey());
}

get_account<T>() automatically validates the 8-byte discriminator to ensure the account data matches the expected type.

Unchecked Deserialization

For PDAs or custom account layouts where discriminator validation might fail, use get_account_unchecked:

use anchor_litesvm::AnchorLiteSVM;

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

    // Fetch without discriminator validation
    let account: UserAccount = ctx.get_account_unchecked(&some_pda).unwrap();
}

Use get_account_unchecked carefully. Without discriminator validation, you might deserialize invalid data. Only use this when you're certain about the account type.

Using the Account Module Directly

You can also use the account deserialization functions directly:

use anchor_litesvm::{get_anchor_account, get_anchor_account_unchecked};
use litesvm::LiteSVM;

#[test]
fn test_direct_account_fetch() {
    let mut svm = LiteSVM::new();
    // ... setup ...

    // Fetch with discriminator check
    let account: UserAccount = get_anchor_account(&svm, &account_pubkey).unwrap();

    // Fetch without discriminator check
    let account: UserAccount = get_anchor_account_unchecked(&svm, &account_pubkey).unwrap();
}

Account Errors

The AccountError enum provides detailed error information:

use anchor_litesvm::AccountError;

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

    let result: Result<UserAccount, AccountError> = ctx.get_account(&nonexistent_pubkey);

    match result {
        Ok(account) => println!("Found account: {}", account.name),
        Err(AccountError::AccountNotFound(pubkey)) => {
            println!("Account not found: {}", pubkey);
        }
        Err(AccountError::DiscriminatorMismatch) => {
            println!("Wrong account type!");
        }
        Err(AccountError::DeserializationError(msg)) => {
            println!("Failed to deserialize: {}", msg);
        }
    }
}
ErrorDescription
AccountNotFound(Pubkey)No account exists at the given address
DiscriminatorMismatchAccount discriminator doesn't match expected type
DeserializationError(String)Failed to deserialize account data

Working with Multiple Account Types

Fetching Different Account Types

use anchor_litesvm::AnchorLiteSVM;
use anchor_lang::prelude::*;

#[account]
pub struct Config {
    pub admin: Pubkey,
    pub fee_bps: u16,
}

#[account]
pub struct UserProfile {
    pub owner: Pubkey,
    pub username: String,
    pub created_at: i64,
}

#[account]
pub struct Order {
    pub user: Pubkey,
    pub amount: u64,
    pub status: OrderStatus,
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub enum OrderStatus {
    Pending,
    Completed,
    Cancelled,
}

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

    // Each account type is deserialized correctly
    let config: Config = ctx.get_account(&config_pda).unwrap();
    let profile: UserProfile = ctx.get_account(&profile_pda).unwrap();
    let order: Order = ctx.get_account(&order_pda).unwrap();

    assert_eq!(config.fee_bps, 100);
    assert_eq!(profile.username, "alice");
    assert!(matches!(order.status, OrderStatus::Pending));
}

Checking Account Existence Before Fetching

use anchor_litesvm::AnchorLiteSVM;

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

    let user_pda = Pubkey::new_unique();

    // Check existence first
    if ctx.account_exists(&user_pda) {
        let account: UserAccount = ctx.get_account(&user_pda).unwrap();
        println!("Found: {}", account.name);
    } else {
        println!("Account not initialized yet");
    }
}

Complete Example

Here's a comprehensive example demonstrating account management:

use anchor_litesvm::AnchorLiteSVM;
use anchor_lang::prelude::*;
use solana_sdk::{
    native_token::LAMPORTS_PER_SOL,
    signature::Signer,
    pubkey::Pubkey,
};

declare_id!("YourProgram11111111111111111111111111111111");

#[account]
pub struct Counter {
    pub count: u64,
    pub authority: Pubkey,
    pub bump: u8,
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub authority: Signer<'info>,
    #[account(
        init,
        payer = authority,
        space = 8 + 8 + 32 + 1,
        seeds = [b"counter", authority.key().as_ref()],
        bump
    )]
    pub counter: Account<'info, Counter>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Increment<'info> {
    pub authority: Signer<'info>,
    #[account(
        mut,
        seeds = [b"counter", authority.key().as_ref()],
        bump = counter.bump
    )]
    pub counter: Account<'info, Counter>,
}

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

    let authority = ctx.create_funded_account(10 * LAMPORTS_PER_SOL);

    // Derive the counter PDA
    let (counter_pda, bump) = Pubkey::find_program_address(
        &[b"counter", authority.pubkey().as_ref()],
        &id(),
    );

    // Verify counter doesn't exist yet
    assert!(!ctx.account_exists(&counter_pda));

    // Initialize
    let init_ix = ctx.program()
        .accounts(Initialize {
            authority: authority.pubkey(),
            counter: counter_pda,
            system_program: solana_sdk::system_program::id(),
        })
        .args(instruction::Initialize {})
        .instruction()
        .unwrap();

    ctx.execute_instruction(init_ix, &[&authority]).unwrap();

    // Verify counter exists and has correct initial state
    assert!(ctx.account_exists(&counter_pda));
    let counter: Counter = ctx.get_account(&counter_pda).unwrap();
    assert_eq!(counter.count, 0);
    assert_eq!(counter.authority, authority.pubkey());
    assert_eq!(counter.bump, bump);

    // Increment
    let increment_ix = ctx.program()
        .accounts(Increment {
            authority: authority.pubkey(),
            counter: counter_pda,
        })
        .args(instruction::Increment {})
        .instruction()
        .unwrap();

    ctx.execute_instruction(increment_ix, &[&authority]).unwrap();

    // Verify incremented
    let counter: Counter = ctx.get_account(&counter_pda).unwrap();
    assert_eq!(counter.count, 1);

    // Increment again
    ctx.execute_instruction(increment_ix.clone(), &[&authority]).unwrap();

    let counter: Counter = ctx.get_account(&counter_pda).unwrap();
    assert_eq!(counter.count, 2);

    println!("Counter test passed! Final count: {}", counter.count);
}