Entendiendo los Eventos de Anchor
Los programas de Anchor pueden emitir eventos que se registran durante la
ejecución de transacciones. Estos eventos están codificados en Base64 en los
registros de transacciones con el prefijo Program data:. El crate
anchor-litesvm proporciona utilidades para analizar y verificar estos eventos.
┌─────────────────────────────────────────────────┐│ Anchor Event Structure │├────────────────┬────────────────────────────────┤│ Discriminator │ Event Data ││ (8 bytes) │ (serialized fields) │└────────────────┴────────────────────────────────┘
Definición de Eventos
Primero, define tus eventos en tu programa de 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,}
Análisis de Eventos
Analizar Todos los Eventos de un Tipo
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);}
Analizar un Evento Individual
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);}
Verificación de Eventos
Verificar que el Evento fue Emitido
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 generará un error si no se encuentran eventos del tipo
especificado en los registros de transacciones.
Verificar el Recuento de Eventos
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);}
Comprobar la Existencia de un Evento
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");}}
Análisis Manual de Eventos
Para casos de uso avanzados, puedes analizar los datos del evento directamente:
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();}
Errores de Eventos
El enum EventError proporciona información detallada sobre errores:
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),}
| Error | Descripción |
|---|---|
EventNotFound | No hay ningún evento del tipo especificado en los registros |
ParseError(String) | Error al analizar los datos del evento |
Base64Error | Error al decodificar Base64 |
InvalidFormat | Los datos del evento tienen una estructura inválida |
AnchorError(String) | Error en la deserialización de Anchor |
Ejemplo completo
Aquí hay un ejemplo completo que demuestra el manejo de eventos:
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!");}
Los eventos son una excelente manera de verificar que tu programa se ejecutó correctamente sin necesidad de obtener y deserializar cuentas. Son especialmente útiles para hacer seguimiento de los cambios de estado a lo largo del tiempo.
Is this page helpful?