Excessively simple RPC client for Ethereum.
Local signing through Cartouche.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 (Cartouche.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 |
|---|---|---|---|
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 |
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 | 1 | 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 computed base fee per gas for the next block. | 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 |
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 |
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_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.
RPC call to get the computed base fee per gas for the next block.
RPC call to get the current base fee per blob gas.
RPC call to call a transaction and preview results.
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.
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.
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.
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.
RPC to get a block by its block hash.
RPC to get a block by its block number.
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)
RPC call to get a transaction receipt. Note, this will return {:ok, %Cartouche.Receipt{}} or {:ok, nil} if the receipt is not yet available.
RPC call to call to get the current max priority fee per gas.
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.
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
Types
@type access_list_result() :: %{ :access_list => Cartouche.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()
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> Cartouche.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.t()) :: {:ok, non_neg_integer()} | {:error, term()}
RPC call to get the computed base fee per gas for the next block.
Examples
iex> Cartouche.RPC.base_fee()
{:ok, 1000000000}
@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> Cartouche.RPC.blob_base_fee()
{:ok, 42}
@spec call_trx( Cartouche.Transaction.V1.t() | Cartouche.Transaction.V2.t() | Cartouche.Transaction.Call.t(), Keyword.t() ) :: {:ok, binary()} | {:error, term()}
RPC call to call a transaction and preview results.
Examples
iex> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Cartouche.RPC.call_trx()
{:ok, <<0x0c>>}
iex> Cartouche.Transaction.V2.new(1, {1, :gwei}, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, [<<2::160>>, <<3::160>>], :goerli)
iex> |> Cartouche.RPC.call_trx()
{:ok, <<0x0d>>}
iex> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Cartouche.RPC.call_trx(decode: :hex_unsigned)
{:ok, 0x0c}
iex> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<10::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Cartouche.RPC.call_trx()
{:error, %{code: 3, message: "execution reverted", revert: <<61, 115, 139, 46>>}}
iex> errors = ["Unauthorized()", "BadNonce()", "NotEnoughSigners()", "NotActiveWithdrawalAddress()", "NotActiveOperator()", "DuplicateSigners()"]
iex> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<10::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Cartouche.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> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<11::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Cartouche.RPC.call_trx(errors: errors)
{:error, %{code: 3, message: "execution reverted", error_abi: "Cool(uint256,string)", error_params: [1, "cat"], revert: ABI.encode("Cool(uint256,string)", [1, "cat"])}}
iex> errors = ["Cool(uint256,string)"]
iex> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<12::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Cartouche.RPC.call_trx(errors: errors)
{:error, %{code: 3, message: "execution reverted", revert: <<>>}}
iex> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<13::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Cartouche.RPC.call_trx()
{:error, %{code: -32602, message: "Failed to decode transaction"}}
RPC call to get the node's coinbase address.
Examples
iex> Cartouche.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( Cartouche.Transaction.V1.t() | Cartouche.Transaction.V2.t() | Cartouche.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 = Cartouche.Transaction.Call.new(<<1::160>>, <<0, 1>>)
iex> {:ok, %{access_list: [{address, [storage_key]}], gas_used: gas_used}} =
...> Cartouche.RPC.create_access_list(call)
iex> {address, storage_key, gas_used}
{<<1::160>>, <<2::256>>, 26026}
@spec debug_trace_call( Cartouche.Transaction.V1.t() | Cartouche.Transaction.V2.t() | Cartouche.Transaction.Call.t(), Keyword.t() ) :: {:ok, Cartouche.DebugTrace.t()} | {:error, term()}
RPC to trace a transaction call speculatively via debug API.
Examples
iex> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
...> |> Cartouche.RPC.debug_trace_call()
{:ok,
%Cartouche.DebugTrace{
failed: false,
gas: 24034,
return_value: ~h[0x0000000000000000000000000000000000000000000000000858898f93629000],
struct_logs: [
%Cartouche.DebugTrace.StructLog{
depth: 1,
gas: 599978568,
gas_cost: 3,
op: :PUSH1,
pc: 0,
stack: []
},
%Cartouche.DebugTrace.StructLog{
depth: 1,
gas: 599978565,
gas_cost: 3,
op: :PUSH1,
pc: 2,
stack: [~h[0x80]]
},
%Cartouche.DebugTrace.StructLog{
depth: 1,
gas: 599978562,
gas_cost: 12,
op: :MSTORE,
pc: 4,
stack: [~h[0x80], ~h[0x40]]
}
]
}}
@spec estimate_gas( Cartouche.Transaction.V1.t() | Cartouche.Transaction.V2.t() | Cartouche.Transaction.Call.t(), Keyword.t() ) :: {:ok, non_neg_integer()} | {:error, term()}
RPC call to call to estimate gas used by a given call.
Examples
iex> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
iex> |> Cartouche.RPC.estimate_gas()
{:ok, 0x0d}
iex> Cartouche.Transaction.V2.new(1, {1, :gwei}, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, [<<2::160>>, <<3::160>>], :goerli)
iex> |> Cartouche.RPC.estimate_gas()
{:ok, 0xdd}
iex> use Cartouche.Hex
iex> Cartouche.Transaction.V2.new(1, {1, :gwei}, {100, :gwei}, 100_000, <<10::160>>, {2, :wei}, <<1, 2, 3>>, [<<2::160>>, <<3::160>>], :goerli)
iex> |> Cartouche.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> Cartouche.RPC.eth_block_number()
{:ok, 0x44}
@spec eth_capabilities(Keyword.t()) :: {:ok, Cartouche.RPC.Capabilities.t()} | {:error, term()}
Returns the node's head and resource-retention capabilities.
Examples
iex> {:ok, capabilities} = Cartouche.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> Cartouche.RPC.eth_chain_id()
{:ok, 0x22}
@spec eth_config(Keyword.t()) :: {:ok, Cartouche.RPC.Configuration.t()} | {:error, term()}
Returns the current, next, and last configured Ethereum forks reported by the node.
Examples
iex> {:ok, config} = Cartouche.RPC.eth_config()
iex> {config.current.chain_id, config.current.fork_id}
{1, <<7, 201, 70, 46>>}
@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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, trx_id} = Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, trx_id} = Cartouche.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()) :: {:ok, Cartouche.FeeHistory.t()} | {:error, term()}
RPC call to call to get the Eip-1559 fee history data.
Examples
iex> Cartouche.RPC.fee_history()
{:ok, %Cartouche.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 fill_transaction( Cartouche.Transaction.V1.t() | Cartouche.Transaction.V2.t() | Cartouche.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
Cartouche.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 Cartouche.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.
Examples
iex> {:ok, trx} =
...> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, :kovan)
...> |> Cartouche.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> Cartouche.RPC.gas_price()
{:ok, 1000000000}
@spec get_balance(<<_::160>>, 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> Cartouche.RPC.get_balance(~h[0x0000000000000000000000000000000000000001])
{:ok, 0x55}
@spec get_block_by_hash(binary(), Keyword.t()) :: {:ok, Cartouche.Block.t()} | {: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> Cartouche.RPC.get_block_by_hash(~h[0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae])
{:ok, %Cartouche.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:Cartouche.Block.deserialize/1currently returnstransactions: []regardless — see ROADMAP Task 66.
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, Cartouche.Block.t()} | {: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> Cartouche.RPC.get_block_by_number(55)
{:ok, %Cartouche.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:Cartouche.Block.deserialize/1currently returnstransactions: []regardless — see ROADMAP Task 66.
Plus any option accepted by send_rpc/3 (e.g. :ethereum_node, :timeout, :req_options).
RPC call to get code for a contract at an address.
Examples
iex> Cartouche.RPC.get_code(<<1::160>>)
{:ok, <<0x11, 0x22, 0x33>>}
@spec get_filter_logs(String.t(), Keyword.t()) :: {:ok, [Cartouche.Filter.Log.t()]} | {:error, term()}
RPC call to fetch the full log backlog for a filter.
Examples
iex> {:ok, [log]} = Cartouche.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 Cartouche.Hex
iex> Cartouche.RPC.get_nonce(~h[0x407d73d8a49eeb85d32cf465507dd71d507100c1])
{:ok, 4}
@spec get_transaction_count(<<_::160>>, 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> Cartouche.RPC.get_transaction_count(~h[0x0000000000000000000000000000000000000001])
{:ok, 0x4}
@spec get_trx_receipt(binary() | String.t(), Keyword.t()) :: {:ok, Cartouche.Receipt.t() | nil} | {:error, term()}
RPC call to get a transaction receipt. Note, this will return {:ok, %Cartouche.Receipt{}} or {:ok, nil} if the receipt is not yet available.
Examples
iex> Cartouche.RPC.get_trx_receipt(~h[0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5])
{:ok,
%Cartouche.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: [
%Cartouche.Receipt.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> Cartouche.RPC.get_trx_receipt("0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5")
{:ok,
%Cartouche.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: [
%Cartouche.Receipt.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> Cartouche.RPC.get_trx_receipt("0xf9e69be4f1ae524854e14dc820c519d8f2b86e52c60e54448abf920d22fb6fe2")
{:ok, %Cartouche.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: [
%Cartouche.Receipt.Log{
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]
]
},
%Cartouche.Receipt.Log{
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]
]
},
%Cartouche.Receipt.Log{
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> Cartouche.RPC.get_trx_receipt(<<1::256>>)
{:error, "failed to decode `eth_getTransactionReceipt` response: %FunctionClauseError{module: Cartouche.Hex, function: :decode_hex_, arity: 1, kind: nil, args: nil, clauses: nil}"}
iex> Cartouche.RPC.get_trx_receipt(<<1::256>>, verbose: true)
{:error, "failed to decode `eth_getTransactionReceipt` response: %FunctionClauseError{module: Cartouche.Hex, function: :decode_hex_, arity: 1, kind: nil, args: nil, clauses: nil}"}
iex> Cartouche.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> Cartouche.RPC.max_priority_fee_per_gas()
{:ok, 1000000001}
RPC call to create a new block filter.
Examples
iex> Cartouche.RPC.new_block_filter()
{:ok, "0xb10cf11e"}
RPC call to create a new pending-transaction filter.
Examples
iex> Cartouche.RPC.new_pending_transaction_filter()
{:ok, "0xpend1ng"}
@spec prepare_trx(<<_::160>>, binary() | {String.t(), [term()]}, Keyword.t()) :: {:ok, Cartouche.Transaction.V1.t() | Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, trx} = Cartouche.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}
%Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, trx} = Cartouche.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}
%Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, trx} = Cartouche.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}
%Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, trx} = Cartouche.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}
%Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, trx} = Cartouche.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}
%Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, trx} = Cartouche.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}
%Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, trx} = Cartouche.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}
%Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, trx} = Cartouche.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}
%Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, trx} = Cartouche.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}
%Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, trx} = Cartouche.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}
%Cartouche.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: []
}
@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> Cartouche.RPC.send_rpc("net_version", [])
{:ok, "3"}
iex> use Cartouche.Hex
iex> Cartouche.RPC.send_rpc("get_balance", ["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"], ethereum_node: "http://example.com")
{:ok, "0x0234c8a3397aab58"}
iex> match?({:error, {:invalid_params, %Jason.EncodeError{}}}, Cartouche.RPC.send_rpc(<<255>>, []))
true
iex> match?({:error, {:invalid_params, %Protocol.UndefinedError{}}}, Cartouche.RPC.send_rpc("net_version", [self()]))
trueOptions
Common options (other RPC wrappers forward opts here):
:ethereum_node— node URL; falls back toApplication.get_env(:cartouche, :ethereum_node):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, Cartouche.RPC, plug: ...).
@spec send_transaction( Cartouche.Transaction.V1.t() | Cartouche.Transaction.V2.t() | Cartouche.Transaction.Call.t(), Keyword.t() ) :: {:ok, binary()} | {:error, term()}
RPC call to eth_sendTransaction.
Examples
iex> {:ok, hash} =
...> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
...> |> Cartouche.RPC.send_transaction()
iex> Base.encode16(hash, case: :lower)
"abababababababababababababababababababababababababababababababab"
@spec send_trx( Cartouche.Transaction.V1.t() | Cartouche.Transaction.V2.t(), Keyword.t() ) :: {:ok, binary()} | {:error, term()}
RPC call to send a raw transaction.
Examples
iex> signer_proc = Cartouche.Test.Signer.start_signer()
iex> {:ok, signed_trx} = Cartouche.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} = Cartouche.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 = Cartouche.Test.Signer.start_signer()
iex> {:ok, signed_trx} = Cartouche.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} = Cartouche.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} = Cartouche.RPC.sign(<<1::160>>, "hello")
iex> byte_size(signature)
65
@spec sign_transaction( Cartouche.Transaction.V1.t() | Cartouche.Transaction.V2.t() | Cartouche.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 Cartouche.Transaction.decode/1.
Examples
iex> {:ok, trx} =
...> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, :kovan)
...> |> Cartouche.RPC.sign_transaction()
iex> trx.nonce
1
@spec trace_call( Cartouche.Transaction.V1.t() | Cartouche.Transaction.V2.t() | Cartouche.Transaction.Call.t(), Keyword.t() ) :: {:ok, Cartouche.TraceCall.t()} | {:error, term()}
RPC to trace a transaction call speculatively.
Examples
iex> Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
...> |> Cartouche.RPC.trace_call()
{:ok,
%Cartouche.TraceCall{
output: "",
state_diff: nil,
trace: [
%Cartouche.Trace{
action: %Cartouche.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"
},
%Cartouche.Trace{
action: %Cartouche.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> Cartouche.Transaction.V2.new(1, {1, :gwei}, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, [<<2::160>>, <<3::160>>], :goerli)
...> |> Cartouche.RPC.trace_call()
{:ok,
%Cartouche.TraceCall{
output: "",
state_diff: nil,
trace: [
%Cartouche.Trace{
action: %Cartouche.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"
},
%Cartouche.Trace{
action: %Cartouche.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( [ Cartouche.Transaction.V1.t() | Cartouche.Transaction.V2.t() | Cartouche.Transaction.Call.t() | {Cartouche.Transaction.V1.t() | Cartouche.Transaction.V2.t() | Cartouche.Transaction.Call.t(), <<_::160>> | nil} ], Keyword.t() ) :: {:ok, [Cartouche.TraceCall.t()]} | {:error, term()}
RPC to trace many transaction calls speculatively.
Examples
iex> t1 = Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>)
iex> t2 = Cartouche.Transaction.V2.new(1, {1, :gwei}, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, [<<2::160>>, <<3::160>>], :goerli)
iex> Cartouche.RPC.trace_call_many([t1, t2])
{:ok, [
%Cartouche.TraceCall{
output: "",
state_diff: nil,
trace: [
%Cartouche.Trace{
action: %Cartouche.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"
},
%Cartouche.Trace{
action: %Cartouche.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
},
%Cartouche.TraceCall{
output: ~h[0x00000000000000000000000079EDBC4F3A6AA2266CD469CC544501743BE8B078],
state_diff: nil,
trace: [
%Cartouche.Trace{
action: %Cartouche.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
},
%Cartouche.TraceCall{
output: <<130, 180, 41, 0>>,
state_diff: nil,
trace: [
%Cartouche.Trace{
action: %Cartouche.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"
},
%Cartouche.Trace{
action: %Cartouche.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"
},
%Cartouche.Trace{
action: %Cartouche.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, [Cartouche.Trace.t()]} | {:error, term()}
RPC call to get a transaction receipt
Examples
iex> Cartouche.RPC.trace_trx("0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5")
{:ok,
[
%Cartouche.Trace{
action: %Cartouche.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"
},
%Cartouche.Trace{
action: %Cartouche.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"
}
]}