Minimal Ethereum JSON-RPC client over Finch.
Provides exactly the read-only RPC surface local payment verification needs:
eth_call, eth_getCode, eth_chainId, and ordered batch requests. It is
not a general-purpose Ethereum client — there is no transaction signing, no
filter/subscription support, and no ABI layer.
Requires the optional :finch dependency at runtime; every request returns
{:error, :missing_dependency} when Finch is unavailable. Users bring their
own Finch pool, exactly as with X402.Facilitator.HTTP:
{:ok, rpc} =
X402.RPC.new(
rpc_url: "https://sepolia.base.org",
finch: MyApp.Finch
)
{:ok, "0x14a34"} = X402.RPC.chain_id(rpc)TLS Verification
rpc_url must use https:// (plain http:// is allowed only for
localhost), and TLS peer verification must be configured on the Finch
pool — see X402.Facilitator.HTTP.secure_pool_opts/0 for a ready-made
configuration.
Telemetry
Every request emits [:x402, :rpc, :request] with :status (:ok or
:error) and :method metadata (the string method name, or :batch).
Summary
Types
One request in a batch: a JSON-RPC method name and its params.
Per-request outcome inside a successful batch response.
A block tag, hex quantity, or EIP-1898 block-hash/number reference.
Structured request errors.
A Finch pool identifier, as accepted by Finch.request/3.
A JSON-RPC error object returned by the node.
Functions
Performs an ordered JSON-RPC batch request in one HTTP round-trip.
Performs an eth_call against the given block (default "latest").
Returns the chain id via eth_chainId, as the node's hex string
(for example "0x14a34" for Base Sepolia).
Decodes a canonical, uint256-bounded JSON-RPC hex quantity.
Returns the bytecode at address via eth_getCode (default block
"latest").
Builds a validated RPC configuration.
Performs a single JSON-RPC request.
Validates that a value is an %X402.RPC{} configuration.
Types
One request in a batch: a JSON-RPC method name and its params.
@type batch_result() :: {:ok, term()} | {:error, {:jsonrpc_error, jsonrpc_error()}}
Per-request outcome inside a successful batch response.
A block tag, hex quantity, or EIP-1898 block-hash/number reference.
@type error() :: :missing_dependency | :insecure_rpc_url | {:transport_error, term()} | {:http_error, non_neg_integer()} | {:invalid_response, term()} | {:jsonrpc_error, jsonrpc_error()}
Structured request errors.
A Finch pool identifier, as accepted by Finch.request/3.
A JSON-RPC error object returned by the node.
@type t() :: %X402.RPC{ finch: finch_name(), rpc_url: String.t(), timeout: pos_integer() }
Validated JSON-RPC endpoint configuration built by new/1.
Functions
@spec batch(t(), [batch_request()]) :: {:ok, [batch_result()]} | {:error, error()}
Performs an ordered JSON-RPC batch request in one HTTP round-trip.
Takes a list of {method, params} tuples and returns {:ok, results}
where results has one entry per request, in request order (responses
are re-ordered by id): each entry is {:ok, result} or
{:error, {:jsonrpc_error, error}}. Transport-level failures fail the
whole batch with {:error, reason}.
An empty request list returns {:ok, []} without any HTTP call.
@spec call(t(), map(), block_reference()) :: {:ok, String.t()} | {:error, error()}
Performs an eth_call against the given block (default "latest").
The call object accepts :to, :data, and optionally :from (atom or
string keys). block may also be an EIP-1898 reference such as
%{"blockHash" => hash, "requireCanonical" => true}.
Returns the raw 0x-prefixed return data.
Returns the chain id via eth_chainId, as the node's hex string
(for example "0x14a34" for Base Sepolia).
@spec decode_quantity(term()) :: {:ok, non_neg_integer()} | {:error, :invalid_quantity}
Decodes a canonical, uint256-bounded JSON-RPC hex quantity.
Rejects signs, empty digits, and leading zeros other than "0x0".
Examples
iex> X402.RPC.decode_quantity("0x2105")
{:ok, 8453}
iex> X402.RPC.decode_quantity("0x00")
{:error, :invalid_quantity}
@spec get_code(t(), String.t(), block_reference()) :: {:ok, String.t()} | {:error, error()}
Returns the bytecode at address via eth_getCode (default block
"latest").
A plain externally-owned account returns {:ok, "0x"}.
Builds a validated RPC configuration.
Options are validated with NimbleOptions:
:rpc_url(String.t/0) - Required. The JSON-RPC endpoint URL. Must usehttps://; plainhttp://is accepted only forlocalhost(local development nodes and tests).:finch- Required. The Finch pool name (atom, pid, or{:via, module, term}).:timeout(pos_integer/0) - Receive timeout per HTTP request, in milliseconds. The default value is5000.
Returns {:error, :insecure_rpc_url} when rpc_url does not use
https:// (with a localhost exemption for development nodes).
Examples
iex> {:ok, rpc} = X402.RPC.new(rpc_url: "https://sepolia.base.org", finch: MyFinch)
iex> rpc.timeout
5000
iex> X402.RPC.new(rpc_url: "http://rpc.example.com", finch: MyFinch)
{:error, :insecure_rpc_url}
Performs a single JSON-RPC request.
Returns the decoded "result" value on success, or a structured error —
node-side failures come back as {:error, {:jsonrpc_error, %{code: _, message: _, data: _}}} and transport failures as
{:error, {:transport_error, reason}}.
Validates that a value is an %X402.RPC{} configuration.
Designed for NimbleOptions custom validation (used by
X402.Verify.EVM).