Ethereum
Best Practices
Reading blocks off Ethereum mainnet looks simple until production traffic hits it. A new block lands every 12 seconds, the head can still reorg before it's justified, and EIP-1559 means the fee floor moves every single block. Code that treats the chain as a flat, stable log of data will eventually act on a block that gets orphaned, or stall when a single RPC call times out. A systematic approach — knowing which block tag to trust, when to retry, what to cache — is what separates a demo from a service that stays correct under load.
This guide pulls together the patterns that matter across four areas: reliability (surviving flaky calls and reorgs), performance (caching and batching against chain 1's data volume), security (validating what you read before you act on it), and maintainability (structuring code so the next engineer can reason about it). Each section assumes you're talking to https://ethereum.therpc.io/YOUR_API_KEY over JSON-RPC, with WebSocket available for live subscriptions.
Core Principles
- Reliability means reading the right block. On a post-Merge PoS chain,
latestcan be reorged out before it justifies. Reliability is choosingfinalized(irreversible after ~12.8 minutes) for anything involving value,safewhen you can tolerate a softer guarantee, andlatestonly for views the user can watch change. It also means every RPC call is treated as fallible — networks drop, nodes lag. - Performance comes from cutting round trips. Finalized blocks never change, so they cache forever;
latestshould be polled sparingly or replaced with anewHeadssubscription. Batch contiguous block reads instead of firing one request per number — Ethereum block ranges are dense and the per-call overhead dominates. - Security means not trusting data you haven't checked. Verify a block's
parentHashchains back to what you last stored, confirm transaction receipts showstatus: 0x1, and never treat apendingblock's contents as committed. Act on state, not on what merely appeared in the mempool. - Maintainability favors a single typed boundary. Wrap every JSON-RPC call behind one client module that knows about tags, retries, and provider fallback, so reorg and finality logic lives in one place rather than scattered across feature code.
- Error handling should retry the transient and surface the rest. Timeouts and 429s get exponential backoff; a malformed response or a chainId mismatch gets logged and raised immediately rather than retried into a loop.
Implementation Guidelines
Error Handling
- Retry with exponential backoff, not fixed delays. A reasonable shape is 3 to 5 attempts starting around 500ms and doubling, with jitter so a fleet of workers doesn't retry in lockstep. Because Ethereum produces a block roughly every 12 seconds, a backoff that grows past ~10 seconds is usually pointless — the data you wanted is either there or genuinely missing.
- Treat interruptions as resumable, not fatal. Track the last block number you fully processed and persist it. When a WebSocket connection drops mid-stream, reconnect and backfill the gap by number rather than assuming you caught every
newHeadsevent — subscriptions can miss frames during a disconnect. - Log with the block number and hash attached. A bare "RPC error" is useless three weeks later. Record the block number, the block hash, the method, and the chainId so you can replay the exact request and tell a reorg apart from a node fault.
- Keep a second provider warm. Configure a fallback endpoint and route to it when the primary returns repeated 5xx or stalls. Pin both to chainId 1 and compare the block hash at a shared height before trusting a switched-over provider, so you don't silently read from a node serving a stale or forked head.
Performance Optimization
- Cache by finality, not by clock. A block at or below the
finalizedtag is immutable, so cache it indefinitely keyed by hash. Anything betweenfinalizedandlatestcan still reorg, so give it a short TTL (a slot or two, roughly 12 to 24 seconds) or skip caching it entirely. - Batch contiguous reads. Fetching blocks N through N+50 as a single JSON-RPC batch instead of 51 separate calls collapses connection overhead and tends to cut wall-clock time by an order of magnitude on dense Ethereum ranges. Keep batches bounded so one slow block doesn't hold the whole response hostage.
- Poll deliberately. If you must poll for the head, align the interval to the 12-second slot rather than hammering every second, add jitter across workers, and back off when the chain hasn't advanced. Polling faster than blocks are produced just burns Compute Units.
- Prefer a
newHeadssubscription for live data. Over the WebSocket endpointwss://ethereum.therpc.io/YOUR_API_KEY,eth_subscribe('newHeads')pushes each new block the moment it's seen, so you react in near real time instead of discovering a block one poll interval late and paying for empty polls in between.
Security Considerations
- Validate the parent linkage. Before you record a block, check its
parentHashequals the hash of the block you stored at the previous height. A mismatch is your earliest signal that the chain reorganized under you. - Make state changes reorg-aware. Don't commit a credit, mint, or settlement off a block at the
latesttag. Wait forfinalized, or stage the change as provisional and reconcile it if the block it depended on gets replaced. A finalized Ethereum block can't revert without a third of staked ETH being slashed, which is the guarantee worth building on. - Defend against transaction replay. EIP-155 binds every signed transaction to chainId 1, so a mainnet transaction can't be replayed onto Sepolia or Holesky and vice versa. Still verify the chainId on anything you accept, and never assume a transaction seen in
pendingactually landed — confirm it by receipt and finalized inclusion. - Watch for anomalies as a signal. A sudden run of empty
newHeads, timestamps that jump backward, a block hash that two providers disagree on at the same height, or a parentHash gap all warrant an alert. On Ethereum these usually mean a reorg or a node serving a forked view, and either should pause irreversible actions.
Maintenance
- Health-check the head, not just the connection. A node can answer
eth_blockNumberwhile sitting minutes behind. Compare its head timestamp against wall clock and flag it stale if the gap exceeds a few slots, and confirmeth_chainIdstill returns0x1on every fallback path. - Alert on the signals you can act on. Head lag in seconds, the gap between
latestandfinalizedwidening past its usual ~2-epoch span, RPC error rate, and reorg depth are all actionable. Raw request count is noise by comparison. - Review block-handling code for tag discipline. In review, the questions that catch real bugs are: which block tag does this read use, and is it correct for the consequence? Is there a path that acts on
latestbefore finality? Does the reorg branch actually roll back state, or just log? Treat any value-moving action read off an unfinalized block as a change that needs justification.
See also
- Syncing Strategies Guide - Optimal synchronization methods
- Chain Reorganization Guide - Handling chain restructuring