A high-performance, real-time EVM block explorer built on Docker using Go and React. Designed for high-throughput chains with sub-second block times — streams blocks and transactions live to the browser via WebSocket with no page refresh required.
Works with any EVM-compatible chain (Ethereum, L2s, custom EVM L1s). Tested at 400ms block times with 50+ transactions per block.
- Real-time streaming — blocks and transactions pushed to the UI as they land on chain
- Parallel block ingestion in Go —
indexeruses goroutines and an RPS rate limiter to fetch blocks + receipts from any EVM RPC - Full data pipeline —
indexer→ Redis Streams → PostgreSQL →api→ WebSocket → frontend - Go-based API gateway —
apiserves REST and uses goroutines for low-latency WebSocket broadcasting - Address activity tracking — transaction counts and history per address
- Instantaneous TPS — calculated from latest block tx count ÷ recent avg block time
- Terminal dark UI — built with React + Vite, zero external UI dependencies
- Resumable indexing —
indexersaves checkpoint to Redis, picks up where it left off on restart - Health endpoints on every service
- Docker 20+
- Docker Compose v2+
- An EVM-compatible RPC endpoint (HTTP JSON-RPC)
# 1. Clone the repo
git clone https://github.com/your-username/faster-explorer.git
cd faster-explorer
# 2. Configure your chain
cp .env.example .env
# Edit .env — set RPC_URL to your chain's HTTP RPC endpoint
# 3. Start everything
docker compose up -d
# 4. Open the explorer
open http://localhost:4001Copy .env.example to .env and set the following:
# Your chain's HTTP JSON-RPC endpoint (required)
RPC_URL=http://your-rpc-node:8545/
# Database
DB_USER=explorer
DB_PASSWORD=change_me_in_production
DB_NAME=explorer
# API base URL (used by the frontend)
VITE_API_URL=http://localhost:4000Note:
eth_getBlockReceiptsmust be supported by your RPC node for receipt data (gas used, status, logs). Most modern EVM nodes support it. If not, receipts will benullbut blocks and transactions will still index.
├── services/
│ ├── fasterindexer/ # Go — polls RPC, indexes blocks/txs/logs into Redis + PostgreSQL
│ ├── api-go/ # Go — REST + WebSocket API
│ └── frontend/ # React 18 + Vite UI
├── database/
│ └── init-db.sql # Schema (auto-applied on first start)
├── docker-compose.yml
├── .env.example
└── .env # Your local config (git-ignored)
| Component | Technology |
|---|---|
| Indexer | Go (indexer), raw JSON-RPC, Redis Streams, PostgreSQL |
| Message Queue | Redis Streams |
| Database | PostgreSQL 15 |
| API | Go (api) + Gorilla WebSocket |
| Frontend | React 18 + Vite |
| Container | Docker + Docker Compose |
GET /api/blocks # Latest blocks
GET /api/blocks/:number # Block by number
GET /api/transactions # Latest transactions
GET /api/transactions/:hash # Transaction by hash
GET /api/addresses/:address # Address activity
GET /stats # Chain stats (TPS, block time, totals)
GET /health # Health check
Connect to ws://localhost:4000 — the server pushes events as they are indexed:
const ws = new WebSocket('ws://localhost:3000');
ws.onmessage = ({ data }) => {
const msg = JSON.parse(data);
// msg.type === 'block' → msg.data = block object
// msg.type === 'tx' → msg.data = transaction object
// msg.type === 'stats' → msg.data = { tps, avg_block_time, ... }
};| Service | Port | Purpose |
|---|---|---|
| Frontend | 4001 | Web UI |
API (api) |
4000 | REST + WebSocket |
| Faster Indexer | 3102 | Health only |
| PostgreSQL | 5432 | Database |
| Redis | 6379 | Streams + cache |
# View logs for a specific service
docker compose logs -f indexer
# Restart a service after editing its source
docker compose up -d --build indexer
# Open a database shell
docker exec -it faster-postgresql psql -U explorer -d explorer
# Check Redis stream lengths
docker exec -it faster-redis redis-cli XLEN blocks:stream
docker exec -it faster-redis redis-cli XLEN transactions:stream
# Reset everything (deletes all indexed data)
docker compose down -v && docker compose up -dChain RPC
│
▼
indexer ──── eth_getBlockByNumber ────┐
└─── eth_getBlockReceipts ─┤
▼
Redis Streams
(blocks:stream,
transactions:stream)
│
▼
PostgreSQL
│
▼
api
/ \
REST ─ ─ WebSocket ──→ Browser
fasterindexer persists its last processed block number in Redis (fasterindexer:lastBlock), so it automatically resumes from where it stopped on restart — no blocks are missed.
No blocks appearing
- Verify your RPC endpoint is reachable:
curl -X POST http://your-rpc:8545/ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' -H 'Content-Type: application/json' - Check
indexerlogs:docker compose logs -f indexer
Transactions not indexing
- Check
indexerlogs for RPC rate-limit errors - Verify
transactions:streamis growing:docker exec faster-redis redis-cli XLEN transactions:stream
Services won't start
docker compose logs # check all logs
docker --version # requires 20+
docker compose version # requires v2+Reset the indexer checkpoint (re-index from a specific block)
docker exec faster-redis redis-cli SET fasterindexer:lastBlock 1234567
docker compose restart indexer# Backup
docker exec faster-postgresql pg_dump -U explorer explorer > backup.sql
# Restore
docker exec -i faster-postgresql psql -U explorer explorer < backup.sqlFor production:
- Set a strong
DB_PASSWORDin.env - Place the API behind a reverse proxy (nginx/Caddy) with TLS
- Restrict Redis and PostgreSQL ports to internal networks only
- Add authentication to the API if exposing publicly
MIT
