Anchor 이벤트 이해하기
Anchor 프로그램은 트랜잭션 실행 중에 로깅되는 이벤트를 발생시킬 수 있습니다.
이러한 이벤트는 Program data: 접두사와 함께 트랜잭션 로그에 Base64로
인코딩됩니다. anchor-litesvm 크레이트는 이러한 이벤트를 파싱하고 검증하는
유틸리티를 제공합니다.
┌─────────────────────────────────────────────────┐│ Anchor Event Structure │├────────────────┬────────────────────────────────┤│ Discriminator │ Event Data ││ (8 bytes) │ (serialized fields) │└────────────────┴────────────────────────────────┘
이벤트 정의
먼저 Anchor 프로그램에서 이벤트를 정의합니다:
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,}
이벤트 파싱
특정 타입의 모든 이벤트 파싱
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);}
단일 이벤트 파싱
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);}
이벤트 검증
이벤트 발생 여부 검증
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는 트랜잭션 로그에서 지정된 타입의 이벤트를 찾지 못하면
패닉을 발생시킵니다.
이벤트 개수 검증
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);}
이벤트 존재 여부 확인
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");}}
수동 이벤트 파싱
고급 사용 사례의 경우, 이벤트 데이터를 직접 파싱할 수 있습니다:
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();}
이벤트 오류
EventError 열거형은 자세한 오류 정보를 제공합니다:
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),}
| 오류 | 설명 |
|---|---|
EventNotFound | 로그에 지정된 유형의 이벤트 없음 |
ParseError(String) | 이벤트 데이터 파싱 실패 |
Base64Error | Base64 디코딩 실패 |
InvalidFormat | 이벤트 데이터의 구조가 유효하지 않음 |
AnchorError(String) | Anchor 역직렬화 실패 |
전체 예제
다음은 이벤트 처리를 보여주는 종합적인 예제입니다:
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!");}
이벤트는 계정을 가져와 역직렬화하지 않고도 프로그램이 올바르게 실행되었는지 확인하는 좋은 방법입니다. 특히 시간에 따른 상태 변화를 추적하는 데 유용합니다.
Is this page helpful?