Reth Execution Extensions (ExEx)
An Execution Extension (ExEx) is a future that runs inside the Reth node, receives every change to the canonical chain (commits, reorgs and reverts) and tells the node how far it has got. Indexers, rollups and MEV tooling use it to follow the chain without polling RPC.
You install one on the builder with install_exex(id, |ctx| async move { Ok(my_exex(ctx)) }). It has two stages: the closure gets an ExExContext and returns an initialisation future, which resolves to eyre::Result of the ExEx future itself; the node then runs that future, which returns eyre::Result<()>.
API at a glance
ctx.head: BlockNumHash- The head of the chain when the node launched.
ctx.config: NodeConfig<<Node::Types as NodeTypes>::ChainSpec>- The node's config, typed by the node's chain spec (NodeConfig<ChainSpec> on an Ethereum node). ctx.config.datadir() resolves the data directory for the chain, so an ExEx can keep its own state next to the node's.
ctx.notifications: ExExNotifications- The stream of ExExNotification::ChainCommitted { new }, ChainReorged { old, new } and ChainReverted { old }. committed_chain() and reverted_chain() return the chain to apply and the one to undo.
ctx.events.send(ExExEvent::FinishedHeight(num_hash))- Reports the highest block the ExEx has processed. Reth may then prune earlier blocks, so send it only once a block is safe to forget. On a reorg the height can go down.
ctx.notifications.set_with_head(ExExHead { block })- Starts the stream after the ExEx's own head instead of the node's: if the ExEx has processed up to block 10, the first notification is block 11. This is how an ExEx catches up after a restart without resyncing the node.
Checked against paradigmxyz/reth at eb4c15e (2026-04-07): crates/exex/exex/src/context.rs, crates/exex/exex/src/notifications.rs, crates/exex/exex/src/event.rs, crates/exex/types/src/notification.rs, crates/exex/types/src/head.rs, crates/node/builder/src/builder/mod.rs
Lessons on this topic
ExEx = "Execution Extension" = Reth's plug-in for indexing / observing chain state. Tempo's tidx is built on it. Build the API from scratch.
Read the canonical minimal ExEx from reth-exex-examples. ~40 lines of Rust; shows the three-variant notification handling.
Build a minimal reorg-safe transfer indexer. Subscribes via ExEx; on each Committed, index Transfer events. Handles reorgs by undoing old block effects.
Testing patterns specific to Reth components. Fixture chains (small chains for stage tests), in-process node tests (full pipeline), golden state…
MEV in production: watch mempool + simulate bundles + submit to builder. 3-component pipeline; ~1000 lines of Rust per searcher.