Shared JSON-RPC transport and typed Ethereum client.
Single calls and batches use config :cartouche, :ethereum_node for the URL,
config :cartouche, Onchain.RPC, [...] for transport defaults, and
config :cartouche, :req_options, [...] for global Req options. Per-call
:rpc_url / :ethereum_node and :req_options override these defaults.
A missing URL returns {:error, {:missing_option, :ethereum_node}}.
The former :onchain batch transport configuration is no longer used.
Both paths emit [:onchain, :rpc, :request] telemetry spans. Refusals now
uniformly return :method_not_found, :namespace_unavailable, or :unavailable
tags; other single-call error shapes remain unchanged. See Onchain.RPC's
node-capability refusal documentation for the live Alchemy provenance and
message-sensitive meaning of -32600 and -32001.
Node-capability refusals
A weaker node than this repo's archive endpoint routinely refuses a call for
one of three reasons: the method is not implemented, the provider has disabled
that namespace on the current plan, or the node cannot complete the request
(historical state pruned, method the gateway does not route, transient
overload). Classification runs in Onchain.RPC on the shared single-call and
batch paths (both item-level and top-level batch errors),
so a codegen'd wrapper, a hand-written wrapper, call/3 and a batched call
apply the same rules to the same wire response.
The classifier is uniform; the provider's wire response is not. Verified live
on Alchemy mainnet 2026-08-25: historical eth_feeHistory at block 20_000_000
answers -32001 "Unable to complete request at this time." as a single call
(classified {:unavailable, map}), but the byte-identical request inside a
JSON-RPC array batch answers -32000 "Internal error" — alone or alongside a
healthy call — which stays {:rpc_error, map}. That is not a gap in the
classifier: -32000 "Internal error" is indistinguishable from a genuine
internal failure, and tagging it would invent a distinction the node does not
make. Batching can therefore downgrade a classifiable refusal to an
unclassifiable one; when a caller needs the capability signal, issue that
probe as a single call. Unrecognized codes keep {:error, {:rpc_error, map}}
unchanged — the classifier names distinguishable cases, it does not guess.
Each classified map retains the observed :code and :message (and :data
when the node sent it). Branch on the tag; inspect the map when you need the
wire detail.
{:error, {:method_not_found, map}}— this node does not implement the method. The reliable standard signal is JSON-RPC-32601("Method not found"). Hosted providers often misuse-32600("Invalid Request") instead: Alchemy mainnet answers-32600"Unsupported method: <method> on ETH_MAINNET"and-32600"eth_baseFee is not available on the ETH_MAINNET...". Those two message shapes are pinned from live responses; a bare-32600without them still passes through as{:rpc_error, map}, because other nodes use that code for genuinely malformed requests. Callers should pick a portable construction (seeOnchain.RPC.base_fee/1) or a different method.{:error, {:namespace_unavailable, map}}— the method exists but this provider plan has disabled the namespace. Observed on Alchemy mainnet as-32600"<method> is not available on the Free tier - upgrade to Pay As You Go, or Enterprise for access."fortrace_*anddebug_*. Callers should use a plan that serves the namespace, or avoid it.{:error, {:unavailable, map}}— the node refused to complete a request it otherwise accepts. Observed on Alchemy mainnet as HTTP 503 /-32001"Unable to complete request at this time."foreth_feeHistoryat block 20_000_000, whileeth_feeHistoryat"latest"succeeds on the same URL. The identical wire error is also returned for some unimplemented methods (erigon_getHeaderByNumber), so this is not a unique pruned-history signal — treat it as "this node cannot serve this request". Callers that need historical state should retry against an archive endpoint.
Codes the classifier does not name, including -32602 ("Invalid params" —
also what reth answers for eth_getStorageValues with empty params, which is
indistinguishable from a genuine bad-params error), reach the caller as
{:error, {:rpc_error, map}} exactly as before.
Input adapters and bang functions
eth_call/3, eth_estimate_gas/2, eth_get_code/2, and
eth_send_raw_transaction/2 accept the former Onchain inputs and delegate
to the typed methods. Hex-returning adapters preserve hex results and wrap
unclassified RPC errors as {:rpc_error, map}. The typed methods keep their
native error shapes. Both surfaces preserve classified node refusals.
get_transaction_receipt/2 returns Onchain.Receipt, and
get_block_by_number/2 returns Onchain.Block; logs use Onchain.Filter.Log.
Bang variants unwrap {:ok, value} and raise RuntimeError on {:error, reason}.
Error Format
Hex-input adapters (eth_call/3, eth_estimate_gas/2, eth_get_code/2,
eth_send_raw_transaction/2, get_transaction_receipt/2, fee_history/2,
and call/3) return {:error, {:rpc_error, map}} for an unclassified RPC
error. A code: 3 revert map includes :revert (raw bytes) and :data
(0x hex). When :errors names a matching custom-error signature, the map
also includes :error_abi and :error_params. Classified node refusals
stay :method_not_found, :namespace_unavailable, or :unavailable.
Typed methods such as get_balance/2, get_block_by_number/2, and
call_trx/2 return the transport error directly.
Local signing through Onchain.Signer is the normal route for submitting
transactions (eth_sendRawTransaction). The node-custody methods
(accounts/1, coinbase/1, fill_transaction/2, sign/3,
sign_transaction/2, send_transaction/2) exist for nodes that hold keys.
The distinction is not key custody as such (Onchain.Signer.CloudKMS
already signs with a key cartouche does not hold) but which side constructs
the transaction: those methods move envelope construction, nonce and fee
policy, EIP-155 v derivation, and low-s normalization into the node.
sign/3 (eth_sign) takes a message, not a pre-computed digest: the node
returns an EIP-191 signature over the data it is handed (execution-apis,
src/eth/sign.yaml). The method is historically dangerous because early
implementations signed the bytes verbatim, which let a caller pass a
transaction hash and get it signed; most nodes ship it disabled today.
API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
chain_id | 1 | Get the network chain ID. | opts: value |
block_number | 1 | Get the current block height. | opts: value |
blob_base_fee! | 1 | Get the current base fee per blob gas. Raises on error. | opts: value |
fee_history! | 2 | Fetch fee history. Raises on error. | block_count: value, opts: value |
fee_history | 2 | Fetch base-fee history and per-block priority-fee percentiles (eth_feeHistory). | block_count: value, opts: value |
batch | 2 | Generic JSON-RPC array batch — invoke many methods in one HTTP request. | requests: value, opts: value |
call! | 3 | Generic JSON-RPC passthrough. Raises on error. | method: value, params: value, opts: value |
call | 3 | Generic JSON-RPC passthrough — invoke any method not covered by a named wrapper. | method: value, params: value, opts: value |
eth_get_code! | 2 | Fetch contract bytecode at an address. Raises on error. | address: value, opts: value |
eth_get_code | 2 | Fetch contract bytecode at an address (eth_getCode). | address: value, opts: value |
get_transaction_count! | 2 | Get the transaction count (nonce) of an address. Raises on error. | address: value, opts: value |
get_transaction_receipt! | 2 | Get a transaction receipt by hash. Raises on error. | tx_hash: value, opts: value |
get_transaction_receipt | 2 | Get a transaction receipt by hash (eth_getTransactionReceipt). | tx_hash: value, opts: value |
chain_id! | 1 | Get the network chain ID. Raises on error. | opts: value |
get_block_access_list! | 2 | Fetch an EIP-7928 block access list. Raises on error. | block: value, opts: value |
get_block_access_list | 2 | Fetch an EIP-7928 block access list (eth_getBlockAccessList). | block: value, opts: value |
get_block_by_number! | 2 | Fetch a block by number or tag. Raises on error. | block_id: value, opts: value |
block_number! | 1 | Get the current block height. Raises on error. | opts: value |
get_balance! | 2 | Get the ETH balance of an address in wei. Raises on error. | address: value, opts: value |
eth_send_raw_transaction! | 2 | Broadcast a signed transaction. Raises on error. | data: value, opts: value |
eth_send_raw_transaction | 2 | Broadcast a signed transaction. | data: value, opts: value |
eth_estimate_gas! | 2 | Estimate the gas a transaction would consume. Raises on error. | tx_params: value, opts: value |
eth_estimate_gas | 2 | Estimate the gas a transaction would consume. | tx_params: value, opts: value |
eth_call! | 3 | Execute a read-only contract call. Raises on error. | address: value, data: value, opts: value |
eth_call | 3 | Execute a read-only contract call (eth_call). | address: value, data: value, opts: value |
send_transaction | 2 | Ask the node to sign and broadcast a transaction with a managed account. | trx: value, opts: value |
sign_transaction | 2 | Ask the node to sign a transaction with a managed account. | trx: value, opts: value |
sign | 3 | Ask the node to sign a message under EIP-191 with a managed account (eth_sign). | account: value, message: value, opts: value |
fill_transaction | 2 | Ask the node to populate missing nonce, gas, and fee fields without signing. | trx: value, opts: value |
coinbase | 1 | Return the node's coinbase (fee recipient) address. | opts: value |
accounts | 1 | List addresses the node manages. | opts: value |
get_filter_logs | 2 | Return every log matching a log filter, not only changes since the last poll. | filter_id: value, opts: value |
eth_get_logs | 2 | Fetch event logs matching a stateless filter (eth_getLogs). | filter: value, opts: value |
new_pending_transaction_filter | 1 | Create a node-side filter that records new pending transaction hashes. | opts: value |
new_block_filter | 1 | Create a node-side filter that records new block hashes. | opts: value |
execute_trx | 3 | Prepare, sign, and submit a transaction to the Ethereum network. | contract: value, call_data: value, opts: value |
prepare_trx | 3 | Prepare and sign a transaction for later submission. | contract: value, call_data: value, opts: value |
fee_history | 2 | Fetch and decode EIP-1559 fee history data. | opts: value |
max_priority_fee_per_gas | 1 | Fetch the current max priority fee per gas. | opts: value |
blob_base_fee | 1 | Fetch the current base fee per blob gas. | opts: value |
base_fee | 1 | Fetch the EIP-1559 base fee per gas for the next block. | opts: value |
eth_get_proof | 3 | Fetch an EIP-1186 account and storage proof. | address: value, storage_keys: value, opts: value |
eth_get_storage_at | 3 | Read a 32-byte contract storage word. | address: value, slot: value, opts: value |
gas_price | 1 | Fetch the current legacy gas price. | opts: value |
debug_trace_call | 2 | Trace a transaction call speculatively with the debug trace API. | trx: value, opts: value |
trace_call_many | 2 | Trace multiple transaction calls speculatively with the parity trace API. | trxs: value, opts: value |
trace_call | 2 | Trace a transaction call speculatively with the parity trace API. | trx: value, opts: value |
trace_trx | 2 | Fetch parity-style traces for a transaction by transaction hash. | trx_id: value, opts: value |
eth_get_block_receipts | 2 | Fetch every receipt in a block (eth_getBlockReceipts). | block: value, opts: value |
eth_get_transaction_by_block_number_and_index | 3 | Fetch one transaction by block number or tag and position (eth_getTransactionByBlockNumberAndIndex). | block: value, transaction_index: value, opts: value |
eth_get_transaction_by_block_hash_and_index | 3 | Fetch one transaction by block hash and position (eth_getTransactionByBlockHashAndIndex). | block_hash: value, transaction_index: value, opts: value |
eth_get_transaction_by_hash | 2 | Fetch one transaction by hash (eth_getTransactionByHash). | tx_hash: value, opts: value |
get_trx_receipt | 2 | Fetch and decode a transaction receipt by transaction hash. | trx_id: value, opts: value |
get_block_by_hash | 2 | Fetch a block by its 32-byte block hash. | block_hash: value, opts: value |
get_block_by_number | 2 | Fetch a block by block number or block tag. | block_number: value, opts: value |
web3_client_version | 1 | Fetch the node client version string. | opts: value |
net_peer_count | 1 | Fetch the number of peers currently connected to the node. | opts: value |
net_listening | 1 | Ask whether the node is listening for network connections. | opts: value |
eth_get_block_transaction_count_by_number | 2 | Count transactions in the block with the given number or tag. | block: value, opts: value |
eth_get_block_transaction_count_by_hash | 2 | Count transactions in the block with the given hash. | block_hash: value, opts: value |
eth_syncing | 1 | Fetch the node's sync status. | opts: value |
eth_block_number | 1 | Fetch the current Ethereum block number. | opts: value |
get_transaction_count | 2 | Fetch an account transaction count at a block selector. | address: value, opts: value |
get_balance | 2 | Fetch an account ETH balance at a block selector. | address: value, opts: value |
get_code | 2 | Fetch contract bytecode at an address and block selector. | address: value, opts: value |
eth_capabilities | 1 | Fetch the node's effective historical-data capabilities. | opts: value |
eth_config | 1 | Fetch the node's EIP-7910 chain and fork configuration. | opts: value |
eth_chain_id | 1 | Fetch the current Ethereum chain id. | opts: value |
estimate_gas | 2 | Estimate gas for a transaction or call object. | trx: value, opts: value |
create_access_list | 2 | Generate an EIP-2930 access list for a transaction or call object. | trx: value, opts: value |
call_trx | 2 | Run eth_call against a transaction or call object without submitting it. | trx: value, opts: value |
send_trx | 2 | Submit a signed Ethereum transaction to the network. | trx: exchange_data, opts: value |
get_nonce | 2 | Fetch an account nonce at a block selector. | account: value, opts: value |
send_batch | 2 | Send several Ethereum JSON-RPC requests as one array batch and return raw results in request order. | requests: value, opts: value |
send_rpc | 3 | Send one Ethereum JSON-RPC request and optionally decode the result. | method: value, params: value, opts: value |
Summary
Types
Decoded eth_createAccessList result, retaining an optional execution error.
Error returned when JSON encoding rejects the outbound request body.
Structured JSON-RPC error envelope returned by an Ethereum node or by local response validation.
All values that can appear inside an {:error, reason} tuple returned by send_rpc/3.
Functions
RPC call to list accounts the node holds keys for.
Returns the next block's base fee in wei via eth_feeHistory(1, "latest", []).
Invoke many raw JSON-RPC calls in one HTTP request.
RPC call to get the current base fee per blob gas.
Get the current base fee per blob gas. Raises on error.
Get the current block height.
Get the current block height. Raises on error.
Generic JSON-RPC passthrough — invoke any method not covered by a named wrapper.
Generic JSON-RPC passthrough. Raises on error.
RPC call to call a transaction and preview results.
Get the network chain ID.
Get the network chain ID. Raises on error.
RPC call to get the node's coinbase address.
Generates the access list and gas used by a call at a block selector.
RPC to trace a transaction call speculatively via debug API.
RPC call to call to estimate gas used by a given call.
RPC to get the current block number.
Execute a read-only contract call (eth_call).
Execute a read-only contract call. Raises on error.
Returns the node's head and resource-retention capabilities.
RPC to get the current chain id.
Returns the current, next, and last configured Ethereum forks reported by the node.
Estimate the gas a transaction would consume.
Estimate the gas a transaction would consume. Raises on error.
Returns every receipt in the block identified by number, tag, or hash.
Returns how many transactions are in the block identified by block_hash.
Returns how many transactions are in the block identified by number or tag.
Fetch contract bytecode at an address (eth_getCode).
Fetch contract bytecode at an address. Raises on error.
Stateless eth_getLogs query.
Fetches an EIP-1186 account and storage proof as Onchain.RPC.Proof.
Reads a 32-byte storage word via eth_getStorageAt (execution-apis v1.0.0-beta.7).
Returns the transaction at transaction_index in the block identified by block_hash.
Returns the transaction at transaction_index in the block identified by number or tag.
Returns the transaction identified by tx_hash.
Broadcast a signed transaction.
Broadcast a signed transaction. Raises on error.
Returns the node's sync status from eth_syncing.
Helper function to work with other Cartouche modules to get a nonce, sign a transction, and transmit it to the network.
RPC call to call to get the Eip-1559 fee history data.
Fetch base-fee history and per-block priority-fee percentiles (eth_feeHistory).
Fetch fee history. Raises on error.
RPC call to fill missing transaction fields via eth_fillTransaction.
RPC call to call to get the current gas price.
RPC to get an account's eth balance.
Get the ETH balance of an address in wei. Raises on error.
Fetch an EIP-7928 block access list (eth_getBlockAccessList).
Fetch an EIP-7928 block access list. Raises on error.
RPC to get a block by its block hash.
RPC to get a block by its block number.
Fetch a block by number or tag. Raises on error.
RPC call to get code for a contract at an address.
RPC call to fetch the full log backlog for a filter.
RPC call to get account nonce.
RPC to get an account's transaction count (i.e. nonce)
Get the transaction count (nonce) of an address. Raises on error.
Get a transaction receipt by hash (eth_getTransactionReceipt).
Get a transaction receipt by hash. Raises on error.
RPC call to get a transaction receipt. Note, this will return {:ok, %Onchain.Receipt{}} or {:ok, nil} if the receipt is not yet available.
RPC call to call to get the current max priority fee per gas.
Returns whether the node is listening for peer connections (net_listening).
Returns the node's connected peer count (net_peerCount).
RPC call to create a new block filter.
RPC call to create a new pending-transaction filter.
Helper function to work with other Cartouche modules to get a nonce, sign a transction, and prepare it to be submitted on-chain.
Sends a JSON-RPC array batch and returns raw results in request order. Uses the same options as send_rpc/3.
Simple RPC client for a JSON-RPC Ethereum node.
RPC call to eth_sendTransaction.
RPC call to send a raw transaction.
RPC call to eth_sign.
RPC call to eth_signTransaction.
RPC to trace a transaction call speculatively.
RPC to trace many transaction calls speculatively.
RPC call to get a transaction receipt
Returns the node client's version string (web3_clientVersion).
Types
@type access_list_result() :: %{ :access_list => Onchain.Transaction.V_2930.access_list(), :gas_used => non_neg_integer(), optional(:error) => String.t() }
Decoded eth_createAccessList result, retaining an optional execution error.
@type invalid_params_error() :: {:invalid_params, Exception.t()}
Error returned when JSON encoding rejects the outbound request body.
@type rpc_error() :: %{ :code => integer(), :message => String.t(), optional(:revert) => binary(), optional(:error_abi) => String.t(), optional(:error_params) => [term()], optional(:trace) => term() }
Structured JSON-RPC error envelope returned by an Ethereum node or by local response validation.
@type send_rpc_error() :: rpc_error() | invalid_params_error() | Req.Response.t() | String.t() | {atom(), term()}
All values that can appear inside an {:error, reason} tuple returned by send_rpc/3.
Functions
RPC call to list accounts the node holds keys for.
Examples
iex> Onchain.RPC.accounts()
{:ok, [<<64, 125, 115, 216, 164, 158, 235, 133, 211, 44, 244, 101, 80, 125, 215, 29, 80, 113, 0, 193>>]}
@spec base_fee(keyword()) :: {:ok, non_neg_integer()} | {:error, term()}
Returns the next block's base fee in wei via eth_feeHistory(1, "latest", []).
The final baseFeePerGas entry is the next block's fee, as specified in
execution-apis v1.0.0-beta.7. This avoids hosted endpoints' differing handling
of the pending block tag. Uses the shared fee-history decoder and transport;
node errors propagate unchanged. An incomplete fee window returns
{:error, :invalid_base_fee_history}.
eth_baseFee landed on execution-apis main on 2026-06-15, but is in no
tagged release (latest verified: v1.0.0-beta.7). Alchemy and Infura mainnet
refused it in live probes on 2026-10-01; see docs/base-fee-portability.md.
Invoke many raw JSON-RPC calls in one HTTP request.
Each request is a {method, params} tuple. Results are returned in request
order even when the node returns the JSON-RPC response array out of order.
@spec blob_base_fee(Keyword.t()) :: {:ok, non_neg_integer()} | {:error, term()}
RPC call to get the current base fee per blob gas.
Examples
iex> Onchain.RPC.blob_base_fee()
{:ok, 42}
@spec blob_base_fee!(keyword()) :: non_neg_integer()
Get the current base fee per blob gas. Raises on error.
Parameters
opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Base fee per blob gas in wei (non_neg_integer)
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
}
},
returns: %{
type: :non_neg_integer,
description: "Base fee per blob gas in wei"
}
}
@spec block_number(keyword()) :: {:ok, non_neg_integer()} | {:error, term()}
Get the current block height.
Parameters
opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Current block number ({:ok, non_neg_integer} | {:error, term})
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
}
},
returns: %{
type: "{:ok, non_neg_integer} | {:error, term}",
description: "Current block number"
}
}
@spec block_number!(keyword()) :: non_neg_integer()
Get the current block height. Raises on error.
Parameters
opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Current block number (non_neg_integer)
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
}
},
returns: %{type: :non_neg_integer, description: "Current block number"}
}
Generic JSON-RPC passthrough — invoke any method not covered by a named wrapper.
Parameters
method- JSON-RPC method name, e.g. "eth_getStorageAt", "debug_traceTransaction", "trace_call", "eth_feeHistory" (value)params- List of params for the method, in the order the JSON-RPC spec requires (value)opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Raw decoded JSON result (no further decoding — caller knows what they asked for) or wrapped error tuple ({:ok, term} | {:error, term})
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
},
params: %{
description: "List of params for the method, in the order the JSON-RPC spec requires",
kind: :value
},
method: %{
description: "JSON-RPC method name, e.g. \"eth_getStorageAt\", \"debug_traceTransaction\", \"trace_call\", \"eth_feeHistory\"",
kind: :value
}
},
returns: %{
type: "{:ok, term} | {:error, term}",
description: "Raw decoded JSON result (no further decoding — caller knows what they asked for) or wrapped error tuple"
}
}
Generic JSON-RPC passthrough. Raises on error.
Parameters
method- JSON-RPC method name (value)params- List of params for the method (value)opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Raw decoded JSON result (term)
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
},
params: %{description: "List of params for the method", kind: :value},
method: %{description: "JSON-RPC method name", kind: :value}
},
returns: %{type: :term, description: "Raw decoded JSON result"}
}
@spec call_trx( Onchain.Transaction.V1.t() | Onchain.Transaction.V2.t() | Onchain.Transaction.Call.t() | map(), Keyword.t() ) :: {:ok, binary()} | {:error, term()}
RPC call to call a transaction and preview results.
Examples
iex> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Onchain.RPC.call_trx()
{:ok, <<0x0c>>}
iex> Onchain.Transaction.V2.new(1, {1, :gwei}, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, [<<2::160>>, <<3::160>>], :goerli)
iex> |> Onchain.RPC.call_trx()
{:ok, <<0x0d>>}
iex> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Onchain.RPC.call_trx(decode: :hex_unsigned)
{:ok, 0x0c}
iex> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<10::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Onchain.RPC.call_trx()
{:error, %{code: 3, message: "execution reverted", revert: <<61, 115, 139, 46>>}}
iex> errors = ["Unauthorized()", "BadNonce()", "NotEnoughSigners()", "NotActiveWithdrawalAddress()", "NotActiveOperator()", "DuplicateSigners()"]
iex> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<10::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Onchain.RPC.call_trx(errors: errors)
{:error, %{code: 3, message: "execution reverted", error_abi: "NotActiveOperator()", error_params: [], revert: <<61, 115, 139, 46>>}}
iex> errors = ["Cool(uint256,string)"]
iex> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<11::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Onchain.RPC.call_trx(errors: errors)
{:error, %{code: 3, message: "execution reverted", error_abi: "Cool(uint256,string)", error_params: [1, "cat"], revert: Onchain.ABI.encode("Cool(uint256,string)", [1, "cat"])}}
iex> errors = ["Cool(uint256,string)"]
iex> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<12::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Onchain.RPC.call_trx(errors: errors)
{:error, %{code: 3, message: "execution reverted", revert: <<>>}}
iex> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<13::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Onchain.RPC.call_trx()
{:error, %{code: -32602, message: "Failed to decode transaction"}}
@spec chain_id(keyword()) :: {:ok, non_neg_integer()} | {:error, term()}
Get the network chain ID.
Parameters
opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Chain ID (1 = mainnet, 11155111 = sepolia, etc.) ({:ok, non_neg_integer} | {:error, term})
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
}
},
returns: %{
type: "{:ok, non_neg_integer} | {:error, term}",
description: "Chain ID (1 = mainnet, 11155111 = sepolia, etc.)"
}
}
@spec chain_id!(keyword()) :: non_neg_integer()
Get the network chain ID. Raises on error.
Parameters
opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Chain ID integer (non_neg_integer)
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
}
},
returns: %{type: :non_neg_integer, description: "Chain ID integer"}
}
RPC call to get the node's coinbase address.
Examples
iex> Onchain.RPC.coinbase()
{:ok, <<254, 59, 85, 126, 143, 182, 43, 137, 244, 145, 107, 114, 27, 229, 92, 235, 130, 141, 189, 115>>}
@spec create_access_list( Onchain.Transaction.V1.t() | Onchain.Transaction.V2.t() | Onchain.Transaction.Call.t(), Keyword.t() ) :: {:ok, access_list_result()} | {:error, term()}
Generates the access list and gas used by a call at a block selector.
A node may return an :error string alongside a usable access list when
execution reverts; the field is retained in the successful result map.
Examples
iex> call = Onchain.Transaction.Call.new(<<1::160>>, <<0, 1>>)
iex> {:ok, %{access_list: [{address, [storage_key]}], gas_used: gas_used}} =
...> Onchain.RPC.create_access_list(call)
iex> {address, storage_key, gas_used}
{<<1::160>>, <<2::256>>, 26026}
@spec debug_trace_call( Onchain.Transaction.V1.t() | Onchain.Transaction.V2.t() | Onchain.Transaction.Call.t(), Keyword.t() ) :: {:ok, Onchain.DebugTrace.t()} | {:error, term()}
RPC to trace a transaction call speculatively via debug API.
Examples
iex> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
...> |> Onchain.RPC.debug_trace_call()
{:ok,
%Onchain.DebugTrace{
failed: false,
gas: 24034,
return_value: ~h[0x0000000000000000000000000000000000000000000000000858898f93629000],
struct_logs: [
%Onchain.DebugTrace.StructLog{
depth: 1,
gas: 599978568,
gas_cost: 3,
op: :PUSH1,
pc: 0,
stack: []
},
%Onchain.DebugTrace.StructLog{
depth: 1,
gas: 599978565,
gas_cost: 3,
op: :PUSH1,
pc: 2,
stack: [~h[0x80]]
},
%Onchain.DebugTrace.StructLog{
depth: 1,
gas: 599978562,
gas_cost: 12,
op: :MSTORE,
pc: 4,
stack: [~h[0x80], ~h[0x40]]
}
]
}}
@spec estimate_gas( Onchain.Transaction.V1.t() | Onchain.Transaction.V2.t() | Onchain.Transaction.Call.t() | map(), Keyword.t() ) :: {:ok, non_neg_integer()} | {:error, term()}
RPC call to call to estimate gas used by a given call.
Examples
iex> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Onchain.RPC.estimate_gas()
{:ok, 0x0d}
iex> Onchain.Transaction.V2.new(1, {1, :gwei}, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, [<<2::160>>, <<3::160>>], :goerli)
iex> |> Onchain.RPC.estimate_gas()
{:ok, 0xdd}
iex> use Onchain.Hex
iex> Onchain.Transaction.V2.new(1, {1, :gwei}, {100, :gwei}, 100_000, <<10::160>>, {2, :wei}, <<1, 2, 3>>, [<<2::160>>, <<3::160>>], :goerli)
iex> |> Onchain.RPC.estimate_gas()
{:error, %{code: 3, message: "execution reverted: Dai/insufficient-balance", revert: ~h[0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000184461692f696e73756666696369656e742d62616c616e63650000000000000000]}}
@spec eth_block_number(Keyword.t()) :: {:ok, non_neg_integer()} | {:error, term()}
RPC to get the current block number.
Docs: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_blocknumber
Examples
iex> Onchain.RPC.eth_block_number()
{:ok, 0x44}
@spec eth_call(String.t() | binary(), String.t(), keyword()) :: {:ok, String.t()} | {:error, term()}
Execute a read-only contract call (eth_call).
Options
:rpc_url— node URL (overridesApplication.get_env(:cartouche, :ethereum_node)):timeout— request timeout in ms (default 30_000):block— block number / tag / 0x hex (default"latest"):errors— list of Solidity custom-error signatures, e.g.["InsufficientBalance(uint256,uint256)", "Unauthorized()"]. When the call reverts with matching revert data, the error map carries decoded:error_abi+:error_paramsalongside the raw:revertbinary and its hex mirror:data.
Revert handling
On code: 3 reverts the inner map widens — see the module's "Error Format"
section. Quick pattern-match shape:
case Onchain.RPC.eth_call(token, calldata, errors: ["InsufficientBalance(uint256,uint256)"]) do
{:ok, hex_result} ->
# Decode hex_result with Onchain.ABI.decode_response/2
:ok
{:error, {:rpc_error, %{code: 3, error_abi: "InsufficientBalance(uint256,uint256)", error_params: [requested, available]}}} ->
{:insufficient, requested, available}
{:error, {:rpc_error, %{code: 3, data: hex_data}}} ->
# Custom error not in :errors list (or :errors omitted) — fall back
# to the hex-mirrored revert payload and decode out-of-band.
# `Onchain.ABI.decode_hex_error/2` expects 0x hex, which is exactly :data.
Onchain.ABI.decode_hex_error(hex_data, ["MyError(uint256)"])
{:error, {:rpc_error, %{message: msg}}} ->
{:rpc, msg}
end
Execute a read-only contract call. Raises on error.
Parameters
address- Contract address as 0x hex string or 20-byte binary (value)data- 0x-prefixed hex-encoded calldata (value)opts- Options: :rpc_url, :timeout, :block (default:[], value)
Returns
Raw 0x-prefixed hex response (string)
# descripex:contract
%{
params: %{
data: %{description: "0x-prefixed hex-encoded calldata", kind: :value},
opts: %{
default: [],
description: "Options: :rpc_url, :timeout, :block",
kind: :value
},
address: %{
description: "Contract address as 0x hex string or 20-byte binary",
kind: :value
}
},
returns: %{type: :string, description: "Raw 0x-prefixed hex response"}
}
@spec eth_capabilities(Keyword.t()) :: {:ok, Onchain.RPC.Capabilities.t()} | {:error, term()}
Returns the node's head and resource-retention capabilities.
Examples
iex> {:ok, capabilities} = Onchain.RPC.eth_capabilities()
iex> {capabilities.head.number, capabilities.blocks.oldest_block}
{42, 0}
@spec eth_chain_id(Keyword.t()) :: {:ok, non_neg_integer()} | {:error, term()}
RPC to get the current chain id.
Docs: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_chainid
Examples
iex> Onchain.RPC.eth_chain_id()
{:ok, 0x22}
@spec eth_config(Keyword.t()) :: {:ok, Onchain.RPC.Configuration.t()} | {:error, term()}
Returns the current, next, and last configured Ethereum forks reported by the node.
Examples
iex> {:ok, config} = Onchain.RPC.eth_config()
iex> {config.current.chain_id, config.current.fork_id}
{1, <<7, 201, 70, 46>>}
@spec eth_estimate_gas(map(), keyword()) :: {:ok, non_neg_integer()} | {:error, term()}
Estimate the gas a transaction would consume.
Parameters
tx_params- Transaction-params map with atom keys. Recognized: :from, :to, :data, :value, :gas, :gas_price, :max_fee_per_gas, :max_priority_fee_per_gas, :access_list. Absent keys are omitted from the call object. (value)opts- Options: :rpc_url, :timeout, :block (default:[], value)
Returns
Estimated gas units as an integer ({:ok, non_neg_integer()} | {:error, term()})
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout, :block",
kind: :value
},
tx_params: %{
description: "Transaction-params map with atom keys. Recognized: :from, :to, :data, :value, :gas, :gas_price, :max_fee_per_gas, :max_priority_fee_per_gas, :access_list. Absent keys are omitted from the call object.",
kind: :value
}
},
returns: %{
type: "{:ok, non_neg_integer()} | {:error, term()}",
description: "Estimated gas units as an integer"
}
}
@spec eth_estimate_gas!(map(), keyword()) :: non_neg_integer()
Estimate the gas a transaction would consume. Raises on error.
Parameters
tx_params- Transaction-params map with atom keys (see eth_estimate_gas/2) (value)opts- Options: :rpc_url, :timeout, :block (default:[], value)
Returns
Estimated gas units (integer)
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout, :block",
kind: :value
},
tx_params: %{
description: "Transaction-params map with atom keys (see eth_estimate_gas/2)",
kind: :value
}
},
returns: %{type: :integer, description: "Estimated gas units"}
}
@spec eth_get_block_receipts(integer() | String.t(), Keyword.t()) :: {:ok, [Onchain.Receipt.t()] | nil} | {:error, term()}
Returns every receipt in the block identified by number, tag, or hash.
Defined by execution-apis v1.0.0-beta.7 (src/eth/block.yaml,
eth_getBlockReceipts). Each receipt is decoded by
Onchain.Receipt.deserialize/1, the same decoder as get_trx_receipt/2.
A null result is {:ok, nil}.
@spec eth_get_block_transaction_count_by_hash(String.t(), Keyword.t()) :: {:ok, non_neg_integer() | nil} | {:error, term()}
Returns how many transactions are in the block identified by block_hash.
Defined by execution-apis v1.0.0-beta.7 (src/eth/block.yaml,
eth_getBlockTransactionCountByHash). The result is a hex quantity, or
null when the node does not know the block.
Examples
iex> Onchain.RPC.eth_get_block_transaction_count_by_hash("0x" <> String.duplicate("cd", 32))
{:ok, 2}
@spec eth_get_block_transaction_count_by_number(integer() | String.t(), Keyword.t()) :: {:ok, non_neg_integer() | nil} | {:error, term()}
Returns how many transactions are in the block identified by number or tag.
Defined by execution-apis v1.0.0-beta.7 (src/eth/block.yaml,
eth_getBlockTransactionCountByNumber). The result is a hex quantity, or
null when the node does not know the block.
Examples
iex> Onchain.RPC.eth_get_block_transaction_count_by_number("latest")
{:ok, 2}
Fetch contract bytecode at an address (eth_getCode).
Parameters
address- Account address as 0x hex string or 20-byte binary (value)opts- Options: :rpc_url, :timeout, :block (default:[], value)
Returns
0x-prefixed bytecode hex string, or "0x" for EOA addresses ({:ok, hex_string} | {:error, term})
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout, :block",
kind: :value
},
address: %{
description: "Account address as 0x hex string or 20-byte binary",
kind: :value
}
},
returns: %{
type: "{:ok, hex_string} | {:error, term}",
description: "0x-prefixed bytecode hex string, or \"0x\" for EOA addresses",
example: "\"0x\" for EOAs, \"0x6080604052...\" for contracts"
}
}
Fetch contract bytecode at an address. Raises on error.
Parameters
address- Account address as 0x hex string or 20-byte binary (value)opts- Options: :rpc_url, :timeout, :block (default:[], value)
Returns
0x-prefixed bytecode hex string (string)
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout, :block",
kind: :value
},
address: %{
description: "Account address as 0x hex string or 20-byte binary",
kind: :value
}
},
returns: %{type: :string, description: "0x-prefixed bytecode hex string"}
}
@spec eth_get_logs(map(), Keyword.t()) :: {:ok, [Onchain.Filter.Log.t()]} | {:error, term()}
Stateless eth_getLogs query.
Accepts the execution-apis filter object: a block range (fromBlock /
toBlock) or a mutually exclusive blockHash, plus optional address and
topics. Logs are decoded with Onchain.Filter.Log, the same decoder
Onchain.Filter and get_filter_logs/2 use. That decoder expects the
mined-log fields (block hash, block number, and transaction hash present).
Examples
iex> {:ok, [log]} = Onchain.RPC.eth_get_logs(%{from_block: 16, to_block: 16})
iex> log.block_number
16
@spec eth_get_proof(String.t() | binary(), [String.t()], keyword()) :: {:ok, Onchain.RPC.Proof.t()} | {:error, term()}
Fetches an EIP-1186 account and storage proof as Onchain.RPC.Proof.
Defined by execution-apis v1.0.0-beta.7. Keys are hex strings of 1 to 64
digits, left-padded to 32 bytes on the wire;
:block selects a block number or tag (default "latest"). Quantities decode
to integers and addresses, hashes and proof nodes to bytes. No local Merkle
verification is performed.
Observed 2026-10-01: Alchemy mainnet served DAI proofs at latest, block
18,000,000 and block 1 (before deployment, with empty storage proof nodes).
The archive node served latest but refused both historical blocks with
-32602, "distance to target block exceeds maximum proof window".
Availability is endpoint-specific; node refusals propagate unchanged.
See docs/state-read-portability.md for verbatim evidence.
@spec eth_get_storage_at(String.t() | binary(), String.t(), keyword()) :: {:ok, <<_::256>>} | {:error, term()}
Reads a 32-byte storage word via eth_getStorageAt (execution-apis v1.0.0-beta.7).
Accepts a hex slot of up to 32 bytes, including leading zeroes. :block selects
a block number or tag (default "latest"). Returns raw bytes.
Observed 2026-10-01: Alchemy mainnet and the archive node returned DAI slot 1
at block 18,000,000; Alchemy also returned zero at block 1 (before deployment).
This does not establish an unlimited history guarantee. Verbatim responses
and refusals are recorded in docs/state-read-portability.md.
@spec eth_get_transaction_by_block_hash_and_index( String.t(), non_neg_integer() | String.t(), Keyword.t() ) :: {:ok, Onchain.Transaction.Info.t()} | {:error, term()}
Returns the transaction at transaction_index in the block identified by block_hash.
Defined by execution-apis v1.0.0-beta.7 (src/eth/transaction.yaml,
eth_getTransactionByBlockHashAndIndex). Same envelope as
eth_get_transaction_by_hash/2. A null result is {:error, :not_found}.
@spec eth_get_transaction_by_block_number_and_index( integer() | String.t(), non_neg_integer() | String.t(), Keyword.t() ) :: {:ok, Onchain.Transaction.Info.t()} | {:error, term()}
Returns the transaction at transaction_index in the block identified by number or tag.
Defined by execution-apis v1.0.0-beta.7 (src/eth/transaction.yaml,
eth_getTransactionByBlockNumberAndIndex). Same envelope as
eth_get_transaction_by_hash/2. A null result is {:error, :not_found}.
@spec eth_get_transaction_by_hash(String.t(), Keyword.t()) :: {:ok, Onchain.Transaction.Info.t()} | {:error, term()}
Returns the transaction identified by tx_hash.
Defined by execution-apis v1.0.0-beta.7 (src/eth/transaction.yaml,
eth_getTransactionByHash). The result is a Onchain.Transaction.Info
envelope: inclusion metadata plus the typed transaction from
Vn.from_json/1. Block fields are nil when the transaction is pending.
block_timestamp is also nil when the node omits blockTimestamp
(Alchemy mainnet sometimes does, observed 2026-10-01).
A null result is {:error, :not_found}.
Broadcast a signed transaction.
Parameters
data- 0x-prefixed hex-encoded signed transaction (value)opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Transaction hash as 0x hex string ({:ok, tx_hash} | {:error, term})
# descripex:contract
%{
params: %{
data: %{
description: "0x-prefixed hex-encoded signed transaction",
kind: :value
},
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
}
},
returns: %{
type: "{:ok, tx_hash} | {:error, term}",
description: "Transaction hash as 0x hex string",
example: "0xabc123..."
}
}
Broadcast a signed transaction. Raises on error.
Parameters
data- 0x-prefixed hex-encoded signed transaction (value)opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Transaction hash as 0x hex string (string)
# descripex:contract
%{
params: %{
data: %{
description: "0x-prefixed hex-encoded signed transaction",
kind: :value
},
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
}
},
returns: %{type: :string, description: "Transaction hash as 0x hex string"}
}
@spec eth_syncing(Keyword.t()) :: {:ok, false | Onchain.RPC.SyncStatus.t()} | {:error, term()}
Returns the node's sync status from eth_syncing.
execution-apis v1.0.0-beta.7 (src/eth/client.yaml, schema SyncingStatus)
defines one union: false when the node is not syncing, or a progress object
whose quantity fields are startingBlock, currentBlock, and highestBlock.
This decodes that union to false or %Onchain.RPC.SyncStatus{}.
Examples
iex> Onchain.RPC.eth_syncing()
{:ok, false}
@spec execute_trx(<<_::160>>, binary() | {String.t(), [term()]}, Keyword.t()) :: {:ok, binary()} | {:error, term()}
Helper function to work with other Cartouche modules to get a nonce, sign a transction, and transmit it to the network.
If you need higher-level functionality, like manual nonce tracking, you may want to use the more granular function calls.
Options:
gas_price- Set the base gas for the transaction, overrides all other gas prices listed below (defaultnil) [note: only compatible with V1 transaction]base_fee- Set the base price for the transaction, if nil, will use base gas price frometh_gasPricecall (defaultnil) [note: only compatible with V2 transactions]base_fee_buffer- Buffer for the gas price when estimating gas (default: 1.2 = 120%) [note: only compatible with V2 transactions]priority_fee- Additional gas to send as a priority fee. (default:{0, :gwei}) [note: only compatible with V2 transactions]gas_limit- Set the gas limit for the transaction (default: callseth_estimateGas)gas_buffer- Buffer if estimating gas limit (default: 1.5 = 150%)value- Value to provide with transaction in wei (default: 0)nonce- Nonce to send with transaction. (default: lookup viaeth_transactionCount)verify- Verify the function is likely to succeed (default: true)trx_type- :v1 for V1 (pre-EIP-1559 transactions), :v2 for V2 (EIP-1559) transactions, andnilfor auto-detect.
Note: if we don't verify, then estimateGas will likely fail if the transaction were to fail.
To prevent this, `gas_limit` should always be supplied when `verify` is set to false.Examples
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, trx_id} = Onchain.RPC.execute_trx(<<1::160>>, {"baz(uint,address)", [50, :binary.decode_unsigned(<<1::160>>)]}, gas_price: {50, :gwei}, value: 0, signer: signer_proc)
iex> <<nonce::integer-size(8), gas_price::integer-size(64), gas_limit::integer-size(24), to::binary>> = trx_id
iex> {nonce, gas_price, gas_limit, to}
{4, 50000000000, 20, <<1::160>>}
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> Onchain.RPC.execute_trx(<<10::160>>, {"baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned]}, gas_price: {50, :gwei}, gas_limit: 100_000, value: 0, nonce: 10, signer: signer_proc)
{:error, %{code: 3, message: "execution reverted", revert: <<61, 115, 139, 46>>}}
iex> # Set base fee and priority fee (v2)
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, trx_id} = Onchain.RPC.execute_trx(<<10::160>>, {"baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned]}, base_fee: {1, :gwei}, priority_fee: {3, :gwei}, gas_limit: 100_000, value: 0, nonce: 10, verify: false, signer: signer_proc)
iex> <<nonce::integer-size(8), max_priority_fee_per_gas::integer-size(64), max_fee_per_gas::integer-size(64), gas_limit::integer-size(24), to::binary>> = trx_id
iex> {nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to}
{10, 3000000000, 4000000000, 100000, <<10::160>>}
@spec fee_history(Keyword.t() | pos_integer()) :: {:ok, Onchain.FeeHistory.t()} | {:error, term()}
RPC call to call to get the Eip-1559 fee history data.
Examples
iex> Onchain.RPC.fee_history()
{:ok, %Onchain.FeeHistory{
base_fee_per_gas: [20566340803, 20460504186, 19629790325, 19239635811, 19090900440, 19048391846],
gas_used_ratio: [0.4794155666666667, 0.3375966, 0.42049746666666665, 0.4690773, 0.49109343333333333],
oldest_block: 16607861,
reward: [[1000000000, 1000000000, 1500000000], [1000000000, 1000000000, 2000000000], [1000000000, 1000000000, 1000000000], [780000000, 1000000000, 2000000000], [1000000000, 1000000000, 1500000000]]
}}
@spec fee_history(pos_integer(), keyword()) :: {:ok, Onchain.FeeHistory.t()} | {:error, term()}
Fetch base-fee history and per-block priority-fee percentiles (eth_feeHistory).
Parameters
block_count- Number of recent blocks to query, 1..1024 (EIP-1474 cap) (value)opts- Options: :newest_block (default "latest"), :reward_percentiles (default [50] — list of ints 0..100, monotonically non-decreasing), :rpc_url, :timeout (default:[], value)
Returns
Deserialized fee history struct: oldest_block, base_fee_per_gas (block_count + 1 entries), gas_used_ratio, reward (block_count rows × length(reward_percentiles) cols) ({:ok, Onchain.FeeHistory.t()} | {:error, term})
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :newest_block (default \"latest\"), :reward_percentiles (default [50] — list of ints 0..100, monotonically non-decreasing), :rpc_url, :timeout",
kind: :value
},
block_count: %{
description: "Number of recent blocks to query, 1..1024 (EIP-1474 cap)",
kind: :value
}
},
returns: %{
type: "{:ok, Onchain.FeeHistory.t()} | {:error, term}",
description: "Deserialized fee history struct: oldest_block, base_fee_per_gas (block_count + 1 entries), gas_used_ratio, reward (block_count rows × length(reward_percentiles) cols)"
}
}
@spec fee_history!(pos_integer(), keyword()) :: Onchain.FeeHistory.t()
Fetch fee history. Raises on error.
Parameters
block_count- Number of recent blocks to query, 1..1024 (value)opts- Options: :newest_block, :reward_percentiles, :rpc_url, :timeout (default:[], value)
Returns
Deserialized fee history struct (Onchain.FeeHistory.t())
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :newest_block, :reward_percentiles, :rpc_url, :timeout",
kind: :value
},
block_count: %{
description: "Number of recent blocks to query, 1..1024",
kind: :value
}
},
returns: %{
type: "Onchain.FeeHistory.t()",
description: "Deserialized fee history struct"
}
}
@spec fill_transaction( Onchain.Transaction.V1.t() | Onchain.Transaction.V2.t() | Onchain.Transaction.Call.t(), Keyword.t() ) :: {:ok, struct()} | {:error, term()}
RPC call to fill missing transaction fields via eth_fillTransaction.
The node populates nonce, gas, and fee fields and returns the completed
unsigned transaction. Spec-conforming nodes return a FillTransactionResult
carrying only tx; geth returns a {raw, tx} pair. Both shapes decode into a
Onchain.Transaction struct.
Decoding prefers the tx object execution-apis requires over geth's
additional raw field, because raw serializes an unsigned transaction in
the signed wire format and so cannot be told apart from a signed one.
A typed envelope's unsigned form is nil signature fields; a legacy one keeps
the chain id in v with r/s zero.
Legacy fills need a chain id, which Onchain.Transaction.V1 holds in v
until a signature replaces it. geth omits chainId from an unsigned legacy
result; pass chain_id: to supply it. Without it — from either side — the
call errors rather than return a transaction that would sign to the wrong
address, and so does a chain_id: that disagrees with the one the response
names: Onchain.Signer takes the chain id from its caller rather than from
the struct, so a disagreement signs a digest the encoded payload does not
match.
Examples
iex> {:ok, trx} =
...> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, :kovan)
...> |> Onchain.RPC.fill_transaction()
iex> trx.nonce
1
@spec gas_price(Keyword.t()) :: {:ok, non_neg_integer()} | {:error, term()}
RPC call to call to get the current gas price.
Examples
iex> Onchain.RPC.gas_price()
{:ok, 1000000000}
@spec get_balance(binary(), Keyword.t()) :: {:ok, non_neg_integer()} | {:error, term()}
RPC to get an account's eth balance.
Docs: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getbalance
Examples
iex> Onchain.RPC.get_balance(~h[0x0000000000000000000000000000000000000001])
{:ok, 0x55}
@spec get_balance!(String.t() | binary(), keyword()) :: non_neg_integer()
Get the ETH balance of an address in wei. Raises on error.
Parameters
address- Account address as 0x hex string or 20-byte binary (value)opts- Options: :rpc_url, :timeout, :block (default:[], value)
Returns
Balance in wei (non_neg_integer)
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout, :block",
kind: :value
},
address: %{
description: "Account address as 0x hex string or 20-byte binary",
kind: :value
}
},
returns: %{type: :non_neg_integer, description: "Balance in wei"}
}
@spec get_block_access_list(integer() | String.t(), keyword()) :: {:ok, [map()] | nil} | {:error, term()}
Fetch an EIP-7928 block access list (eth_getBlockAccessList).
Parameters
block- Block number, tag, or 0x-prefixed 32-byte block hash (value)opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Raw camelCase EIP-7928 account-access entries, or nil when unavailable ({:ok, [map] | nil} | {:error, term})
# descripex:contract
%{
params: %{
block: %{
description: "Block number, tag, or 0x-prefixed 32-byte block hash",
kind: :value
},
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
}
},
returns: %{
type: "{:ok, [map] | nil} | {:error, term}",
description: "Raw camelCase EIP-7928 account-access entries, or nil when unavailable"
}
}
Fetch an EIP-7928 block access list. Raises on error.
Parameters
block- Block number, tag, or 0x-prefixed 32-byte block hash (value)opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Raw camelCase account-access entries or nil ([map] | nil)
# descripex:contract
%{
params: %{
block: %{
description: "Block number, tag, or 0x-prefixed 32-byte block hash",
kind: :value
},
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
}
},
returns: %{
type: "[map] | nil",
description: "Raw camelCase account-access entries or nil"
}
}
@spec get_block_by_hash(binary(), Keyword.t()) :: {:ok, Onchain.Block.t() | nil} | {:error, term()}
RPC to get a block by its block hash.
Docs: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbyhash
Examples
iex> Onchain.RPC.get_block_by_hash(~h[0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae])
{:ok, %Onchain.Block{
difficulty: 0x4ea3f27bc,
extra_data: ~h[0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32],
gas_limit: 0x1388,
gas_used: 0x0,
hash: ~h[0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae],
logs_bloom: ~h[0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000],
miner: ~h[0xbb7b8287f3f0a933474a79eae42cbca977791171],
mix_hash: ~h[0x4fffe9ae21f1c9e15207b1f472d5bbdd68c9595d461666602f2be20daf5e7843],
nonce: 0x689056015818adbe,
number: 0x1b4,
parent_hash: ~h[0xe99e022112df268087ea7eafaf4790497fd21dbeeb6bd7a1721df161a6657a54],
receipts_root: ~h[0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421],
sha3_uncles: ~h[0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347],
size: 0x220,
state_root: ~h[0xddc8b0234c2e0cad087c8b389aa7ef01f7d79b2570bccb77ce48648aa61c904d],
timestamp: 0x55ba467c,
total_difficulty: 0x78ed983323d,
transactions: [],
transactions_root: ~h[0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421],
uncles: [],
base_fee_per_gas: nil,
withdrawals_root: nil,
withdrawals: nil,
parent_beacon_block_root: nil,
blob_gas_used: nil,
excess_blob_gas: nil
}}Options
:include_transaction_details— whentrue, the node returns full transaction objects intransactions; whenfalse(default), just hashes. Forwarded toeth_getBlockByHashas the second wire param (real nodes reject single-param calls with-32602 Invalid params). Note:Onchain.Block.deserialize/1decodes full transaction objects when requested and retains hashes otherwise.
Plus any option accepted by send_rpc/3 (e.g. :ethereum_node, :timeout, :req_options).
@spec get_block_by_number(non_neg_integer() | String.t(), Keyword.t()) :: {:ok, Onchain.Block.t() | nil} | {:error, term()}
RPC to get a block by its block number.
Docs: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbynumber
Examples
iex> Onchain.RPC.get_block_by_number(55)
{:ok, %Onchain.Block{
difficulty: 0x4ea3f27bc,
extra_data: ~h[0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32],
gas_limit: 0x1388,
gas_used: 0x0,
hash: ~h[0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae],
logs_bloom: ~h[0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000],
miner: ~h[0xbb7b8287f3f0a933474a79eae42cbca977791171],
mix_hash: ~h[0x4fffe9ae21f1c9e15207b1f472d5bbdd68c9595d461666602f2be20daf5e7843],
nonce: 0x689056015818adbe,
number: 0x1b4,
parent_hash: ~h[0xe99e022112df268087ea7eafaf4790497fd21dbeeb6bd7a1721df161a6657a54],
receipts_root: ~h[0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421],
sha3_uncles: ~h[0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347],
size: 0x220,
state_root: ~h[0xddc8b0234c2e0cad087c8b389aa7ef01f7d79b2570bccb77ce48648aa61c904d],
timestamp: 0x55ba467c,
total_difficulty: 0x78ed983323d,
transactions: [],
transactions_root: ~h[0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421],
uncles: [],
base_fee_per_gas: nil,
withdrawals_root: nil,
withdrawals: nil,
parent_beacon_block_root: nil,
blob_gas_used: nil,
excess_blob_gas: nil
}}Options
:include_transaction_details— whentrue, the node returns full transaction objects intransactions; whenfalse(default), just hashes. Forwarded toeth_getBlockByNumberas the second wire param. Note:Onchain.Block.deserialize/1decodes full transaction objects when requested and retains hashes otherwise.
Plus any option accepted by send_rpc/3 (e.g. :ethereum_node, :timeout, :req_options).
@spec get_block_by_number!(integer() | String.t(), keyword()) :: Onchain.Block.t() | nil
Fetch a block by number or tag. Raises on error.
Parameters
block_id- Block number (integer) or tag string (value)opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Decoded Onchain.Block struct (map | nil)
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
},
block_id: %{
description: "Block number (integer) or tag string",
kind: :value
}
},
returns: %{type: "map | nil", description: "Decoded Onchain.Block struct"}
}
RPC call to get code for a contract at an address.
Examples
iex> Onchain.RPC.get_code(<<1::160>>)
{:ok, <<0x11, 0x22, 0x33>>}
@spec get_filter_logs(String.t(), Keyword.t()) :: {:ok, [Onchain.Filter.Log.t()]} | {:error, term()}
RPC call to fetch the full log backlog for a filter.
Examples
iex> {:ok, [log]} = Onchain.RPC.get_filter_logs("0xf11735")
iex> log.address
<<181, 165, 242, 38, 148, 53, 44, 21, 176, 3, 35, 132, 74, 213, 69, 171, 178, 177, 16, 40>>
@spec get_nonce(<<_::160>>, Keyword.t()) :: {:ok, non_neg_integer()} | {:error, term()}
RPC call to get account nonce.
Examples
iex> use Onchain.Hex
iex> Onchain.RPC.get_nonce(~h[0x407d73d8a49eeb85d32cf465507dd71d507100c1])
{:ok, 4}
@spec get_transaction_count(binary(), Keyword.t()) :: {:ok, non_neg_integer()} | {:error, term()}
RPC to get an account's transaction count (i.e. nonce)
Docs: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactioncount
Examples
iex> Onchain.RPC.get_transaction_count(~h[0x0000000000000000000000000000000000000001])
{:ok, 0x4}
@spec get_transaction_count!(String.t() | binary(), keyword()) :: non_neg_integer()
Get the transaction count (nonce) of an address. Raises on error.
Parameters
address- Account address as 0x hex string or 20-byte binary (value)opts- Options: :rpc_url, :timeout, :block (default:[], value)
Returns
Transaction count (nonce) (non_neg_integer)
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout, :block",
kind: :value
},
address: %{
description: "Account address as 0x hex string or 20-byte binary",
kind: :value
}
},
returns: %{type: :non_neg_integer, description: "Transaction count (nonce)"}
}
@spec get_transaction_receipt(String.t(), keyword()) :: {:ok, Onchain.Receipt.t() | nil} | {:error, term()}
Get a transaction receipt by hash (eth_getTransactionReceipt).
Parameters
tx_hash- 0x-prefixed hex transaction hash (value)opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Onchain.Receipt struct, or nil if the transaction is pending/unknown ({:ok, map | nil} | {:error, term})
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
},
tx_hash: %{description: "0x-prefixed hex transaction hash", kind: :value}
},
returns: %{
type: "{:ok, map | nil} | {:error, term}",
description: "Onchain.Receipt struct, or nil if the transaction is pending/unknown"
}
}
@spec get_transaction_receipt!(String.t(), keyword()) :: Onchain.Receipt.t() | nil
Get a transaction receipt by hash. Raises on error.
Parameters
tx_hash- 0x-prefixed hex transaction hash (value)opts- Options: :rpc_url, :timeout (default:[], value)
Returns
Onchain.Receipt struct or nil (map | nil)
# descripex:contract
%{
params: %{
opts: %{
default: [],
description: "Options: :rpc_url, :timeout",
kind: :value
},
tx_hash: %{description: "0x-prefixed hex transaction hash", kind: :value}
},
returns: %{type: "map | nil", description: "Onchain.Receipt struct or nil"}
}
@spec get_trx_receipt(binary() | String.t(), Keyword.t()) :: {:ok, Onchain.Receipt.t() | nil} | {:error, term()}
RPC call to get a transaction receipt. Note, this will return {:ok, %Onchain.Receipt{}} or {:ok, nil} if the receipt is not yet available.
Examples
iex> Onchain.RPC.get_trx_receipt(~h[0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5])
{:ok,
%Onchain.Receipt{
transaction_hash: ~h[0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5],
transaction_index: 0x66,
block_hash: ~h[0xa957d47df264a31badc3ae823e10ac1d444b098d9b73d204c40426e57f47e8c3],
block_number: 0xeff35f,
from: ~h[0x6221a9c005f6e47eb398fd867784cacfdcfff4e7],
to: ~h[0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2],
cumulative_gas_used: 0xa12515,
effective_gas_price: 0x5a9c688d4,
gas_used: 0xb4c8,
contract_address: nil,
logs: [
%Onchain.Filter.Log{
log_index: 1,
block_number: 0x01b4,
block_hash: ~h[0xaa8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcfdf829c5a142f1fccd7d],
transaction_hash: ~h[0xaadf829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf],
transaction_index: 0,
address: ~h[0x16c5785ac562ff41e2dcfdf829c5a142f1fccd7d],
data: ~h[0x0000000000000000000000000000000000000000000000000000000000000000],
topics: [
~h[0x59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a5]
]
}
],
logs_bloom: ~h[0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001],
type: 0x02,
status: 0x01,
}
}
iex> Onchain.RPC.get_trx_receipt("0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5")
{:ok,
%Onchain.Receipt{
transaction_hash: ~h[0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5],
transaction_index: 0x66,
block_hash: ~h[0xa957d47df264a31badc3ae823e10ac1d444b098d9b73d204c40426e57f47e8c3],
block_number: 0xeff35f,
from: ~h[0x6221a9c005f6e47eb398fd867784cacfdcfff4e7],
to: ~h[0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2],
cumulative_gas_used: 0xa12515,
effective_gas_price: 0x5a9c688d4,
gas_used: 0xb4c8,
contract_address: nil,
logs: [
%Onchain.Filter.Log{
log_index: 1,
block_number: 0x01b4,
block_hash: ~h[0xaa8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcfdf829c5a142f1fccd7d],
transaction_hash: ~h[0xaadf829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf],
transaction_index: 0,
address: ~h[0x16c5785ac562ff41e2dcfdf829c5a142f1fccd7d],
data: ~h[0x0000000000000000000000000000000000000000000000000000000000000000],
topics: [
~h[0x59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a5]
]
}
],
logs_bloom: ~h[0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001],
type: 0x02,
status: 0x01,
}
}
iex> Onchain.RPC.get_trx_receipt("0xf9e69be4f1ae524854e14dc820c519d8f2b86e52c60e54448abf920d22fb6fe2")
{:ok, %Onchain.Receipt{
transaction_hash: ~h[0xf9e69be4f1ae524854e14dc820c519d8f2b86e52c60e54448abf920d22fb6fe2],
transaction_index: 0,
block_hash: ~h[0x4bc3c26b1a599ced9876d9bf9a17c9bd58ec8b71a68e75335de7f2820e9336ca],
block_number: 10493428,
from: ~h[0xb03d1100c68e58aa1895f8c1f230c0851ff41851],
to: ~h[0x9d8ec03e9ddb71f04da9db1e38837aaac1782a97],
cumulative_gas_used: 222642,
effective_gas_price: 1200000010,
gas_used: 222642,
contract_address: nil,
logs: [
%Onchain.Filter.Log{
removed: false,
log_index: 0,
block_number: 10493428,
block_hash: ~h[0x4bc3c26b1a599ced9876d9bf9a17c9bd58ec8b71a68e75335de7f2820e9336ca],
transaction_hash: ~h[0xf9e69be4f1ae524854e14dc820c519d8f2b86e52c60e54448abf920d22fb6fe2],
transaction_index: 0,
address: ~h[0x9d8ec03e9ddb71f04da9db1e38837aaac1782a97],
data: ~h[0x000000000000000000000000cb372382aa9a9e6f926714f4305afac4566f75380000000000000000000000000000000000000000000000000000000000000000],
topics: [
~h[0x3ffe5de331422c5ec98e2d9ced07156f640bb51e235ef956e50263d4b28d3ae4],
~h[0x0000000000000000000000002326aba712500ae3114b664aeb51dba2c2fb416d],
~h[0x0000000000000000000000002326aba712500ae3114b664aeb51dba2c2fb416d]
]
},
%Onchain.Filter.Log{
removed: false,
log_index: 1,
block_number: 10493428,
block_hash: ~h[0x4bc3c26b1a599ced9876d9bf9a17c9bd58ec8b71a68e75335de7f2820e9336ca],
transaction_hash: ~h[0xf9e69be4f1ae524854e14dc820c519d8f2b86e52c60e54448abf920d22fb6fe2],
transaction_index: 0,
address: ~h[0xcb372382aa9a9e6f926714f4305afac4566f7538],
data: ~h[0x0000000000000000000000000000000000000000000000000000000000000000],
topics: [
~h[0xe0d20d95fbbe7375f6edead77b5ce5c5b096e7dac85848c45c37a95eaf17fe62],
~h[0x0000000000000000000000009d8ec03e9ddb71f04da9db1e38837aaac1782a97],
~h[0x00000000000000000000000054f0a87eb5c8c8ba70243de1ac19e735b41b10a2],
~h[0x0000000000000000000000000000000000000000000000000000000000000000]
]
},
%Onchain.Filter.Log{
removed: false,
log_index: 2,
block_number: 10493428,
block_hash: ~h[0x4bc3c26b1a599ced9876d9bf9a17c9bd58ec8b71a68e75335de7f2820e9336ca],
transaction_hash: ~h[0xf9e69be4f1ae524854e14dc820c519d8f2b86e52c60e54448abf920d22fb6fe2],
transaction_index: 0,
address: ~h[0xcb372382aa9a9e6f926714f4305afac4566f7538],
data: <<>>,
topics: [
~h[0x0000000000000000000000000000000000000000000000000000000000000055]
]
}
],
logs_bloom: ~h[0x00800000000000000000000400000000000000000000000000000000000000000000000000000000000000000000002000200040000000000000000200001000000000000000000000000000000000000000000000000000000000000010000000008000020000004000000200000800000000000000000000220000000000000000000000000800000000000400000000000000000000000000000000000000000000040000000000008000008000000000000000000000000000000004000000800000000000004000000000000000000000000000000004080000000020000000000000000080000000000000000000000000000000000000000000000000],
type: 0,
status: 1
}}
iex> Onchain.RPC.get_trx_receipt(<<1::256>>)
{:error, "failed to decode `eth_getTransactionReceipt` response: %FunctionClauseError{module: Onchain.Hex, function: :decode_hex_, arity: 1, kind: nil, args: nil, clauses: nil}"}
iex> Onchain.RPC.get_trx_receipt(<<1::256>>, verbose: true)
{:error, "failed to decode `eth_getTransactionReceipt` response: %FunctionClauseError{module: Onchain.Hex, function: :decode_hex_, arity: 1, kind: nil, args: nil, clauses: nil}"}
iex> Onchain.RPC.get_trx_receipt(<<2::256>>)
{:ok, nil}
@spec max_priority_fee_per_gas(Keyword.t()) :: {:ok, non_neg_integer()} | {:error, term()}
RPC call to call to get the current max priority fee per gas.
Examples
iex> Onchain.RPC.max_priority_fee_per_gas()
{:ok, 1000000001}
Returns whether the node is listening for peer connections (net_listening).
net_listening is not in any tagged execution-apis release. It landed on
main after v1.0.0-beta.7 in commit 7c58b32 (#843, 2026-08-24). This
wrapper does not make the method standard.
Observed 2026-10-01 on the hosted lanes from the multi-endpoint seam: Alchemy
(ETHEREUM_ALCHEMY_URL) and Infura (ETHEREUM_INFURA_URL) both answered
HTTP 200 with JSON true. Neither lane refused the method.
A refusing endpoint's JSON-RPC error is classified by send_rpc/3, not by a
per-method probe. -32601, and -32600 whose message says the method is
unsupported or not available, become
{:error, {:method_not_found, map}}. A plan-disabled namespace becomes
{:error, {:namespace_unavailable, map}}. -32001 whose message says the
node is unable to complete the request becomes
{:error, {:unavailable, map}}.
Examples
iex> Onchain.RPC.net_listening()
{:ok, true}
@spec net_peer_count(Keyword.t()) :: {:ok, non_neg_integer()} | {:error, term()}
Returns the node's connected peer count (net_peerCount).
net_peerCount is not in any tagged execution-apis release. It landed on
main after v1.0.0-beta.7 in commit 7c58b32 (#843, 2026-08-24). This
wrapper does not make the method standard.
Observed 2026-10-01 on Alchemy mainnet (ETHEREUM_ALCHEMY_URL), HTTP 400:
{"code":-32600,"message":"net_peerCount is not available on the ETH_MAINNET. For more information see our docs: https://docs.alchemy.com/alchemy/documentation/apis/ethereum"}That refusal surfaces as
{:error, {:method_not_found, %{code: -32600, message: "net_peerCount is not available on the ETH_MAINNET. For more information see our docs: https://docs.alchemy.com/alchemy/documentation/apis/ethereum"}}}.
The same classification is send_rpc/3's shared refusal tag. There is no
separate capability probe. Infura answered a peer-count quantity on the same
day; a hosted success is not evidence the method is standard.
Examples
iex> Onchain.RPC.net_peer_count()
{:ok, 2}
RPC call to create a new block filter.
Examples
iex> Onchain.RPC.new_block_filter()
{:ok, "0xb10cf11e"}
RPC call to create a new pending-transaction filter.
Examples
iex> Onchain.RPC.new_pending_transaction_filter()
{:ok, "0xpend1ng"}
@spec prepare_trx(<<_::160>>, binary() | {String.t(), [term()]}, Keyword.t()) :: {:ok, Onchain.Transaction.V1.t() | Onchain.Transaction.V2.t()} | {:error, term()}
Helper function to work with other Cartouche modules to get a nonce, sign a transction, and prepare it to be submitted on-chain.
If you need higher-level functionality, like manual nonce tracking, you may want to use the more granular function calls.
Options:
gas_price- Set the gas price for a v1 (non-Eip1559) transaction, if nil, comes frometh_gasPrice(defaultnil) [note: only compatible with V1 transaction]base_fee- Set the base price for the transaction, if nil, will use base gas price frometh_feeHistory(defaultnil) [note: only compatible with V2 transactions]base_fee_buffer- Buffer for the gas price or base fee when estimating gas price. Ingored ifgas_price(for v1) orbase_fee(for v2) is specified directly (default: 1.2 = 120%)priority_fee- Additional gas to send as a priority fee. (default:{0, :gwei}) [note: only compatible with V2 transactions]gas_limit- Set the gas limit for the transaction (default: callseth_estimateGas)gas_buffer- Buffer if estimating gas limit (default: 1.5 = 150%)value- Value to provide with transaction in wei (default: 0)nonce- Nonce to send with transaction. (default: lookup viaeth_transactionCount)verify- Verify the function is likely to succeed (default: true)trx_type- :v1 for V1 (pre-EIP-1559 transactions), :v2 for V2 (EIP-1559) transactions, andnilfor auto-detect.
Note: if we don't verify, then estimateGas will likely fail if the transaction were to fail.
To prevent this, `gas_limit` should always be supplied when `verify` is set to false.Examples
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, trx} = Onchain.RPC.prepare_trx(<<1::160>>, {"baz(uint,address)", [50, :binary.decode_unsigned(<<1::160>>)]}, gas_price: {50, :gwei}, nonce: 10, value: 0, signer: signer_proc)
iex> %{trx|v: nil, r: nil, s: nil}
%Onchain.Transaction.V1{
nonce: 10,
gas_price: 50000000000,
gas_limit: 20,
to: <<1::160>>,
value: 0,
data: <<162, 145, 173, 214, 0::248, 50, 0::248, 1>>
}
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, trx} = Onchain.RPC.prepare_trx(<<1::160>>, {"baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned]}, gas_price: {50, :gwei}, gas_limit: 100_000, value: 0, signer: signer_proc)
iex> %{trx|v: nil, r: nil, s: nil}
%Onchain.Transaction.V1{
nonce: 4,
gas_price: 50000000000,
gas_limit: 100000,
to: <<1::160>>,
value: 0,
data: <<162, 145, 173, 214, 0::248, 50, 0::248, 1>>
}
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, trx} = Onchain.RPC.prepare_trx(<<1::160>>, {"baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned]}, gas_price: {50, :gwei}, gas_limit: 100_000, value: 0, nonce: 10, signer: signer_proc)
iex> %{trx|v: nil, r: nil, s: nil}
%Onchain.Transaction.V1{
nonce: 10,
gas_price: 50000000000,
gas_limit: 100000,
to: <<1::160>>,
value: 0,
data: <<162, 145, 173, 214, 0::248, 50, 0::248, 1>>
}
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> Onchain.RPC.prepare_trx(<<10::160>>, {"baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned]}, gas_price: {50, :gwei}, gas_limit: 100_000, value: 0, nonce: 10, signer: signer_proc)
{:error, %{code: 3, message: "execution reverted", revert: <<61, 115, 139, 46>>}}
iex> # Set gas price directly
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, trx} = Onchain.RPC.prepare_trx(<<10::160>>, {"baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned]}, gas_price: {50, :gwei}, gas_limit: 100_000, value: 0, nonce: 10, verify: false, signer: signer_proc)
iex> %{trx|v: nil, r: nil, s: nil}
%Onchain.Transaction.V1{
nonce: 10,
gas_price: 50000000000,
gas_limit: 100000,
to: <<10::160>>,
value: 0,
data: <<162, 145, 173, 214, 0::248, 50, 0::248, 1>>
}
iex> # Default gas price v1
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, trx} = Onchain.RPC.prepare_trx(<<10::160>>, {"baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned]}, gas_limit: 100_000, trx_type: :v1, value: 0, nonce: 10, verify: false, signer: signer_proc)
iex> %{trx|v: nil, r: nil, s: nil}
%Onchain.Transaction.V1{
nonce: 10,
gas_price: 1200000000,
gas_limit: 100000,
to: <<10::160>>,
value: 0,
data: <<162, 145, 173, 214, 0::248, 50, 0::248, 1>>
}
iex> # Default gas price v2
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, trx} = Onchain.RPC.prepare_trx(<<10::160>>, {"baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned]}, gas_limit: 100_000, trx_type: :v2, value: 0, nonce: 10, verify: false, signer: signer_proc)
iex> %{trx|signature_y_parity: nil, signature_r: nil, signature_s: nil}
%Onchain.Transaction.V2{
chain_id: 5,
nonce: 10,
gas_limit: 100000,
destination: <<10::160>>,
amount: 0,
max_fee_per_gas: 25679608965,
max_priority_fee_per_gas: 1000000001,
data: <<162, 145, 173, 214, 0::248, 50, 0::248, 1>>,
access_list: []
}
iex> # Default gas price (trx_type: nil)
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, trx} = Onchain.RPC.prepare_trx(<<10::160>>, {"baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned]}, gas_limit: 100_000, value: 0, nonce: 10, verify: false, signer: signer_proc)
iex> %{trx|signature_y_parity: nil, signature_r: nil, signature_s: nil}
%Onchain.Transaction.V2{
chain_id: 5,
nonce: 10,
gas_limit: 100000,
destination: <<10::160>>,
amount: 0,
max_fee_per_gas: 25679608965,
max_priority_fee_per_gas: 1000000001,
data: <<162, 145, 173, 214, 0::248, 50, 0::248, 1>>,
access_list: []
}
iex> # Set priority fee (v2)
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, trx} = Onchain.RPC.prepare_trx(<<10::160>>, {"baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned]}, priority_fee: {3, :gwei}, gas_limit: 100_000, value: 0, nonce: 10, verify: false, signer: signer_proc)
iex> %{trx|signature_y_parity: nil, signature_r: nil, signature_s: nil}
%Onchain.Transaction.V2{
chain_id: 5,
nonce: 10,
gas_limit: 100000,
destination: <<10::160>>,
amount: 0,
max_fee_per_gas: 27679608964,
max_priority_fee_per_gas: 3000000000,
data: <<162, 145, 173, 214, 0::248, 50, 0::248, 1>>,
access_list: []
}
iex> # Set base fee and priority fee (v2)
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, trx} = Onchain.RPC.prepare_trx(<<10::160>>, {"baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned]}, base_fee: {1, :gwei}, priority_fee: {3, :gwei}, gas_limit: 100_000, value: 0, nonce: 10, verify: false, signer: signer_proc)
iex> %{trx|signature_y_parity: nil, signature_r: nil, signature_s: nil}
%Onchain.Transaction.V2{
chain_id: 5,
nonce: 10,
gas_limit: 100000,
destination: <<10::160>>,
amount: 0,
max_fee_per_gas: 4000000000,
max_priority_fee_per_gas: 3000000000,
data: <<162, 145, 173, 214, 0::248, 50, 0::248, 1>>,
access_list: []
}
iex> # Sets chain id
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, trx} = Onchain.RPC.prepare_trx(<<10::160>>, {"baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned]}, base_fee: {1, :gwei}, priority_fee: {3, :gwei}, gas_limit: 100_000, value: 0, nonce: 10, verify: false, signer: signer_proc, chain_id: 99)
iex> %{trx|signature_y_parity: nil, signature_r: nil, signature_s: nil}
%Onchain.Transaction.V2{
chain_id: 99,
nonce: 10,
gas_limit: 100000,
destination: <<10::160>>,
amount: 0,
max_fee_per_gas: 4000000000,
max_priority_fee_per_gas: 3000000000,
data: <<162, 145, 173, 214, 0::248, 50, 0::248, 1>>,
access_list: []
}
Sends a JSON-RPC array batch and returns raw results in request order. Uses the same options as send_rpc/3.
@spec send_rpc(binary(), [term()], Keyword.t()) :: {:ok, term()} | {:error, send_rpc_error()} | :invalid_hex
Simple RPC client for a JSON-RPC Ethereum node.
Examples
iex> Onchain.RPC.send_rpc("net_version", [])
{:ok, "3"}
iex> use Onchain.Hex
iex> Onchain.RPC.send_rpc("get_balance", ["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"], ethereum_node: "http://example.com")
{:ok, "0x0234c8a3397aab58"}
iex> match?({:error, {:invalid_params, %Jason.EncodeError{}}}, Onchain.RPC.send_rpc(<<255>>, []))
true
iex> match?({:error, {:invalid_params, %Protocol.UndefinedError{}}}, Onchain.RPC.send_rpc("net_version", [self()]))
trueOptions
Common options (other RPC wrappers forward opts here):
:rpc_url/:ethereum_node— node URL (:rpc_urltakes precedence); falls back toApplication.get_env(:cartouche, :ethereum_node):retry— opt-in[max_retries: 2, backoff_ms: 100]; only transport failures retry, before typed decoding. JSON-RPC application errors are final.:timeout— Reqreceive_timeoutin ms:headers— extra request headers:verbose— whentrue, decode failures log at:errorinstead of:info:req_options— a keyword list merged into theReq.request/1options (highest precedence), exposing Req's whole pipeline (retries, redirects, a customfinch:pool, telemetry, proxies, plugs). A global default can be set withconfig :cartouche, :req_options, [...]. Tests stub the transport by passingreq_options: [plug: ...](or configuringconfig :cartouche, Onchain.RPC, plug: ...).
@spec send_transaction( Onchain.Transaction.V1.t() | Onchain.Transaction.V2.t() | Onchain.Transaction.Call.t(), Keyword.t() ) :: {:ok, binary()} | {:error, term()}
RPC call to eth_sendTransaction.
Examples
iex> {:ok, hash} =
...> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
...> |> Onchain.RPC.send_transaction()
iex> Base.encode16(hash, case: :lower)
"abababababababababababababababababababababababababababababababab"
@spec send_trx( Onchain.Transaction.V1.t() | Onchain.Transaction.V2.t() | String.t(), Keyword.t() ) :: {:ok, binary()} | {:error, term()}
RPC call to send a raw transaction.
Examples
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, signed_trx} = Onchain.Transaction.build_signed_trx(<<1::160>>, 5, {"baz(uint,address)", [50, :binary.decode_unsigned(<<1::160>>)]}, {50, :gwei}, 100_000, 0, chain_id: :goerli, signer: signer_proc)
iex> {:ok, trx_id} = Onchain.RPC.send_trx(signed_trx)
iex> <<nonce::integer-size(8), gas_price::integer-size(64), gas_limit::integer-size(24), to::binary>> = trx_id
iex> {nonce, gas_price, gas_limit, to}
{5, 50000000000, 100000, <<1::160>>}
iex> signer_proc = Onchain.Test.Signer.start_signer()
iex> {:ok, signed_trx} = Onchain.Transaction.build_signed_trx_v2(<<1::160>>, 5, {"baz(uint,address)", [50, :binary.decode_unsigned(<<1::160>>)]}, {50, :gwei}, {10, :gwei}, 100_000, 0, [], chain_id: :goerli, signer: signer_proc)
iex> {:ok, trx_id} = Onchain.RPC.send_trx(signed_trx)
iex> <<nonce::integer-size(8), max_priority_fee_per_gas::integer-size(64), max_fee_per_gas::integer-size(64), gas_limit::integer-size(24), to::binary>> = trx_id
iex> {nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to}
{5, 50000000000, 10000000000, 100000, <<1::160>>}
RPC call to eth_sign.
The node signs message with a managed account and returns an EIP-191
signature over it — message is the data to be signed, not a digest the
node signs verbatim. Most nodes disable this method; when they do, the
node's error is returned unchanged.
Examples
iex> {:ok, signature} = Onchain.RPC.sign(<<1::160>>, "hello")
iex> byte_size(signature)
65
@spec sign_transaction( Onchain.Transaction.V1.t() | Onchain.Transaction.V2.t() | Onchain.Transaction.Call.t(), Keyword.t() ) :: {:ok, struct()} | {:error, term()}
RPC call to eth_signTransaction.
The node signs with its keystore and returns a raw transaction, which is
decoded through Onchain.Transaction.decode/1.
Examples
iex> {:ok, trx} =
...> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, :kovan)
...> |> Onchain.RPC.sign_transaction()
iex> trx.nonce
1
@spec trace_call( Onchain.Transaction.V1.t() | Onchain.Transaction.V2.t() | Onchain.Transaction.Call.t(), Keyword.t() ) :: {:ok, Onchain.TraceCall.t()} | {:error, term()}
RPC to trace a transaction call speculatively.
Examples
iex> Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
...> |> Onchain.RPC.trace_call()
{:ok,
%Onchain.TraceCall{
output: "",
state_diff: nil,
trace: [
%Onchain.RPC.Trace{
action: %Onchain.RPC.Trace.Action{
call_type: "call",
init: nil,
from: <<0::160>>,
gas: 499_978_072,
input: ~h[0xd1692f56000000000000000000000000142da9114e5a98e015aa95afca0585e84832a612000000000000000000000000142da9114e5a98e015aa95afca0585e84832a6120000000000000000000000000000000000000000000000000000000000000000],
to: ~h[0x13172EE393713FBA9925A9A752341EBD31E8D9A7],
value: 0
},
block_hash: nil,
block_number: nil,
gas_used: 492_166_471,
error: "Reverted",
output: "",
result_code: nil,
result_address: nil,
subtraces: 1,
trace_address: [],
transaction_hash: nil,
transaction_position: nil,
type: "call"
},
%Onchain.RPC.Trace{
action: %Onchain.RPC.Trace.Action{
call_type: nil,
init: ~h[0x60e03461009157601f6101ec38819003918201601f19168301916001600160401b038311848410176100965780849260609460405283398101031261009157610047816100ac565b906100606040610059602084016100ac565b92016100ac565b9060805260a05260c05260405161012b90816100c18239608051816088015260a051816045015260c0518160c60152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036100915756fe608060405260043610156013575b3660ba57005b6000803560e01c8063238ac9331460775763c34c08e51460325750600d565b34607457806003193601126074576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b80fd5b5034607457806003193601126074577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166080908152602090f35b600036818037808036817f00000000000000000000000000000000000000000000000000000000000000005af4903d918282803e60f357fd5bf3fea264697066735822122032b5603d6937ceb7a252e16379744d8545670ff4978c8d76c985d051dfcfe46c64736f6c6343000817003300000000000000000000000049e5d261e95f6a02505078bb339fecb210a0b634000000000000000000000000142da9114e5a98e015aa95afca0585e84832a612000000000000000000000000142da9114e5a98e015aa95afca0585e84832a612],
from: ~h[0x13172EE393713FBA9925A9A752341EBD31E8D9A7],
gas: 492_133_529,
input: nil,
to: nil,
value: 0
},
block_hash: nil,
block_number: nil,
gas_used: nil,
error: "contract address collision",
output: nil,
result_code: nil,
result_address: nil,
subtraces: 0,
trace_address: [0],
transaction_hash: nil,
transaction_position: nil,
type: "create"
}
],
vm_trace: nil
}
}
iex> Onchain.Transaction.V2.new(1, {1, :gwei}, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, [<<2::160>>, <<3::160>>], :goerli)
...> |> Onchain.RPC.trace_call()
{:ok,
%Onchain.TraceCall{
output: "",
state_diff: nil,
trace: [
%Onchain.RPC.Trace{
action: %Onchain.RPC.Trace.Action{
call_type: "call",
init: nil,
from: <<0::160>>,
gas: 499_978_072,
input: ~h[0xd1692f56000000000000000000000000142da9114e5a98e015aa95afca0585e84832a612000000000000000000000000142da9114e5a98e015aa95afca0585e84832a6120000000000000000000000000000000000000000000000000000000000000000],
to: ~h[0x13172EE393713FBA9925A9A752341EBD31E8D9A7],
value: 0
},
block_hash: nil,
block_number: nil,
gas_used: 492_166_471,
error: "Reverted",
output: "",
result_code: nil,
result_address: nil,
subtraces: 1,
trace_address: [],
transaction_hash: nil,
transaction_position: nil,
type: "call"
},
%Onchain.RPC.Trace{
action: %Onchain.RPC.Trace.Action{
call_type: nil,
init: ~h[0x60e03461009157601f6101ec38819003918201601f19168301916001600160401b038311848410176100965780849260609460405283398101031261009157610047816100ac565b906100606040610059602084016100ac565b92016100ac565b9060805260a05260c05260405161012b90816100c18239608051816088015260a051816045015260c0518160c60152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036100915756fe608060405260043610156013575b3660ba57005b6000803560e01c8063238ac9331460775763c34c08e51460325750600d565b34607457806003193601126074576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b80fd5b5034607457806003193601126074577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166080908152602090f35b600036818037808036817f00000000000000000000000000000000000000000000000000000000000000005af4903d918282803e60f357fd5bf3fea264697066735822122032b5603d6937ceb7a252e16379744d8545670ff4978c8d76c985d051dfcfe46c64736f6c6343000817003300000000000000000000000049e5d261e95f6a02505078bb339fecb210a0b634000000000000000000000000142da9114e5a98e015aa95afca0585e84832a612000000000000000000000000142da9114e5a98e015aa95afca0585e84832a612],
from: ~h[0x13172EE393713FBA9925A9A752341EBD31E8D9A7],
gas: 492_133_529,
input: nil,
to: nil,
value: 0
},
block_hash: nil,
block_number: nil,
gas_used: nil,
error: "contract address collision",
output: nil,
result_code: nil,
result_address: nil,
subtraces: 0,
trace_address: [0],
transaction_hash: nil,
transaction_position: nil,
type: "create"
}
],
vm_trace: nil
}
}
@spec trace_call_many( [ Onchain.Transaction.V1.t() | Onchain.Transaction.V2.t() | Onchain.Transaction.Call.t() | {Onchain.Transaction.V1.t() | Onchain.Transaction.V2.t() | Onchain.Transaction.Call.t(), <<_::160>> | nil} ], Keyword.t() ) :: {:ok, [Onchain.TraceCall.t()]} | {:error, term()}
RPC to trace many transaction calls speculatively.
Examples
iex> t1 = Onchain.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
iex> t2 = Onchain.Transaction.V2.new(1, {1, :gwei}, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, [<<2::160>>, <<3::160>>], :goerli)
iex> Onchain.RPC.trace_call_many([t1, t2])
{:ok, [
%Onchain.TraceCall{
output: "",
state_diff: nil,
trace: [
%Onchain.RPC.Trace{
action: %Onchain.RPC.Trace.Action{
call_type: "call",
init: nil,
from: <<0::160>>,
gas: 499_978_072,
input: ~h[0xd1692f56000000000000000000000000142da9114e5a98e015aa95afca0585e84832a612000000000000000000000000142da9114e5a98e015aa95afca0585e84832a6120000000000000000000000000000000000000000000000000000000000000000],
to: ~h[0x13172EE393713FBA9925A9A752341EBD31E8D9A7],
value: 0
},
block_hash: nil,
block_number: nil,
gas_used: 492_166_471,
error: "Reverted",
output: "",
result_code: nil,
result_address: nil,
subtraces: 1,
trace_address: [],
transaction_hash: nil,
transaction_position: nil,
type: "call"
},
%Onchain.RPC.Trace{
action: %Onchain.RPC.Trace.Action{
call_type: nil,
init: ~h[0x60e03461009157601f6101ec38819003918201601f19168301916001600160401b038311848410176100965780849260609460405283398101031261009157610047816100ac565b906100606040610059602084016100ac565b92016100ac565b9060805260a05260c05260405161012b90816100c18239608051816088015260a051816045015260c0518160c60152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036100915756fe608060405260043610156013575b3660ba57005b6000803560e01c8063238ac9331460775763c34c08e51460325750600d565b34607457806003193601126074576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b80fd5b5034607457806003193601126074577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166080908152602090f35b600036818037808036817f00000000000000000000000000000000000000000000000000000000000000005af4903d918282803e60f357fd5bf3fea264697066735822122032b5603d6937ceb7a252e16379744d8545670ff4978c8d76c985d051dfcfe46c64736f6c6343000817003300000000000000000000000049e5d261e95f6a02505078bb339fecb210a0b634000000000000000000000000142da9114e5a98e015aa95afca0585e84832a612000000000000000000000000142da9114e5a98e015aa95afca0585e84832a612],
from: ~h[0x13172EE393713FBA9925A9A752341EBD31E8D9A7],
gas: 492_133_529,
input: nil,
to: nil,
value: 0
},
block_hash: nil,
block_number: nil,
gas_used: nil,
error: "contract address collision",
output: nil,
result_code: nil,
result_address: nil,
subtraces: 0,
trace_address: [0],
transaction_hash: nil,
transaction_position: nil,
type: "create"
}
],
vm_trace: nil
},
%Onchain.TraceCall{
output: ~h[0x00000000000000000000000079EDBC4F3A6AA2266CD469CC544501743BE8B078],
state_diff: nil,
trace: [
%Onchain.RPC.Trace{
action: %Onchain.RPC.Trace.Action{
call_type: "call",
init: nil,
from: <<0::160>>,
gas: 499_945_916,
input: ~h[0xd6d38d3f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000081a60808060405234610016576107fe908161001c8239f35b600080fdfe6040608081526004908136101561001557600080fd5b600091823560e01c80630c0a769b146102eb57806350a4548914610255578063c3da3590146100fc5763f1afb11f1461004d57600080fd5b8291346100f85760803660031901126100f857610068610389565b61007061039f565b6100786103b5565b6001600160a01b03908116929091606435918390610097848288610482565b1693843b156100f457879460649386928851998a978896634232cd6360e01b88521690860152602485015260448401525af19081156100eb57506100d85750f35b6100e1906103fc565b6100e85780f35b80fd5b513d84823e3d90fd5b8780fd5b5050fd5b503461025157606036600319011261025157610116610389565b67ffffffffffffffff929060243584811161024d5761013890369085016103cb565b9190946044359081116102495761015290369086016103cb565b9590928681036102395791958793926001600160a01b0380891693909290865b83811061017d578780f35b6101a88561019461018f848887610448565b61046e565b168c6101a184878c610448565b3591610482565b6101b661018f828685610448565b6101c182858a610448565b3590873b15610235578a51631e573fb760e31b81526001600160a01b03909116818d019081526020810192909252908990829081906040010381838b5af1801561022b57908991610217575b5050600101610172565b610220906103fc565b6100f457873861020d565b8a513d8b823e3d90fd5b8980fd5b845163b4fa3fb360e01b81528690fd5b8680fd5b8580fd5b8280fd5b50346102515760a03660031901126102515761026f610389565b9161027861039f565b6102806103b5565b906001600160a01b039060643582811691908290036102e6578288971693843b156100f457879460849385879389519a8b988997639032317760e01b895216908701521660248501526044840152833560648401525af19081156100eb57506100d85750f35b600080fd5b5090346102515760603660031901126102515782610307610389565b61030f61039f565b604435916001600160a01b03906103298482858516610482565b1690813b15610385578451631e573fb760e31b81526001600160a01b039091169581019586526020860192909252909384919082908490829060400103925af19081156100eb5750610379575080f35b610382906103fc565b80f35b8380fd5b600435906001600160a01b03821682036102e657565b602435906001600160a01b03821682036102e657565b604435906001600160a01b03821682036102e657565b9181601f840112156102e65782359167ffffffffffffffff83116102e6576020808501948460051b0101116102e657565b67ffffffffffffffff811161041057604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff82111761041057604052565b91908110156104585760051b0190565b634e487b7160e01b600052603260045260246000fd5b356001600160a01b03811681036102e65790565b60405163095ea7b360e01b602082018181526001600160a01b0385166024840152604480840196909652948252949390926104be606485610426565b83516000926001600160a01b039291858416918591829182855af1906104e26105a4565b82610572575b5081610567575b50156104ff575b50505050509050565b60405196602088015216602486015280604486015260448552608085019085821067ffffffffffffffff8311176105535750610548939461054391604052826105fc565b6105fc565b8038808080806104f6565b634e487b7160e01b81526041600452602490fd5b90503b1515386104ef565b8051919250811591821561058a575b505090386104e8565b61059d92506020809183010191016105e4565b3880610581565b3d156105df573d9067ffffffffffffffff821161041057604051916105d3601f8201601f191660200184610426565b82523d6000602084013e565b606090565b908160209103126102e6575180151581036102e65790565b60408051908101916001600160a01b031667ffffffffffffffff8311828410176104105761066c926040526000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af16106666105a4565b916106f4565b8051908282159283156106dc575b505050156106855750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b6106ec93508201810191016105e4565b38828161067a565b919290156107565750815115610708575090565b3b156107115790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156107695750805190602001fd5b6040519062461bcd60e51b82528160208060048301528251908160248401526000935b8285106107af575050604492506000838284010152601f80199101168101030190fd5b848101820151868601604401529381019385935061078c56fea264697066735822122065151e6cccce6828ff0901f46ab142cb8aa214fc37379817e3635a556dd638a564736f6c63430008170033000000000000],
to: ~h[0x2926631647877E9A84BB7E3A0821D643BF8D63C0],
value: 0
},
block_hash: nil,
block_number: nil,
gas_used: 4298,
error: nil,
output: ~h[0x00000000000000000000000079EDBC4F3A6AA2266CD469CC544501743BE8B078],
result_code: nil,
result_address: nil,
subtraces: 0,
trace_address: [],
transaction_hash: nil,
transaction_position: nil,
type: "call"
}
],
vm_trace: nil
},
%Onchain.TraceCall{
output: <<130, 180, 41, 0>>,
state_diff: nil,
trace: [
%Onchain.RPC.Trace{
action: %Onchain.RPC.Trace.Action{
call_type: "call",
init: nil,
from: <<0::160>>,
gas: 499_977_072,
input: ~h[0xdd560874000000000000000000000000000000000000000000000000000000000000000400000000000000000000000079edbc4f3a6aa2266cd469cc544501743be8b078000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000640c0a769b000000000000000000000000aec1f48e02cfb822be958b68c7957156eb3f0b6e0000000000000000000000001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000000000000000000000000000000000000000f429000000000000000000000000000000000000000000000000000000000],
to: ~h[0x6E995746B61C48C5BDF58FC788B1AEA08DFB7E43],
value: 0
},
block_hash: nil,
block_number: nil,
gas_used: 4202,
error: "Reverted",
output: ~h[0x82B42900],
result_code: nil,
result_address: nil,
subtraces: 1,
trace_address: [],
transaction_hash: nil,
transaction_position: nil,
type: "call"
},
%Onchain.RPC.Trace{
action: %Onchain.RPC.Trace.Action{
call_type: "delegatecall",
init: nil,
from: ~h[0x6E995746B61C48C5BDF58FC788B1AEA08DFB7E43],
gas: 492_162_171,
input: ~h[0xdd560874000000000000000000000000000000000000000000000000000000000000000400000000000000000000000079edbc4f3a6aa2266cd469cc544501743be8b078000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000640c0a769b000000000000000000000000aec1f48e02cfb822be958b68c7957156eb3f0b6e0000000000000000000000001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000000000000000000000000000000000000000f429000000000000000000000000000000000000000000000000000000000],
to: ~h[0x49E5D261E95F6A02505078BB339FECB210A0B634],
value: 0
},
block_hash: nil,
block_number: nil,
gas_used: 1362,
error: "Reverted",
output: <<130, 180, 41, 0>>,
result_code: nil,
result_address: nil,
subtraces: 1,
trace_address: [0],
transaction_hash: nil,
transaction_position: nil,
type: "call"
},
%Onchain.RPC.Trace{
action: %Onchain.RPC.Trace.Action{
call_type: "staticcall",
init: nil,
from: ~h[0x6E995746B61C48C5BDF58FC788B1AEA08DFB7E43],
gas: 484_471_386,
input: ~h[0xC34C08E5],
to: ~h[0x6E995746B61C48C5BDF58FC788B1AEA08DFB7E43],
value: 0
},
block_hash: nil,
block_number: nil,
gas_used: 190,
error: nil,
output: ~h[0x000000000000000000000000142DA9114E5A98E015AA95AFCA0585E84832A612],
result_code: nil,
result_address: nil,
subtraces: 0,
trace_address: [0, 0],
transaction_hash: nil,
transaction_position: nil,
type: "call"
}
],
vm_trace: nil
}
]}
@spec trace_trx(binary() | String.t(), Keyword.t()) :: {:ok, [Onchain.RPC.Trace.t()]} | {:error, term()}
RPC call to get a transaction receipt
Examples
iex> Onchain.RPC.trace_trx("0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5")
{:ok,
[
%Onchain.RPC.Trace{
action: %Onchain.RPC.Trace.Action{
call_type: "call",
from: ~h[0x83806d539d4ea1c140489a06660319c9a303f874],
gas: 0x01a1f8,
input: <<>>,
to: ~h[0x1c39ba39e4735cb65978d4db400ddd70a72dc750],
value: 0x7a16c911b4d00000,
},
block_hash: ~h[0x7eb25504e4c202cf3d62fd585d3e238f592c780cca82dacb2ed3cb5b38883add],
block_number: 3068185,
gas_used: 0x2982,
output: <<>>,
subtraces: 2,
trace_address: [~h[0x1c39ba39e4735cb65978d4db400ddd70a72dc750]],
transaction_hash: ~h[0x17104ac9d3312d8c136b7f44d4b8b47852618065ebfa534bd2d3b5ef218ca1f3],
transaction_position: 2,
type: "call"
},
%Onchain.RPC.Trace{
action: %Onchain.RPC.Trace.Action{
call_type: "call",
from: ~h[0x83806d539d4ea1c140489a06660319c9a303f874],
gas: 0x01a1f8,
input: <<>>,
to: ~h[0x1c39ba39e4735cb65978d4db400ddd70a72dc750],
value: 0x7a16c911b4d00000,
},
block_hash: ~h[0x7eb25504e4c202cf3d62fd585d3e238f592c780cca82dacb2ed3cb5b38883add],
block_number: 3068186,
gas_used: 0x2982,
output: <<>>,
subtraces: 2,
trace_address: [~h[0x1c39ba39e4735cb65978d4db400ddd70a72dc750]],
transaction_hash: ~h[0x17104ac9d3312d8c136b7f44d4b8b47852618065ebfa534bd2d3b5ef218ca1f3],
transaction_position: 2,
type: "call"
}
]}
Returns the node client's version string (web3_clientVersion).
web3_clientVersion is not in execution-apis at any tag or on main.
EIP-1474 is the only definition.
Observed 2026-10-01, HTTP 200, no JSON-RPC refusal. Alchemy
(ETHEREUM_ALCHEMY_URL) returned
"reth/v2.5.2-5a6940e/x86_64-unknown-linux-gnu". Infura
(ETHEREUM_INFURA_URL) returned
"Geth/v1.17.6-stable-3d84c6b2/linux-arm64/go1.27.1".
A refusing endpoint's JSON-RPC error is classified by send_rpc/3, not by a
per-method probe: {:error, {:method_not_found, map}},
{:error, {:namespace_unavailable, map}}, or
{:error, {:unavailable, map}}.
Examples
iex> Onchain.RPC.web3_client_version()
{:ok, "TestClient/v1"}