A module to help build, sign and encode Ethereum transactions.
API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
build_signed_trx_v2 | 9 | Build and sign an EIP-1559 transaction. | address: value, nonce: exchange_data, call_data: value, max_priority_fee_per_gas: exchange_data, max_fee_per_gas: exchange_data, gas_limit: exchange_data, amount: value, access_list: value, opts: value |
build_signed_trx | 7 | Build and sign a legacy transaction. | address: value, nonce: exchange_data, call_data: value, gas_price: exchange_data, gas_limit: exchange_data, value: value, opts: value |
build_trx_v2 | 9 | Build an EIP-1559 transaction for a contract call or raw calldata. | address: value, nonce: exchange_data, call_data: value, max_priority_fee_per_gas: exchange_data, max_fee_per_gas: exchange_data, gas_limit: exchange_data, amount: value, access_list: value, chain_id: exchange_data |
build_trx | 7 | Build a legacy transaction for a contract call or raw calldata. | address: value, nonce: exchange_data, call_data: value, gas_price: exchange_data, gas_limit: exchange_data, value: value, chain_id: exchange_data |
decode | 1 | Decode raw Ethereum transaction bytes into the matching transaction struct (V1/V_2930/V2/V3/V4 by envelope byte). | encoded: value |
encode | 1 | Encode a concrete transaction struct (V1/V2/V3/V4) into raw RLP/typed-RLP transaction bytes by dispatching on struct. | transaction: value |
Summary
Functions
Builds and signs a transaction, to be ready to be passed to JSON-RPC.
Builds and signs a V2 transaction, to be ready to be passed to JSON-RPC.
Builds a v1-style call to a given contract
Builds a v2 (eip-1559)-style call to a given contract
Decodes raw Ethereum transaction bytes into the matching transaction struct.
Encodes a concrete transaction struct into raw transaction bytes, mirroring decode/1.
Functions
@spec build_signed_trx( <<_::160>>, integer(), binary() | {String.t(), [term()]}, integer() | {integer(), :wei | :gwei} | nil, integer(), integer() | {integer(), :wei | :gwei}, Keyword.t() ) :: {:ok, Cartouche.Transaction.V1.t()} | {:error, String.t()}
Builds and signs a transaction, to be ready to be passed to JSON-RPC.
Optionally takes a callback to modify the transaction before it is signed.
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, signer: signer_proc, chain_id: :goerli)
iex> {:ok, signer} = Cartouche.Transaction.V1.recover_signer(signed_trx, 5)
iex> Cartouche.Hex.to_address(signer)
"0x63Cc7c25e0cdb121aBb0fE477a6b9901889F99A7"
@spec build_signed_trx_v2( <<_::160>>, integer(), binary() | {String.t(), [term()]}, integer() | {integer(), :wei | :gwei} | nil, integer() | {integer(), :wei | :gwei} | nil, integer(), integer() | {integer(), :wei | :gwei}, list(), Keyword.t() ) :: {:ok, Cartouche.Transaction.V2.t()} | {:error, String.t()}
Builds and signs a V2 transaction, to be ready to be passed to JSON-RPC.
Optionally takes a callback to modify the transaction before it is signed.
Examples
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, [], signer: signer_proc, chain_id: :goerli)
iex> {:ok, signer} = Cartouche.Transaction.V2.recover_signer(signed_trx)
iex> Cartouche.Hex.to_address(signer)
"0x63Cc7c25e0cdb121aBb0fE477a6b9901889F99A7"
@spec build_trx( <<_::160>>, integer(), binary() | {String.t(), [term()]}, integer() | {integer(), :wei | :gwei} | nil, integer(), integer() | {integer(), :wei | :gwei}, atom() | integer() | nil ) :: Cartouche.Transaction.V1.t()
Builds a v1-style call to a given contract
Examples
iex> use Cartouche.Hex
iex> Cartouche.Transaction.build_trx(<<1::160>>, 5, {"baz(uint,address)", [50, :binary.decode_unsigned(<<1::160>>)]}, {50, :gwei}, 100_000, 0, 5)
%Cartouche.Transaction.V1{
nonce: 5,
gas_price: 50000000000,
gas_limit: 100000,
to: <<1::160>>,
value: 0,
data: ~h[0xA291ADD600000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001],
v: 5,
r: 0,
s: 0
}
iex> use Cartouche.Hex
iex> call_data = ~h[0xA291ADD600000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001]
...> Cartouche.Transaction.build_trx(<<1::160>>, 5, call_data, {50, :gwei}, 100_000, 0, 5)
%Cartouche.Transaction.V1{
nonce: 5,
gas_price: 50000000000,
gas_limit: 100000,
to: <<1::160>>,
value: 0,
data: ~h[0xA291ADD600000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001],
v: 5,
r: 0,
s: 0
}
@spec build_trx_v2( <<_::160>>, integer(), binary() | {String.t(), [term()]}, integer() | {integer(), :wei | :gwei} | nil, integer() | {integer(), :wei | :gwei} | nil, integer(), integer() | {integer(), :wei | :gwei}, list(), atom() | integer() | nil ) :: Cartouche.Transaction.V2.t()
Builds a v2 (eip-1559)-style call to a given contract
Examples
iex> use Cartouche.Hex
iex> Cartouche.Transaction.build_trx_v2(<<1::160>>, 6, {"baz(uint,address)", [50, :binary.decode_unsigned(<<1::160>>)]}, {50, :gwei}, {10, :gwei}, 100_000, 0, [<<1::160>>], :goerli)
%Cartouche.Transaction.V2{
chain_id: 5,
nonce: 6,
max_priority_fee_per_gas: 50000000000,
max_fee_per_gas: 10000000000,
gas_limit: 100000,
destination: <<1::160>>,
amount: 0,
data: ~h[0xA291ADD600000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001],
access_list: [<<1::160>>],
signature_y_parity: nil,
signature_r: nil,
signature_s: nil
}
iex> use Cartouche.Hex
iex> call_data = ~h[0xA291ADD600000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001]
...> Cartouche.Transaction.build_trx_v2(<<1::160>>, 5, call_data, {50, :gwei}, {10, :gwei}, 100_000, 0, [<<1::160>>], :goerli)
%Cartouche.Transaction.V2{
chain_id: 5,
nonce: 5,
max_priority_fee_per_gas: 50000000000,
max_fee_per_gas: 10000000000,
gas_limit: 100000,
destination: <<1::160>>,
amount: 0,
data: ~h[0xA291ADD600000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001],
access_list: [<<1::160>>],
signature_y_parity: nil,
signature_r: nil,
signature_s: nil
}
@spec decode(binary()) :: {:ok, Cartouche.Transaction.V1.t() | Cartouche.Transaction.V_2930.t() | Cartouche.Transaction.V2.t() | Cartouche.Transaction.V3.t() | Cartouche.Transaction.V4.t()} | {:error, String.t() | :empty_transaction | :unknown_envelope_type}
Decodes raw Ethereum transaction bytes into the matching transaction struct.
Dispatches typed envelopes by their first byte:
0x01-Cartouche.Transaction.V_29300x02-Cartouche.Transaction.V20x03-Cartouche.Transaction.V30x04-Cartouche.Transaction.V4
Legacy transactions are untyped RLP and are decoded as
Cartouche.Transaction.V1 when the first byte is an RLP prefix (>= 0x80).
Empty input returns {:error, :empty_transaction}. Unknown typed envelopes
(< 0x80 except supported typed envelopes) return {:error, :unknown_envelope_type}.
Examples
iex> tx = Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, :kovan)
iex> {:ok, decoded} = Cartouche.Transaction.decode(Cartouche.Transaction.V1.encode(tx))
iex> decoded == tx
true
iex> Cartouche.Transaction.decode(<<>>)
{:error, :empty_transaction}
@spec encode( Cartouche.Transaction.V1.t() | Cartouche.Transaction.V2.t() | Cartouche.Transaction.V3.t() | Cartouche.Transaction.V4.t() ) :: binary()
Encodes a concrete transaction struct into raw transaction bytes, mirroring decode/1.
Dispatches by struct so callers don't need to know which versioned encoder to
invoke. The output is the same shape decode/1 accepts:
%Cartouche.Transaction.V1{}- untyped RLP-encoded legacy/EIP-155 bytes (leading byte>= 0x80).%Cartouche.Transaction.V2{}-0x02-prefixed EIP-2718 typed RLP.%Cartouche.Transaction.V3{}-0x03-prefixed EIP-2718 typed RLP (EIP-4844 blob transaction envelope).%Cartouche.Transaction.V4{}-0x04-prefixed EIP-2718 typed RLP (EIP-7702 authorization-list transaction envelope).
Each leaf encoder already emits the EIP-2718 envelope byte where applicable; this function is a pure pattern-match-and-delegate.
Examples
iex> tx = Cartouche.Transaction.V1.new(1, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, :kovan)
iex> {:ok, ^tx} = tx |> Cartouche.Transaction.encode() |> Cartouche.Transaction.decode()
iex> Cartouche.Transaction.encode(tx) == Cartouche.Transaction.V1.encode(tx)
true
iex> tx = Cartouche.Transaction.V2.new(1, {1, :gwei}, {100, :gwei}, 100_000, <<1::160>>, {2, :wei}, <<1, 2, 3>>, [], :goerli)
iex> <<0x02, _::binary>> = Cartouche.Transaction.encode(tx)
iex> {:ok, ^tx} = tx |> Cartouche.Transaction.encode() |> Cartouche.Transaction.decode()