Mollusk
Το Mollusk είναι ένα ελαφρύ πλαίσιο δοκιμών για τον έλεγχο προγραμμάτων Solana. Παρέχει μια απλή διεπαφή για τον έλεγχο εντολών προγραμμάτων Solana σε ένα ελαχιστοποιημένο περιβάλλον Solana Virtual Machine (SVM). Όλοι οι λογαριασμοί δοκιμών πρέπει να οριστούν ρητά, διασφαλίζοντας ντετερμινιστικές και επαναλαμβανόμενες δοκιμές.
Εγκατάσταση
Προσθέστε το mollusk-svm
ως εξάρτηση στο Cargo.toml
:
$cargo add mollusk-svm --dev
[dev-dependencies]mollusk-svm = "0.5"
Για τη συγκριτική αξιολόγηση της χρήσης υπολογιστικών μονάδων, προσθέστε το
mollusk-svm-bencher
ως εξάρτηση στο Cargo.toml
:
$cargo add mollusk-svm-bencher --dev
[dev-dependencies]mollusk-svm-bencher = "0.5"
Για να χρησιμοποιήσετε το Token Program, το token2022 program (Token Extensions)
και το Associated Token Program για δοκιμές με το Mollusk, προσθέστε το
mollusk-svm-programs-token
ως εξάρτηση στο Cargo.toml
:
$cargo add mollusk-svm-programs-token --dev
[dev-dependencies]mollusk-svm-programs-token = "0.5"
Mollusk SVM
Το παρακάτω παράδειγμα δείχνει μια ελάχιστη ρύθμιση για τον έλεγχο ενός βασικού προγράμματος Solana χρησιμοποιώντας το Mollusk.
Πρόγραμμα Hello World
Αυτό το παράδειγμα δείχνει πώς να ελέγξετε ένα βασικό πρόγραμμα Solana χρησιμοποιώντας το Mollusk. Το πρόγραμμα απλά εκτυπώνει "Hello, world!" στα αρχεία καταγραφής του προγράμματος όταν καλείται.
Η εκτέλεση του cargo build-sbf
δημιουργεί το μεταγλωττισμένο πρόγραμμα στο
/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()]);}}
Για να ελέγξετε ένα πρόγραμμα Solana με το Mollusk:
- Δημιουργήστε ένα στιγμιότυπο
Mollusk
- Αρχικοποιήστε το Mollusk με ένα program ID και τη διαδρομή προς το μεταγλωττισμένο πρόγραμμα (αρχείο.so
) - Δημιουργήστε μια εντολή - Δημιουργήστε μια εντολή για να καλέσετε το πρόγραμμα
- Επεξεργαστείτε και επικυρώστε - Επεξεργαστείτε την εντολή χρησιμοποιώντας το Mollusk και επικυρώστε το αποτέλεσμα
#[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()]);}}
Για να εκτελέσετε τη δοκιμή, τρέξτε cargo test
.
Όταν η δοκιμή εκτελεστεί επιτυχώς, θα δείτε έξοδο παρόμοια με την ακόλουθη:
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
Η δομή
Mollusk
παρέχει μια απλή διεπαφή για τη δοκιμή προγραμμάτων Solana. Όλα τα πεδία μπορούν
να τροποποιηθούν μέσω μερικών βοηθητικών μεθόδων, αλλά οι χρήστες μπορούν επίσης
να έχουν άμεση πρόσβαση και να τα τροποποιήσουν αν επιθυμούν περισσότερο έλεγχο.
Για να αρχικοποιήσετε το Mollusk με μια προεπιλεγμένη παρουσία, χρησιμοποιήστε
τη μέθοδο Mollusk::default
.
// Default instance with no custom programslet mollusk = Mollusk::default();
Για να αρχικοποιήσετε το Mollusk με ένα συγκεκριμένο πρόγραμμα, χρησιμοποιήστε
τη μέθοδο 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");
Για να προσθέσετε ένα πρόγραμμα στο Mollusk, χρησιμοποιήστε τη μέθοδο
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(),);
Όταν παρέχετε τη διαδρομή αρχείου, μην συμπεριλάβετε την επέκταση .so
. Για
παράδειγμα, το "path/to/my_program"
είναι σωστό, αλλά το
"path/to/my_program.so"
δεν είναι.
Επεξεργασία εντολών
Το Mollusk παρέχει τέσσερις κύριες μεθόδους για την επεξεργασία εντολών:
Μέθοδος | Περιγραφή |
---|---|
process_instruction | Επεξεργάζεται μια εντολή και επιστρέφει το αποτέλεσμα. |
process_and_validate_instruction | Επεξεργάζεται μια εντολή και εκτελεί μια σειρά ελέγχων στο αποτέλεσμα, προκαλώντας πανικό αν αποτύχει κάποιος έλεγχος. |
process_instruction_chain | Επεξεργάζεται πολλαπλές εντολές και επιστρέφει το αποτέλεσμα. |
process_and_validate_instruction_chain | Επεξεργάζεται πολλαπλές εντολές και εκτελεί μια σειρά ελέγχων σε κάθε αποτέλεσμα, προκαλώντας πανικό αν αποτύχει κάποιος έλεγχος. |
Το
InstructionResult
περιέχει τις λεπτομέρειες μιας επεξεργασμένης εντολής.
Μονή εντολή
Χρησιμοποιήστε τη μέθοδο process_instruction
για να επεξεργαστείτε μια μονή
εντολή χωρίς ελέγχους στο αποτέλεσμα. Μπορείτε να επικυρώσετε χειροκίνητα τα
αποτελέσματα μετά την επεξεργασία.
pub fn process_instruction(&self,instruction: &Instruction,accounts: &[(Pubkey, Account)],) -> InstructionResult
Το ακόλουθο παράδειγμα επεξεργάζεται μια εντολή μεταφοράς SOL χωρίς ελέγχους επικύρωσης.
Τα παρακάτω παραδείγματα εκτελούν το Mollusk στη συνάρτηση main
για λόγους
επίδειξης. Στην πράξη, συνήθως θα χρησιμοποιείτε το Mollusk σε μια ενότητα
δοκιμών που επισημαίνεται με το χαρακτηριστικό #[test]
.
use {mollusk_svm::Mollusk,solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},};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 = system_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);}
Μονή εντολή με ελέγχους
Χρησιμοποιήστε τη μέθοδο process_and_validate_instruction
για την επεξεργασία
μιας μεμονωμένης εντολής με ελέγχους επικύρωσης. Αυτή η μέθοδος θα διακόψει τη
λειτουργία αν αποτύχει οποιοσδήποτε έλεγχος.
pub fn process_and_validate_instruction(&self,instruction: &Instruction,accounts: &[(Pubkey, Account)],checks: &[Check],) -> InstructionResult
Το ακόλουθο παράδειγμα επεξεργάζεται μια εντολή μεταφοράς SOL με ελέγχους επικύρωσης.
use {mollusk_svm::{Mollusk, result::Check},solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},};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 = system_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);}
Πολλαπλές εντολές
Χρησιμοποιήστε τη μέθοδο process_instruction_chain
για την επεξεργασία
πολλαπλών εντολών διαδοχικά χωρίς ελέγχους επικύρωσης.
pub fn process_instruction_chain(&self,instructions: &[Instruction],accounts: &[(Pubkey, Account)],) -> InstructionResult
Το ακόλουθο παράδειγμα επεξεργάζεται δύο εντολές μεταφοράς SOL χωρίς ελέγχους επικύρωσης.
use {mollusk_svm::Mollusk,solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},};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![system_instruction::transfer(&alice, &bob, 300_000), // Alice -> Bobsystem_instruction::transfer(&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);}
Πολλαπλές εντολές με ελέγχους
Χρησιμοποιήστε τη μέθοδο process_and_validate_instruction_chain
για την
επεξεργασία πολλαπλών εντολών με ελέγχους επικύρωσης μετά από κάθε εντολή. Κάθε
εντολή έχει το δικό της σύνολο ελέγχων που πρέπει να περάσουν.
pub fn process_and_validate_instruction_chain(&self,instructions: &[(&Instruction, &[Check])],accounts: &[(Pubkey, Account)],) -> InstructionResult
Το ακόλουθο παράδειγμα επεξεργάζεται μια αλυσίδα δύο εντολών μεταφοράς SOL με ελέγχους επικύρωσης μετά από κάθε εντολή.
use {mollusk_svm::{result::Check, Mollusk},solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},};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 = system_instruction::transfer(&alice, &bob, 300_000);let transfer2 = system_instruction::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);}
Έλεγχοι Επικύρωσης
Το Mollusk παρέχει ένα σύνολο από βοηθητικές μεθόδους για τον έλεγχο των αποτελεσμάτων μιας επεξεργασμένης εντολής.
use mollusk_svm::result::Check;
Χρησιμοποιήστε τις ακόλουθες μεθόδους για την επικύρωση των αποτελεσμάτων εντολών:
// 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])
Χρησιμοποιήστε τα ακόλουθα για την επικύρωση των καταστάσεων Λογαριασμών:
// 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()
Μόνιμη Κατάσταση Λογαριασμού
Το
MolluskContext
είναι ένα περίβλημα γύρω από το Mollusk
που διατηρεί την κατάσταση του
λογαριασμού σε πολλαπλές κλήσεις εντολών μέσω του account_store
. Οι μέθοδοι
για την επεξεργασία εντολών είναι πανομοιότυπες με το Mollusk
.
Σε αντίθεση με το Mollusk
, που απαιτεί την παράδοση του accounts
σε κάθε
μέθοδο (π.χ. process_instruction
), το MolluskContext
διαχειρίζεται τους
λογαριασμούς εσωτερικά μέσω του account_store
. Αυτό εξαλείφει την ανάγκη για
την παράμετρο accounts
κατά την επεξεργασία εντολών.
Δημιουργήστε ένα account_store
χρησιμοποιώντας τη μέθοδο with_context
:
use std::collections::HashMap;use solana_sdk::{account::Account, pubkey::Pubkey, system_program};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);
Το ακόλουθο παράδειγμα επεξεργάζεται δύο ξεχωριστές εντολές μεταφοράς SOL με
μόνιμη κατάσταση λογαριασμού μεταξύ των εντολών μέσω του account_store
.
use {mollusk_svm::Mollusk,solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},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 = system_instruction::transfer(&sender, &recipient, 200_000);context.process_instruction(&instruction1);// Second transfer: 100,000 lamports (state persists from first transfer)let instruction2 = system_instruction::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);}
Sysvars του Mollusk
Το Mollusk παρέχει μια προσαρμοσμένη
Sysvars
δομή για την τροποποίηση των τιμών της για δοκιμές.
Χρησιμοποιήστε τη μέθοδο warp_to_slot
για να ενημερώσετε το ρολόι sysvar ώστε
να προσομοιώσετε την κίνηση προς τα εμπρός ή προς τα πίσω στο χρόνο σε ένα
συγκεκριμένο slot.
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);}
Το ακόλουθο παράδειγμα δείχνει πώς να τροποποιήσετε το sysvar του Mollusk
απευθείας προσπελαύνοντας το πεδίο sysvars
για να αλλάξετε τις παραμέτρους του
rent. Μπορείτε να τροποποιήσετε άλλες τιμές sysvar με τον ίδιο τρόπο.
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));}
Συγκριτική αξιολόγηση μονάδων υπολογισμού
Το
MolluskComputeUnitBencher
παρακολουθεί τη χρήση μονάδων υπολογισμού των εντολών ενός προγράμματος. Τα
αποτελέσματα καταγράφονται σε αρχείο markdown.
Απαιτεί το
mollusk-svm-bencher
ως εξάρτηση.
Το παρακάτω παράδειγμα συγκρίνει τη χρήση μονάδων υπολογισμού μιας εντολής μεταφοράς SOL.
use {mollusk_svm::Mollusk,mollusk_svm_bencher::MolluskComputeUnitBencher,solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},};fn main() {// Initialize Mollusklet mollusk = Mollusk::default();// Create test accountslet sender = Pubkey::new_unique();let receiver = Pubkey::new_unique();// Transfer instructionlet transfer = system_instruction::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();}
Τα αποτελέσματα της συγκριτικής αξιολόγησης καταγράφονται στο καθορισμένο
out_dir
ως αρχείο markdown με όνομα compute_units.md
.
#### 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
Χρησιμοποιήστε το
mollusk-svm-programs-token
crate για να προσθέσετε το token program, το token2022 program (token
extensions) και το associated token program στο Mollusk για δοκιμές.
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);
Το παρακάτω παράδειγμα δείχνει τη δοκιμή μιας μεταφοράς token χρησιμοποιώντας το Mollusk.
Το παρακάτω παράδειγμα ορίζει χειροκίνητα τους λογαριασμούς δοκιμής για λόγους
επίδειξης. Το mollusk-svm-programs-token
περιλαμβάνει επίσης βοηθητικές
συναρτήσεις για τη δημιουργία των λογαριασμών mint και token account.
use {mollusk_svm::{result::Check, Mollusk},mollusk_svm_programs_token::token,solana_sdk::{account::Account, program_pack::Pack, pubkey::Pubkey},spl_token::{instruction::transfer_checked,state::{Account as TokenAccount, 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: spl_token::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: spl_token::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?