Mollusk ist ein leichtgewichtiges Test-Harness zum Testen von Solana-Programmen. Es bietet eine einfache Schnittstelle zum Testen von Solana-Programm-Anweisungen in einer minimierten Solana Virtual Machine (SVM)-Umgebung. Alle Test-Accounts müssen explizit definiert werden, um deterministische und wiederholbare Tests zu gewährleisten.
Installation
Fügen Sie mollusk-svm als Abhängigkeit in Cargo.toml hinzu:
$cargo add mollusk-svm --dev
[dev-dependencies]mollusk-svm = "0.7"
Um die Compute-Unit-Nutzung zu benchmarken, fügen Sie mollusk-svm-bencher als
Abhängigkeit in Cargo.toml hinzu:
$cargo add mollusk-svm-bencher --dev
[dev-dependencies]mollusk-svm-bencher = "0.7"
Um das Token Program, Token2022 Program (Token Extensions) und Associated Token
Program für Tests mit Mollusk zu verwenden, fügen Sie
mollusk-svm-programs-token als Abhängigkeit in Cargo.toml hinzu:
$cargo add mollusk-svm-programs-token --dev
[dev-dependencies]mollusk-svm-programs-token = "0.7"
Mollusk SVM
Das folgende Beispiel zeigt ein minimales Setup zum Testen eines einfachen Solana-Programms mit Mollusk.
Hello World-Programm
Dieses Beispiel demonstriert, wie man ein einfaches Solana-Programm mit Mollusk testet. Das Programm gibt beim Aufruf einfach "Hello, world!" in den Programm-Logs aus.
Das Ausführen von cargo build-sbf generiert das kompilierte Programm unter
/target/deploy/<program_name>.so.
use solana_program::{account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,};entrypoint!(process_instruction);pub fn process_instruction(_program_id: &Pubkey,_accounts: &[AccountInfo],_instruction_data: &[u8],) -> ProgramResult {msg!("Hello, world!");Ok(())}#[cfg(test)]mod tests {use mollusk_svm::{result::Check, Mollusk};use solana_sdk::{instruction::Instruction, pubkey::Pubkey};#[test]fn test_hello_world() {let program_id = Pubkey::new_unique();let mollusk = Mollusk::new(&program_id, "target/deploy/hello_world");let instruction = Instruction::new_with_bytes(program_id, &[], vec![]);mollusk.process_and_validate_instruction(&instruction, &[], &[Check::success()]);}}
Um ein Solana-Programm mit Mollusk zu testen:
- Erstellen Sie eine
Mollusk-Instanz - Initialisieren Sie Mollusk mit einer Programm-ID und dem Pfad zum kompilierten Programm (.so-Datei) - Erstellen Sie eine Anweisung - Erstellen Sie eine Anweisung zum Aufrufen des Programms
- Verarbeiten und validieren - Verarbeiten Sie die Anweisung mit Mollusk und validieren Sie das Ergebnis
#[cfg(test)]mod tests {use mollusk_svm::{result::Check, Mollusk};use solana_sdk::{instruction::Instruction, pubkey::Pubkey};#[test]fn test_hello_world() {let program_id = Pubkey::new_unique();let mollusk = Mollusk::new(&program_id, "target/deploy/hello_world");let instruction = Instruction::new_with_bytes(program_id, &[], vec![]);mollusk.process_and_validate_instruction(&instruction, &[], &[Check::success()]);}}
Um den Test auszuführen, führen Sie cargo test aus.
Wenn der Test erfolgreich ausgeführt wird, sehen Sie eine Ausgabe ähnlich der folgenden:
running 1 test[2025-09-22T19:25:50.427685000Z DEBUG solana_runtime::message_processor::stable_log] Program 11157t3sqMV725NVRLrVQbAu98Jjfk1uCKehJnXXQs invoke [1][2025-09-22T19:25:50.429669000Z DEBUG solana_runtime::message_processor::stable_log] Program log: Hello, world![2025-09-22T19:25:50.429690000Z DEBUG solana_runtime::message_processor::stable_log] Program 11157t3sqMV725NVRLrVQbAu98Jjfk1uCKehJnXXQs consumed 211 of 1400000 compute units[2025-09-22T19:25:50.429726000Z DEBUG solana_runtime::message_processor::stable_log] Program 11157t3sqMV725NVRLrVQbAu98Jjfk1uCKehJnXXQs successtest tests::test_hello_world ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02sDoc-tests hello_world
Die
Mollusk-Struktur
bietet eine einfache Schnittstelle zum Testen von Solana-Programmen. Alle Felder
können durch eine Handvoll Hilfsmethoden manipuliert werden, aber Benutzer
können auch direkt darauf zugreifen und sie ändern, wenn sie mehr Kontrolle
wünschen.
Um Mollusk mit einer Standardinstanz zu initialisieren, verwenden Sie die
Methode Mollusk::default.
// Default instance with no custom programslet mollusk = Mollusk::default();
Um Mollusk mit einem bestimmten Programm zu initialisieren, verwenden Sie die
Methode Mollusk::new.
// Initialize Mollusk with a specific program from a file pathlet program_id = Pubkey::new_unique();let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
Um ein Programm zu Mollusk hinzuzufügen, verwenden Sie die Methode
Mollusk::add_program.
let mollusk = Mollusk::default();let program_id = Pubkey::new_unique();// Add a program to Molluskmollusk.add_program(&program_id,"target/deploy/my_program",&bpf_loader_upgradeable::id(),);
Geben Sie beim Angeben des Dateipfads nicht die Erweiterung .so an. Zum
Beispiel ist "path/to/my_program" korrekt, aber "path/to/my_program.so"
nicht.
Verarbeitung von Anweisungen
Mollusk bietet vier Hauptmethoden zur Verarbeitung von Anweisungen:
| Methode | Beschreibung |
|---|---|
process_instruction | Verarbeitet eine Anweisung und gibt das Ergebnis zurück. |
process_and_validate_instruction | Verarbeitet eine Anweisung und führt eine Reihe von Prüfungen am Ergebnis durch, wobei bei fehlgeschlagenen Prüfungen ein Panic ausgelöst wird. |
process_instruction_chain | Verarbeitet mehrere Anweisungen und gibt das Ergebnis zurück. |
process_and_validate_instruction_chain | Verarbeitet mehrere Anweisungen und führt eine Reihe von Prüfungen an jedem Ergebnis durch, wobei bei fehlgeschlagenen Prüfungen ein Panic ausgelöst wird. |
Das
InstructionResult
enthält die Details einer verarbeiteten Anweisung.
Einzelne Anweisung
Verwenden Sie die process_instruction-Methode, um eine einzelne Anweisung ohne
Prüfungen des Ergebnisses zu verarbeiten. Sie können die Ergebnisse nach der
Verarbeitung manuell validieren.
pub fn process_instruction(&self,instruction: &Instruction,accounts: &[(Pubkey, Account)],) -> InstructionResult
Das folgende Beispiel verarbeitet eine SOL-Transfer-Anweisung ohne Validierungsprüfungen.
Die folgenden Beispiele führen Mollusk in der main-Funktion zu
Demonstrationszwecken aus. In der Praxis verwenden Sie Mollusk normalerweise in
einem Testmodul, das mit dem #[test]-Attribut annotiert ist.
use mollusk_svm::Mollusk;use solana_sdk::{account::Account, pubkey::Pubkey};use solana_system_interface::{instruction::transfer, program::ID as SYSTEM_PROGRAM_ID};fn main() {// Initialize Mollusklet mollusk = Mollusk::default();// Set up accountslet sender = Pubkey::new_unique();let recipient = Pubkey::new_unique();let initial_lamports = 1_000_000;let transfer_amount = 250_000;// Create transfer instructionlet instruction = transfer(&sender, &recipient, transfer_amount);// Define initial account stateslet accounts = vec![(sender,Account {lamports: initial_lamports,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},),(recipient,Account {lamports: 0,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},),];// Process the instructionlet result = mollusk.process_instruction(&instruction, &accounts);println!("{:#?}", result);// Check the resultassert!(result.program_result.is_ok());assert_eq!(result.get_account(&sender).unwrap().lamports, 750_000);assert_eq!(result.get_account(&recipient).unwrap().lamports, 250_000);}
Einzelne Anweisung mit Prüfungen
Verwenden Sie die process_and_validate_instruction-Methode, um eine einzelne
Anweisung mit Validierungsprüfungen zu verarbeiten. Diese Methode wird
abbrechen, wenn eine Prüfung fehlschlägt.
pub fn process_and_validate_instruction(&self,instruction: &Instruction,accounts: &[(Pubkey, Account)],checks: &[Check],) -> InstructionResult
Das folgende Beispiel verarbeitet eine SOL-Transfer-Anweisung mit Validierungsprüfungen.
use {mollusk_svm::{result::Check, Mollusk},solana_sdk::{account::Account, pubkey::Pubkey},solana_system_interface::{instruction::transfer, program::ID as SYSTEM_PROGRAM_ID},};fn main() {let mollusk = Mollusk::default();let sender = Pubkey::new_unique();let recipient = Pubkey::new_unique();let initial_lamports = 1_000_000;let transfer_amount = 250_000;let instruction = transfer(&sender, &recipient, transfer_amount);let accounts = vec![(sender,Account {lamports: initial_lamports,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},),(recipient,Account {lamports: 0,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},),];// Define validation checkslet checks = vec![Check::success(),Check::account(&sender).lamports(750_000).build(),Check::account(&recipient).lamports(250_000).build(),];// Process and validate (will panic if any check fails)let result = mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);println!("{:#?}", result);}
Mehrere Anweisungen
Verwenden Sie die process_instruction_chain-Methode, um mehrere Anweisungen
sequenziell ohne Validierungsprüfungen zu verarbeiten.
pub fn process_instruction_chain(&self,instructions: &[Instruction],accounts: &[(Pubkey, Account)],) -> InstructionResult
Das folgende Beispiel verarbeitet zwei SOL-Transfer-Anweisungen ohne Validierungsprüfungen.
use {mollusk_svm::Mollusk,solana_sdk::{account::Account, pubkey::Pubkey},solana_system_interface::{instruction::transfer, program::ID as SYSTEM_PROGRAM_ID},};fn main() {let mollusk = Mollusk::default();// Set up accountslet alice = Pubkey::new_unique();let bob = Pubkey::new_unique();let charlie = Pubkey::new_unique();let initial_lamports = 1_000_000;// Create chain of transferslet instructions = vec![transfer(&alice, &bob, 300_000), // Alice -> Bobtransfer(&bob, &charlie, 100_000), // Bob -> Charlie];let accounts = vec![(alice,Account {lamports: initial_lamports,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},),(bob,Account {lamports: 0,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},),(charlie,Account {lamports: 0,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},),];// Process the instruction chainlet result = mollusk.process_instruction_chain(&instructions, &accounts);println!("{:#?}", result);// Final balances: Alice=700K, Bob=200K, Charlie=100Kassert_eq!(result.get_account(&alice).unwrap().lamports, 700_000);assert_eq!(result.get_account(&bob).unwrap().lamports, 200_000);assert_eq!(result.get_account(&charlie).unwrap().lamports, 100_000);}
Mehrere Anweisungen mit Prüfungen
Verwenden Sie die process_and_validate_instruction_chain-Methode, um mehrere
Anweisungen mit Validierungsprüfungen nach jeder Anweisung zu verarbeiten. Jede
Anweisung hat ihre eigenen Prüfungen, die bestanden werden müssen.
pub fn process_and_validate_instruction_chain(&self,instructions: &[(&Instruction, &[Check])],accounts: &[(Pubkey, Account)],) -> InstructionResult
Das folgende Beispiel verarbeitet eine Kette von zwei SOL-Transfer-Anweisungen mit Validierungsprüfungen nach jeder Anweisung.
use {mollusk_svm::{result::Check, Mollusk},solana_sdk::{account::Account, pubkey::Pubkey},solana_system_interface::{instruction::transfer, program::ID as SYSTEM_PROGRAM_ID},};fn main() {let mollusk = Mollusk::default();// Create accountslet alice = Pubkey::new_unique();let bob = Pubkey::new_unique();let charlie = Pubkey::new_unique();let initial_lamports = 1_000_000;// Create transfer instructionslet transfer1 = transfer(&alice, &bob, 300_000);let transfer2 = transfer(&bob, &charlie, 100_000);// Initial accountslet accounts = vec![(alice,Account {lamports: initial_lamports,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},),(bob,Account {lamports: 0,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},),(charlie,Account {lamports: 0,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},),];// Define checks for each instructionlet checks_after_transfer1 = vec![Check::success(),Check::account(&alice).lamports(700_000) // 1M - 300K.build(),Check::account(&bob).lamports(300_000) // 0 + 300K.build(),Check::account(&charlie).lamports(0) // Unchanged.build(),];let checks_after_transfer2 = vec![Check::success(),Check::account(&alice).lamports(700_000) // Unchanged from previous.build(),Check::account(&bob).lamports(200_000) // 300K - 100K.build(),Check::account(&charlie).lamports(100_000) // 0 + 100K.build(),];// Process with validation at each steplet instruction_and_checks = [(&transfer1, checks_after_transfer1.as_slice()),(&transfer2, checks_after_transfer2.as_slice()),];// Execute chain (panics if any check fails)let result = mollusk.process_and_validate_instruction_chain(&instruction_and_checks, &accounts);println!("{:#?}", result);}
Validierungsprüfungen
Mollusk bietet eine Reihe von Hilfsmethoden, um die Ergebnisse einer verarbeiteten Anweisung zu überprüfen.
use mollusk_svm::result::Check;
Verwenden Sie die folgenden Methoden, um Anweisungsergebnisse zu validieren:
// Program execution succeededCheck::success()// Program returned specific errorCheck::err(ProgramError::InvalidArgument)// Instruction level errorCheck::instruction_err(InstructionError::InsufficientFunds)// Check with specific program resultCheck::program_result(ProgramResult::Success)// Compute units consumedCheck::compute_units(1000)// Execution timeCheck::time(100)// Return data from instruction executionCheck::return_data(&[1, 2, 3, 4])
Verwenden Sie Folgendes, um Kontenzustände zu validieren:
// Single account validationCheck::account(&pubkey).lamports(1_000_000) // Exact lamports.owner(&program_id) // Account owner.data(&expected_data) // Exact data match.data_slice(8, &[1, 2, 3]) // Partial data match at offset.executable(false) // Executable flag.space(100) // Account data size.closed() // Account is closed (0 lamports).rent_exempt() // Account is rent-exempt.build()// Check all accounts are rent exemptCheck::all_rent_exempt()
Persistenter Kontenzustand
Der
MolluskContext
ist ein Wrapper um Mollusk, der den Kontenzustand über mehrere
Anweisungsaufrufe hinweg durch seinen account_store beibehält. Die Methoden
zur Verarbeitung von Anweisungen sind identisch mit Mollusk.
Im Gegensatz zu Mollusk, das die Übergabe von accounts an jede Methode
erfordert (z. B. process_instruction), verwaltet MolluskContext Konten
intern über seinen account_store. Dies macht den accounts-Parameter bei der
Verarbeitung von Anweisungen überflüssig.
Erstellen Sie einen account_store mit der with_context-Methode:
use std::collections::HashMap;use solana_sdk::{account::Account, pubkey::Pubkey};use solana_system_interface::program::ID as SYSTEM_PROGRAM_ID;use mollusk_svm::Mollusk;let mollusk = Mollusk::default();let account_address = Pubkey::new_unique();let mut account_store = HashMap::new();account_store.insert(account_address,Account {lamports: 1_000_000,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},);let context = mollusk.with_context(account_store);
Das folgende Beispiel verarbeitet zwei separate SOL-Transfer-Anweisungen mit
persistentem Kontenzustand zwischen den Anweisungen durch den account_store.
use {mollusk_svm::Mollusk,solana_sdk::{account::Account, pubkey::Pubkey},solana_system_interface::{instruction::transfer, program::ID as SYSTEM_PROGRAM_ID},std::collections::HashMap,};fn main() {// Initialize Mollusklet mollusk = Mollusk::default();// Create accountslet sender = Pubkey::new_unique();let recipient = Pubkey::new_unique();// Create account store with initial balanceslet mut account_store = HashMap::new();account_store.insert(sender,Account {lamports: 1_000_000,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},);account_store.insert(recipient,Account {lamports: 0,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},);// Create a stateful contextlet context = mollusk.with_context(account_store);// First transfer: 200,000 lamportslet instruction1 = transfer(&sender, &recipient, 200_000);context.process_instruction(&instruction1);// Second transfer: 100,000 lamports (state persists from first transfer)let instruction2 = transfer(&sender, &recipient, 100_000);context.process_instruction(&instruction2);// Check final balanceslet store = context.account_store.borrow();let sender_account = store.get(&sender).unwrap();let recipient_account = store.get(&recipient).unwrap();println!("Sender: {:#?}", sender_account);println!("Recipient: {:#?}", recipient_account);}
Mollusk Sysvars
Mollusk bietet eine benutzerdefinierte
Sysvars-Struktur
zum Ändern ihrer Werte für Tests.
Verwenden Sie die warp_to_slot-Methode, um die Sysvar-Clock zu aktualisieren
und das Vor- oder Zurückspringen in der Zeit zu einem bestimmten Slot zu
simulieren.
use mollusk_svm::Mollusk;fn main() {// Initialize Mollusklet mut mollusk = Mollusk::default();// Show initial slotprintln!("Initial slot: {}", mollusk.sysvars.clock.slot);// Warp to slot 1000mollusk.warp_to_slot(100);println!("After warp: {}", mollusk.sysvars.clock.slot);// Warp to slot 10mollusk.warp_to_slot(10);println!("After second warp: {}", mollusk.sysvars.clock.slot);}
Das folgende Beispiel zeigt, wie man die Mollusk-Sysvar direkt modifiziert,
indem man auf das Feld sysvars zugreift, um die Rent-Parameter zu ändern.
Andere Sysvar-Werte können auf die gleiche Weise geändert werden.
use {mollusk_svm::Mollusk, solana_sdk::rent::Rent};fn main() {let mut mollusk = Mollusk::default();// Show default rentprintln!("Default rent exemption for 1000 bytes: {} lamports",mollusk.sysvars.rent.minimum_balance(1000));// Customize rent parametersmollusk.sysvars.rent = Rent {lamports_per_byte_year: 1,exemption_threshold: 1.0,burn_percent: 0,};// Show custom rentprintln!("Custom rent exemption for 1000 bytes: {} lamports",mollusk.sysvars.rent.minimum_balance(1000));}
Compute-Unit-Benchmarking
MolluskComputeUnitBencher
verfolgt die Compute-Unit-Nutzung der Anweisungen eines Programms. Die
Ergebnisse werden in eine Markdown-Datei geschrieben.
Erfordert
mollusk-svm-bencher
als Abhängigkeit.
Das folgende Beispiel benchmarkt die Compute-Unit-Nutzung einer SOL-Transfer-Anweisung.
use {mollusk_svm::Mollusk,mollusk_svm_bencher::MolluskComputeUnitBencher,solana_sdk::{account::Account, pubkey::Pubkey},solana_system_interface::{instruction::transfer, program::ID as SYSTEM_PROGRAM_ID},};fn main() {// Initialize Mollusklet mollusk = Mollusk::default();// Create test accountslet sender = Pubkey::new_unique();let receiver = Pubkey::new_unique();// Transfer instructionlet transfer = transfer(&sender, &receiver, 100_000);let accounts = vec![(sender,Account {lamports: 1_000_000,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},),(receiver,Account {lamports: 0,data: vec![],owner: SYSTEM_PROGRAM_ID,executable: false,rent_epoch: 0,},),];// Run benchmarkMolluskComputeUnitBencher::new(mollusk).bench(("transfer", &transfer, &accounts)).must_pass(true).out_dir("./target/benches").execute();}
Die Benchmark-Ergebnisse werden in den angegebenen out_dir als Markdown-Datei
mit dem Namen compute_units.md geschrieben.
#### 2025-09-19 22:28:53.691839 UTCSolana CLI Version: solana-cli 2.2.20 (src:dabc99a5; feat:3073396398,client:Agave)| Name | CUs | Delta || -------- | --- | ------- || transfer | 150 | - new - |
Token Program testen
Verwende die
mollusk-svm-programs-token
Crate, um das Token Program, Token2022 Program (Token Extensions) und Associated
Token Program zu Mollusk für Tests hinzuzufügen.
use {mollusk_svm::Mollusk,mollusk_svm_programs_token::{associated_token, token, token2022},};let mut mollusk = Mollusk::default();// Add SPL Token Programtoken::add_program(&mut mollusk);// Add SPL Token-2022 Programtoken2022::add_program(&mut mollusk);// Add Associated Token Account Programassociated_token::add_program(&mut mollusk);
Das folgende Beispiel demonstriert das Testen eines Token-Transfers mit Mollusk.
Das folgende Beispiel definiert die Test-Accounts manuell zu
Demonstrationszwecken. Die mollusk-svm-programs-token enthält auch
Hilfsfunktionen zum Erstellen der Mint- und Token-Accounts.
use {mollusk_svm::{result::Check, Mollusk},mollusk_svm_programs_token::token,solana_sdk::{account::Account, program_pack::Pack, pubkey::Pubkey},spl_token_interface::{instruction::transfer_checked,state::{Account as TokenAccount, AccountState, Mint},},};fn main() {// Initialize Mollusk with Token programlet mut mollusk = Mollusk::default();token::add_program(&mut mollusk);// Create account keyslet mint = Pubkey::new_unique();let source = Pubkey::new_unique();let destination = Pubkey::new_unique();let authority = Pubkey::new_unique();// Token configurationlet decimals = 6;let transfer_amount = 1_000_000; // 1 token with 6 decimalslet initial_balance = 10_000_000; // 10 tokens// Calculate rent-exempt minimumslet mint_rent = mollusk.sysvars.rent.minimum_balance(Mint::LEN);let account_rent = mollusk.sysvars.rent.minimum_balance(TokenAccount::LEN);// Create mint accountlet mut mint_data = vec![0u8; Mint::LEN];Mint::pack(Mint {mint_authority: Some(authority).into(),supply: initial_balance,decimals,is_initialized: true,freeze_authority: None.into(),},&mut mint_data,).unwrap();// Create source token accountlet mut source_data = vec![0u8; TokenAccount::LEN];TokenAccount::pack(TokenAccount {mint,owner: authority,amount: initial_balance,delegate: None.into(),state: AccountState::Initialized,is_native: None.into(),delegated_amount: 0,close_authority: None.into(),},&mut source_data,).unwrap();// Create destination token accountlet mut destination_data = vec![0u8; TokenAccount::LEN];TokenAccount::pack(TokenAccount {mint,owner: Pubkey::new_unique(),amount: 0,delegate: None.into(),state: AccountState::Initialized,is_native: None.into(),delegated_amount: 0,close_authority: None.into(),},&mut destination_data,).unwrap();// Setup accounts for transfer_checkedlet accounts = vec![(source,Account {lamports: account_rent,data: source_data,owner: token::ID,executable: false,rent_epoch: 0,},),(mint,Account {lamports: mint_rent,data: mint_data,owner: token::ID,executable: false,rent_epoch: 0,},),(destination,Account {lamports: account_rent,data: destination_data,owner: token::ID,executable: false,rent_epoch: 0,},),(authority,Account {lamports: 1_000_000,data: vec![],owner: Pubkey::default(),executable: false,rent_epoch: 0,},),];// Create transfer_checked instructionlet instruction = transfer_checked(&token::ID,&source,&mint,&destination,&authority,&[],transfer_amount,decimals,).unwrap();// Expected balances after transferlet expected_source_balance = (initial_balance - transfer_amount).to_le_bytes();let expected_dest_balance = transfer_amount.to_le_bytes();// Define validation checkslet checks = vec![Check::success(),Check::account(&source).data_slice(64, &expected_source_balance) // Token amount is at offset 64.build(),Check::account(&destination).data_slice(64, &expected_dest_balance).build(),];// Process and validate the instructionlet result = mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);println!("{:#?}", result);// Deserialize token account datalet source_account = result.get_account(&source).unwrap();let source_token = TokenAccount::unpack(&source_account.data).unwrap();println!("Source Token Account: {:#?}", source_token);let destination_account = result.get_account(&destination).unwrap();let dest_token = TokenAccount::unpack(&destination_account.data).unwrap();println!("Destination Token Account: {:#?}", dest_token);}
Is this page helpful?