Architecture
The gateway
The gateway is the network-access layer that sits between the untrusted public internet and the chain. It is three components glued together by docker-compose.yml:
Why a gateway and not expose the chain directly? Three reasons: (1) rate limiting per IP for the public JSON-RPC, (2) batched-call cap to prevent JSON-RPC amplification, (3) a normalized read API in front of the chain for wallets that should not parse EVM logs directly.
At a glance
| RPC proxy | OpenResty (nginx + Lua) on :8080 |
| Indexer | Node.js, polls every 1.5s, writes to Postgres |
| Read API | Node.js on :3000 |
| Database | PostgreSQL 15 ("SyncDB") |
| Rate limit | per-IP token bucket, configurable |
| Batch cap | configurable, blocks eth_sendRawTransaction inside batches |
The three pieces
RPC proxy (:8080)
- Forwards
eth_*calls to the chain JSON-RPC. - Per-IP rate limit (token bucket) — see
gateway/openresty/conf/rate-limit.conf. - Batch-call cap: more than N calls in a single batch ? rejected.
- The
debug_*namespace is hard-blocked at the proxy. - The
eth_sendRawTransactionis allowed but the proxy strips thefromand forces a re-check ofnonceandgasPriceagainst the chain before forwarding.
Indexer
- Polls
eth_getBlockByNumber("latest")every 1.5 seconds. - For each new block: writes block, transactions, receipts, logs, and an internal-token-transfer row.
- Idempotent: re-indexing the same block is a no-op.
- Backfill mode for first start: walks from genesis to tip before tailing.
Read API (:3000)
- HTTP/JSON for the wallet and the explorer.
- Endpoints:
/v1/address/{addr}/balance,/v1/address/{addr}/txs,/v1/tx/{hash},/v1/block/{n}. - No write endpoints. The gateway never holds keys.
What is and is not in the gateway
No key custody. The gateway never has a private key. The wallet signs, the gateway forwards.
No transaction submission from the API. The read API does not accept
eth_sendRawTransaction. Transactions always come in through the RPC proxy so the rate limit and batch cap apply.The Blockscout instance is not part of the gateway. It is a separate
explorer/ service that reads from the chain directly, not from SyncDB.