Handling Chain Reorganizations
How EVM Data Stream reports chain reorganizations on the original subscription, what delivery guarantees apply, and how to reconcile events on the client.
A block that looked canonical can be replaced by a competing block a few seconds later. Every event the stream delivered from the replaced block (headers, transactions, receipts, logs, transfers, and traces) is then no longer true.
EVM Data Stream reports this on the same subscription that delivered the original event. There is no separate reorg topic and no includeRemoved option: corrections are always on.
Reorg corrections are currently enabled on Ethereum Mainnet and Polygon Mainnet. Support on BNB Smart Chain and Robinhood Mainnet will be announced separately. Until then, apply your own confirmation depth on those networks if you need reorg safety.
The event envelope
Every event, including a correction, carries the same four fields:
eventId
string
Stable identifier for the event. A correction reuses the eventId of the event it removes.
removed
boolean
false for a canonical event, true for a correction that removes a previously delivered event.
revision
number
Version number. For one eventId, a greater revision is always the newer state.
data
any
The topic payload. A correction repeats the payload of the event it removes.
A delivered event and its later correction:
{
"eventId": "eth:mainnet:block:0x19ddc6864bd5ec1cdbd493e3d3653b529d79293a6773517018153cdff6ac0be2:newHeads:0x19ddc6864bd5ec1cdbd493e3d3653b529d79293a6773517018153cdff6ac0be2",
"removed": false,
"revision": 116876399,
"data": { "number": "0x18d1e1e", "hash": "0x19ddc6864bd5ec1cdbd493e3d3653b529d79293a6773517018153cdff6ac0be2", "…": "…" }
}{
"eventId": "eth:mainnet:block:0x19ddc6864bd5ec1cdbd493e3d3653b529d79293a6773517018153cdff6ac0be2:newHeads:0x19ddc6864bd5ec1cdbd493e3d3653b529d79293a6773517018153cdff6ac0be2",
"removed": true,
"revision": 116876512,
"data": { "number": "0x18d1e1e", "hash": "0x19ddc6864bd5ec1cdbd493e3d3653b529d79293a6773517018153cdff6ac0be2", "…": "…" }
}How to apply events
Store every
removed: falseevent byeventId.When the same
eventIdarrives withremoved: true, undo or delete the stored value.Events from the new canonical branch may arrive before or after the removals. Apply every event by
eventIdand keep the one with the greaterrevision.Ignore any event whose
revisionis not greater than the one you already applied for thateventId. This also makes duplicate deliveries harmless.
A minimal client reducer:
What is guaranteed
Corrections are always on. A client that does not want them ignores
removed: true.Corrections pass the same filters as the original event. A filtered subscription never receives a correction for an event it could not have received.
A correction is sent only for an event this subscription received.
Corrections are ordered. For one reorg, corrections go from the orphaned tip downward. Within a block, traces come before receipts, and receipts come before the block.
Delivery is at-least-once. A repeated event is safe to apply because
eventIdis stable andrevisionidentifies the newer state.
What is not guaranteed
Events of the new canonical branch may arrive before the old branch is removed.
Exactly-once delivery.
A common order across different subscriptions on one socket, or across API replicas.
Replay of events published while you were disconnected, corrections included. After a reconnect, rebuild state from your own source (for example, an RPC node) before trusting the stream again.
Finality
Inclusion in a block is not finality. The stream delivers events as soon as blocks are observed, and a correction may follow. If your application must act only on final data, such as crediting a deposit, wait for your own confirmation depth or for the network's finalized block before acting, and use corrections to reverse anything you showed provisionally.
Last updated
Was this helpful?