وثائق سولاناتطوير البرامجاختبار البرامج

Mollusk

Mollusk هو إطار اختبار خفيف لاختبار برامج سولانا. يوفر واجهة بسيطة لاختبار تعليمات برنامج سولانا في بيئة مصغرة من الآلة الافتراضية لسولانا (SVM). يجب تحديد جميع حسابات الاختبار بشكل صريح، مما يضمن اختبارات قابلة للتكرار ومحددة.

التثبيت

أضف mollusk-svm كاعتمادية في Cargo.toml:

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

لقياس استخدام وحدات الحوسبة، أضف mollusk-svm-bencher كاعتمادية في Cargo.toml:

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

لاستخدام Token Program وToken2022 Program (Token Extensions) و Associated Token Program للاختبار مع Mollusk، أضف mollusk-svm-programs-token كاعتمادية في Cargo.toml:

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

Mollusk SVM

يوضح المثال التالي الإعداد الأساسي لاختبار برنامج سولانا بسيط باستخدام Mollusk.

برنامج Hello World

يوضح هذا المثال كيفية اختبار برنامج سولانا أساسي باستخدام Mollusk. البرنامج يقوم ببساطة بطباعة "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:

  1. إنشاء نسخة Mollusk - تهيئة Mollusk بمعرف البرنامج ومسار البرنامج المترجم (ملف .so)
  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. البرنامج يقوم ببساطة بطباعة "Hello, world!" في سجلات البرنامج عند استدعائه.

تشغيل cargo build-sbf ينشئ البرنامج المترجم في /target/deploy/<program_name>.so.

لاختبار برنامج سولانا باستخدام Mollusk:

  1. إنشاء نسخة Mollusk - تهيئة Mollusk بمعرف البرنامج ومسار البرنامج المترجم (ملف .so)
  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 واجهة بسيطة لاختبار برامج سولانا. يمكن التعامل مع جميع الحقول من خلال مجموعة من الدوال المساعدة، ولكن يمكن للمستخدمين أيضًا الوصول إليها وتعديلها مباشرة إذا رغبوا في مزيد من التحكم.

لتهيئة 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 بدون فحوصات التحقق.

تقوم الأمثلة أدناه بتشغيل Mollusk في دالة main لأغراض التوضيح. في الممارسة العملية، ستستخدم عادةً Mollusk في وحدة اختبار مشروحة بسمة #[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.

تعليمة واحدة مع فحوصات

استخدم طريقة 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::{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.

تعليمات متعددة

استخدم طريقة 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},
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.

تعليمات متعددة مع فحوصات

استخدم طريقة 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},
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.

فحوصات التحقق

يوفر 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()

حالة الحساب المستمرة

يعتبر MolluskContext غلافاً حول Mollusk يحافظ على حالة الحساب عبر استدعاءات تعليمات متعددة من خلال account_store الخاص به. طرق معالجة التعليمات مطابقة لـ Mollusk.

على عكس Mollusk، الذي يتطلب تمرير accounts إلى كل دالة (مثل process_instruction)، يدير MolluskContext الحسابات داخلياً من خلال account_store الخاص به. هذا يلغي الحاجة إلى معامل accounts عند معالجة التعليمات.

أنشئ account_store باستخدام دالة 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);

يعالج المثال التالي تعليمتي تحويل SOL منفصلتين مع حالة حساب مستمرة بين التعليمات من خلال 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.

متغيرات النظام Mollusk

يوفر Mollusk بنية Sysvars مخصصة لتعديل قيمها للاختبار.

استخدم دالة warp_to_slot لتحديث ساعة sysvar لمحاكاة التحرك للأمام أو للخلف في الوقت إلى 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.

يوضح المثال التالي كيفية تعديل sysvar الخاص بـ Mollusk مباشرة من خلال الوصول إلى حقل sysvars لتغيير معاملات rent. يمكنك تعديل قيم sysvar الأخرى بنفس الطريقة.

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 استخدام وحدات الحوسبة لتعليمات البرنامج. تُكتب النتائج في ملف markdown.

يتطلب mollusk-svm-bencher كتبعية.

المثال التالي يقيس أداء استخدام وحدة الحوسبة لتعليمات تحويل 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();
}

تُكتب نتائج المعايير القياسية إلى out_dir المحدد كملف markdown باسم 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 - |

اختبار 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 أيضاً دوال مساعدة لإنشاء حسابات mint و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_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?

تدار بواسطة

© 2026 مؤسسة سولانا.
جميع الحقوق محفوظة.