Documentação SolanaDesenvolvendo programasProgramas de teste

Mollusk

Mollusk é um framework leve para testar programas Solana. Ele fornece uma interface simples para testar instruções de programas Solana em um ambiente minimizado da Máquina Virtual Solana (SVM). Todas as contas de teste devem ser explicitamente definidas, garantindo testes determinísticos e reproduzíveis.

Instalação

Adicione mollusk-svm como dependência no Cargo.toml:

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

Para benchmarking do uso de unidades de computação, adicione mollusk-svm-bencher como dependência no Cargo.toml:

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

Para usar o Token Program, Token2022 Program (Token Extensions) e Associated Token Program para testes com Mollusk, adicione mollusk-svm-programs-token como dependência no Cargo.toml:

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

Mollusk SVM

O exemplo a seguir mostra uma configuração mínima para testar um programa Solana básico usando Mollusk.

Programa Hello World

Este exemplo demonstra como testar um programa Solana básico usando Mollusk. O programa simplesmente imprime "Hello, world!" nos logs do programa quando invocado.

Executar cargo build-sbf gera o programa compilado em /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()]);
}
}

Para testar um programa Solana com Mollusk:

  1. Crie uma instância Mollusk - Inicialize o Mollusk com um ID de programa e o caminho para o programa compilado (arquivo .so)
  2. Construa uma instrução - Crie uma instrução para invocar o programa
  3. Processe e valide - Processe a instrução usando Mollusk e valide o resultado
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()]);
}
}

Para executar o teste, execute cargo test.

Quando o teste for executado com sucesso, você verá uma saída semelhante à seguinte:

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

Programa Hello World

Este exemplo demonstra como testar um programa Solana básico usando Mollusk. O programa simplesmente imprime "Hello, world!" nos logs do programa quando invocado.

Executar cargo build-sbf gera o programa compilado em /target/deploy/<program_name>.so.

Para testar um programa Solana com Mollusk:

  1. Crie uma instância Mollusk - Inicialize o Mollusk com um ID de programa e o caminho para o programa compilado (arquivo .so)
  2. Construa uma instrução - Crie uma instrução para invocar o programa
  3. Processe e valide - Processe a instrução usando Mollusk e valide o resultado
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()]);
}
}

Para executar o teste, execute cargo test.

Quando o teste for executado com sucesso, você verá uma saída semelhante à seguinte:

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()]);
}
}

A estrutura Mollusk fornece uma interface simples para testar programas Solana. Todos os campos podem ser manipulados através de alguns métodos auxiliares, mas os usuários também podem acessar e modificá-los diretamente se desejarem mais controle.

Para inicializar o Mollusk com uma instância padrão, use o método Mollusk::default.

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

Para inicializar o Mollusk com um programa específico, use o método 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");

Para adicionar um programa ao Mollusk, use o método 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(),
);

Ao fornecer o caminho do arquivo, não inclua a extensão .so. Por exemplo, "path/to/my_program" está correto, mas "path/to/my_program.so" não está.

Processando instruções

O Mollusk fornece quatro métodos principais para processar instruções:

MétodoDescrição
process_instructionProcessa uma instrução e retorna o resultado.
process_and_validate_instructionProcessa uma instrução e realiza uma série de verificações no resultado, gerando pânico se alguma verificação falhar.
process_instruction_chainProcessa múltiplas instruções e retorna o resultado.
process_and_validate_instruction_chainProcessa múltiplas instruções e realiza uma série de verificações em cada resultado, gerando pânico se alguma verificação falhar.

O InstructionResult contém os detalhes de uma instrução processada.

Instrução única

Use o método process_instruction para processar uma única instrução sem verificações no resultado. Você pode validar manualmente os resultados após o processamento.

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

O exemplo a seguir processa uma instrução de transferência de SOL sem verificações de validação.

Os exemplos abaixo executam o Mollusk na função main para fins de demonstração. Na prática, você normalmente usará o Mollusk em um módulo de teste anotado com o atributo #[test].

Single Instruction
use {
mollusk_svm::Mollusk,
solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},
};
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 = system_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.

Instrução única com verificações

Use o método process_and_validate_instruction para processar uma única instrução com verificações de validação. Este método entrará em pânico se qualquer verificação falhar.

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

O exemplo a seguir processa uma instrução de transferência de SOL com verificações de validação.

With Validation
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 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.

Múltiplas instruções

Use o método process_instruction_chain para processar múltiplas instruções sequencialmente sem verificações de validação.

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

O exemplo a seguir processa duas instruções de transferência de SOL sem verificações de validação.

Multiple Instructions
use {
mollusk_svm::Mollusk,
solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},
};
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![
system_instruction::transfer(&alice, &bob, 300_000), // Alice -> Bob
system_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 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.

Múltiplas instruções com verificações

Use o método process_and_validate_instruction_chain para processar múltiplas instruções com verificações de validação após cada instrução. Cada instrução tem seu próprio conjunto de verificações que devem ser aprovadas.

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

O exemplo a seguir processa uma cadeia de duas instruções de transferência de SOL com verificações de validação após cada instrução.

With Validation
use {
mollusk_svm::{result::Check, Mollusk},
solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},
};
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 = system_instruction::transfer(&alice, &bob, 300_000);
let transfer2 = system_instruction::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.

Verificações de validação

O Mollusk fornece um conjunto de métodos auxiliares para verificar os resultados de uma instrução processada.

Example
use mollusk_svm::result::Check;

Use os seguintes métodos para validar os resultados da instrução:

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])

Use o seguinte para validar estados de conta:

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()

Estado persistente da conta

O MolluskContext é um wrapper em torno do Mollusk que mantém o estado da conta em várias chamadas de instrução através do seu account_store. Os métodos para processar instruções são idênticos ao Mollusk.

Ao contrário do Mollusk, que requer passar accounts para cada método (por exemplo process_instruction), o MolluskContext gerencia contas internamente através do seu account_store. Isso elimina a necessidade do parâmetro accounts ao processar instruções.

Crie um account_store usando o método with_context:

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

O exemplo a seguir processa duas instruções separadas de transferência de SOL com estado de conta persistente entre as instruções através do account_store.

Stateful Testing
use {
mollusk_svm::Mollusk,
solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},
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 = 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 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 do Mollusk

O Mollusk fornece uma estrutura personalizada Sysvars para modificar seus valores para testes.

Use o método warp_to_slot para atualizar o relógio sysvar para simular o avanço ou retrocesso no tempo para um slot específico.

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.

O exemplo a seguir mostra como modificar o sysvar do Mollusk diretamente acessando o campo sysvars para alterar os parâmetros de rent. Você pode modificar outros valores de sysvar da mesma maneira.

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 de unidades de computação

MolluskComputeUnitBencher rastreia o uso de unidades de computação das instruções de um programa. Os resultados são escritos em um arquivo markdown.

Requer mollusk-svm-bencher como dependência.

O exemplo a seguir faz o benchmarking do uso de unidades de computação de uma instrução de transferência de SOL.

Benchmark Compute Units
use {
mollusk_svm::Mollusk,
mollusk_svm_bencher::MolluskComputeUnitBencher,
solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},
};
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 = 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 benchmark
MolluskComputeUnitBencher::new(mollusk)
.bench(("transfer", &transfer, &accounts))
.must_pass(true)
.out_dir("./target/benches")
.execute();
}

Os resultados do benchmark são escritos no out_dir especificado como um arquivo markdown denominado 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 - |

Testes do Token Program

Use o crate mollusk-svm-programs-token para adicionar o Token Program, o programa token2022 (Token Extensions) e o Associated Token Program ao Mollusk para testes.

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

O exemplo a seguir demonstra o teste de uma transferência de token usando o Mollusk.

O exemplo abaixo define manualmente as contas de teste para fins de demonstração. O mollusk-svm-programs-token também inclui funções auxiliares para criar as contas de mint e token.

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::{
instruction::transfer_checked,
state::{Account as TokenAccount, 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: spl_token::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: spl_token::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?