Building a real-time dashboard for blockchain data looks easy until you try to make it fast, correct, and affordable at the same time. “Just read from an RPC” works for a demo, but it collapses under load, misses reorgs, and turns into a spaghetti bowl of polling jobs.
This post lays out a production-grade approach: what to stream, where to compute, how to deal with chain quirks, and what a sane architecture looks like for dashboards that operators and users can actually trust.
Start with the user: what does “real-time” mean?
“Real-time” is not a single requirement. Define it per metric and per audience:
- Tip-of-chain updates (seconds): new blocks, mempool activity, latest swaps, liquidation events.
- Near-real-time aggregates (10–60s): volume per pool, active wallets, gas spend by category.
- Backfilled truth (minutes to hours): historical accuracy after reorg windows, late-arriving logs, price corrections.
Be explicit in the UI. If a KPI is “finalized-ish,” say so. A dashboard that pretends everything is final at head block will eventually embarrass you during a reorg.
Data sources: RPC is necessary, but not sufficient
Most teams start by polling eth_getLogs or subscribing to newHeads over WebSockets. That’s fine as an ingestion primitive, not as your analytics engine.
Your typical options:
- Direct node (RPC/WebSocket): lowest-level, flexible, but you own reliability, indexing, and scaling.
- Managed RPC providers (Alchemy/Infura/QuickNode): great for uptime, but rate limits and missing historical guarantees can bite.
- Specialized indexers (The Graph, Envio, Subsquid): faster queries and structured data, but you adopt their data model and constraints.
- Chain-native datasets (BigQuery public crypto tables, Dune-like pipelines): amazing for historical analysis; usually not “seconds-latency.”
Opinionated take: for real-time dashboards, use event streaming from RPC/WebSocket plus your own canonical store, and optionally complement with an indexer for developer velocity.
Architecture pattern: stream → normalize → store → serve
A robust real-time dashboard has four layers.
1) Ingestion: blocks, logs, traces (choose carefully)
Most dashboards can be built on logs alone. If you need internal calls (e.g., aggregators, sandwich detection, protocol fee routing), you may need traces, which are heavier and less uniformly supported.
A practical ingestion setup:
- Subscribe to new block headers.
- For each block, fetch logs for a curated set of contracts (protocols you care about).
- Persist a “raw events” record keyed by
(chain_id, block_number, tx_hash, log_index).
Avoid “poll the last N blocks every second” designs. They waste requests and still miss edge cases.
2) Normalization: ABI decoding and semantic events
A dashboard user doesn’t want topic0 and hex strings. Decode events using ABIs and emit “semantic” records:
Swap(pool, tokenIn, tokenOut, amountIn, amountOut, trader, blockTime)Mint(positionId, owner, liquidityDelta, priceRange, fees)Liquidation(vault, collateral, debtRepaid, liquidator)
Keep normalization deterministic. Version your decoders because contracts and ABIs evolve (proxy upgrades, new pools, new event signatures).
3) Storage: separate raw, operational, and analytical stores
One database rarely does everything well.
- Raw store (append-only): Postgres or object storage for immutable raw logs/blocks.
- Operational store (low-latency reads): Postgres with proper indexes, or a key-value store for “latest state” (Redis).
- Analytical store (fast aggregates): ClickHouse is a strong default; it excels at time-series aggregations and high write volume.
If you need one pragmatic setup: Postgres + ClickHouse. Postgres holds entities and latest state; ClickHouse holds event facts and rollups.
4) Serving: APIs, WebSockets, and caching strategy
Dashboards feel real-time when you push updates.
- Use WebSockets or SSE for live tiles (latest block, latest swap, alerts).
- Use HTTP for heavy charts (candles, top traders, cumulative volume).
- Cache expensive aggregates (e.g., top pools last 24h) for short TTLs.
A common anti-pattern is running every chart as a live query over raw events. Pre-aggregate.
Handling reorgs: the difference between “fast” and “correct”
Reorgs are not theoretical. If you build on Ethereum L1, you’ll see them; on some L2s and sidechains, you’ll see different finality semantics.
Minimum viable reorg strategy:
- Track canonical chain by parent hash.
- When a new head arrives that doesn’t extend your current head, detect the fork.
- Rollback derived records for orphaned blocks (soft delete by block hash).
- Reprocess the canonical branch.
In your schema, include block_hash and is_canonical flags on events. Never key truth purely by block number.
Aggregations that stay fast: rollups and materialized views
Your UI will ask questions like:
- “Volume per pool per minute for 24h.”
- “Unique traders per hour.”
- “Top tokens by net inflow.”
Compute rollups continuously:
- Windowed rollups (1m, 5m, 1h) written as new rows.
- Materialized views (where supported) or scheduled merges.
- A “latest snapshot” table for leaderboard endpoints.
Example: swaps stream into ClickHouse swaps table; a background job maintains swaps_1m(pool, minute, volume_usd, count).
Pricing and USD metrics: the silent complexity
Most business dashboards want USD values. On-chain events don’t carry reliable USD.
Practical approaches:
- Use on-chain oracle prices (Chainlink) for major assets.
- For DEX assets, compute spot price from pool state at block time.
- Maintain a price service that outputs
(token, timestamp)->priceand version it.
Be disciplined: if you revise prices after backfills, your historical charts will shift. That’s acceptable if you’re transparent and consistent.
Real example: a DeFi protocol operations dashboard
A typical “ops dashboard” for a lending protocol might show:
- New deposits/borrows in the last 5 minutes (live stream)
- Health factor distribution (near-real-time aggregate)
- Liquidation events with links to transactions (tip-of-chain)
- TVL and revenue charts (rollups + price service)
In practice, you’d ingest protocol events (Borrow, Repay, Liquidate), normalize into semantic tables, compute per-market rollups, and push alerts when liquidation spikes occur. The team that tries to query this live from RPC will spend their nights chasing missing logs and provider timeouts.
Checklist: production concerns teams forget
- Idempotency: every block/event reprocessing should be safe.
- Backfill pipeline: you will need to rebuild from genesis or from a snapshot.
- Rate limits: batch log requests, use block ranges, and retry with jitter.
- Observability: lag to head, dropped events, reorg count, decode failures.
- Schema evolution: new pools, new event fields, upgraded contracts.
Conclusion
A real-time blockchain dashboard is a streaming system with a UI on top—not a set of RPC calls dressed up as analytics. The winning architecture separates ingestion from computation, keeps raw data immutable, treats reorgs as first-class, and serves pre-aggregated results with push-based updates.
If you design for “seconds-latency” where it matters and “finalized truth” where it counts, you’ll ship dashboards that stay fast under load, stay accurate during chain turbulence, and stay maintainable as your protocol and data needs evolve.