Events with emit!
#[event] types and emit! macro write structured logs indexers can parse. Prefer events over ad-hoc msg! for observable state changes.
Recipe
#[event]
pub struct TradeExecuted {
pub user: Pubkey,
pub amount_in: u64,
pub amount_out: u64,
}
emit!(TradeExecuted { user: ctx.accounts.user.key(), amount_in, amount_out });When to reach for this: Indexers, analytics, or clients need reliable post-hoc observation.
Working Example
#[event]
pub struct PositionOpened {
pub owner: Pubkey,
pub market: Pubkey,
pub size: u64,
pub timestamp: i64,
}
pub fn open_position(ctx: Context<OpenPosition>, size: u64) -> Result<()> {
let clock = Clock::get()?;
emit!(PositionOpened {
owner: ctx.accounts.owner.key(),
market: ctx.accounts.market.key(),
size,
timestamp: clock.unix_timestamp,
});
Ok(())
}What this demonstrates:
- Events registered in IDL
- emit! uses self-CPI pattern internally
- Fields must be Borsh-serializable
- Indexers subscribe to program logs
Deep Dive
Self-CPI
Classic emit! triggers a self-CPI that some indexers rely on. For guaranteed capture in all runtimes, see emit_cpi!.
Field Design
Include keys needed to join off-chain data; avoid huge blobs in events.
Gotchas
- Huge event payloads - CU and log limits.. Fix: Store hash on-chain, details off-chain.
- msg! only for debugging - Not machine parseable.. Fix: Use #[event].
- Private fields not in IDL - Must be pub in event struct.. Fix: All event fields pub.
- Emitting secrets - Logs are public.. Fix: Never emit private keys.
- Missing event on error path - Incomplete analytics.. Fix: Emit before failure or use separate error events.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| emit_cpi! | Reliable capture | Simple local dev |
| Account state only | No logs needed | Indexer-driven analytics |
| Geyser gRPC | Infrastructure heavy | App-level events |
FAQs
Anchor 0.32.1 error format?
Custom codes in IDL plus #[msg] strings.
emit! vs emit_cpi!?
emit_cpi! costs more CU but improves capture reliability.
Should I use msg! on mainnet?
Avoid except behind feature flags.
How do clients get error codes?
From IDL via Anchor or Codama codegen.
Do events appear in IDL?
Yes, #[event] types are listed.
Can constraints return custom errors?
Yes with @ MyError::Variant.
What uses CU in logging?
Each log line and formatted string.
How to test errors?
anchor test expecting error codes.
Are logs consensus data?
Yes, but not an API contract; use events.
Next read?
See Related for events.
Related
- emit_cpi! & Self-CPI Events - reliable emit
- Logging & CU Cost - CU
- Building an Indexer - indexers
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.