Instalación
Añade las dependencias necesarias:
cargo add --dev anchor-litesvm litesvm litesvm-utils
¿Qué es anchor-litesvm?
El crate anchor-litesvm ofrece una sintaxis simplificada similar a
anchor-client pero sin la sobrecarga de RPC. Consigue una reducción del 78%
en el código en comparación con LiteSVM puro, manteniendo la seguridad de
tipos con los tipos de Anchor.
AnchorContext
- Contexto de prueba compatible con producción
- Los mismos patrones de API que anchor-client
- Gestiona la instancia de LiteSVM, el pagador y el programa
- Ejecuta instrucciones sin sobrecarga de RPC
API del Programa - Construcción fluida de instrucciones - Manejo de cuentas y argumentos con seguridad de tipos - Sintaxis familiar de anchor-client
Deserialización de Cuentas - Obtén y deserializa cuentas de Anchor - Manejo automático del discriminador - Soporte para PDAs y layouts personalizados
Análisis de Eventos
- Analiza eventos desde los logs de transacciones
- Verifica la emisión de eventos
- Deserialización de eventos con seguridad de tipos
Ejemplo Rápido
Con Anchor 1.0, usa declare_program! para generar tipos de cliente a partir
del IDL de tu programa. Esta macro crea los módulos client::accounts::* e
client::args::* para la construcción de instrucciones con seguridad de tipos:
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 IDLanchor_lang::declare_program!(my_program);#[test]fn test_anchor_program() {// One-line setup — reads program keypair for the correct IDlet 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.svmlet user = ctx.svm.create_funded_account(10_000_000_000).unwrap();// Derive PDAlet 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 typeslet 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 chainctx.execute_instruction(ix, &[&user]).unwrap().assert_success();// Fetch and deserialize the accountlet account: my_program::MyAccount = ctx.get_account(&pda).unwrap();assert_eq!(account.name, "test");}
declare_program!(my_program) lee el IDL desde target/idl/my_program.json
en tiempo de compilación y genera los módulos client::accounts::* e
client::args::*. Esto requiere compilar tu programa primero.
Comparación: LiteSVM sin procesar vs anchor-litesvm
Antes (LiteSVM sin procesar)
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 discriminatorlet 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 instructionlet 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 skiplet account_data = svm.get_account(&pda).unwrap().data;let account: UserAccount = UserAccount::try_deserialize(&mut &account_data[8..]).unwrap();
Después (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();
Componentes principales
Constructor AnchorLiteSVM
| Método | Descripción |
|---|---|
new() | Crea una nueva instancia del constructor |
with_payer(keypair) | Establece un keypair de pagador personalizado |
deploy_program(id, bytes) | Agrega un programa para desplegar |
build() | Construye el AnchorContext |
build_with_program(id, bytes) | Método conveniente para un solo programa |
build_with_programs(programs) | Despliega múltiples programas |
AnchorContext
| Método | Descripción |
|---|---|
svm | Acceso directo a la instancia subyacente de LiteSVM (campo público) |
program_id | El ID del programa (campo público) |
program() | Devuelve Program para la construcción de instrucciones |
payer() | Obtener el keypair del pagador |
execute_instruction(ix, signers) | Ejecutar una sola instrucción |
execute_instructions(ixs, signers) | Ejecutar múltiples instrucciones en una transacción |
send_and_confirm_transaction(&tx) | Enviar una transacción sin procesar |
get_account<T>(pubkey) | Obtener y deserializar una cuenta de Anchor |
get_account_unchecked<T>(pubkey) | Obtener sin verificación de discriminador |
create_funded_account(lamports) | Crear y financiar un nuevo keypair |
airdrop(pubkey, lamports) | Transferir SOL a una dirección |
latest_blockhash() | Obtener el blockhash actual |
account_exists(pubkey) | Verificar si existe una cuenta |
deploy_program(id, bytes) | Desplegar un programa adicional (mediante ProgramTestExt) |
Programa
| Método | Descripción |
|---|---|
accounts(accounts) | Establecer las cuentas de instrucción (cualquier tipo ToAccountMetas) |
args(args) | Establecer los argumentos de instrucción (cualquier tipo InstructionData) |
instruction() | Construir la Instruction final |
id() | Obtener el ID del programa |
ctx.svm — TestHelpers y AssertionHelpers
ctx.svm es un campo público LiteSVM con los traits TestHelpers y
AssertionHelpers disponibles a través de litesvm-utils:
| Método | Descripción |
|---|---|
ctx.svm.create_funded_account(lamports) | Crear y financiar un keypair |
ctx.svm.create_token_mint(authority, decimals) | Crear un mint de token SPL |
ctx.svm.create_associated_token_account(mint, owner) | Crear una ATA |
ctx.svm.mint_to(mint, token_account, authority, amount) | Acuñar tokens |
ctx.svm.get_pda(seeds, program_id) | Derivar una dirección PDA |
ctx.svm.get_pda_with_bump(seeds, program_id) | Derivar PDA con bump seed |
ctx.svm.assert_token_balance(token_account, expected) | Verificar saldo de tokens |
ctx.svm.assert_account_closed(pubkey) | Verificar que la cuenta fue cerrada |
ctx.svm.assert_sol_balance(pubkey, expected) | Verificar saldo de SOL |
Solución de problemas
Errores comunes
| Error | Causa | Solución |
|---|---|---|
AccountNotFound | La cuenta no existe | Asegúrate de que la cuenta esté creada antes de consultarla |
DiscriminatorMismatch | Tipo de cuenta incorrecto | Verifica que estás usando el struct de cuenta correcto |
DeserializationError | Datos de cuenta inválidos | Comprueba que la cuenta fue inicializada correctamente |
| No se añadieron programas | Se llamó a build() sin deploy_program() | Añade al menos un programa antes de compilar |
| Tipos de cliente faltantes | declare_program! no fue llamado | Ejecuta primero anchor build para generar el IDL, luego llama a declare_program! |
Is this page helpful?