dispatch-gateway
Sits between consumers and providers. Handles provider discovery, QoS scoring, geographic routing, TAP receipt issuance, quorum consensus, and rate limiting.Routes
| Method | Path | Description |
|---|---|---|
| POST | /rpc/{chain_id} | JSON-RPC request |
| POST | /rpc | JSON-RPC with chain from X-Chain-Id header |
| GET | /ws/{chain_id} | WebSocket proxy |
| GET | /health | Liveness check |
| GET | /version | Version info |
| GET | /providers/{chain_id} | List active providers for chain |
| GET | /metrics | Prometheus metrics |
| POST | /rav/aggregate | TAP agent submits receipts, receives signed RAV |
Provider selection
- Query registry for providers serving the requested
(chain_id, tier)pair - Score each provider by QoS (latency EMA, availability, block freshness)
- Apply geographic bonus (15% score boost for same-region providers)
- Weighted random selection from top-k candidates
- For deterministic methods: quorum dispatch (majority wins). For all others: concurrent dispatch (first valid wins)
eth_call, eth_getLogs, eth_getBalance, eth_getCode, eth_getTransactionCount, eth_getStorageAt, eth_getBlockByHash, eth_getTransactionByHash, eth_getTransactionReceipt. All other methods use concurrent dispatch.
QoS scoring
| Metric | Weight |
|---|---|
| Latency (p50 EMA) | 35% |
| Availability | 35% |
| Block freshness (blocks behind chain head) | 30% |
eth_blockNumber probe fires to every provider every 10 seconds. Results feed freshness and availability scores.
New providers start with a neutral score and receive a geographic bonus until latency data accumulates.
Consumer address requirement
Every request to the gateway must include anX-Consumer-Address header containing the consumer’s Ethereum address:
402 Payment Required. The address must have GRT deposited in PaymentsEscrow or the provider will reject the request.
TAP receipt issuance
The gateway signs a fresh EIP-712 TAP receipt for every request:data_service:RPCDataServiceaddressservice_provider: selected provider’s addressnonce: randomuint64value:CU_weight × base_price_per_cuin GRT weitimestamp_ns: current Unix nanosecondsmetadata: first 20 bytes = consumer address (fromX-Consumer-Address)
metadata is how dispatch-service determines whose escrow to check and how the RAV’s payer field is set — ensuring on-chain collect() debits the correct account.
The receipt is sent to dispatch-service in the TAP-Receipt HTTP header.
Dynamic discovery
The gateway polls the RPC network subgraph every 60 seconds (configurable) and rebuilds its provider registry. Providers appearing in the subgraph are probed for liveness before being added to the active pool. Static provider config (via[[providers]] in gateway.toml) takes precedence over subgraph discovery and is used when the subgraph is unavailable.
Batch JSON-RPC
Batch requests are dispatched concurrently — each item in the batch is routed independently. Per-item errors are isolated and don’t fail the whole batch.Rate limiting
Per-IP token bucket viagovernor. Configurable requests-per-second and burst size. Returns 429 Too Many Requests when the bucket is exhausted.
Metrics
Prometheus endpoint atGET /metrics:
dispatch_requests_total{chain_id, method, status}— request counterdispatch_request_duration_seconds{chain_id, method}— latency histogram