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:

  1. The server silently waits for your subscription (Hello is not sent until the first Subscribe)
  2. After sending subscribe, the first message you receive is Hello with server metadata
  3. Then a snapshot of the current state (TPS, if subscribed)
  4. After that, a live stream of BlockStart events and TPS updates

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:

FieldWherePurposeExample
seqOuter envelope ({"seq": 42, ...})Client tracking number. Monotonically increasing per connection. Use this for ?resume_from= on reconnect.42
seqnoInside 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