Tài liệu SolanaLiteSVMRustThùng Hàng Bổ Sunganchor-litesvm

Chương trình & Ngữ cảnh

Tạo Kiểu Client với declare_program!

Anchor 1.0 giới thiệu declare_program!, đọc IDL của chương trình tại thời điểm biên dịch và tạo ra các module client::accounts::*client::args::* an toàn kiểu dữ liệu. Đây là cách được khuyến nghị để xây dựng hướng dẫn trong kiểm thử.

// At the top of your test file, generate client modules from the IDL
anchor_lang::declare_program!(my_program);
// Now use the generated types:
// my_program::client::accounts::Initialize { ... }
// my_program::client::args::Initialize { ... }
// my_program::MyAccount (for account deserialization)

Chạy anchor build trước để tạo target/idl/my_program.json. declare_program! đọc tệp này tại thời điểm biên dịch. Tên macro phải khớp với tên crate của chương trình (snake_case).

Thiết Lập Môi Trường Kiểm Thử

Trình xây dựng AnchorLiteSVM cung cấp API liền mạch để cấu hình môi trường kiểm thử của bạn.

Thiết Lập Cơ Bản

use anchor_litesvm::AnchorLiteSVM;
use solana_sdk::signature::{read_keypair_file, Signer};
anchor_lang::declare_program!(my_program);
#[test]
fn test_basic_setup() {
// Read the program keypair to get the correct program ID
let program_keypair = read_keypair_file("target/deploy/my_program-keypair.json").unwrap();
let mut ctx = AnchorLiteSVM::new()
.deploy_program(
program_keypair.pubkey(),
include_bytes!("../target/deploy/my_program.so"),
)
.build();
println!("Program deployed: {}", ctx.program().id());
}

Cách Viết Tắt cho Một Chương Trình

use anchor_litesvm::AnchorLiteSVM;
use solana_sdk::signature::{read_keypair_file, Signer};
#[test]
fn test_single_program() {
let program_keypair = read_keypair_file("target/deploy/my_program-keypair.json").unwrap();
// Convenience method for single-program setups
let mut ctx = AnchorLiteSVM::build_with_program(
program_keypair.pubkey(),
include_bytes!("../target/deploy/my_program.so"),
);
}

Nhiều Chương Trình

use anchor_litesvm::AnchorLiteSVM;
use solana_sdk::signature::{read_keypair_file, Signer};
#[test]
fn test_multiple_programs() {
let program_a_kp = read_keypair_file("target/deploy/program_a-keypair.json").unwrap();
let program_b_kp = read_keypair_file("target/deploy/program_b-keypair.json").unwrap();
// Deploy multiple programs — first one becomes the primary (ctx.program_id)
let mut ctx = AnchorLiteSVM::build_with_programs(&[
(program_a_kp.pubkey(), include_bytes!("../target/deploy/program_a.so") as &[u8]),
(program_b_kp.pubkey(), include_bytes!("../target/deploy/program_b.so") as &[u8]),
]);
// Primary program is program_a
assert_eq!(ctx.program().id(), program_a_kp.pubkey());
}

Người Thanh Toán Tùy Chỉnh

use anchor_litesvm::AnchorLiteSVM;
use solana_keypair::Keypair;
use solana_signer::Signer;
#[test]
fn test_custom_payer() {
let custom_payer = Keypair::new();
let mut ctx = AnchorLiteSVM::new()
.with_payer(custom_payer)
.deploy_program(PROGRAM_ID, include_bytes!("../target/deploy/your_program.so"))
.build();
println!("Payer: {}", ctx.payer().pubkey());
}

Nếu bạn không chỉ định người thanh toán, AnchorLiteSVM sẽ tự động tạo và nạp tiền cho một tài khoản.

AnchorContext

AnchorContext là giao diện chính để tương tác với môi trường kiểm thử của bạn.

Thuộc Tính Cốt Lõi

use anchor_litesvm::AnchorLiteSVM;
use solana_signer::Signer;
#[test]
fn test_context_properties() {
let mut ctx = AnchorLiteSVM::build_with_program(
PROGRAM_ID,
include_bytes!("../target/deploy/your_program.so"),
);
// ctx.svm — direct access to the underlying LiteSVM instance (public field)
let balance = ctx.svm.get_balance(&some_pubkey);
// ctx.payer() — the payer keypair
let payer = ctx.payer();
println!("Payer pubkey: {}", payer.pubkey());
// ctx.program() — Program instance for instruction building
let program = ctx.program();
println!("Program ID: {}", program.id());
// ctx.latest_blockhash()
let blockhash = ctx.latest_blockhash();
}

Tạo Tài Khoản Được Nạp Tiền

use anchor_litesvm::AnchorLiteSVM;
use anchor_litesvm::TestHelpers;
use solana_keypair::Keypair;
use solana_signer::Signer;
#[test]
fn test_create_accounts() {
let mut ctx = AnchorLiteSVM::build_with_program(
PROGRAM_ID,
include_bytes!("../target/deploy/your_program.so"),
);
// Option 1: via AnchorContext (returns Result<Keypair>)
let user = ctx.create_funded_account(10_000_000_000).unwrap();
// Option 2: via ctx.svm with TestHelpers (same result)
let user2 = ctx.svm.create_funded_account(10_000_000_000).unwrap();
// Airdrop to an existing address
let another_user = Keypair::new();
ctx.airdrop(&another_user.pubkey(), 5_000_000_000).unwrap();
}

Kiểm Tra Sự Tồn Tại của Tài Khoản

use anchor_litesvm::AnchorLiteSVM;
#[test]
fn test_account_exists() {
let mut ctx = AnchorLiteSVM::build_with_program(
PROGRAM_ID,
include_bytes!("../target/deploy/your_program.so"),
);
let some_pda = ctx.svm.get_pda(&[b"vault"], &PROGRAM_ID);
if ctx.account_exists(&some_pda) {
println!("Account exists!");
} else {
println!("Account not found");
}
}

Xây Dựng Lệnh Với Program

Struct Program cung cấp một API linh hoạt để xây dựng các lệnh, phản ánh anchor-client. Sử dụng các kiểu được tạo bởi declare_program! để đảm bảo an toàn kiểu dữ liệu.

Xây Dựng Lệnh Cơ Bản

use anchor_litesvm::AnchorLiteSVM;
use anchor_lang::system_program;
use solana_signer::Signer;
anchor_lang::declare_program!(my_program);
#[test]
fn test_build_instruction() {
let mut ctx = AnchorLiteSVM::build_with_program(
PROGRAM_ID,
include_bytes!("../target/deploy/my_program.so"),
);
let user = ctx.svm.create_funded_account(10_000_000_000).unwrap();
let user_pda = ctx.svm.get_pda(&[b"user", user.pubkey().as_ref()], &PROGRAM_ID);
// Build instruction using types generated by declare_program!
let ix = ctx.program()
.accounts(my_program::client::accounts::Initialize {
user: user.pubkey(),
user_account: user_pda,
system_program: system_program::ID,
})
.args(my_program::client::args::Initialize {
name: "test".to_string(),
})
.instruction()
.unwrap();
println!("Instruction built successfully!");
}

Phương thức .accounts() chấp nhận bất kỳ kiểu nào triển khai ToAccountMetas, điều mà tất cả các struct tài khoản Anchor đều thực hiện tự động. Tương tự, .args() chấp nhận bất kỳ kiểu nào triển khai InstructionData.

Thực Thi Lệnh

use anchor_litesvm::AnchorLiteSVM;
use anchor_lang::system_program;
use solana_signer::Signer;
anchor_lang::declare_program!(my_program);
#[test]
fn test_execute_instruction() {
let mut ctx = AnchorLiteSVM::build_with_program(
PROGRAM_ID,
include_bytes!("../target/deploy/my_program.so"),
);
let user = ctx.svm.create_funded_account(10_000_000_000).unwrap();
let user_pda = ctx.svm.get_pda(&[b"user", user.pubkey().as_ref()], &PROGRAM_ID);
let ix = ctx.program()
.accounts(my_program::client::accounts::Initialize {
user: user.pubkey(),
user_account: user_pda,
system_program: system_program::ID,
})
.args(my_program::client::args::Initialize { name: "test".to_string() })
.instruction()
.unwrap();
// Execute — returns TransactionResult
ctx.execute_instruction(ix, &[&user])
.unwrap()
.assert_success();
}

Thực Thi Nhiều Lệnh

use anchor_litesvm::AnchorLiteSVM;
use solana_signer::Signer;
anchor_lang::declare_program!(my_program);
#[test]
fn test_execute_multiple_instructions() {
let mut ctx = AnchorLiteSVM::build_with_program(
PROGRAM_ID,
include_bytes!("../target/deploy/my_program.so"),
);
let user = ctx.svm.create_funded_account(10_000_000_000).unwrap();
let ix1 = ctx.program()
.accounts(my_program::client::accounts::Initialize { /* ... */ })
.args(my_program::client::args::Initialize { name: "first".to_string() })
.instruction()
.unwrap();
let ix2 = ctx.program()
.accounts(my_program::client::accounts::Update { /* ... */ })
.args(my_program::client::args::Update { name: "second".to_string() })
.instruction()
.unwrap();
// Execute multiple instructions in one transaction
ctx.execute_instructions(vec![ix1, ix2], &[&user])
.unwrap()
.assert_success();
}

Sử Dụng send_and_confirm_transaction

use anchor_litesvm::AnchorLiteSVM;
use solana_signer::Signer;
use solana_transaction::Transaction;
#[test]
fn test_send_transaction() {
let mut ctx = AnchorLiteSVM::build_with_program(
PROGRAM_ID,
include_bytes!("../target/deploy/your_program.so"),
);
let user = ctx.svm.create_funded_account(10_000_000_000).unwrap();
let ix = ctx.program()
.accounts(/* ... */)
.args(/* ... */)
.instruction()
.unwrap();
// Build transaction manually if needed
let tx = Transaction::new_signed_with_payer(
&[ix],
Some(&ctx.payer().pubkey()),
&[ctx.payer(), &user],
ctx.latest_blockhash(),
);
// Note: takes &Transaction (reference)
ctx.send_and_confirm_transaction(&tx).unwrap();
}

Ví Dụ Hoàn Chỉnh

Đây là một ví dụ toàn diện sử dụng mẫu escrow với declare_program!:

use anchor_litesvm::AnchorLiteSVM;
use anchor_litesvm::{AssertionHelpers, TestHelpers};
use anchor_lang::system_program;
use solana_sdk::signature::{read_keypair_file, Signer};
use spl_associated_token_account::get_associated_token_address;
use litesvm_token::spl_token;
anchor_lang::declare_program!(anchor_escrow);
#[test]
fn test_make_and_take() {
// 1. One-line setup
let program_keypair = read_keypair_file("target/deploy/anchor_escrow-keypair.json").unwrap();
let program_id = program_keypair.pubkey();
let mut ctx = AnchorLiteSVM::build_with_program(
program_id,
include_bytes!("../target/deploy/anchor_escrow.so"),
);
// 2. Create test accounts using TestHelpers on ctx.svm
let maker = ctx.svm.create_funded_account(10_000_000_000).unwrap();
let taker = ctx.svm.create_funded_account(10_000_000_000).unwrap();
// 3. Create token mints and funded ATAs
let mint_a = ctx.svm.create_token_mint(&maker, 9).unwrap();
let mint_b = ctx.svm.create_token_mint(&maker, 9).unwrap();
let maker_ata_a = ctx.svm
.create_associated_token_account(&mint_a.pubkey(), &maker)
.unwrap();
ctx.svm.mint_to(&mint_a.pubkey(), &maker_ata_a, &maker, 1_000_000_000).unwrap();
let taker_ata_b = ctx.svm
.create_associated_token_account(&mint_b.pubkey(), &taker)
.unwrap();
ctx.svm.mint_to(&mint_b.pubkey(), &taker_ata_b, &maker, 500_000_000).unwrap();
// 4. Derive PDAs
let seed: u64 = 42;
let escrow_pda = ctx.svm.get_pda(
&[b"escrow", maker.pubkey().as_ref(), &seed.to_le_bytes()],
&program_id,
);
let vault = get_associated_token_address(&escrow_pda, &mint_a.pubkey());
// 5. Build and execute Make instruction using generated client types
let make_ix = ctx.program()
.accounts(anchor_escrow::client::accounts::Make {
maker: maker.pubkey(),
escrow: escrow_pda,
mint_a: mint_a.pubkey(),
mint_b: mint_b.pubkey(),
maker_ata_a,
vault,
associated_token_program: spl_associated_token_account::id(),
token_program: spl_token::id(),
system_program: system_program::ID,
})
.args(anchor_escrow::client::args::Make {
seed,
receive: 500_000_000,
amount: 1_000_000_000,
})
.instruction()
.unwrap();
ctx.execute_instruction(make_ix, &[&maker])
.unwrap()
.assert_success();
// 6. Assert state using AssertionHelpers on ctx.svm
assert!(ctx.account_exists(&escrow_pda));
ctx.svm.assert_token_balance(&vault, 1_000_000_000);
ctx.svm.assert_token_balance(&maker_ata_a, 0);
// 7. Execute Take
let taker_ata_a = get_associated_token_address(&taker.pubkey(), &mint_a.pubkey());
let maker_ata_b = get_associated_token_address(&maker.pubkey(), &mint_b.pubkey());
let take_ix = ctx.program()
.accounts(anchor_escrow::client::accounts::Take {
taker: taker.pubkey(),
maker: maker.pubkey(),
escrow: escrow_pda,
mint_a: mint_a.pubkey(),
mint_b: mint_b.pubkey(),
vault,
taker_ata_a,
taker_ata_b,
maker_ata_b,
associated_token_program: spl_associated_token_account::id(),
token_program: spl_token::id(),
system_program: system_program::ID,
})
.args(anchor_escrow::client::args::Take {})
.instruction()
.unwrap();
ctx.execute_instruction(take_ix, &[&taker])
.unwrap()
.assert_success();
// 8. Final assertions
ctx.svm.assert_account_closed(&escrow_pda);
ctx.svm.assert_account_closed(&vault);
ctx.svm.assert_token_balance(&taker_ata_a, 1_000_000_000);
ctx.svm.assert_token_balance(&maker_ata_b, 500_000_000);
}

Is this page helpful?