Mollusk
Mollusk là một công cụ kiểm thử nhẹ dùng để kiểm thử các chương trình Solana. Nó cung cấp một giao diện đơn giản để kiểm thử các lệnh của chương trình Solana trong môi trường Máy Ảo Solana (SVM) đã được thu gọn. Tất cả các tài khoản kiểm thử phải được định nghĩa rõ ràng, đảm bảo các bài kiểm thử có tính xác định và có thể lặp lại.
Cài đặt
Thêm mollusk-svm
như một dependency trong Cargo.toml
:
$cargo add mollusk-svm --dev
[dev-dependencies]mollusk-svm = "0.5"
Để đánh giá hiệu suất sử dụng đơn vị tính toán, thêm mollusk-svm-bencher
như
một dependency trong Cargo.toml
:
$cargo add mollusk-svm-bencher --dev
[dev-dependencies]mollusk-svm-bencher = "0.5"
Để sử dụng Token Program, Token2022 Program (Token Extensions), và Associated
Token Program cho việc kiểm thử với Mollusk, thêm mollusk-svm-programs-token
như một dependency trong Cargo.toml
:
$cargo add mollusk-svm-programs-token --dev
[dev-dependencies]mollusk-svm-programs-token = "0.5"
Mollusk SVM
Ví dụ sau đây cho thấy một thiết lập tối thiểu để kiểm thử một chương trình Solana cơ bản sử dụng Mollusk.
Chương trình Hello World
Ví dụ này minh họa cách kiểm thử một chương trình Solana cơ bản sử dụng Mollusk. Chương trình đơn giản in ra "Hello, world!" vào nhật ký chương trình khi được gọi.
Chạy cargo build-sbf
sẽ tạo ra chương trình đã biên dịch tại
/target/deploy/<program_name>.so
.
use solana_program::{account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,};entrypoint!(process_instruction);pub fn process_instruction(_program_id: &Pubkey,_accounts: &[AccountInfo],_instruction_data: &[u8],) -> ProgramResult {msg!("Hello, world!");Ok(())}#[cfg(test)]mod tests {use mollusk_svm::{result::Check, Mollusk};use solana_sdk::{instruction::Instruction, pubkey::Pubkey};#[test]fn test_hello_world() {let program_id = Pubkey::new_unique();let mollusk = Mollusk::new(&program_id, "target/deploy/hello_world");let instruction = Instruction::new_with_bytes(program_id, &[], vec![]);mollusk.process_and_validate_instruction(&instruction, &[], &[Check::success()]);}}
Để kiểm thử một chương trình Solana với Mollusk:
- Tạo một thể hiện
Mollusk
- Khởi tạo Mollusk với một program ID và đường dẫn đến chương trình đã biên dịch (tệp.so
) - Xây dựng một lệnh - Tạo một lệnh để gọi chương trình
- Xử lý và xác thực - Xử lý lệnh sử dụng Mollusk và xác thực kết quả
#[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()]);}}
Để thực thi bài kiểm tra, hãy chạy cargo test
.
Khi bài kiểm tra chạy thành công, bạn sẽ thấy kết quả tương tự như sau:
running 1 test[2025-09-22T19:25:50.427685000Z DEBUG solana_runtime::message_processor::stable_log] Program 11157t3sqMV725NVRLrVQbAu98Jjfk1uCKehJnXXQs invoke [1][2025-09-22T19:25:50.429669000Z DEBUG solana_runtime::message_processor::stable_log] Program log: Hello, world![2025-09-22T19:25:50.429690000Z DEBUG solana_runtime::message_processor::stable_log] Program 11157t3sqMV725NVRLrVQbAu98Jjfk1uCKehJnXXQs consumed 211 of 1400000 compute units[2025-09-22T19:25:50.429726000Z DEBUG solana_runtime::message_processor::stable_log] Program 11157t3sqMV725NVRLrVQbAu98Jjfk1uCKehJnXXQs successtest tests::test_hello_world ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02sDoc-tests hello_world
Cấu trúc
Mollusk
cung cấp một giao diện đơn giản để kiểm tra các chương trình Solana. Tất cả các
trường có thể được điều chỉnh thông qua một số phương thức hỗ trợ, nhưng người
dùng cũng có thể trực tiếp truy cập và sửa đổi chúng nếu họ muốn kiểm soát nhiều
hơn.
Để khởi tạo Mollusk với một thể hiện mặc định, hãy sử dụng phương thức
Mollusk::default
.
// Default instance with no custom programslet mollusk = Mollusk::default();
Để khởi tạo Mollusk với một chương trình cụ thể, hãy sử dụng phương thức
Mollusk::new
.
// Initialize Mollusk with a specific program from a file pathlet program_id = Pubkey::new_unique();let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
Để thêm một chương trình vào Mollusk, hãy sử dụng phương thức
Mollusk::add_program
.
let mollusk = Mollusk::default();let program_id = Pubkey::new_unique();// Add a program to Molluskmollusk.add_program(&program_id,"target/deploy/my_program",&bpf_loader_upgradeable::id(),);
Khi cung cấp đường dẫn tệp, không bao gồm phần mở rộng .so
. Ví dụ,
"path/to/my_program"
là đúng, nhưng "path/to/my_program.so"
thì không.
Xử lý các chỉ thị
Mollusk cung cấp bốn phương thức chính để xử lý các chỉ thị:
Phương thức | Mô tả |
---|---|
process_instruction | Xử lý một chỉ thị và trả về kết quả. |
process_and_validate_instruction | Xử lý một chỉ thị và thực hiện một loạt kiểm tra trên kết quả, gây panic nếu bất kỳ kiểm tra nào thất bại. |
process_instruction_chain | Xử lý nhiều chỉ thị và trả về kết quả. |
process_and_validate_instruction_chain | Xử lý nhiều chỉ thị và thực hiện một loạt kiểm tra trên mỗi kết quả, gây panic nếu bất kỳ kiểm tra nào thất bại. |
InstructionResult
chứa các chi tiết của một chỉ thị đã được xử lý.
Chỉ thị đơn lẻ
Sử dụng phương thức process_instruction
để xử lý một chỉ thị đơn lẻ mà không
kiểm tra kết quả. Bạn có thể tự xác thực kết quả sau khi xử lý.
pub fn process_instruction(&self,instruction: &Instruction,accounts: &[(Pubkey, Account)],) -> InstructionResult
Ví dụ sau đây xử lý một chỉ thị chuyển SOL mà không có kiểm tra xác thực.
Các ví dụ dưới đây chạy Mollusk trong hàm main
cho mục đích minh họa. Trong
thực tế, bạn thường sẽ sử dụng Mollusk trong một module kiểm thử được chú thích
với thuộc tính #[test]
.
use {mollusk_svm::Mollusk,solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},};fn main() {// Initialize Mollusklet mollusk = Mollusk::default();// Set up accountslet sender = Pubkey::new_unique();let recipient = Pubkey::new_unique();let initial_lamports = 1_000_000;let transfer_amount = 250_000;// Create transfer instructionlet instruction = system_instruction::transfer(&sender, &recipient, transfer_amount);// Define initial account stateslet accounts = vec![(sender,Account {lamports: initial_lamports,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},),(recipient,Account {lamports: 0,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},),];// Process the instructionlet result = mollusk.process_instruction(&instruction, &accounts);println!("{:#?}", result);// Check the resultassert!(result.program_result.is_ok());assert_eq!(result.get_account(&sender).unwrap().lamports, 750_000);assert_eq!(result.get_account(&recipient).unwrap().lamports, 250_000);}
Chỉ thị đơn lẻ với kiểm tra
Sử dụng phương thức process_and_validate_instruction
để xử lý một chỉ thị đơn
lẻ với kiểm tra xác thực. Phương thức này sẽ gây panic nếu bất kỳ kiểm tra nào
thất bại.
pub fn process_and_validate_instruction(&self,instruction: &Instruction,accounts: &[(Pubkey, Account)],checks: &[Check],) -> InstructionResult
Ví dụ sau đây xử lý một chỉ thị chuyển SOL với kiểm tra xác thực.
use {mollusk_svm::{Mollusk, result::Check},solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},};fn main() {let mollusk = Mollusk::default();let sender = Pubkey::new_unique();let recipient = Pubkey::new_unique();let initial_lamports = 1_000_000;let transfer_amount = 250_000;let instruction = system_instruction::transfer(&sender, &recipient, transfer_amount);let accounts = vec![(sender,Account {lamports: initial_lamports,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},),(recipient,Account {lamports: 0,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},),];// Define validation checkslet checks = vec![Check::success(),Check::account(&sender).lamports(750_000).build(),Check::account(&recipient).lamports(250_000).build(),];// Process and validate (will panic if any check fails)let result = mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);println!("{:#?}", result);}
Nhiều chỉ thị
Sử dụng phương thức process_instruction_chain
để xử lý nhiều chỉ thị tuần tự
mà không có kiểm tra xác thực.
pub fn process_instruction_chain(&self,instructions: &[Instruction],accounts: &[(Pubkey, Account)],) -> InstructionResult
Ví dụ sau đây xử lý hai chỉ thị chuyển SOL mà không có kiểm tra xác thực.
use {mollusk_svm::Mollusk,solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},};fn main() {let mollusk = Mollusk::default();// Set up accountslet alice = Pubkey::new_unique();let bob = Pubkey::new_unique();let charlie = Pubkey::new_unique();let initial_lamports = 1_000_000;// Create chain of transferslet instructions = vec![system_instruction::transfer(&alice, &bob, 300_000), // Alice -> Bobsystem_instruction::transfer(&bob, &charlie, 100_000), // Bob -> Charlie];let accounts = vec![(alice,Account {lamports: initial_lamports,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},),(bob,Account {lamports: 0,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},),(charlie,Account {lamports: 0,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},),];// Process the instruction chainlet result = mollusk.process_instruction_chain(&instructions, &accounts);println!("{:#?}", result);// Final balances: Alice=700K, Bob=200K, Charlie=100Kassert_eq!(result.get_account(&alice).unwrap().lamports, 700_000);assert_eq!(result.get_account(&bob).unwrap().lamports, 200_000);assert_eq!(result.get_account(&charlie).unwrap().lamports, 100_000);}
Nhiều chỉ thị với kiểm tra
Sử dụng phương thức process_and_validate_instruction_chain
để xử lý nhiều chỉ
thị với kiểm tra xác thực sau mỗi chỉ thị. Mỗi chỉ thị có bộ kiểm tra riêng phải
vượt qua.
pub fn process_and_validate_instruction_chain(&self,instructions: &[(&Instruction, &[Check])],accounts: &[(Pubkey, Account)],) -> InstructionResult
Ví dụ sau đây xử lý một chuỗi hai chỉ thị chuyển SOL với kiểm tra xác thực sau mỗi chỉ thị.
use {mollusk_svm::{result::Check, Mollusk},solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},};fn main() {let mollusk = Mollusk::default();// Create accountslet alice = Pubkey::new_unique();let bob = Pubkey::new_unique();let charlie = Pubkey::new_unique();let initial_lamports = 1_000_000;// Create transfer instructionslet transfer1 = system_instruction::transfer(&alice, &bob, 300_000);let transfer2 = system_instruction::transfer(&bob, &charlie, 100_000);// Initial accountslet accounts = vec![(alice,Account {lamports: initial_lamports,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},),(bob,Account {lamports: 0,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},),(charlie,Account {lamports: 0,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},),];// Define checks for each instructionlet checks_after_transfer1 = vec![Check::success(),Check::account(&alice).lamports(700_000) // 1M - 300K.build(),Check::account(&bob).lamports(300_000) // 0 + 300K.build(),Check::account(&charlie).lamports(0) // Unchanged.build(),];let checks_after_transfer2 = vec![Check::success(),Check::account(&alice).lamports(700_000) // Unchanged from previous.build(),Check::account(&bob).lamports(200_000) // 300K - 100K.build(),Check::account(&charlie).lamports(100_000) // 0 + 100K.build(),];// Process with validation at each steplet instruction_and_checks = [(&transfer1, checks_after_transfer1.as_slice()),(&transfer2, checks_after_transfer2.as_slice()),];// Execute chain (panics if any check fails)let result = mollusk.process_and_validate_instruction_chain(&instruction_and_checks, &accounts);println!("{:#?}", result);}
Kiểm tra xác thực
Mollusk cung cấp một tập hợp phương thức hỗ trợ để kiểm tra kết quả của một lệnh đã được xử lý.
use mollusk_svm::result::Check;
Sử dụng các phương thức sau để xác thực kết quả lệnh:
// Program execution succeededCheck::success()// Program returned specific errorCheck::err(ProgramError::InvalidArgument)// Instruction level errorCheck::instruction_err(InstructionError::InsufficientFunds)// Check with specific program resultCheck::program_result(ProgramResult::Success)// Compute units consumedCheck::compute_units(1000)// Execution timeCheck::time(100)// Return data from instruction executionCheck::return_data(&[1, 2, 3, 4])
Sử dụng các phương thức sau để xác thực trạng thái Tài khoản:
// Single account validationCheck::account(&pubkey).lamports(1_000_000) // Exact lamports.owner(&program_id) // Account owner.data(&expected_data) // Exact data match.data_slice(8, &[1, 2, 3]) // Partial data match at offset.executable(false) // Executable flag.space(100) // Account data size.closed() // Account is closed (0 lamports).rent_exempt() // Account is rent-exempt.build()// Check all accounts are rent exemptCheck::all_rent_exempt()
Trạng thái Tài khoản Liên tục
MolluskContext
là một wrapper bao quanh Mollusk
để duy trì trạng thái tài khoản qua nhiều
lệnh gọi thông qua account_store
. Các phương thức xử lý lệnh giống hệt với
Mollusk
.
Không giống như Mollusk
, yêu cầu truyền accounts
vào mỗi phương thức (ví dụ
process_instruction
), MolluskContext
quản lý tài khoản nội bộ thông qua
account_store
. Điều này loại bỏ nhu cầu tham số accounts
khi xử lý các lệnh.
Tạo một account_store
bằng cách sử dụng phương thức with_context
:
use std::collections::HashMap;use solana_sdk::{account::Account, pubkey::Pubkey, system_program};use mollusk_svm::Mollusk;let mollusk = Mollusk::default();let account_address = Pubkey::new_unique();let mut account_store = HashMap::new();account_store.insert(account_address,Account {lamports: 1_000_000,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},);let context = mollusk.with_context(account_store);
Ví dụ sau xử lý hai lệnh chuyển SOL riêng biệt với trạng thái tài khoản liên tục
giữa các lệnh thông qua account_store
.
use {mollusk_svm::Mollusk,solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},std::collections::HashMap,};fn main() {// Initialize Mollusklet mollusk = Mollusk::default();// Create accountslet sender = Pubkey::new_unique();let recipient = Pubkey::new_unique();// Create account store with initial balanceslet mut account_store = HashMap::new();account_store.insert(sender,Account {lamports: 1_000_000,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},);account_store.insert(recipient,Account {lamports: 0,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},);// Create a stateful contextlet context = mollusk.with_context(account_store);// First transfer: 200,000 lamportslet instruction1 = system_instruction::transfer(&sender, &recipient, 200_000);context.process_instruction(&instruction1);// Second transfer: 100,000 lamports (state persists from first transfer)let instruction2 = system_instruction::transfer(&sender, &recipient, 100_000);context.process_instruction(&instruction2);// Check final balanceslet store = context.account_store.borrow();let sender_account = store.get(&sender).unwrap();let recipient_account = store.get(&recipient).unwrap();println!("Sender: {:#?}", sender_account);println!("Recipient: {:#?}", recipient_account);}
Mollusk Sysvars
Mollusk cung cấp một
Sysvars
cấu trúc tùy chỉnh để sửa đổi giá trị của nó cho việc kiểm thử.
Sử dụng phương thức warp_to_slot
để cập nhật đồng hồ sysvar nhằm mô phỏng việc
di chuyển tiến hoặc lùi thời gian đến một slot cụ thể.
use mollusk_svm::Mollusk;fn main() {// Initialize Mollusklet mut mollusk = Mollusk::default();// Show initial slotprintln!("Initial slot: {}", mollusk.sysvars.clock.slot);// Warp to slot 1000mollusk.warp_to_slot(100);println!("After warp: {}", mollusk.sysvars.clock.slot);// Warp to slot 10mollusk.warp_to_slot(10);println!("After second warp: {}", mollusk.sysvars.clock.slot);}
Ví dụ sau cho thấy cách sửa đổi trực tiếp Mollusk sysvar bằng cách truy cập vào
trường sysvars
để thay đổi các tham số rent. Bạn có thể sửa đổi các giá trị
sysvar khác theo cách tương tự.
use {mollusk_svm::Mollusk, solana_sdk::rent::Rent};fn main() {let mut mollusk = Mollusk::default();// Show default rentprintln!("Default rent exemption for 1000 bytes: {} lamports",mollusk.sysvars.rent.minimum_balance(1000));// Customize rent parametersmollusk.sysvars.rent = Rent {lamports_per_byte_year: 1,exemption_threshold: 1.0,burn_percent: 0,};// Show custom rentprintln!("Custom rent exemption for 1000 bytes: {} lamports",mollusk.sysvars.rent.minimum_balance(1000));}
Đánh giá hiệu suất đơn vị tính toán
MolluskComputeUnitBencher
theo dõi mức sử dụng đơn vị tính toán của các lệnh trong chương trình. Kết quả
được ghi vào tệp markdown.
Yêu cầu
mollusk-svm-bencher
làm dependency.
Ví dụ sau đánh giá hiệu suất mức sử dụng đơn vị tính toán của lệnh chuyển SOL.
use {mollusk_svm::Mollusk,mollusk_svm_bencher::MolluskComputeUnitBencher,solana_sdk::{account::Account, pubkey::Pubkey, system_instruction, system_program},};fn main() {// Initialize Mollusklet mollusk = Mollusk::default();// Create test accountslet sender = Pubkey::new_unique();let receiver = Pubkey::new_unique();// Transfer instructionlet transfer = system_instruction::transfer(&sender, &receiver, 100_000);let accounts = vec![(sender,Account {lamports: 1_000_000,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},),(receiver,Account {lamports: 0,data: vec![],owner: system_program::id(),executable: false,rent_epoch: 0,},),];// Run benchmarkMolluskComputeUnitBencher::new(mollusk).bench(("transfer", &transfer, &accounts)).must_pass(true).out_dir("./target/benches").execute();}
Kết quả đánh giá hiệu suất được ghi vào out_dir
đã chỉ định dưới dạng tệp
markdown có tên compute_units.md
.
#### 2025-09-19 22:28:53.691839 UTCSolana CLI Version: solana-cli 2.2.20 (src:dabc99a5; feat:3073396398,client:Agave)| Name | CUs | Delta || -------- | --- | ------- || transfer | 150 | - new - |
Kiểm thử Token Program
Sử dụng
mollusk-svm-programs-token
crate để thêm token program, token2022 program (token extensions), và associated
token program vào Mollusk để kiểm thử.
use {mollusk_svm::Mollusk,mollusk_svm_programs_token::{associated_token, token, token2022},};let mut mollusk = Mollusk::default();// Add SPL Token Programtoken::add_program(&mut mollusk);// Add SPL Token-2022 Programtoken2022::add_program(&mut mollusk);// Add Associated Token Account Programassociated_token::add_program(&mut mollusk);
Ví dụ sau minh họa việc kiểm thử chuyển token sử dụng Mollusk.
Ví dụ dưới đây định nghĩa thủ công các tài khoản kiểm thử cho mục đích minh
họa. mollusk-svm-programs-token
cũng bao gồm các hàm trợ giúp để tạo mint và
token account.
use {mollusk_svm::{result::Check, Mollusk},mollusk_svm_programs_token::token,solana_sdk::{account::Account, program_pack::Pack, pubkey::Pubkey},spl_token::{instruction::transfer_checked,state::{Account as TokenAccount, Mint},},};fn main() {// Initialize Mollusk with Token programlet mut mollusk = Mollusk::default();token::add_program(&mut mollusk);// Create account keyslet mint = Pubkey::new_unique();let source = Pubkey::new_unique();let destination = Pubkey::new_unique();let authority = Pubkey::new_unique();// Token configurationlet decimals = 6;let transfer_amount = 1_000_000; // 1 token with 6 decimalslet initial_balance = 10_000_000; // 10 tokens// Calculate rent-exempt minimumslet mint_rent = mollusk.sysvars.rent.minimum_balance(Mint::LEN);let account_rent = mollusk.sysvars.rent.minimum_balance(TokenAccount::LEN);// Create mint accountlet mut mint_data = vec![0u8; Mint::LEN];Mint::pack(Mint {mint_authority: Some(authority).into(),supply: initial_balance,decimals,is_initialized: true,freeze_authority: None.into(),},&mut mint_data,).unwrap();// Create source token accountlet mut source_data = vec![0u8; TokenAccount::LEN];TokenAccount::pack(TokenAccount {mint,owner: authority,amount: initial_balance,delegate: None.into(),state: spl_token::state::AccountState::Initialized,is_native: None.into(),delegated_amount: 0,close_authority: None.into(),},&mut source_data,).unwrap();// Create destination token accountlet mut destination_data = vec![0u8; TokenAccount::LEN];TokenAccount::pack(TokenAccount {mint,owner: Pubkey::new_unique(),amount: 0,delegate: None.into(),state: spl_token::state::AccountState::Initialized,is_native: None.into(),delegated_amount: 0,close_authority: None.into(),},&mut destination_data,).unwrap();// Setup accounts for transfer_checkedlet accounts = vec![(source,Account {lamports: account_rent,data: source_data,owner: token::ID,executable: false,rent_epoch: 0,},),(mint,Account {lamports: mint_rent,data: mint_data,owner: token::ID,executable: false,rent_epoch: 0,},),(destination,Account {lamports: account_rent,data: destination_data,owner: token::ID,executable: false,rent_epoch: 0,},),(authority,Account {lamports: 1_000_000,data: vec![],owner: Pubkey::default(),executable: false,rent_epoch: 0,},),];// Create transfer_checked instructionlet instruction = transfer_checked(&token::ID,&source,&mint,&destination,&authority,&[],transfer_amount,decimals,).unwrap();// Expected balances after transferlet expected_source_balance = (initial_balance - transfer_amount).to_le_bytes();let expected_dest_balance = transfer_amount.to_le_bytes();// Define validation checkslet checks = vec![Check::success(),Check::account(&source).data_slice(64, &expected_source_balance) // Token amount is at offset 64.build(),Check::account(&destination).data_slice(64, &expected_dest_balance).build(),];// Process and validate the instructionlet result = mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);println!("{:#?}", result);// Deserialize token account datalet source_account = result.get_account(&source).unwrap();let source_token = TokenAccount::unpack(&source_account.data).unwrap();println!("Source Token Account: {:#?}", source_token);let destination_account = result.get_account(&destination).unwrap();let dest_token = TokenAccount::unpack(&destination_account.data).unwrap();println!("Destination Token Account: {:#?}", dest_token);}
Is this page helpful?