Documentation Index
Fetch the complete documentation index at: https://docs.monad.xyz/llms.txt
Use this file to discover all available pages before exploring further.
How to Consume Execution Events in Rust: A Basic Example
This guide walks through building a minimal Rust application that reads execution events from a live Monad node. By the end of this guide, you’ll have a working event consumer that prints each EVM action as it happens.
Prerequisites
This guide assumes you have:
- A running Monad node with execution events enabled (setup guide)
Project Setup
Here is the repo with the code.
Create a new Rust project:
cargo new --bin exec-events-demo
cd exec-events-demo
Replace Cargo.toml with:
[package]
name = "exec-events-demo"
version = "0.1.0"
edition = "2021"
[dependencies]
monad-exec-events = { git = "https://github.com/category-labs/monad-bft", tag = "release/exec-events-sdk-v1.0" }
monad-event-ring = { git = "https://github.com/category-labs/monad-bft", tag = "release/exec-events-sdk-v1.0" }
The Code
Replace src/main.rs with:
use std::time::Duration;
use monad_event_ring::{DecodedEventRing, EventNextResult, EventPayloadResult, EventRingPath};
use monad_exec_events::{ExecEvent, ExecEventReaderExt, ExecEventRing, ExecEventType};
/// Default path for the execution event ring
const EVENT_RING_PATH: &str =
"/var/lib/hugetlbfs/user/monad/pagesize-2MB/event-rings/monad-exec-events";
fn main() {
// Resolve the event ring path (full path bypasses hugetlbfs lookup)
let event_ring_path =
EventRingPath::resolve(EVENT_RING_PATH).expect("Failed to resolve path");
// Open the live event ring
let ring = ExecEventRing::new(&event_ring_path).expect("Failed to open event ring");
println!("Connected to event ring");
println!("Waiting for events...\n");
// Create a reader and rewind to the start of the current block
let mut reader = ring.create_reader();
reader.consensus_prev(Some(ExecEventType::BlockStart));
loop {
match reader.next_descriptor() {
EventNextResult::Ready(event) => {
let seqno = event.info().seqno;
// Try to read the event payload
match event.try_read() {
EventPayloadResult::Ready(exec_event) => {
print_event(&exec_event, seqno);
}
EventPayloadResult::Expired => {
eprintln!("[{}] Payload expired!", seqno);
reader.reset();
}
}
}
EventNextResult::Gap => {
eprintln!("Warning: event gap occurred (reader too slow)");
reader.reset();
}
EventNextResult::NotReady => {
// No events available, wait briefly
std::thread::sleep(Duration::from_millis(10));
}
}
}
}
fn print_event(event: &ExecEvent, seqno: u64) {
match event {
ExecEvent::BlockStart(block) => {
println!(
"[{}] BLOCK_START: number={}",
seqno, block.block_tag.block_number
);
}
ExecEvent::BlockEnd(_) => {
println!("[{}] BLOCK_END", seqno);
}
ExecEvent::TxnHeaderStart { txn_index, .. } => {
println!("[{}] TXN_START: index={}", seqno, txn_index);
}
ExecEvent::TxnEvmOutput { txn_index, output } => {
println!(
"[{}] TXN_OUTPUT: index={} gas_used={}",
seqno, txn_index, output.receipt.gas_used
);
}
ExecEvent::TxnLog { txn_index, txn_log, .. } => {
println!(
"[{}] LOG: txn={} topics={}",
seqno, txn_index, txn_log.topic_count
);
}
ExecEvent::TxnEnd => {
println!("[{}] TXN_END", seqno);
}
// Silently ignore other events (call frames, storage access, etc.)
_ => {}
}
}
Build and Run
cargo build --release
./target/release/exec-events-demo
Expected Output
You’ll see a stream of events as your node processes transactions:
Connected to event ring
Waiting for events...
[508183802] BLOCK_START: number=45199825
[508183811] TXN_START: index=0
[508183822] TXN_OUTPUT: index=0 gas_used=0
[508183823] LOG: txn=0 topics=3
[508183838] TXN_END
[508183942] BLOCK_END
Key Concepts
Event Ring
The event ring is a shared memory buffer where the execution daemon writes events. Multiple readers can consume from it simultaneously, each maintaining their own position.
EventNextResult
- Ready: An event is available to process
- NotReady: No new events yet (daemon hasn’t written any)
- Gap: Reader fell behind and events were overwritten - call
reset() to recover
Event Types
Common events you’ll see:
BlockStart / BlockEnd - Block boundaries
TxnHeaderStart / TxnEnd - Transaction boundaries
TxnEvmOutput - Transaction execution result (contains receipt with gas_used)
TxnLog - EVM log emission (Solidity events)
AccountAccess - Account touched during execution
StorageAccess - Contract storage read/write
Next Steps