Documentazione SolanaSviluppo di programmiProgrammi di test

Mollusk

Mollusk è un test harness leggero per testare i programmi Solana. Fornisce un'interfaccia semplice per testare le istruzioni dei programmi Solana in un ambiente Solana Virtual Machine (SVM) minimizzato. Tutti gli account di test devono essere definiti esplicitamente, garantendo test deterministici e ripetibili.

Installazione

Aggiungi mollusk-svm come dipendenza in Cargo.toml:

Terminal
$
cargo add mollusk-svm --dev
Cargo.toml
[dev-dependencies]
mollusk-svm = "0.7"

Per effettuare il benchmark dell'utilizzo delle compute unit, aggiungi mollusk-svm-bencher come dipendenza in Cargo.toml:

Terminal
$
cargo add mollusk-svm-bencher --dev
Cargo.toml
[dev-dependencies]
mollusk-svm-bencher = "0.7"

Per utilizzare il Token Program, il token2022 program (Token Extensions) e l'Associated Token Program per i test con Mollusk, aggiungi mollusk-svm-programs-token come dipendenza in Cargo.toml:

Terminal
$
cargo add mollusk-svm-programs-token --dev
Cargo.toml
[dev-dependencies]
mollusk-svm-programs-token = "0.7"

Mollusk SVM

L'esempio seguente mostra una configurazione minima per testare un programma Solana di base utilizzando Mollusk.

Programma Hello World

Questo esempio dimostra come testare un programma Solana di base utilizzando Mollusk. Il programma stampa semplicemente "Hello, world!" nei log del programma quando viene invocato.

Eseguendo cargo build-sbf viene generato il programma compilato in /target/deploy/<program_name>.so.

src/lib.rs
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()]);
}
}

Per testare un programma Solana con Mollusk:

  1. Crea un'istanza Mollusk - Inizializza Mollusk con un program ID e il percorso del programma compilato (file .so)
  2. Costruisci un'istruzione - Crea un'istruzione per invocare il programma
  3. Elabora e valida - Elabora l'istruzione utilizzando Mollusk e valida il risultato
src/lib.rs
#[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()]);
}
}

Per eseguire il test, esegui cargo test.

Quando il test viene eseguito con successo, vedrai un output simile al seguente:

Terminal
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 success
test tests::test_hello_world ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
Doc-tests hello_world

Programma Hello World

Questo esempio dimostra come testare un programma Solana di base utilizzando Mollusk. Il programma stampa semplicemente "Hello, world!" nei log del programma quando viene invocato.

Eseguendo cargo build-sbf viene generato il programma compilato in /target/deploy/<program_name>.so.

Per testare un programma Solana con Mollusk:

  1. Crea un'istanza Mollusk - Inizializza Mollusk con un program ID e il percorso del programma compilato (file .so)
  2. Costruisci un'istruzione - Crea un'istruzione per invocare il programma
  3. Elabora e valida - Elabora l'istruzione utilizzando Mollusk e valida il risultato
src/lib.rs
#[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()]);
}
}

Per eseguire il test, esegui cargo test.

Quando il test viene eseguito con successo, vedrai un output simile al seguente:

Terminal
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 success
test tests::test_hello_world ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
Doc-tests hello_world
lib.rs
hello_world.so
Cargo.toml
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()]);
}
}

La struct Mollusk fornisce un'interfaccia semplice per testare i programmi Solana. Tutti i campi possono essere manipolati tramite una manciata di metodi helper, ma gli utenti possono anche accedere e modificarli direttamente se desiderano un maggiore controllo.

Per inizializzare Mollusk con un'istanza predefinita usa il metodo Mollusk::default.

Example
// Default instance with no custom programs
let mollusk = Mollusk::default();

Per inizializzare Mollusk con un programma specifico usa il metodo Mollusk::new.

Example
// Initialize Mollusk with a specific program from a file path
let program_id = Pubkey::new_unique();
let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");

Per aggiungere un programma a Mollusk usa il metodo Mollusk::add_program.

Example
let mollusk = Mollusk::default();
let program_id = Pubkey::new_unique();
// Add a program to Mollusk
mollusk.add_program(
&program_id,
"target/deploy/my_program",
&bpf_loader_upgradeable::id(),
);

Quando fornisci il percorso del file, non includere l'estensione .so. Ad esempio, "path/to/my_program" è corretto, ma "path/to/my_program.so" non lo è.

Elaborazione delle istruzioni

Mollusk fornisce quattro metodi principali per elaborare le istruzioni:

MetodoDescrizione
process_instructionElabora un'istruzione e restituisce il risultato.
process_and_validate_instructionElabora un'istruzione ed esegue una serie di controlli sul risultato, generando un panic se qualche controllo fallisce.
process_instruction_chainElabora più istruzioni e restituisce il risultato.
process_and_validate_instruction_chainElabora più istruzioni ed esegue una serie di controlli su ogni risultato, generando un panic se qualche controllo fallisce.

La struct InstructionResult contiene i dettagli di un'istruzione elaborata.

Singola istruzione

Utilizza il metodo process_instruction per elaborare una singola istruzione senza controlli sul risultato. Puoi validare manualmente i risultati dopo l'elaborazione.

Method Signature
pub fn process_instruction(
&self,
instruction: &Instruction,
accounts: &[(Pubkey, Account)],
) -> InstructionResult

L'esempio seguente elabora un'istruzione di trasferimento SOL senza controlli di validazione.

Gli esempi seguenti eseguono Mollusk nella funzione main a scopo dimostrativo. In pratica, utilizzerai tipicamente Mollusk in un modulo di test annotato con l'attributo #[test].

Single Instruction
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 Mollusk
let mollusk = Mollusk::default();
// Set up accounts
let sender = Pubkey::new_unique();
let recipient = Pubkey::new_unique();
let initial_lamports = 1_000_000;
let transfer_amount = 250_000;
// Create transfer instruction
let instruction = transfer(&sender, &recipient, transfer_amount);
// Define initial account states
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,
},
),
];
// Process the instruction
let result = mollusk.process_instruction(&instruction, &accounts);
println!("{:#?}", result);
// Check the result
assert!(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);
}
Console
Click to execute the code.

Singola istruzione con controlli

Utilizza il metodo process_and_validate_instruction per elaborare una singola istruzione con controlli di validazione. Questo metodo andrà in panic se qualsiasi controllo fallisce.

Method Signature
pub fn process_and_validate_instruction(
&self,
instruction: &Instruction,
accounts: &[(Pubkey, Account)],
checks: &[Check],
) -> InstructionResult

L'esempio seguente elabora un'istruzione di trasferimento SOL con controlli di validazione.

With Validation
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 checks
let 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);
}
Console
Click to execute the code.

Istruzioni multiple

Utilizza il metodo process_instruction_chain per elaborare più istruzioni sequenzialmente senza controlli di validazione.

Method Signature
pub fn process_instruction_chain(
&self,
instructions: &[Instruction],
accounts: &[(Pubkey, Account)],
) -> InstructionResult

L'esempio seguente elabora due istruzioni di trasferimento SOL senza controlli di validazione.

Multiple Instructions
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 accounts
let alice = Pubkey::new_unique();
let bob = Pubkey::new_unique();
let charlie = Pubkey::new_unique();
let initial_lamports = 1_000_000;
// Create chain of transfers
let instructions = vec![
transfer(&alice, &bob, 300_000), // Alice -> Bob
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 chain
let result = mollusk.process_instruction_chain(&instructions, &accounts);
println!("{:#?}", result);
// Final balances: Alice=700K, Bob=200K, Charlie=100K
assert_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);
}
Console
Click to execute the code.

Istruzioni multiple con controlli

Utilizza il metodo process_and_validate_instruction_chain per elaborare più istruzioni con controlli di validazione dopo ogni istruzione. Ogni istruzione ha il proprio set di controlli che devono essere superati.

Method Signature
pub fn process_and_validate_instruction_chain(
&self,
instructions: &[(&Instruction, &[Check])],
accounts: &[(Pubkey, Account)],
) -> InstructionResult

L'esempio seguente elabora una catena di due istruzioni di trasferimento SOL con controlli di validazione dopo ogni istruzione.

With Validation
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 accounts
let alice = Pubkey::new_unique();
let bob = Pubkey::new_unique();
let charlie = Pubkey::new_unique();
let initial_lamports = 1_000_000;
// Create transfer instructions
let transfer1 = transfer(&alice, &bob, 300_000);
let transfer2 = transfer(&bob, &charlie, 100_000);
// Initial accounts
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,
},
),
];
// Define checks for each instruction
let 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 step
let 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);
}
Console
Click to execute the code.

Controlli di validazione

Mollusk fornisce un set di metodi helper per verificare i risultati di un'istruzione elaborata.

Example
use mollusk_svm::result::Check;

Utilizza i seguenti metodi per validare i risultati delle istruzioni:

Example
// Program execution succeeded
Check::success()
// Program returned specific error
Check::err(ProgramError::InvalidArgument)
// Instruction level error
Check::instruction_err(InstructionError::InsufficientFunds)
// Check with specific program result
Check::program_result(ProgramResult::Success)
// Compute units consumed
Check::compute_units(1000)
// Execution time
Check::time(100)
// Return data from instruction execution
Check::return_data(&[1, 2, 3, 4])

Utilizza quanto segue per validare gli stati degli account:

Example
// Single account validation
Check::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 exempt
Check::all_rent_exempt()

Stato persistente degli account

Il MolluskContext è un wrapper attorno a Mollusk che mantiene lo stato degli account attraverso molteplici chiamate di istruzioni tramite il suo account_store. I metodi per elaborare le istruzioni sono identici a Mollusk.

A differenza di Mollusk, che richiede di passare accounts a ogni metodo (ad es. process_instruction), MolluskContext gestisce gli account internamente tramite il suo account_store. Questo elimina la necessità del parametro accounts durante l'elaborazione delle istruzioni.

Crea un account_store utilizzando il metodo with_context:

Example
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);

L'esempio seguente elabora due istruzioni di trasferimento SOL separate con stato persistente degli account tra le istruzioni tramite l'account_store.

Stateful Testing
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 Mollusk
let mollusk = Mollusk::default();
// Create accounts
let sender = Pubkey::new_unique();
let recipient = Pubkey::new_unique();
// Create account store with initial balances
let 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 context
let context = mollusk.with_context(account_store);
// First transfer: 200,000 lamports
let 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 balances
let 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);
}
Console
Click to execute the code.

Sysvars di Mollusk

Mollusk fornisce una struct personalizzata Sysvars per modificare i suoi valori per i test.

Utilizza il metodo warp_to_slot per aggiornare il clock sysvar per simulare il movimento avanti o indietro nel tempo verso uno slot specifico.

Warp to Slot
use mollusk_svm::Mollusk;
fn main() {
// Initialize Mollusk
let mut mollusk = Mollusk::default();
// Show initial slot
println!("Initial slot: {}", mollusk.sysvars.clock.slot);
// Warp to slot 1000
mollusk.warp_to_slot(100);
println!("After warp: {}", mollusk.sysvars.clock.slot);
// Warp to slot 10
mollusk.warp_to_slot(10);
println!("After second warp: {}", mollusk.sysvars.clock.slot);
}
Console
Click to execute the code.

L'esempio seguente mostra come modificare direttamente la sysvar di Mollusk accedendo al campo sysvars per cambiare i parametri di rent. Puoi modificare altri valori sysvar allo stesso modo.

Modify Sysvars
use {mollusk_svm::Mollusk, solana_sdk::rent::Rent};
fn main() {
let mut mollusk = Mollusk::default();
// Show default rent
println!(
"Default rent exemption for 1000 bytes: {} lamports",
mollusk.sysvars.rent.minimum_balance(1000)
);
// Customize rent parameters
mollusk.sysvars.rent = Rent {
lamports_per_byte_year: 1,
exemption_threshold: 1.0,
burn_percent: 0,
};
// Show custom rent
println!(
"Custom rent exemption for 1000 bytes: {} lamports",
mollusk.sysvars.rent.minimum_balance(1000)
);
}
Console
Click to execute the code.

Benchmarking delle compute unit

MolluskComputeUnitBencher traccia l'utilizzo delle compute unit delle istruzioni di un programma. I risultati vengono scritti in un file markdown.

Richiede mollusk-svm-bencher come dipendenza.

L'esempio seguente esegue il benchmark dell'utilizzo delle compute unit di un'istruzione di trasferimento SOL.

Benchmark Compute Units
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 Mollusk
let mollusk = Mollusk::default();
// Create test accounts
let sender = Pubkey::new_unique();
let receiver = Pubkey::new_unique();
// Transfer instruction
let 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 benchmark
MolluskComputeUnitBencher::new(mollusk)
.bench(("transfer", &transfer, &accounts))
.must_pass(true)
.out_dir("./target/benches")
.execute();
}

I risultati del benchmark vengono scritti nel out_dir specificato come file markdown denominato compute_units.md.

comput_units.md
#### 2025-09-19 22:28:53.691839 UTC
Solana CLI Version: solana-cli 2.2.20 (src:dabc99a5; feat:3073396398,
client:Agave)
| Name | CUs | Delta |
| -------- | --- | ------- |
| transfer | 150 | - new - |

Testing del Token Program

Utilizza il crate mollusk-svm-programs-token per aggiungere il Token Program, il token2022 program (Token Extensions) e l'Associated Token Program a Mollusk per i test.

Example
use {
mollusk_svm::Mollusk,
mollusk_svm_programs_token::{associated_token, token, token2022},
};
let mut mollusk = Mollusk::default();
// Add SPL Token Program
token::add_program(&mut mollusk);
// Add SPL Token-2022 Program
token2022::add_program(&mut mollusk);
// Add Associated Token Account Program
associated_token::add_program(&mut mollusk);

L'esempio seguente dimostra il testing di un trasferimento di token utilizzando Mollusk.

L'esempio seguente definisce manualmente gli account di test a scopo dimostrativo. Il mollusk-svm-programs-token include anche funzioni helper per creare i mint e i token account.

Token Transfer Example
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 program
let mut mollusk = Mollusk::default();
token::add_program(&mut mollusk);
// Create account keys
let mint = Pubkey::new_unique();
let source = Pubkey::new_unique();
let destination = Pubkey::new_unique();
let authority = Pubkey::new_unique();
// Token configuration
let decimals = 6;
let transfer_amount = 1_000_000; // 1 token with 6 decimals
let initial_balance = 10_000_000; // 10 tokens
// Calculate rent-exempt minimums
let mint_rent = mollusk.sysvars.rent.minimum_balance(Mint::LEN);
let account_rent = mollusk.sysvars.rent.minimum_balance(TokenAccount::LEN);
// Create mint account
let 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 account
let 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 account
let 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_checked
let 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 instruction
let instruction = transfer_checked(
&token::ID,
&source,
&mint,
&destination,
&authority,
&[],
transfer_amount,
decimals,
)
.unwrap();
// Expected balances after transfer
let expected_source_balance = (initial_balance - transfer_amount).to_le_bytes();
let expected_dest_balance = transfer_amount.to_le_bytes();
// Define validation checks
let 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 instruction
let result = mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);
println!("{:#?}", result);
// Deserialize token account data
let 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);
}
Console
Click to execute the code.

Is this page helpful?

Gestito da

© 2026 Solana Foundation.
Tutti i diritti riservati.
Rimani Connesso