Installation
Ajoutez les dépendances nécessaires :
cargo add --dev anchor-litesvm litesvm litesvm-utils
Qu'est-ce qu'anchor-litesvm ?
La crate anchor-litesvm offre une syntaxe simplifiée similaire à
anchor-client mais sans la surcharge RPC. Elle permet une réduction de 78%
du code par rapport à LiteSVM brut, tout en maintenant la sécurité des types
avec les types Anchor.
AnchorContext
- Contexte de test compatible avec la production
- Mêmes patterns d'API qu'anchor-client
- Gère l'instance LiteSVM, le payeur et le programme
- Exécute les instructions sans surcharge RPC
API Programme - Construction fluide des instructions - Gestion des comptes et des arguments avec sécurité des types - Syntaxe anchor-client familière
Désérialisation des comptes - Récupération et désérialisation des comptes Anchor - Gestion automatique des discriminateurs - Prise en charge des PDAs et des structures personnalisées
Analyse des événements
- Analyse des événements à partir des journaux de transaction
- Assertion de l'émission des événements
- Désérialisation des événements avec sécurité des types
Exemple rapide
Avec Anchor 1.0, utilisez declare_program! pour générer des types client à
partir de l'IDL de votre programme. Cette macro crée les modules
client::accounts::* et client::args::* pour une construction d'instructions
sécurisée par les types :
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) lit l'IDL depuis target/idl/my_program.json
au moment de la compilation et génère les modules client::accounts::* et
client::args::*. Cela nécessite de compiler votre programme au préalable.
Comparaison : LiteSVM brut vs anchor-litesvm
Avant (LiteSVM brut)
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();
Aprè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();
Composants clés
Constructeur AnchorLiteSVM
| Méthode | Description |
|---|---|
new() | Crée une nouvelle instance du constructeur |
with_payer(keypair) | Définit un keypair payeur personnalisé |
deploy_program(id, bytes) | Ajoute un programme à déployer |
build() | Construit l'AnchorContext |
build_with_program(id, bytes) | Raccourci pour un programme unique |
build_with_programs(programs) | Déploie plusieurs programmes |
AnchorContext
| Méthode | Description |
|---|---|
svm | Accès direct à l'instance LiteSVM sous-jacente (champ public) |
program_id | L'identifiant du programme (champ public) |
program() | Retourne Program pour la construction d'instructions |
payer() | Obtenir le keypair payeur |
execute_instruction(ix, signers) | Exécuter une seule instruction |
execute_instructions(ixs, signers) | Exécuter plusieurs instructions en une seule transaction |
send_and_confirm_transaction(&tx) | Envoyer une transaction brute |
get_account<T>(pubkey) | Récupérer et désérialiser un compte Anchor |
get_account_unchecked<T>(pubkey) | Récupérer sans vérification du discriminant |
create_funded_account(lamports) | Créer et approvisionner un nouveau keypair |
airdrop(pubkey, lamports) | Airdrop de SOL vers une adresse |
latest_blockhash() | Obtenir le blockhash actuel |
account_exists(pubkey) | Vérifier si un compte existe |
deploy_program(id, bytes) | Déployer un programme supplémentaire (via ProgramTestExt) |
Programme
| Méthode | Description |
|---|---|
accounts(accounts) | Définir les comptes de l'instruction (tout type ToAccountMetas) |
args(args) | Définir les arguments de l'instruction (tout type InstructionData) |
instruction() | Construire l'Instruction final |
id() | Obtenir l'identifiant du programme |
ctx.svm — TestHelpers & AssertionHelpers
ctx.svm est un champ public LiteSVM avec les traits TestHelpers et
AssertionHelpers disponibles via litesvm-utils :
| Méthode | Description |
|---|---|
ctx.svm.create_funded_account(lamports) | Créer et financer un keypair |
ctx.svm.create_token_mint(authority, decimals) | Créer un mint de token SPL |
ctx.svm.create_associated_token_account(mint, owner) | Créer un ATA |
ctx.svm.mint_to(mint, token_account, authority, amount) | Minter des tokens |
ctx.svm.get_pda(seeds, program_id) | Dériver une adresse PDA |
ctx.svm.get_pda_with_bump(seeds, program_id) | Dériver un PDA avec bump seed |
ctx.svm.assert_token_balance(token_account, expected) | Vérifier le solde de tokens |
ctx.svm.assert_account_closed(pubkey) | Vérifier que le compte a été fermé |
ctx.svm.assert_sol_balance(pubkey, expected) | Vérifier le solde SOL |
Dépannage
Erreurs courantes
| Erreur | Cause | Solution |
|---|---|---|
AccountNotFound | Le compte n'existe pas | S'assurer que le compte est créé avant de le récupérer |
DiscriminatorMismatch | Type de compte incorrect | Vérifier que vous utilisez la bonne structure de compte |
DeserializationError | Données de compte invalides | Vérifier que le compte a été correctement initialisé |
| Aucun programme ajouté | build() appelé sans deploy_program() | Ajouter au moins un programme avant de compiler |
| Types client manquants | declare_program! non appelé | Exécuter anchor build d'abord pour générer l'IDL, puis appeler declare_program! |
Is this page helpful?