Skip to main content

Module cpi_event

Module cpi_event 

Source
Expand description

Self-CPI event emission: the wire format, the verification primitives, and the runtime half of the one-line macro surface.

Log output is lossy. Transaction metadata is not. A program that needs events to arrive at indexers regardless of log truncation invokes itself with a distinctive CPI whose bytes carry the event payload. This serves the same persistence role as Anchor’s emit_cpi!.

§The one-liner

Programs normally use the macro-generated context surface:

ⓘ
#[hopper::context(event_cpi)]        // ← one attribute option
pub struct Deposit {
    #[account(mut)]
    pub vault: Vault,
}

#[hopper::program]
mod vault_prog {
    #[instruction(0)]
    fn deposit(ctx: Context<Deposit>, amount: u64) -> ProgramResult {
        // ... state changes ...
        ctx.emit_event_cpi(&Deposited { amount: WireU64::new(amount) })?;  // ← one call
        Ok(())
    }
}

event_cpi appends two trailing accounts to the context (the event-authority PDA and the program account itself, the same two Anchor’s #[event_cpi] appends), validates them at bind, exposes ctx.emit_event_cpi(&event) on the bound context, and the #[hopper::program] dispatcher grows a self-CPI sink on the reserved [0xE0, 0x1E] marker that authenticates each event before accepting it. The macro emits the sink path only for contexts that opt in.

§Wire format

[0..2]   CPI_EVENT_MARKER   (0xE0, 0x1E)
[2]      event tag          (the byte from `#[hopper::event(tag = N)]`)
[3..]    event payload      (the event's Pod bytes)

Three bytes of instruction-data overhead per event. Anchor’s emit_cpi! spends sixteen: the 8-byte EVENT_IX_TAG_LE instruction discriminator plus the event’s own 8-byte account-style discriminator. Hopper’s 2-byte marker + 1-byte tag table carries the same routing information for 13 fewer instruction-data bytes per event (5 fewer counting only the event-identification layer: 3-byte marker+tag vs one 8-byte hash discriminator).

§Why the sink verifies

The generated sink is not a bare no-op. Any program can CPI into any other program, so an unauthenticated sink would let an attacker program plant forged “events” in your program’s inner-instruction list. The sink therefore requires the event-authority PDA, derived with EVENT_AUTHORITY_SEED under this program’s id, to sign the CPI. Only this program’s own invoke_signed can produce that signature, which is exactly Anchor’s authenticity argument for its event_authority account.

On-chain verification uses crate::pda::find_and_verify_pda. Anchor v0.31+ pins the same PDA against a compile-time constant; Hopper has no compile-time program id, so it derives at runtime. Off-chain hosts have no sha256 syscall (see crate::pda), so host builds enforce the marker and the signer flag and document the address pin as an on-chain check.

§Manual wiring (appendix)

The pre-event_cpi manual pattern remains supported for programs that want custom control. Declare the sentinel yourself:

ⓘ
#[instruction(discriminator = [0xE0, 0x1E])]
fn __hopper_event_sink(ctx: &mut Context<'_>) -> ProgramResult {
    // Recommended: authenticate instead of no-op'ing.
    hopper_runtime::cpi_event::handle_event_sink(ctx, ctx.instruction_data())
}

pass the event-authority PDA + program account in your context, and emit through crate::hopper_emit_cpi! (or encode_event_cpi + invoke_event_cpi for full control).

Constants§

CPI_EVENT_MARKER
The reserved self-CPI event discriminator.
EVENT_AUTHORITY_SEED
Canonical PDA seed for the Hopper event-authority. The #[hopper::context(event_cpi)] machinery derives, verifies, and signs with this seed; manual programs must match it so the CPI signer resolves.
HOST_EVENT_AUTHORITY_BUMP
The bump byte host builds report for the event authority.
MAX_EVENT_PAYLOAD
Maximum event payload accepted by the emit helpers’ stack buffer.

Traits§

CpiEvent
A self-CPI-emittable event: a stable 1-byte tag plus a borrowed payload view.

Functions§

decode_event_cpi
Decode the CPI wire format back into (tag, payload).
encode_event_cpi
Fill an out buffer with the CPI wire format for an event.
handle_event_sink
The event sink: validate an incoming self-CPI event instruction.
invoke_event_cpi
Invoke a self-CPI carrying the encoded event payload.
verify_event_authority
Verify an account is this program’s event-authority PDA and return its bump.