X402.RPC (X402 v0.6.0)

Copy Markdown View Source

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.

Structured request errors.

A Finch pool identifier, as accepted by Finch.request/3.

A JSON-RPC error object returned by the node.

t()

Validated JSON-RPC endpoint configuration built by new/1.

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).

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

batch_request()

@type batch_request() :: {String.t(), list()}

One request in a batch: a JSON-RPC method name and its params.

batch_result()

@type batch_result() :: {:ok, term()} | {:error, {:jsonrpc_error, jsonrpc_error()}}

Per-request outcome inside a successful batch response.

error()

@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.

finch_name()

@type finch_name() :: atom() | pid() | {:via, module(), term()}

A Finch pool identifier, as accepted by Finch.request/3.

jsonrpc_error()

@type jsonrpc_error() :: %{
  code: integer() | nil,
  message: String.t() | nil,
  data: term()
}

A JSON-RPC error object returned by the node.

t()

@type t() :: %X402.RPC{
  finch: finch_name(),
  rpc_url: String.t(),
  timeout: pos_integer()
}

Validated JSON-RPC endpoint configuration built by new/1.

Functions

batch(rpc, requests)

(since 0.6.0)
@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.

call(rpc, call_object, block \\ "latest")

(since 0.6.0)
@spec call(t(), map(), String.t()) :: {: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). Returns the raw 0x-prefixed return data.

chain_id(rpc)

(since 0.6.0)
@spec chain_id(t()) :: {:ok, String.t()} | {:error, error()}

Returns the chain id via eth_chainId, as the node's hex string (for example "0x14a34" for Base Sepolia).

get_code(rpc, address, block \\ "latest")

(since 0.6.0)
@spec get_code(t(), String.t(), String.t()) :: {:ok, String.t()} | {:error, error()}

Returns the bytecode at address via eth_getCode (default block "latest").

A plain externally-owned account returns {:ok, "0x"}.

new(opts)

(since 0.6.0)
@spec new(keyword()) :: {:ok, t()} | {:error, :insecure_rpc_url}

Builds a validated RPC configuration.

Options are validated with NimbleOptions:

  • :rpc_url (String.t/0) - Required. The JSON-RPC endpoint URL. Must use https://; plain http:// is accepted only for localhost (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 is 5000.

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}

request(rpc, method, params)

(since 0.6.0)
@spec request(t(), String.t(), list()) :: {:ok, term()} | {:error, error()}

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}}.

validate_config(rpc)

(since 0.6.0)
@spec validate_config(term()) :: {:ok, t()} | {:error, String.t()}

Validates that a value is an %X402.RPC{} configuration.

Designed for NimbleOptions custom validation (used by X402.Verify.EVM).