Skip to content

v1.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 30 May 06:44
v1.0.0
27e3afe

v1.0.0 — first stable release

exec-rest-api is a single-binary REST + SSE proxy that sits in front of any Ethereum execution client (Geth, Nethermind, Erigon, Reth, anvil, …). It turns the JSON-RPC interface into a developer-friendly HTTP API: decimal numbers instead of hex, RFC 9457 problem details instead of {"error": {"code": -32000, …}}, RFC 8288 cursor pagination, content negotiation for raw RLP, and Server-Sent Event streams instead of a parallel WebSocket protocol.

This is the first tagged release intended for production use. The public surface — endpoints, response shapes, error types, SSE framing, configuration flags, headers, and /metrics series — is committed under semver for the 1.x line.

Endpoint surface

Chain & health

  • GET /chain, /chain/id, /chain/sync-status, /chain/client, /chain/peers
  • GET /health, /health/ready

Blocks

  • GET /blocks/{id}, /blocks/{id}/header, /blocks/{id}/transactions, /blocks/{id}/transactions/{index}, /blocks/{id}/transaction-count, /blocks/{id}/receipts, /blocks/{id}/traces
  • POST /blocks/{id}/traces/replay, /blocks/{id}/debug-traces

Accounts

  • GET /accounts/{addr} (composite), /accounts/{addr}/balance, /nonce, /code, /storage/{slot}, /proof, /transaction-template
  • POST /accounts/{addr}/proof/search

Transactions

  • GET /transactions/{hash}, /transactions/{hash}/receipt, /transactions/{hash}/trace
  • POST /transactions (raw tx submission, JSON or RLP body), /transactions/{hash}/debug-trace

Logs & traces

  • GET /logs, POST /logs/search (paginated)
  • GET /traces, POST /traces/search, GET /traces/{txHash}/{traceAddress} (paginated)

Computed reads

  • POST /call, /gas-estimate, /access-list, /simulate
  • POST /traces/call, /traces/call-many, /traces/raw-transaction, /traces/replay-transaction/{hash}
  • POST /debug-traces/call

Gas

  • GET /gas/price, /gas/priority-fee, /gas/blob-base-fee, /gas/fee-history

Streams (SSE)

  • GET /streams/blocks, /streams/logs, /streams/pending-transactions, /streams/sync-status

Utilities & observability

  • POST /utils/keccak256
  • GET /metrics (Prometheus text)

Design highlights

  • No hex on the wire. Block numbers, gas, nonce, log index, etc. are JSON numbers when safe and decimal strings when they may exceed 2⁵³ (wei).
  • RFC 9457 Problem Details for every error, including a discriminated transaction-rejected/* family for mempool rejections and a path-not-supported 404 that includes a catalogue of available URLs.
  • Reverts are not errors. eth_call / eth_estimateGas / eth_simulateV1 / trace-call return 200 with { "reverted": true, "data": "0x…", "reason": …, "panicCode": … } — Error(string) and Panic(uint256) are ABI-decoded for you.
  • EIP-7702 delegation detection. GET /accounts/{addr} surfaces a delegatedTo field when the account holds the 0xef0100-prefixed delegation marker.
  • Cursor pagination. Opaque base64url cursors for /logs and /traces carry the original filter, the frozen toBlock, and a boundary block hash for reorg detection (409 chain-reorged). Internal block-range chunking honours upstream caps automatically.
  • RLP content negotiation. Accept: application/vnd.ethereum.rlp on /blocks/{id}, /blocks/{id}/header, /blocks/{id}/receipts, and /transactions/{hash} returns raw bytes verbatim from debug_getRaw*. POST /transactions symmetrically accepts Content-Type: application/vnd.ethereum.rlp.
  • SSE multiplexing. One upstream eth_subscribe per unique (kind, params) is fanned out to all client streams. WS reconnects re-issue subscriptions and emit event: gap; Last-Event-ID replay is supported for blocks and logs within a configurable window. Reorgs re-emit affected logs with removed: true.
  • Per-request observability. Every response carries X-Request-ID; non-streaming responses also carry X-Upstream-Method (the JSON-RPC method(s) the proxy invoked) and X-Block-Height (current chain head when known).
  • Prometheus metrics. Seven series cover HTTP request count/duration, upstream call count/duration, live SSE connections, live upstream subscriptions, and chain head block. Hand-written exporter — no client library dependency.

Operational footprint

  • Single Python process; pure asyncio (aiohttp). One config flag is required (--upstream-http); everything else has defensible defaults.
  • One runtime dependency: aiohttp. No web framework, no ORM, no scheduler, no Prometheus client.
  • Structured JSON logging (auto-selected on non-TTY) with request_id, method, path, status, latency_ms, upstream_method, upstream_latency_ms on every line.
  • CLI flags and env vars are interchangeable (--upstream-http ↔ EXEC_REST_API_UPSTREAM_HTTP); flags win.

Install

Four ways to run it:

pipx install exec-rest-api           # PyPI (recommended)
pip install exec-rest-api            # any virtualenv

# Single-file .pyz (Python 3.10+ on PATH)
curl -LO https://github.com/ajsutton/exec-rest-api/releases/download/v1.0.0/exec-rest-api.pyz
chmod +x exec-rest-api.pyz && ./exec-rest-api.pyz --upstream-http http://localhost:8545

# OCI image (multi-arch: linux/amd64, linux/arm64)
docker run --rm -p 8080:8080 ghcr.io/ajsutton/exec-rest-api:1.0.0 \
  --upstream-http http://host.docker.internal:8545

Supply-chain verification

All artefacts are signed via cosign keyless using GitHub Actions OIDC. A CycloneDX SBOM is attached.

# .pyz
cosign verify-blob \
  --certificate exec-rest-api.pyz.crt --signature exec-rest-api.pyz.sig \
  --certificate-identity 'https://github.com/ajsutton/exec-rest-api/.github/workflows/release.yml@refs/tags/v1.0.0' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  exec-rest-api.pyz

# OCI image
cosign verify \
  --certificate-identity 'https://github.com/ajsutton/exec-rest-api/.github/workflows/release.yml@refs/tags/v1.0.0' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  ghcr.io/ajsutton/exec-rest-api:1.0.0

See docs/operations.md for the hardened systemd unit, hardened docker run invocation, and Prometheus scrape config.

Compatibility

  • Python 3.10, 3.11, 3.12 (verified in CI on ubuntu, macos, windows).
  • Tested upstreams: Geth, Erigon, Reth, anvil. Any execution client implementing the standard eth_* / web3_* / net_* / trace_* / debug_* JSON-RPC methods should work; methods the upstream doesn't implement surface as 501 method-not-supported-by-upstream.

What's next

No breaking changes planned for 1.x. Bug fixes, additional content-negotiation representations, additional Prometheus series, and operator-side polish (improved JSON log fields, more granular metrics labels) are likely; the surface that's documented here will keep working.