Документация SolanaLiteSVMRustДополнительные контейнерыanchor-litesvm

Быстрый старт

Установка

Добавьте необходимые зависимости:

cargo add --dev anchor-litesvm litesvm litesvm-utils

Что такое anchor-litesvm?

Крейт anchor-litesvm предоставляет упрощённый синтаксис, аналогичный anchor-client, но без накладных расходов RPC. Он обеспечивает сокращение кода на 78% по сравнению с чистым LiteSVM, сохраняя типобезопасность с типами Anchor.

AnchorContext

  • Контекст тестирования, совместимый с production-окружением
  • Те же паттерны API, что и в anchor-client
  • Управляет экземпляром LiteSVM, плательщиком и программой
  • Выполнение инструкций без накладных расходов RPC

Program API — Построение инструкций в стиле цепочки вызовов — Типобезопасная обработка аккаунтов и аргументов — Привычный синтаксис anchor-client

Десериализация аккаунтов — Получение и десериализация аккаунтов Anchor — Автоматическая обработка дискриминаторов — Поддержка PDA и пользовательских структур данных

Парсинг событий

  • Парсинг событий из логов транзакций
  • Проверка генерации событий
  • Типобезопасная десериализация событий

Быстрый пример

В Anchor 1.0 используйте declare_program! для генерации клиентских типов из IDL вашей программы. Этот макрос создаёт модули client::accounts::* и client::args::* для типобезопасного построения инструкций:

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) считывает IDL из target/idl/my_program.json во время компиляции и генерирует модули client::accounts::* и client::args::*. Для этого необходимо предварительно собрать вашу программу.

Сравнение: Raw LiteSVM и anchor-litesvm

До (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();

После (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();

Ключевые компоненты

Конструктор AnchorLiteSVM

МетодОписание
new()Создаёт новый экземпляр конструктора
with_payer(keypair)Устанавливает пользовательский keypair плательщика
deploy_program(id, bytes)Добавляет программу для развёртывания
build()Собирает AnchorContext
build_with_program(id, bytes)Удобный метод для одной программы
build_with_programs(programs)Развернуть несколько программ

AnchorContext

МетодОписание
svmПрямой доступ к базовому экземпляру LiteSVM (публичное поле)
program_idИдентификатор программы (публичное поле)
program()Возвращает Program для построения инструкций
payer()Получить keypair плательщика
execute_instruction(ix, signers)Выполнить одну инструкцию
execute_instructions(ixs, signers)Выполнить несколько инструкций в одной транзакции
send_and_confirm_transaction(&tx)Отправить необработанную транзакцию
get_account<T>(pubkey)Получить и десериализовать аккаунт Anchor
get_account_unchecked<T>(pubkey)Получить без проверки дискриминатора
create_funded_account(lamports)Создать и пополнить новый keypair
airdrop(pubkey, lamports)Перевести SOL на адрес через аирдроп
latest_blockhash()Получить текущий blockhash
account_exists(pubkey)Проверить существование аккаунта
deploy_program(id, bytes)Развернуть дополнительную программу (через ProgramTestExt)

Program

МетодОписание
accounts(accounts)Установить аккаунты инструкции (любой тип ToAccountMetas)
args(args)Установить аргументы инструкции (любой тип InstructionData)
instruction()Собрать финальный Instruction
id()Получить идентификатор программы

ctx.svm — TestHelpers и AssertionHelpers

ctx.svm — публичное поле LiteSVM с трейтами TestHelpers и AssertionHelpers, доступными через litesvm-utils:

МетодОписание
ctx.svm.create_funded_account(lamports)Создать и пополнить keypair
ctx.svm.create_token_mint(authority, decimals)Создать минт токена SPL
ctx.svm.create_associated_token_account(mint, owner)Создать ATA
ctx.svm.mint_to(mint, token_account, authority, amount)Выпустить токены
ctx.svm.get_pda(seeds, program_id)Получить адрес PDA
ctx.svm.get_pda_with_bump(seeds, program_id)Получить PDA с bump seed
ctx.svm.assert_token_balance(token_account, expected)Проверить баланс токенов
ctx.svm.assert_account_closed(pubkey)Проверить, что аккаунт закрыт
ctx.svm.assert_sol_balance(pubkey, expected)Проверить баланс SOL

Устранение неполадок

Распространённые ошибки

ОшибкаПричинаРешение
AccountNotFoundАккаунт не существуетУбедитесь, что аккаунт создан перед его получением
DiscriminatorMismatchНеверный тип аккаунтаУбедитесь, что используете правильную структуру аккаунта
DeserializationErrorНедопустимые данные аккаунтаПроверьте, что аккаунт был правильно инициализирован
No programs addedВызван build() без deploy_program()Добавьте хотя бы одну программу перед сборкой
Missing client typesdeclare_program! не вызванСначала выполните anchor build для генерации IDL, затем вызовите declare_program!

Is this page helpful?