Skip to main content

dispatch-service

Runs on the indexer alongside an Ethereum node. Validates TAP receipts, proxies JSON-RPC requests, signs responses, and persists receipts to PostgreSQL.

Routes

MethodPathDescription
POST/rpc/{chain_id}JSON-RPC request (single or batch)
GET/ws/{chain_id}WebSocket proxy for eth_subscribe
GET/healthLiveness check
GET/versionVersion info
GET/chainsList of supported chains
GET/block/{chain_id}Unauthenticated probe — returns current block number (used by gateways for QoS freshness scoring)

Request flow

Gateway POST /rpc/42161
  + TAP-Receipt: { signed EIP-712 receipt }


TAP middleware
  - recover signer from EIP-712 signature
  - check sender is in authorized_senders
  - check timestamp not stale (configurable window)
  - persist receipt to tap_receipts table (async)


Parse JSON-RPC method + params


Forward to backend Ethereum client (chain_id → backend URL mapping)


Sign response attestation:
  keccak256(chainId || method || paramsHash || responseHash || blockNumber || blockHash)


Return JSON-RPC response
  + X-Dispatch-Attestation: <signature>

TAP receipt validation

The service validates every incoming receipt before forwarding the request:
  • Signature: EIP-712 ECDSA recovery against GraphTallyCollector as verifying contract
  • Sender authorisation: recovered signer must be in authorized_senders config list
  • Staleness: timestamp_ns must be within the configured window
  • Data service: data_service field must match RPCDataService address
Invalid receipts are rejected with 400 Bad Request.

Archive tier detection

The service inspects block parameters to determine if a request requires archive state:
  • Hex block numbers below current head - 128 → routed to archive backend
  • String tags "earliest", "pending" → archive
  • JSON integers → parsed and compared against head
This allows a single endpoint to serve both standard and archive requests when both backends are configured.