LiteSVM avec Rust
Le package litesvm fournit l'infrastructure de test essentielle pour créer un environnement Solana léger où vous pouvez manipuler directement l'état des comptes et exécuter des transactions sur vos programmes.
Premiers pas
Ajoutez LiteSVM à votre projet :
cargo add --dev litesvmPrincipes de base de LiteSVM
Commencez par déclarer l'ID de votre programme et créer une instance LiteSVM.
Utilisez exactement le même ID de programme que celui défini dans votre programme pour garantir que les transactions s'exécutent correctement et ne génèrent pas d'erreurs ProgramMismatch pendant les tests :
use litesvm::LiteSVM;
use solana_pubkey::{pubkey, Pubkey};
const program_id: Pubkey = pubkey!("22222222222222222222222222222222222222222222");
#[test]
fn test() {
// Create a new instance of LiteSVM
let mut svm = LiteSVM::new();
// Load the program with the right publickey
svm.add_program_from_file(program_id, "target/deploy/program.so");
}Pour exécuter des tests, créez un objet transaction et utilisez la fonction .send_transaction(tx) :
use litesvm::LiteSVM;
use solana_transaction::Transaction;
#[test]
fn test() {
// Create a new instance of LiteSVM
let mut svm = LiteSVM::new();
// Create a new Transaction
let mut tx = Transaction::new_signed_with_payer(
&[...ixs],
Some(&payer.pubkey()),
&[...signersKeypair],
svm.latest_blockhash(),
);
// Send the Transaction
let result = svm.send_transaction(tx).unwrap();
}Comptes
Lors des tests de programmes Solana avec LiteSVM, vous travaillerez avec plusieurs types de comptes qui reflètent des scénarios d'exécution de programme réels.
Comprendre comment construire correctement ces comptes est essentiel pour des tests efficaces.
Comptes système
Le type de compte le plus fondamental est le compte système, qui existe en deux variantes principales :
Comptes payeurs : comptes avec des lamports qui financent la création de comptes de programme ou les transferts de lamports
Comptes non initialisés : comptes vides sans lamports, généralement utilisés pour représenter des comptes de programme en attente d'initialisation
Les comptes système ne contiennent pas de données et appartiennent au Programme Système. La principale différence entre les payeurs et les comptes non initialisés est leur solde en lamports : les payeurs ont des fonds, tandis que les comptes non initialisés commencent vides.
Voici comment créer un compte payer dans LiteSVM :
use litesvm::LiteSVM;
use solana_account::Account;
use solana_keypair::Keypair;
use solana_pubkey::{pubkey, Pubkey};
#[test]
fn test() {
// Create a new instance of LiteSVM
let mut svm = LiteSVM::new();
// Create a new Account
let account = Keypair::new();
// Add the Account with the modified data
svm.set_account(
account.pubkey(),
Account {
lamports: 100_000_000,
data: [],
owner: ID,
executable: false,
rent_epoch: 0,
},
);
}Comptes de programme
Pour les comptes de programme qui contiennent des structures de données personnalisées, vous pouvez utiliser une approche similaire.
Vous devrez également sérialiser les données du compte dans un tableau d'octets, ce qui peut être fait manuellement ou en utilisant une bibliothèque comme borsh, bincode, ou solana_program_pack.
use litesvm::LiteSVM;
use solana_account::Account;
use solana_keypair::Keypair;
use solana_pubkey::{pubkey, Pubkey};
#[test]
fn test() {
// Create a new instance of LiteSVM
let mut svm = LiteSVM::new();
// Create a new Account
let account = Keypair::new();
let mut account_data = [0; SIZE_OF_THE_ACCOUNT];
// Serialize the account data into the byte array defined above
// ...
let lamports = svm.minimum_balance_for_rent_exemption(SIZE_OF_THE_ACCOUNT);
// Add the Account with the modified data
svm.set_account(
account.pubkey(),
Account {
lamports,
data: account_data,
owner: ID,
executable: false,
rent_epoch: 0,
},
)
}Comptes de jetons
Pour sérialiser les données des comptes SPL Token, vous pouvez utiliser spl_token::Mint et spl_token::Account, qui implémentent solana_program_pack::Pack.
use litesvm::LiteSVM;
use solana_keypair::Keypair;
use solana_pubkey::{pubkey, Pubkey};
use solana_account::Account;
use spl_token::{ID as TOKEN_PROGRAM_ID, state::{Mint, Account as TokenAccount}};
use solana_program_pack::Pack;
#[test]
fn test() {
// Create a new instance of LiteSVM
let mut svm = LiteSVM::new();
// Create a new Mint Account
let mint = Keypair::new();
// Populate the data of the Mint Account
let mint_data = Mint {
mint_authority: None.into(),
supply: 0,
decimals: 6,
is_initialized: true,
freeze_authority: None.into(),
};
let mut mint_account_data = vec![0; Mint::LEN];
Mint::pack(mint_data, &mut mint_account_data).unwrap();
// Grab the minimum amount of lamports to make it rent exempt
let lamports = svm.minimum_balance_for_rent_exemption(Mint::LEN);
// Add the Mint Account
svm.set_account(
mint.pubkey(),
Account {
lamports,
data: mint_account_data,
owner: TOKEN_PROGRAM_ID,
executable: false,
rent_epoch: 0,
},
);
// Create a new Token Account
let token_account = Keypair::new();
let owner = Keypair::new();
// Populate the data of the Token Account
let token_account_data = TokenAccount {
mint: mint.pubkey(),
owner: owner.pubkey(),
amount: 0,
delegate: None.into(),
state: spl_token::state::AccountState::Initialized,
is_native: None.into(),
delegated_amount: 0,
close_authority: None.into(),
};
let mut token_account_data_bytes = vec![0; TokenAccount::LEN];
TokenAccount::pack(token_account_data, &mut token_account_data_bytes).unwrap();
// Grab the minimum amount of lamports to make it rent exempt
let lamports = svm.minimum_balance_for_rent_exemption(TokenAccount::LEN);
// Add the Token Account
svm.set_account(
token_account.pubkey(),
Account {
lamports,
data: token_account_data_bytes,
owner: TOKEN_PROGRAM_ID,
executable: false,
rent_epoch: 0,
},
);
}Execution
Avec les comptes créés et ajoutés à votre instance LiteSVM, vous pouvez maintenant envoyer des transactions et valider la logique de votre programme.
Avant d'envoyer une transaction, vous pouvez simuler le résultat :
let simulated_result = svm.simulate_transaction(tx);Ensuite, envoyez la transaction et examinez ses journaux :
let result = svm.send_transaction(tx);
let logs = result.logs;Fonctionnalités avancées
Avant et après l'exécution, l'ensemble du registre contenu dans votre instance LiteSVM est lisible et personnalisable.
Vous pouvez manipuler les valeurs sysvar comme l'horloge :
// Change the Clock
let mut new_clock = svm.get_sysvar::<Clock>();
new_clock.unix_timestamp = 1735689600;
svm.set_sysvar::<Clock>(&new_clock);
// Jump to a certain Slot
svm.warp_to_slot(500);
// Expire the current blockhash
svm.expire_blockhash();Vous pouvez également lire les données de compte et de protocole :
// Get all the information about an account (data, lamports, owner, ...)
svm.get_account(&account.publickey);
// Get the lamport balance of an account
svm.get_balance(&account.publickey);
// Get the number of Compute Unit used till now
svm.get_compute_budget();Ou configurer le comportement du runtime :
// Sets the compute budget
let compute_budget = ComputeBudget::default();
compute_budget.compute_unit_limit = 2_000_000;
svm.with_compute_budget(compute_budget);
// Sets Sigverify as active
svm.with_sigverify(true);
// Sets the Blockhash check as active
svm.with_blockhash_check(true);
// Sets the default Sysvars
svm.with_sysvars();
// Set the FeatureSet to use
svm.with_feature_set(FeatureSet::default())