Quick Start
Monad Events Stream is a WebSocket service for real-time streaming of Monad blockchain execution events. The service reads events directly from the Monad validator's shared memory and delivers them to clients over WebSocket with less than 5 ms latency.
It provides 25+ event types (block lifecycle, transactions, EVM logs, internal calls, storage access), finality tracking (MonadBFT consensus stages), server-side filtering (7 fields), lossless delivery (cursor resume with 100K-entry buffer), and metrics (TPS, parallel efficiency, contention analysis).
Connecting via websocat (2 minutes)
# Installation
# macOS: brew install websocat
# Linux: cargo install websocat
# Connect
websocat ws://localhost:8443/v1/ws -n -B 262144
# After connecting, send a subscription:
{"subscribe":["BlockStart","TPS"]}
What will happen:
- The server silently waits for your subscription (Hello is not sent until the first Subscribe)
- After sending
subscribe, the first message you receive isHellowith server metadata - Then a snapshot of the current state (TPS, if subscribed)
- After that, a live stream of
BlockStartevents andTPSupdates
Example Response
{"seq":0,"Hello":{"protocol_version":1,"server_version":"0.2.0","chain_id":10143,"block_number":59400123,"available_filters":["txn_index","log_index","address","topics","sender","to","function_selector"],"blocked_events":["AccountAccess","StorageAccess","RecordError","EvmError"],"limits":{"backfill_events":100000,"buffer_per_client":4096,"slow_off_limit":10000,"heartbeat_interval":30,"heartbeat_timeout":60,"max_subscribes":3}}}
{"seq":54321,"TPS":2450}
{"seq":54322,"Events":[{"event_name":"BlockStart","block_number":59400124,"txn_idx":null,"txn_hash":null,"commit_stage":"Proposed","payload":{"type":"BlockStart","block_number":59400124,"block_id":"0x...","round":1,"epoch":100,"parent_eth_hash":"0x...","timestamp":1708345678,"beneficiary":"0x...","gas_limit":30000000,"base_fee_per_gas":"0x3b9aca00"},"seqno":9876543210,"timestamp_ns":1708345678000000000}]}
Connecting via JavaScript
const ws = new WebSocket("ws://localhost:8443/v1/ws");
ws.onopen = () => {
ws.send(JSON.stringify({ subscribe: ["BlockStart", "TPS"] }));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.Hello) console.log("Connected:", msg.Hello.server_version);
if (msg.tps) console.log("TPS:", msg.tps);
if (msg.Events) {
for (const e of msg.Events) {
console.log(e.event_name, e.block_number);
}
}
};
Key Concepts
Before diving deeper, here are the essential terms you'll encounter throughout the documentation:
seq vs seqno — Two Different Sequence Numbers
Every message from the server has two sequence numbers that serve different purposes:
| Field | Where | Purpose | Example |
|---|---|---|---|
seq | Outer envelope ({"seq": 42, ...}) | Client tracking number. Monotonically increasing per connection. Use this for ?resume_from= on reconnect. | 42 |
seqno | Inside event payload ("seqno": 9876543210) | Event ring position. Internal to the validator's ring buffer. Informational only — do not use for resume. | 9876543210 |
Rule: Always track the outer seq for cursor resume. Ignore seqno for protocol purposes.
Important: Control messages (Hello, Warning, Error) use seq: 0. Do not update your resume cursor from these; only track seq > 0.
commit_stage — Block Consensus Stage
Every event carries a commit_stage field showing the block's consensus stage at the moment the event was serialized (not the current live stage):
- Live stream: Almost always
"Proposed", because events are written at execution time, before consensus - Resume/replay: May be
"Voted","Finalized", etc., because the block progressed while the message was in the buffer null: Block not tracked (rare edge case, delivered as fail-open)
To track the current stage of a block, subscribe to Lifecycle events.
Blocked Events
The server suppresses 12 high-volume event types by default (AccountAccess, StorageAccess, perf markers, etc.). These are listed in the blocked_events field of the Hello message.
If you need them, the server must be started with --unrestricted. See DEPLOYMENT.md for configuration.
Ring Buffer & Resume
The server keeps the last 100,000 messages in an in-memory ring buffer. On reconnect with ?resume_from=<seq>, the server replays missed messages. If your cursor is too old (outside the buffer), you get a snapshot of current state instead. The buffer is lost on server restart.
See Reliability for details.
Next Steps
- Connection & Lifecycle: endpoints, Hello frame, heartbeat
- Subscriptions & Filters: simple/advanced subscribe, field filters
- Finality & Correlation: MonadBFT stages,
min_stage, transaction correlation - Message Format: all message types with JSON examples
- Event Reference: all 25+ event types with payload fields
- Reliability: cursor resume, backpressure, drop detection
- Practical Examples: real-world scenarios
- Troubleshooting: common problems and solutions