Hiểu về Sự kiện Anchor
Các chương trình Anchor có thể phát ra các sự kiện được ghi lại trong quá trình
thực thi giao dịch. Các sự kiện này được mã hóa Base64 trong nhật ký giao dịch
với tiền tố Program data:. Crate anchor-litesvm cung cấp các tiện ích để
phân tích cú pháp và xác nhận các sự kiện này.
┌─────────────────────────────────────────────────┐│ Anchor Event Structure │├────────────────┬────────────────────────────────┤│ Discriminator │ Event Data ││ (8 bytes) │ (serialized fields) │└────────────────┴────────────────────────────────┘
Định nghĩa Sự kiện
Trước tiên, hãy định nghĩa các sự kiện trong chương trình Anchor của bạn:
use anchor_lang::prelude::*;#[event]pub struct TransferEvent {pub from: Pubkey,pub to: Pubkey,pub amount: u64,pub timestamp: i64,}#[event]pub struct InitializeEvent {pub authority: Pubkey,pub name: String,}
Phân tích cú pháp Sự kiện
Phân tích tất cả Sự kiện của một Kiểu
use anchor_litesvm::{AnchorLiteSVM, EventHelpers};use solana_signer::Signer;#[test]fn test_parse_events() {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();// Execute instruction that emits eventslet ix = ctx.program().accounts(Transfer { /* ... */ }).args(TransferArgs { amount: 1000 }).instruction().unwrap();let result = ctx.execute_instruction(ix, &[&user]).unwrap();// Parse all TransferEvent events from the transactionlet events: Vec<TransferEvent> = result.parse_events().unwrap();assert_eq!(events.len(), 1);assert_eq!(events[0].amount, 1000);}
Phân tích một Sự kiện Đơn lẻ
use anchor_litesvm::{AnchorLiteSVM, EventHelpers};#[test]fn test_parse_single_event() {let mut ctx = AnchorLiteSVM::build_with_program(PROGRAM_ID, include_bytes!("../target/deploy/your_program.so"));// ... execute instruction ...// Get the first event of the typelet event: TransferEvent = result.parse_event().unwrap();println!("Transfer: {} -> {} ({})", event.from, event.to, event.amount);}
Xác nhận Sự kiện
Xác nhận Sự kiện Đã Được Phát ra
use anchor_litesvm::{AnchorLiteSVM, EventHelpers};#[test]fn test_assert_event_emitted() {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(Initialize { /* ... */ }).args(InitializeArgs { name: "test".to_string() }).instruction().unwrap();let result = ctx.execute_instruction(ix, &[&user]).unwrap();// Assert that at least one InitializeEvent was emittedresult.assert_event_emitted::<InitializeEvent>();}
assert_event_emitted sẽ gây panic nếu không tìm thấy sự kiện nào thuộc kiểu
đã chỉ định trong nhật ký giao dịch.
Đếm số lượng Sự kiện
use anchor_litesvm::{AnchorLiteSVM, EventHelpers};#[test]fn test_assert_event_count() {let mut ctx = AnchorLiteSVM::build_with_program(PROGRAM_ID, include_bytes!("../target/deploy/your_program.so"));// Execute instruction that emits multiple eventslet ix = ctx.program().accounts(BatchTransfer { /* ... */ }).args(BatchTransferArgs { recipients: vec![...], amounts: vec![...] }).instruction().unwrap();let result = ctx.execute_instruction(ix, &[&user]).unwrap();// Assert exactly 3 TransferEvent events were emittedresult.assert_event_count::<TransferEvent>(3);}
Kiểm tra sự tồn tại của Sự kiện
use anchor_litesvm::{AnchorLiteSVM, EventHelpers};#[test]fn test_has_event() {let mut ctx = AnchorLiteSVM::build_with_program(PROGRAM_ID, include_bytes!("../target/deploy/your_program.so"));// ... execute instruction ...// Check without panickingif result.has_event::<TransferEvent>() {let event: TransferEvent = result.parse_event().unwrap();println!("Transfer occurred: {}", event.amount);} else {println!("No transfer in this transaction");}}
Phân tích cú pháp Sự kiện Thủ công
Đối với các trường hợp sử dụng nâng cao, bạn có thể phân tích dữ liệu sự kiện trực tiếp:
use anchor_litesvm::parse_event_data;#[test]fn test_manual_parse() {// If you have the base64-encoded event datalet base64_data = "SGVsbG8gV29ybGQ="; // Examplelet event: TransferEvent = parse_event_data(base64_data).unwrap();}
Lỗi Sự kiện
Enum EventError cung cấp thông tin lỗi chi tiết:
use anchor_litesvm::EventError;match result.parse_event::<TransferEvent>() {Ok(event) => println!("Amount: {}", event.amount),Err(EventError::EventNotFound) => println!("No event found"),Err(EventError::ParseError(msg)) => println!("Parse error: {}", msg),Err(EventError::Base64Error) => println!("Invalid Base64 encoding"),Err(EventError::InvalidFormat) => println!("Malformed event data"),Err(EventError::AnchorError(msg)) => println!("Anchor error: {}", msg),}
| Lỗi | Mô tả |
|---|---|
EventNotFound | Không có sự kiện thuộc loại được chỉ định trong nhật ký |
ParseError(String) | Không thể phân tích dữ liệu sự kiện |
Base64Error | Không thể giải mã Base64 |
InvalidFormat | Dữ liệu sự kiện có cấu trúc không hợp lệ |
AnchorError(String) | Anchor deserialization failed |
Ví Dụ Hoàn Chỉnh
Dưới đây là một ví dụ toàn diện minh họa cách xử lý sự kiện:
use anchor_litesvm::{AnchorLiteSVM, EventHelpers, TestHelpers};use anchor_lang::{prelude::*, system_program};use solana_signer::Signer;// declare_program! generates client types AND re-exports event structsanchor_lang::declare_program!(vault);#[test]fn test_vault_events() {let mut ctx = AnchorLiteSVM::build_with_program(VAULT_PROGRAM_ID,include_bytes!("../target/deploy/vault.so"),);let authority = ctx.svm.create_funded_account(100_000_000_000).unwrap();let depositor = ctx.svm.create_funded_account(50_000_000_000).unwrap();let vault_pda = ctx.svm.get_pda(&[b"vault", authority.pubkey().as_ref()],&VAULT_PROGRAM_ID,);// Create vaultlet create_ix = ctx.program().accounts(vault::client::accounts::CreateVault {authority: authority.pubkey(),vault: vault_pda,system_program: system_program::ID,}).args(vault::client::args::CreateVault {}).instruction().unwrap();let result = ctx.execute_instruction(create_ix, &[&authority]).unwrap();// Assert VaultCreated eventresult.assert_event_emitted::<vault::VaultCreated>();let created_event: vault::VaultCreated = result.parse_event().unwrap();assert_eq!(created_event.vault, vault_pda);assert_eq!(created_event.authority, authority.pubkey());// Depositlet deposit_ix = ctx.program().accounts(vault::client::accounts::DepositToVault {vault: vault_pda,depositor: depositor.pubkey(),system_program: system_program::ID,}).args(vault::client::args::Deposit { amount: 10_000_000_000 }).instruction().unwrap();let result = ctx.execute_instruction(deposit_ix, &[&depositor]).unwrap();// Assert Deposit eventresult.assert_event_emitted::<vault::Deposit>();let deposit_event: vault::Deposit = result.parse_event().unwrap();assert_eq!(deposit_event.amount, 10_000_000_000);assert_eq!(deposit_event.depositor, depositor.pubkey());// Withdrawallet withdraw_ix = ctx.program().accounts(vault::client::accounts::WithdrawFromVault {vault: vault_pda,authority: authority.pubkey(),recipient: authority.pubkey(),}).args(vault::client::args::Withdraw { amount: 5_000_000_000 }).instruction().unwrap();let result = ctx.execute_instruction(withdraw_ix, &[&authority]).unwrap();// Check withdrawal eventassert!(result.has_event::<vault::Withdrawal>());let withdraw_event: vault::Withdrawal = result.parse_event().unwrap();assert_eq!(withdraw_event.amount, 5_000_000_000);println!("All event tests passed!");}
Sự kiện là một cách tuyệt vời để xác minh rằng chương trình của bạn đã thực thi đúng mà không cần phải tải và giải mã hóa các tài khoản. Chúng đặc biệt hữu ích cho việc theo dõi các thay đổi trạng thái theo thời gian.
Is this page helpful?