솔라나 문서프로그램 개발하기테스트 프로그램

Mollusk

Mollusk는 Solana 프로그램을 테스트하기 위한 경량 테스트 하네스입니다. 간소화된 Solana 가상 머신(SVM) 환경에서 Solana 프로그램 명령어를 테스트하기 위한 간단한 인터페이스를 제공합니다. 모든 테스트 계정은 명시적으로 정의되어야 하므로 결정적이고 반복 가능한 테스트를 보장합니다.

설치

mollusk-svmCargo.toml에 의존성으로 추가하세요:

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

컴퓨팅 유닛 사용량을 벤치마크하려면 mollusk-svm-bencherCargo.toml에 의존성으로 추가하세요:

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

Mollusk로 테스트할 때 Token Program, Token2022 Program(Token Extensions) 및 Associated Token Program을 사용하려면 mollusk-svm-programs-tokenCargo.toml에 의존성으로 추가하세요:

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

Mollusk SVM

다음 예제는 Mollusk를 사용하여 기본 Solana 프로그램을 테스트하기 위한 최소한의 설정을 보여줍니다.

Hello World 프로그램

이 예제는 Mollusk를 사용하여 기본 Solana 프로그램을 테스트하는 방법을 보여줍니다. 이 프로그램은 호출될 때 프로그램 로그에 "Hello, world!"를 출력합니다.

cargo build-sbf를 실행하면 /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()]);
}
}

Mollusk로 Solana 프로그램을 테스트하려면:

  1. Mollusk 인스턴스 생성 - 프로그램 ID와 컴파일된 프로그램 경로(.so 파일)로 Mollusk 초기화
  2. 명령어 구성 - 프로그램을 호출하기 위한 명령어 생성
  3. 처리 및 검증 - Mollusk를 사용하여 명령어를 처리하고 결과 검증
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()]);
}
}

테스트를 실행하려면 cargo test를 실행하세요.

테스트가 성공적으로 실행되면 다음과 유사한 출력이 표시됩니다:

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

Hello World 프로그램

이 예제는 Mollusk를 사용하여 기본 Solana 프로그램을 테스트하는 방법을 보여줍니다. 이 프로그램은 호출될 때 프로그램 로그에 "Hello, world!"를 출력합니다.

cargo build-sbf를 실행하면 /target/deploy/<program_name>.so에 컴파일된 프로그램이 생성됩니다.

Mollusk로 Solana 프로그램을 테스트하려면:

  1. Mollusk 인스턴스 생성 - 프로그램 ID와 컴파일된 프로그램 경로(.so 파일)로 Mollusk 초기화
  2. 명령어 구성 - 프로그램을 호출하기 위한 명령어 생성
  3. 처리 및 검증 - Mollusk를 사용하여 명령어를 처리하고 결과 검증
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()]);
}
}

테스트를 실행하려면 cargo test를 실행하세요.

테스트가 성공적으로 실행되면 다음과 유사한 출력이 표시됩니다:

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

Mollusk 구조체는 Solana 프로그램을 테스트하기 위한 간단한 인터페이스를 제공합니다. 모든 필드는 몇 가지 헬퍼 메서드를 통해 조작할 수 있지만, 사용자가 더 많은 제어를 원할 경우 직접 접근하고 수정할 수도 있습니다.

기본 인스턴스로 Mollusk를 초기화하려면 Mollusk::default 메서드를 사용하세요.

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

특정 프로그램으로 Mollusk를 초기화하려면 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");

Mollusk에 프로그램을 추가하려면 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(),
);

파일 경로를 제공할 때 .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 메서드를 사용하세요. 처리 후 수동으로 결과를 검증할 수 있습니다.

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

다음 예제는 검증 검사 없이 SOL 전송 명령어를 처리합니다.

아래 예제는 시연 목적으로 main 함수에서 Mollusk를 실행합니다. 실제로는 일반적으로 #[test] 속성으로 주석이 달린 테스트 모듈에서 Mollusk를 사용하게 됩니다.

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.

검증이 포함된 단일 명령어

process_and_validate_instruction 메서드를 사용하여 검증 확인이 포함된 단일 명령어를 처리합니다. 이 메서드는 검증에 실패하면 패닉을 발생시킵니다.

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

다음 예제는 검증 확인이 포함된 SOL 전송 명령어를 처리합니다.

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.

다중 명령어

process_instruction_chain 메서드를 사용하여 검증 확인 없이 여러 명령어를 순차적으로 처리합니다.

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

다음 예제는 검증 확인 없이 두 개의 SOL 전송 명령어를 처리합니다.

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.

검증이 포함된 다중 명령어

process_and_validate_instruction_chain 메서드를 사용하여 각 명령어 후 검증 확인이 포함된 여러 명령어를 처리합니다. 각 명령어에는 통과해야 하는 자체 검증 세트가 있습니다.

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

다음 예제는 각 명령어 후 검증 확인이 포함된 두 개의 SOL 전송 명령어 체인을 처리합니다.

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.

유효성 검사

Mollusk는 처리된 명령어의 결과를 확인하기 위한 헬퍼 메소드 세트를 제공합니다.

Example
use mollusk_svm::result::Check;

다음 메소드를 사용하여 명령어 결과의 유효성을 검사하세요:

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

다음을 사용하여 계정 상태의 유효성을 검사하세요:

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

영구적인 계정 상태

MolluskContextMollusk를 감싸는 래퍼로, account_store를 통해 여러 명령어 호출 간에 계정 상태를 유지합니다. 명령어를 처리하는 메소드는 Mollusk와 동일합니다.

각 메소드에 accounts를 전달해야 하는 Mollusk(예: process_instruction)와 달리, MolluskContextaccount_store를 통해 내부적으로 계정을 관리합니다. 이로 인해 명령어를 처리할 때 accounts 매개변수가 필요하지 않습니다.

with_context 메소드를 사용하여 account_store를 생성하세요:

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

다음 예제는 account_store를 통해 명령어 간에 영구적인 계정 상태를 유지하면서 두 개의 별도 SOL 전송 명령어를 처리합니다.

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.

Mollusk 시스템 변수

Mollusk는 테스트를 위해 값을 수정할 수 있는 사용자 정의 Sysvars 구조체를 제공합니다.

warp_to_slot 메소드를 사용하여 시스템 변수 시계를 업데이트하여 특정 slot으로 시간을 앞으로 또는 뒤로 이동하는 시뮬레이션을 할 수 있습니다.

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.

다음 예제는 sysvars 필드에 접근하여 rent 매개변수를 변경하기 위해 Mollusk 시스템 변수를 직접 수정하는 방법을 보여줍니다. 다른 시스템 변수 값도 같은 방식으로 수정할 수 있습니다.

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.

컴퓨트 유닛 벤치마킹

MolluskComputeUnitBencher 프로그램 명령어의 컴퓨트 유닛 사용량을 추적합니다. 결과는 마크다운 파일로 작성됩니다.

의존성으로 mollusk-svm-bencher가 필요합니다.

다음 예제는 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();
}

벤치마크 결과는 지정된 out_dircompute_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 - |

Token Program 테스팅

mollusk-svm-programs-token 크레이트를 사용하여 Token Program, Token2022 Program(Token Extensions) 및 Associated Token Program을 테스트를 위해 Mollusk에 추가합니다.

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

다음 예제는 Mollusk를 사용한 토큰 전송 테스트를 보여줍니다.

아래 예제는 설명을 위해 테스트 계정을 수동으로 정의합니다. mollusk-svm-programs-token에는 민트와 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::{
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?

목차

페이지 편집
Mollusk | Solana