Documentación de SolanaLiteSVMRustCajas adicionalesanchor-litesvm

Eventos

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 events
let 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 transaction
let 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 type
let 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 emitted
result.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 events
let 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 emitted
result.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 panicking
if 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 data
let base64_data = "SGVsbG8gV29ybGQ="; // Example
let 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),
}
ErrorDescripción
EventNotFoundNo hay ningún evento del tipo especificado en los registros
ParseError(String)Error al analizar los datos del evento
Base64ErrorError al decodificar Base64
InvalidFormatLos 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 structs
anchor_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 vault
let 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 event
result.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());
// Deposit
let 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 event
result.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());
// Withdrawal
let 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 event
assert!(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?