# `X402.RPC`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/rpc.ex#L1)

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

# `batch_request`

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

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

# `batch_result`

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

Per-request outcome inside a successful batch response.

# `block_reference`

```elixir
@type block_reference() ::
  String.t() | %{required(String.t()) =&gt; String.t() | boolean()}
```

A block tag, hex quantity, or EIP-1898 block-hash/number reference.

# `error`

```elixir
@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`

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

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

# `jsonrpc_error`

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

A JSON-RPC error object returned by the node.

# `t`

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

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

# `batch`
*since 0.6.0* 

```elixir
@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`
*since 0.6.0* 

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

# `chain_id`
*since 0.6.0* 

```elixir
@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).

# `decode_quantity`
*since 0.9.0* 

```elixir
@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}

# `get_code`
*since 0.6.0* 

```elixir
@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"}`.

# `new`
*since 0.6.0* 

```elixir
@spec new(keyword()) :: {:ok, t()} | {:error, :insecure_rpc_url}
```

Builds a validated RPC configuration.

Options are validated with `NimbleOptions`:

* `:rpc_url` (`t: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` (`t: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`
*since 0.6.0* 

```elixir
@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`
*since 0.6.0* 

```elixir
@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`).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
