Ethereum
Block Management Best Practices
In development you fetch a block, log it, and move on. Production is a different animal. You're processing every one of the ~7,200 blocks Ethereum produces per day without gaps, surviving node hiccups, and staying correct when the head reorgs out from under you. The cost of a missed block or a double-counted reorg isn't a console warning — it's drifted balances and support tickets. That's why the rigor here goes well past the happy-path code you'd write for a quick script.
The patterns below are the ones that hold up against sustained mainnet traffic: retrying failed reads without losing data, detecting reorgs by watching parentHash, processing wide block ranges without exhausting memory, and failing over to a second provider when the primary degrades. Each comes with a checklist you can lift straight into a readiness review before you point real users at https://ethereum.therpc.io/YOUR_API_KEY.
Error Handling and Recovery
- Bounded retries with growing backoff. Wrap each
eth_getBlockin a retry loop of 3 to 5 attempts with delays that double from a fraction of a second. Since a fresh block arrives every 12 seconds, treat a block that's still missing after a few seconds as genuinely absent rather than retrying indefinitely against an empty result. - Make timeouts non-destructive. Keep a persisted cursor of the last block number you fully committed. If a read times out, the worker can crash and restart without skipping a height — it resumes from the cursor and refetches, so a network blip never silently swallows a block.
- Detect reorgs by parent hash and roll back. On each new block, compare its
parentHashto the hash you stored for the previous height. If they differ, the chain reorganized: walk back to the common ancestor, undo any state derived from the orphaned blocks, then replay the new canonical ones. Never mutate finalized-block-derived state — only the unfinalized tail can move. - Configure a fallback node for continuity. A second endpoint takes over when the primary returns sustained errors or stalls. Verify the standby reports
eth_chainIdof0x1and agrees on the hash at a recent shared height before you trust its data, so failover doesn't quietly hand you a forked or lagging view.
Performance Optimization
- Cache finalized blocks, expire the rest. Blocks at or below
finalizedare immutable — store them keyed by hash with no expiry. The unfinalized tail betweenfinalizedandlatestcan still change, so cap that cache to a short window (a couple of slots) sized to your reorg tolerance, not to RAM convenience. - Batch to amortize round trips. A single JSON-RPC batch covering a contiguous range replaces dozens of individual
eth_getBlockByNumbercalls. The win is mostly in connection and request overhead, which dominates on Ethereum's dense ranges where each block carries hundreds of transactions. - Right-size polling. When you poll the head, tie the interval to the 12-second slot and stop polling harder when the chain hasn't moved. Every poll spends Compute Units; polling faster than blocks are produced buys nothing but cost.
- Stream large ranges, don't buffer them. Backfilling thousands of blocks, process in fixed batches (10 to 50) and release each batch before fetching the next. Holding a year of fully-hydrated Ethereum blocks in memory at once will OOM the worker — a bounded sliding window keeps memory flat regardless of range size.
Security Considerations
- Scale your finality bar to the value. On post-Merge Ethereum the meaningful safety line isn't a raw confirmation count — it's the
finalizedtag, reached after roughly two epochs (~12.8 minutes), beyond which a revert would require slashing a third of staked ETH. Low-value or reversible actions can act onsafeor a handful of blocks; settlement-grade flows should wait forfinalized. - Check integrity before you process. Confirm each block's
parentHashchains to the prior stored block, and that itsnumberis exactly one above it. A gap or a mismatch means either a reorg or a node fault — handle it before you derive any state. - Avoid replay confusion. EIP-155 ties signed transactions to chainId 1, so a mainnet transaction can't be replayed on Sepolia and vice versa. Validate the chainId on anything you ingest, and only act on a transaction once its inclusion is confirmed by receipt at a finalized height, never from a mempool sighting.
- Treat anomalies as a tripwire. Backward-moving timestamps, two providers disagreeing on the block hash at one height, an unexpected parentHash gap, or a sudden cluster of orphaned heads all point to a reorg or a misbehaving node. Pause irreversible processing and alert rather than pushing the suspect data through.
Implementation Examples
Development Checklist
- Type the block shape. Ethereum returns block fields as hex strings —
number,timestamp,gasLimit,baseFeePerGasall come back as0x-prefixed quantities. TypeScript types around your parsing layer catch the classic bug of comparing a hex string as if it were a decimal number, and document which fields exist post-Merge (an emptyunclesarray,sha3Unclesas the empty-list hash) versus pre-Merge. - Cover every failure branch. Error handling should account for the block being null (not yet produced), the call timing out, a 429 from rate limits, and a parentHash mismatch. Each needs a distinct response, not a single catch-all that swallows them.
- Instrument from the start. Log the block number and hash on every processed block, emit a metric for head lag in seconds, and count reorgs by depth. You want this wired before launch, not bolted on during an incident.
- Test the unhappy paths. Unit tests should cover a missing block, a simulated reorg where
parentHashdoesn't match, a batch where one read fails, and the resume-from-cursor restart. Happy-path block fetching rarely breaks; the reorg and recovery code is where regressions hide.
Production Checklist
- Alert on the metrics that mean something. Head lag beyond a few slots, the
latest-to-finalizedgap drifting past its normal ~2-epoch span, RPC error rate, reorg depth, and processing throughput against the ~12-second block cadence. These tell you the pipeline is falling behind or the chain is misbehaving. - Wrap node calls in a circuit breaker. When a provider crosses an error threshold, trip the breaker so you stop hammering a failing node and shed load to the fallback. This keeps a single degraded upstream from cascading into your own database and queue backlog.
- Run at least two providers. A single endpoint is a single point of failure. Two independent providers, both pinned to chainId 1, let you keep ingesting blocks during a regional outage or a provider's maintenance window, and let you cross-check head hashes when something looks off.
- Health-check continuously. Verify the node's head timestamp tracks wall clock within a few slots, that
eth_chainIdreturns0x1, and that your cursor is advancing. A node can answer requests while quietly stuck — only a freshness check catches that. - Define degradation behavior in advance. When the primary slows, route to the standby; when both struggle, back off polling, widen retry budgets, and keep serving reads from finalized-block cache rather than failing user requests outright.
Common Pitfalls to Avoid
- Thin error handling drops blocks silently. A single uncaught timeout that crashes a worker without a persisted cursor leaves a hole in your block history that nothing flags. The data looks complete until a balance reconciliation says otherwise.
- No reorg handling corrupts state. If you commit credits or event-derived state off
latestand never checkparentHash, a routine head reorg leaves you having acted on a block that no longer exists on the canonical chain. On Ethereum these short reorgs are normal before finality, so this isn't an edge case — it's a matter of when. - Blind spots become outages. Without head-lag and reorg metrics, a node that fell behind or a provider serving a forked view goes unnoticed until users report wrong data. By then you're debugging in production with no signal.
- Untested recovery code rots. The retry, fallback, and reorg-rollback paths run rarely, so without tests they break unnoticed and fail exactly when you need them. A pipeline that's never exercised its failure branches is fragile by construction.
See also
- Working with Blocks - Fundamental concepts of block handling
- Chain Reorganization Guide - Handling chain reorgs
- Block Confirmations Guide - Understanding transaction finality