Packages

Ethereum library for Gleam - JSON-RPC client, transaction signing, ABI encoding, and wallet management on the BEAM

Current section

Files

Jump to
gleeth src gleeth@crypto@transaction.erl
Raw

src/gleeth@crypto@transaction.erl

-module(gleeth@crypto@transaction).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleeth/crypto/transaction.gleam").
-export([build_legacy/0, build_eip1559/0, legacy_to/2, legacy_value_hex/2, legacy_value_ether/2, legacy_gas_limit_int/2, legacy_gas_limit_hex/2, legacy_gas_price_hex/2, legacy_gas_price_gwei/2, legacy_nonce_int/2, legacy_nonce_hex/2, legacy_chain/2, legacy_data_hex/2, eip1559_to/2, eip1559_value_hex/2, eip1559_value_ether/2, eip1559_gas_limit_int/2, eip1559_gas_limit_hex/2, eip1559_max_fee_hex/2, eip1559_max_fee_gwei/2, eip1559_max_priority_fee_hex/2, eip1559_max_priority_fee_gwei/2, eip1559_nonce_int/2, eip1559_nonce_hex/2, eip1559_chain/2, eip1559_data_hex/2, eip1559_access_list/2, sign_transaction/2, sign_eip1559_transaction/2, create_legacy_transaction/7, sign_legacy/2, create_eth_transfer/6, create_contract_call/6, create_eip1559_transaction/9, sign_eip1559/2, recover_sender/1, recover_sender_eip1559/1, signed_transaction_to_string/1, hash_raw_transaction/1, get_transaction_hash/1, get_eip1559_transaction_hash/1, signed_eip1559_transaction_to_string/1, error_to_string/1, decode_legacy/1, decode_eip1559/1, decode/1]).
-export_type([unsigned_transaction/0, signed_transaction/0, eip1559_transaction/0, access_list_entry/0, signed_eip1559_transaction/0, transaction_type/0, transaction_error/0, legacy_builder/0, eip1559_builder/0, decoded_transaction/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type unsigned_transaction() :: {unsigned_transaction,
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
integer()}.
-type signed_transaction() :: {signed_transaction,
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
integer(),
binary(),
binary(),
binary(),
binary()}.
-type eip1559_transaction() :: {eip1559_transaction,
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
integer(),
list(access_list_entry())}.
-type access_list_entry() :: {access_list_entry, binary(), list(binary())}.
-type signed_eip1559_transaction() :: {signed_eip1559_transaction,
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
integer(),
list(access_list_entry()),
binary(),
binary(),
binary(),
binary()}.
-type transaction_type() :: legacy | access_list | eip1559.
-type transaction_error() :: {invalid_address, binary()} |
{invalid_amount, binary()} |
{invalid_gas, binary()} |
{invalid_nonce, binary()} |
{invalid_chain_id, binary()} |
{invalid_data, binary()} |
{signing_failed, binary()} |
{encoding_failed, binary()}.
-type legacy_builder() :: {legacy_builder,
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(integer())}.
-type eip1559_builder() :: {eip1559_builder,
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(integer()),
list(access_list_entry())}.
-type decoded_transaction() :: {decoded_legacy, signed_transaction()} |
{decoded_eip1559, signed_eip1559_transaction()}.
-file("src/gleeth/crypto/transaction.gleam", 152).
?DOC(" Create a new empty legacy transaction builder.\n").
-spec build_legacy() -> legacy_builder().
build_legacy() ->
{legacy_builder, none, none, none, none, none, none, none}.
-file("src/gleeth/crypto/transaction.gleam", 165).
?DOC(" Create a new empty EIP-1559 transaction builder.\n").
-spec build_eip1559() -> eip1559_builder().
build_eip1559() ->
{eip1559_builder, none, none, none, none, none, none, none, none, []}.
-file("src/gleeth/crypto/transaction.gleam", 184).
?DOC(" Set the recipient address on a legacy builder.\n").
-spec legacy_to(legacy_builder(), binary()) -> legacy_builder().
legacy_to(Builder, Address) ->
{legacy_builder,
{some, Address},
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 189).
?DOC(" Set the value as a raw hex string on a legacy builder.\n").
-spec legacy_value_hex(legacy_builder(), binary()) -> legacy_builder().
legacy_value_hex(Builder, Hex_val) ->
{legacy_builder,
erlang:element(2, Builder),
{some, Hex_val},
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 198).
?DOC(
" Set the value from an ether amount string on a legacy builder.\n"
" Converts ether to wei hex using the wei module.\n"
).
-spec legacy_value_ether(legacy_builder(), binary()) -> legacy_builder().
legacy_value_ether(Builder, Ether_str) ->
case gleeth@wei:from_ether(Ether_str) of
{ok, Hex_val} ->
{legacy_builder,
erlang:element(2, Builder),
{some, Hex_val},
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder)};
{error, _} ->
Builder
end.
-file("src/gleeth/crypto/transaction.gleam", 209).
?DOC(" Set the gas limit from an integer on a legacy builder.\n").
-spec legacy_gas_limit_int(legacy_builder(), integer()) -> legacy_builder().
legacy_gas_limit_int(Builder, Limit) ->
{legacy_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
{some, gleeth@wei:from_int(Limit)},
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 214).
?DOC(" Set the gas limit as a raw hex string on a legacy builder.\n").
-spec legacy_gas_limit_hex(legacy_builder(), binary()) -> legacy_builder().
legacy_gas_limit_hex(Builder, Hex_val) ->
{legacy_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
{some, Hex_val},
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 222).
?DOC(" Set the gas price as a raw hex string on a legacy builder.\n").
-spec legacy_gas_price_hex(legacy_builder(), binary()) -> legacy_builder().
legacy_gas_price_hex(Builder, Hex_val) ->
{legacy_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
{some, Hex_val},
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 230).
?DOC(" Set the gas price from a gwei amount string on a legacy builder.\n").
-spec legacy_gas_price_gwei(legacy_builder(), binary()) -> legacy_builder().
legacy_gas_price_gwei(Builder, Gwei_str) ->
case gleeth@wei:from_gwei(Gwei_str) of
{ok, Hex_val} ->
{legacy_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
{some, Hex_val},
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder)};
{error, _} ->
Builder
end.
-file("src/gleeth/crypto/transaction.gleam", 241).
?DOC(" Set the nonce from an integer on a legacy builder.\n").
-spec legacy_nonce_int(legacy_builder(), integer()) -> legacy_builder().
legacy_nonce_int(Builder, N) ->
{legacy_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
{some, gleeth@wei:from_int(N)},
erlang:element(7, Builder),
erlang:element(8, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 246).
?DOC(" Set the nonce as a raw hex string on a legacy builder.\n").
-spec legacy_nonce_hex(legacy_builder(), binary()) -> legacy_builder().
legacy_nonce_hex(Builder, Hex_val) ->
{legacy_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
{some, Hex_val},
erlang:element(7, Builder),
erlang:element(8, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 254).
?DOC(" Set the chain ID on a legacy builder.\n").
-spec legacy_chain(legacy_builder(), integer()) -> legacy_builder().
legacy_chain(Builder, Id) ->
{legacy_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
{some, Id}}.
-file("src/gleeth/crypto/transaction.gleam", 259).
?DOC(" Set the calldata as a raw hex string on a legacy builder.\n").
-spec legacy_data_hex(legacy_builder(), binary()) -> legacy_builder().
legacy_data_hex(Builder, Hex_val) ->
{legacy_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
{some, Hex_val},
erlang:element(8, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 268).
?DOC(" Set the recipient address on an EIP-1559 builder.\n").
-spec eip1559_to(eip1559_builder(), binary()) -> eip1559_builder().
eip1559_to(Builder, Address) ->
{eip1559_builder,
{some, Address},
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 273).
?DOC(" Set the value as a raw hex string on an EIP-1559 builder.\n").
-spec eip1559_value_hex(eip1559_builder(), binary()) -> eip1559_builder().
eip1559_value_hex(Builder, Hex_val) ->
{eip1559_builder,
erlang:element(2, Builder),
{some, Hex_val},
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 281).
?DOC(" Set the value from an ether amount string on an EIP-1559 builder.\n").
-spec eip1559_value_ether(eip1559_builder(), binary()) -> eip1559_builder().
eip1559_value_ether(Builder, Ether_str) ->
case gleeth@wei:from_ether(Ether_str) of
{ok, Hex_val} ->
{eip1559_builder,
erlang:element(2, Builder),
{some, Hex_val},
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder)};
{error, _} ->
Builder
end.
-file("src/gleeth/crypto/transaction.gleam", 292).
?DOC(" Set the gas limit from an integer on an EIP-1559 builder.\n").
-spec eip1559_gas_limit_int(eip1559_builder(), integer()) -> eip1559_builder().
eip1559_gas_limit_int(Builder, Limit) ->
{eip1559_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
{some, gleeth@wei:from_int(Limit)},
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 300).
?DOC(" Set the gas limit as a raw hex string on an EIP-1559 builder.\n").
-spec eip1559_gas_limit_hex(eip1559_builder(), binary()) -> eip1559_builder().
eip1559_gas_limit_hex(Builder, Hex_val) ->
{eip1559_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
{some, Hex_val},
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 308).
?DOC(" Set the max fee per gas as a raw hex string on an EIP-1559 builder.\n").
-spec eip1559_max_fee_hex(eip1559_builder(), binary()) -> eip1559_builder().
eip1559_max_fee_hex(Builder, Hex_val) ->
{eip1559_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
{some, Hex_val},
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 316).
?DOC(" Set the max fee per gas from a gwei amount on an EIP-1559 builder.\n").
-spec eip1559_max_fee_gwei(eip1559_builder(), binary()) -> eip1559_builder().
eip1559_max_fee_gwei(Builder, Gwei_str) ->
case gleeth@wei:from_gwei(Gwei_str) of
{ok, Hex_val} ->
{eip1559_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
{some, Hex_val},
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder)};
{error, _} ->
Builder
end.
-file("src/gleeth/crypto/transaction.gleam", 327).
?DOC(" Set the max priority fee per gas as a raw hex string on an EIP-1559 builder.\n").
-spec eip1559_max_priority_fee_hex(eip1559_builder(), binary()) -> eip1559_builder().
eip1559_max_priority_fee_hex(Builder, Hex_val) ->
{eip1559_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
{some, Hex_val},
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 335).
?DOC(" Set the max priority fee per gas from a gwei amount on an EIP-1559 builder.\n").
-spec eip1559_max_priority_fee_gwei(eip1559_builder(), binary()) -> eip1559_builder().
eip1559_max_priority_fee_gwei(Builder, Gwei_str) ->
case gleeth@wei:from_gwei(Gwei_str) of
{ok, Hex_val} ->
{eip1559_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
{some, Hex_val},
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder)};
{error, _} ->
Builder
end.
-file("src/gleeth/crypto/transaction.gleam", 347).
?DOC(" Set the nonce from an integer on an EIP-1559 builder.\n").
-spec eip1559_nonce_int(eip1559_builder(), integer()) -> eip1559_builder().
eip1559_nonce_int(Builder, N) ->
{eip1559_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
{some, gleeth@wei:from_int(N)},
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 352).
?DOC(" Set the nonce as a raw hex string on an EIP-1559 builder.\n").
-spec eip1559_nonce_hex(eip1559_builder(), binary()) -> eip1559_builder().
eip1559_nonce_hex(Builder, Hex_val) ->
{eip1559_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
{some, Hex_val},
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 360).
?DOC(" Set the chain ID on an EIP-1559 builder.\n").
-spec eip1559_chain(eip1559_builder(), integer()) -> eip1559_builder().
eip1559_chain(Builder, Id) ->
{eip1559_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
{some, Id},
erlang:element(10, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 365).
?DOC(" Set the calldata as a raw hex string on an EIP-1559 builder.\n").
-spec eip1559_data_hex(eip1559_builder(), binary()) -> eip1559_builder().
eip1559_data_hex(Builder, Hex_val) ->
{eip1559_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
{some, Hex_val},
erlang:element(9, Builder),
erlang:element(10, Builder)}.
-file("src/gleeth/crypto/transaction.gleam", 373).
?DOC(" Set the access list on an EIP-1559 builder.\n").
-spec eip1559_access_list(eip1559_builder(), list(access_list_entry())) -> eip1559_builder().
eip1559_access_list(Builder, Entries) ->
{eip1559_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
Entries}.
-file("src/gleeth/crypto/transaction.gleam", 452).
-spec require_option(gleam@option:option(binary()), binary()) -> {ok, binary()} |
{error, transaction_error()}.
require_option(Opt, Field_name) ->
case Opt of
{some, Val} ->
{ok, Val};
none ->
{error,
{invalid_data,
<<"Missing required field: "/utf8, Field_name/binary>>}}
end.
-file("src/gleeth/crypto/transaction.gleam", 462).
-spec require_option_int(gleam@option:option(integer()), binary()) -> {ok,
integer()} |
{error, transaction_error()}.
require_option_int(Opt, Field_name) ->
case Opt of
{some, Val} ->
{ok, Val};
none ->
{error,
{invalid_data,
<<"Missing required field: "/utf8, Field_name/binary>>}}
end.
-file("src/gleeth/crypto/transaction.gleam", 815).
?DOC(
" Encode a hex string as raw bytes without stripping leading zeros.\n"
" Used for address and data fields which are byte strings, not integers.\n"
).
-spec encode_raw_hex(binary()) -> gleeth@encoding@rlp:rlp_item().
encode_raw_hex(Hex_string) ->
case gleeth@utils@hex:decode(Hex_string) of
{ok, Bytes} ->
{rlp_bytes, Bytes};
{error, _} ->
{rlp_bytes, <<>>}
end.
-file("src/gleeth/crypto/transaction.gleam", 721).
?DOC(" EIP-155 signing hash: keccak256(RLP([nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0]))\n").
-spec create_signing_hash(unsigned_transaction()) -> bitstring().
create_signing_hash(Transaction) ->
Items = {rlp_list,
[gleeth@encoding@rlp:encode_hex_field(erlang:element(6, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(5, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(4, Transaction)),
encode_raw_hex(erlang:element(2, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(3, Transaction)),
encode_raw_hex(erlang:element(7, Transaction)),
gleeth@encoding@rlp:encode_int(erlang:element(8, Transaction)),
gleeth@encoding@rlp:encode_int(0),
gleeth@encoding@rlp:encode_int(0)]},
gleeth@crypto@keccak:keccak256_binary(gleeth@encoding@rlp:encode(Items)).
-file("src/gleeth/crypto/transaction.gleam", 808).
-spec encode_access_list_entry(access_list_entry()) -> gleeth@encoding@rlp:rlp_item().
encode_access_list_entry(Entry) ->
Storage_keys = gleam@list:map(
erlang:element(3, Entry),
fun encode_raw_hex/1
),
{rlp_list,
[encode_raw_hex(erlang:element(2, Entry)), {rlp_list, Storage_keys}]}.
-file("src/gleeth/crypto/transaction.gleam", 804).
?DOC(" Encode an access list as an RLP list of [address, [storageKeys]] entries\n").
-spec encode_access_list(list(access_list_entry())) -> gleeth@encoding@rlp:rlp_item().
encode_access_list(Entries) ->
{rlp_list, gleam@list:map(Entries, fun encode_access_list_entry/1)}.
-file("src/gleeth/crypto/transaction.gleam", 760).
?DOC(" EIP-1559 signing hash: keccak256(0x02 || RLP([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to, value, data, accessList]))\n").
-spec create_eip1559_signing_hash(eip1559_transaction()) -> bitstring().
create_eip1559_signing_hash(Transaction) ->
Items = {rlp_list,
[gleeth@encoding@rlp:encode_int(erlang:element(9, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(7, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(6, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(5, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(4, Transaction)),
encode_raw_hex(erlang:element(2, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(3, Transaction)),
encode_raw_hex(erlang:element(8, Transaction)),
encode_access_list(erlang:element(10, Transaction))]},
Payload = gleeth@encoding@rlp:encode(Items),
gleeth@crypto@keccak:keccak256_binary(<<16#02, Payload/bitstring>>).
-file("src/gleeth/crypto/transaction.gleam", 823).
?DOC(" Strip leading zero bytes for minimal big-endian integer encoding\n").
-spec strip_leading_zeros(bitstring()) -> bitstring().
strip_leading_zeros(Data) ->
case Data of
<<0:8, Rest/bitstring>> ->
strip_leading_zeros(Rest);
_ ->
Data
end.
-file("src/gleeth/crypto/transaction.gleam", 738).
?DOC(" RLP-encode the signed transaction: [nonce, gasPrice, gasLimit, to, value, data, v, r, s]\n").
-spec create_raw_transaction(
unsigned_transaction(),
integer(),
bitstring(),
bitstring()
) -> binary().
create_raw_transaction(Transaction, V, R, S) ->
Items = {rlp_list,
[gleeth@encoding@rlp:encode_hex_field(erlang:element(6, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(5, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(4, Transaction)),
encode_raw_hex(erlang:element(2, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(3, Transaction)),
encode_raw_hex(erlang:element(7, Transaction)),
gleeth@encoding@rlp:encode_int(V),
{rlp_bytes, strip_leading_zeros(R)},
{rlp_bytes, strip_leading_zeros(S)}]},
gleeth@utils@hex:encode(gleeth@encoding@rlp:encode(Items)).
-file("src/gleeth/crypto/transaction.gleam", 634).
?DOC(
" Sign a legacy (EIP-155) transaction with a wallet.\n"
" Produces an RLP-encoded raw transaction suitable for eth_sendRawTransaction.\n"
).
-spec sign_transaction(unsigned_transaction(), gleeth@crypto@wallet:wallet()) -> {ok,
signed_transaction()} |
{error, transaction_error()}.
sign_transaction(Transaction, Wallet) ->
Signing_hash = create_signing_hash(Transaction),
gleam@result:'try'(
begin
_pipe = gleeth@crypto@wallet:sign_hash(Wallet, Signing_hash),
gleam@result:map_error(
_pipe,
fun(Err) ->
{signing_failed,
<<"Failed to sign transaction: "/utf8,
(gleeth@crypto@wallet:error_to_string(Err))/binary>>}
end
)
end,
fun(Signature) ->
Eip155_v = (erlang:element(4, Signature) + (2 * erlang:element(
8,
Transaction
)))
+ 35,
V_hex = <<"0x"/utf8,
(string:lowercase(gleam@int:to_base16(Eip155_v)))/binary>>,
R_hex = gleeth@utils@hex:encode(erlang:element(2, Signature)),
S_hex = gleeth@utils@hex:encode(erlang:element(3, Signature)),
Raw_tx = create_raw_transaction(
Transaction,
Eip155_v,
erlang:element(2, Signature),
erlang:element(3, Signature)
),
{ok,
{signed_transaction,
erlang:element(2, Transaction),
erlang:element(3, Transaction),
erlang:element(4, Transaction),
erlang:element(5, Transaction),
erlang:element(6, Transaction),
erlang:element(7, Transaction),
erlang:element(8, Transaction),
V_hex,
R_hex,
S_hex,
Raw_tx}}
end
).
-file("src/gleeth/crypto/transaction.gleam", 778).
?DOC(" RLP-encode the signed EIP-1559 transaction: 0x02 || RLP([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to, value, data, accessList, v, r, s])\n").
-spec create_eip1559_raw_transaction(
eip1559_transaction(),
integer(),
bitstring(),
bitstring()
) -> binary().
create_eip1559_raw_transaction(Transaction, V, R, S) ->
Items = {rlp_list,
[gleeth@encoding@rlp:encode_int(erlang:element(9, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(7, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(6, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(5, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(4, Transaction)),
encode_raw_hex(erlang:element(2, Transaction)),
gleeth@encoding@rlp:encode_hex_field(erlang:element(3, Transaction)),
encode_raw_hex(erlang:element(8, Transaction)),
encode_access_list(erlang:element(10, Transaction)),
gleeth@encoding@rlp:encode_int(V),
{rlp_bytes, strip_leading_zeros(R)},
{rlp_bytes, strip_leading_zeros(S)}]},
Payload = gleeth@encoding@rlp:encode(Items),
gleeth@utils@hex:encode(<<16#02, Payload/bitstring>>).
-file("src/gleeth/crypto/transaction.gleam", 675).
?DOC(
" Sign an EIP-1559 (Type 2) transaction with a wallet.\n"
" Produces a type-prefixed RLP-encoded raw transaction: 0x02 || RLP([...]).\n"
).
-spec sign_eip1559_transaction(
eip1559_transaction(),
gleeth@crypto@wallet:wallet()
) -> {ok, signed_eip1559_transaction()} | {error, transaction_error()}.
sign_eip1559_transaction(Transaction, Wallet) ->
Signing_hash = create_eip1559_signing_hash(Transaction),
gleam@result:'try'(
begin
_pipe = gleeth@crypto@wallet:sign_hash(Wallet, Signing_hash),
gleam@result:map_error(
_pipe,
fun(Err) ->
{signing_failed,
<<"Failed to sign transaction: "/utf8,
(gleeth@crypto@wallet:error_to_string(Err))/binary>>}
end
)
end,
fun(Signature) ->
V = erlang:element(4, Signature),
V_hex = <<"0x"/utf8,
(string:lowercase(gleam@int:to_base16(V)))/binary>>,
R_hex = gleeth@utils@hex:encode(erlang:element(2, Signature)),
S_hex = gleeth@utils@hex:encode(erlang:element(3, Signature)),
Raw_tx = create_eip1559_raw_transaction(
Transaction,
V,
erlang:element(2, Signature),
erlang:element(3, Signature)
),
{ok,
{signed_eip1559_transaction,
erlang:element(2, Transaction),
erlang:element(3, Transaction),
erlang:element(4, Transaction),
erlang:element(5, Transaction),
erlang:element(6, Transaction),
erlang:element(7, Transaction),
erlang:element(8, Transaction),
erlang:element(9, Transaction),
erlang:element(10, Transaction),
V_hex,
R_hex,
S_hex,
Raw_tx}}
end
).
-file("src/gleeth/crypto/transaction.gleam", 887).
?DOC(" Validate chain ID\n").
-spec validate_chain_id(integer()) -> {ok, nil} | {error, transaction_error()}.
validate_chain_id(Chain_id) ->
case Chain_id > 0 of
true ->
{ok, nil};
false ->
{error, {invalid_chain_id, <<"Chain ID must be positive"/utf8>>}}
end.
-file("src/gleeth/crypto/transaction.gleam", 895).
?DOC(" Check if character is valid hex\n").
-spec is_hex_char(binary()) -> boolean().
is_hex_char(Char) ->
case Char of
<<"0"/utf8>> ->
true;
<<"1"/utf8>> ->
true;
<<"2"/utf8>> ->
true;
<<"3"/utf8>> ->
true;
<<"4"/utf8>> ->
true;
<<"5"/utf8>> ->
true;
<<"6"/utf8>> ->
true;
<<"7"/utf8>> ->
true;
<<"8"/utf8>> ->
true;
<<"9"/utf8>> ->
true;
<<"a"/utf8>> ->
true;
<<"b"/utf8>> ->
true;
<<"c"/utf8>> ->
true;
<<"d"/utf8>> ->
true;
<<"e"/utf8>> ->
true;
<<"f"/utf8>> ->
true;
<<"A"/utf8>> ->
true;
<<"B"/utf8>> ->
true;
<<"C"/utf8>> ->
true;
<<"D"/utf8>> ->
true;
<<"E"/utf8>> ->
true;
<<"F"/utf8>> ->
true;
_ ->
false
end.
-file("src/gleeth/crypto/transaction.gleam", 835).
?DOC(" Validate Ethereum address format\n").
-spec validate_address(binary()) -> {ok, nil} | {error, transaction_error()}.
validate_address(Address) ->
Cleaned = case gleam_stdlib:string_starts_with(Address, <<"0x"/utf8>>) of
true ->
gleam@string:drop_start(Address, 2);
false ->
Address
end,
case string:length(Cleaned) of
40 ->
case begin
_pipe = gleam@string:to_graphemes(Cleaned),
gleam@list:all(_pipe, fun is_hex_char/1)
end of
true ->
{ok, nil};
false ->
{error,
{invalid_address,
<<"Address contains non-hex characters"/utf8>>}}
end;
0 ->
{ok, nil};
_ ->
{error,
{invalid_address, <<"Address must be 40 hex characters"/utf8>>}}
end.
-file("src/gleeth/crypto/transaction.gleam", 855).
?DOC(" Validate hex amount (like value, gas, nonce)\n").
-spec validate_hex_amount(binary()) -> {ok, nil} | {error, transaction_error()}.
validate_hex_amount(Amount) ->
Cleaned = case gleam_stdlib:string_starts_with(Amount, <<"0x"/utf8>>) of
true ->
gleam@string:drop_start(Amount, 2);
false ->
Amount
end,
case begin
_pipe = gleam@string:to_graphemes(Cleaned),
gleam@list:all(_pipe, fun is_hex_char/1)
end of
true ->
{ok, nil};
false ->
{error,
{invalid_amount, <<"Amount contains non-hex characters"/utf8>>}}
end.
-file("src/gleeth/crypto/transaction.gleam", 868).
?DOC(" Validate hex data\n").
-spec validate_hex_data(binary()) -> {ok, nil} | {error, transaction_error()}.
validate_hex_data(Data) ->
case Data of
<<"0x"/utf8>> ->
{ok, nil};
_ ->
Cleaned = case gleam_stdlib:string_starts_with(Data, <<"0x"/utf8>>) of
true ->
gleam@string:drop_start(Data, 2);
false ->
Data
end,
case begin
_pipe = gleam@string:to_graphemes(Cleaned),
gleam@list:all(_pipe, fun is_hex_char/1)
end of
true ->
{ok, nil};
false ->
{error,
{invalid_data,
<<"Data contains non-hex characters"/utf8>>}}
end
end.
-file("src/gleeth/crypto/transaction.gleam", 909).
?DOC(" Normalize address to lowercase with 0x prefix\n").
-spec normalize_address(binary()) -> binary().
normalize_address(Address) ->
case Address of
<<""/utf8>> ->
<<""/utf8>>;
_ ->
Cleaned = case gleam_stdlib:string_starts_with(
Address,
<<"0x"/utf8>>
) of
true ->
gleam@string:drop_start(Address, 2);
false ->
Address
end,
<<"0x"/utf8, (string:lowercase(Cleaned))/binary>>
end.
-file("src/gleeth/crypto/transaction.gleam", 923).
?DOC(" Normalize hex string to lowercase with 0x prefix\n").
-spec normalize_hex(binary()) -> binary().
normalize_hex(Hex) ->
Cleaned = case gleam_stdlib:string_starts_with(Hex, <<"0x"/utf8>>) of
true ->
gleam@string:drop_start(Hex, 2);
false ->
Hex
end,
<<"0x"/utf8, (string:lowercase(Cleaned))/binary>>.
-file("src/gleeth/crypto/transaction.gleam", 932).
?DOC(" Normalize hex data (keep \"0x\" for empty data)\n").
-spec normalize_hex_data(binary()) -> binary().
normalize_hex_data(Data) ->
case Data of
<<""/utf8>> ->
<<"0x"/utf8>>;
<<"0x"/utf8>> ->
<<"0x"/utf8>>;
_ ->
normalize_hex(Data)
end.
-file("src/gleeth/crypto/transaction.gleam", 504).
?DOC(
" Create a new unsigned legacy (Type 0) transaction.\n"
"\n"
" All numeric parameters must be `0x`-prefixed hex strings. The Ethereum\n"
" JSON-RPC API returns values in this format, so results from\n"
" `methods.get_gas_price`, `methods.get_transaction_count`, etc. can be\n"
" passed directly.\n"
"\n"
" - `to` - recipient address (`\"0x...\"`, 40 hex chars after prefix)\n"
" - `value` - amount in wei as hex (e.g. `\"0xde0b6b3a7640000\"` for 1 ETH)\n"
" - `gas_limit` - gas limit as hex (e.g. `\"0x5208\"` for 21000)\n"
" - `gas_price` - gas price in wei as hex\n"
" - `nonce` - sender's transaction count as hex\n"
" - `data` - calldata as hex (`\"0x\"` for simple transfers)\n"
" - `chain_id` - network chain ID as integer (1 for mainnet, 11155111 for Sepolia)\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(tx) = transaction.create_legacy_transaction(\n"
" \"0x70997970C51812dc3A010C7d01b50e0d17dc79C8\",\n"
" \"0xde0b6b3a7640000\", // 1 ETH in wei\n"
" \"0x5208\", // 21000 gas\n"
" \"0x3b9aca00\", // 1 gwei gas price\n"
" \"0x0\", // nonce 0\n"
" \"0x\", // no calldata\n"
" 1, // mainnet\n"
" )\n"
" ```\n"
).
-spec create_legacy_transaction(
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
integer()
) -> {ok, unsigned_transaction()} | {error, transaction_error()}.
create_legacy_transaction(
To,
Value,
Gas_limit,
Gas_price,
Nonce,
Data,
Chain_id
) ->
gleam@result:'try'(
validate_address(To),
fun(_) ->
gleam@result:'try'(
validate_hex_amount(Value),
fun(_) ->
gleam@result:'try'(
validate_hex_amount(Gas_limit),
fun(_) ->
gleam@result:'try'(
validate_hex_amount(Gas_price),
fun(_) ->
gleam@result:'try'(
validate_hex_amount(Nonce),
fun(_) ->
gleam@result:'try'(
validate_hex_data(Data),
fun(_) ->
gleam@result:'try'(
validate_chain_id(
Chain_id
),
fun(_) ->
{ok,
{unsigned_transaction,
normalize_address(
To
),
normalize_hex(
Value
),
normalize_hex(
Gas_limit
),
normalize_hex(
Gas_price
),
normalize_hex(
Nonce
),
normalize_hex_data(
Data
),
Chain_id}}
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/gleeth/crypto/transaction.gleam", 387).
?DOC(
" Validate and sign a legacy builder, producing a SignedTransaction.\n"
" Returns an error if required fields (to, value, gas_limit, gas_price,\n"
" nonce, chain_id) are missing.\n"
).
-spec sign_legacy(legacy_builder(), gleeth@crypto@wallet:wallet()) -> {ok,
signed_transaction()} |
{error, transaction_error()}.
sign_legacy(Builder, W) ->
gleam@result:'try'(
require_option(erlang:element(2, Builder), <<"to"/utf8>>),
fun(To_val) ->
gleam@result:'try'(
require_option(erlang:element(3, Builder), <<"value"/utf8>>),
fun(Value_val) ->
gleam@result:'try'(
require_option(
erlang:element(4, Builder),
<<"gas_limit"/utf8>>
),
fun(Gas_limit_val) ->
gleam@result:'try'(
require_option(
erlang:element(5, Builder),
<<"gas_price"/utf8>>
),
fun(Gas_price_val) ->
gleam@result:'try'(
require_option(
erlang:element(6, Builder),
<<"nonce"/utf8>>
),
fun(Nonce_val) ->
gleam@result:'try'(
require_option_int(
erlang:element(8, Builder),
<<"chain_id"/utf8>>
),
fun(Chain_id_val) ->
Data_val = gleam@option:unwrap(
erlang:element(
7,
Builder
),
<<"0x"/utf8>>
),
gleam@result:'try'(
create_legacy_transaction(
To_val,
Value_val,
Gas_limit_val,
Gas_price_val,
Nonce_val,
Data_val,
Chain_id_val
),
fun(Tx) ->
sign_transaction(
Tx,
W
)
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/gleeth/crypto/transaction.gleam", 537).
?DOC(
" Create a simple ETH transfer transaction (no calldata).\n"
"\n"
" Convenience wrapper around `create_legacy_transaction` with `data` set to\n"
" `\"0x\"`. All numeric parameters are `0x`-prefixed hex strings.\n"
).
-spec create_eth_transfer(
binary(),
binary(),
binary(),
binary(),
binary(),
integer()
) -> {ok, unsigned_transaction()} | {error, transaction_error()}.
create_eth_transfer(To, Value_wei, Gas_limit, Gas_price, Nonce, Chain_id) ->
create_legacy_transaction(
To,
Value_wei,
Gas_limit,
Gas_price,
Nonce,
<<"0x"/utf8>>,
Chain_id
).
-file("src/gleeth/crypto/transaction.gleam", 560).
?DOC(
" Create a contract interaction transaction (zero value).\n"
"\n"
" Convenience wrapper around `create_legacy_transaction` with `value` set to\n"
" `\"0x0\"`. All numeric parameters are `0x`-prefixed hex strings.\n"
).
-spec create_contract_call(
binary(),
binary(),
binary(),
binary(),
binary(),
integer()
) -> {ok, unsigned_transaction()} | {error, transaction_error()}.
create_contract_call(
Contract_address,
Call_data,
Gas_limit,
Gas_price,
Nonce,
Chain_id
) ->
create_legacy_transaction(
Contract_address,
<<"0x0"/utf8>>,
Gas_limit,
Gas_price,
Nonce,
Call_data,
Chain_id
).
-file("src/gleeth/crypto/transaction.gleam", 595).
?DOC(
" Create and validate an EIP-1559 (Type 2) transaction.\n"
"\n"
" All numeric parameters must be `0x`-prefixed hex strings. Fee parameters\n"
" from `methods.get_gas_price` and `methods.get_max_priority_fee` can be\n"
" passed directly.\n"
"\n"
" - `to` - recipient address\n"
" - `value` - amount in wei as hex\n"
" - `gas_limit` - gas limit as hex\n"
" - `max_fee_per_gas` - maximum total fee per gas in wei as hex\n"
" - `max_priority_fee_per_gas` - tip per gas in wei as hex\n"
" - `nonce` - sender's transaction count as hex\n"
" - `data` - calldata as hex (`\"0x\"` for simple transfers)\n"
" - `chain_id` - network chain ID as integer\n"
" - `access_list` - EIP-2930 access list (pass `[]` if not needed)\n"
).
-spec create_eip1559_transaction(
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
integer(),
list(access_list_entry())
) -> {ok, eip1559_transaction()} | {error, transaction_error()}.
create_eip1559_transaction(
To,
Value,
Gas_limit,
Max_fee_per_gas,
Max_priority_fee_per_gas,
Nonce,
Data,
Chain_id,
Access_list
) ->
gleam@result:'try'(
validate_address(To),
fun(_) ->
gleam@result:'try'(
validate_hex_amount(Value),
fun(_) ->
gleam@result:'try'(
validate_hex_amount(Gas_limit),
fun(_) ->
gleam@result:'try'(
validate_hex_amount(Max_fee_per_gas),
fun(_) ->
gleam@result:'try'(
validate_hex_amount(
Max_priority_fee_per_gas
),
fun(_) ->
gleam@result:'try'(
validate_hex_amount(Nonce),
fun(_) ->
gleam@result:'try'(
validate_hex_data(Data),
fun(_) ->
gleam@result:'try'(
validate_chain_id(
Chain_id
),
fun(_) ->
{ok,
{eip1559_transaction,
normalize_address(
To
),
normalize_hex(
Value
),
normalize_hex(
Gas_limit
),
normalize_hex(
Max_fee_per_gas
),
normalize_hex(
Max_priority_fee_per_gas
),
normalize_hex(
Nonce
),
normalize_hex_data(
Data
),
Chain_id,
Access_list}}
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/gleeth/crypto/transaction.gleam", 416).
?DOC(
" Validate and sign an EIP-1559 builder, producing a SignedEip1559Transaction.\n"
" Returns an error if required fields are missing.\n"
).
-spec sign_eip1559(eip1559_builder(), gleeth@crypto@wallet:wallet()) -> {ok,
signed_eip1559_transaction()} |
{error, transaction_error()}.
sign_eip1559(Builder, W) ->
gleam@result:'try'(
require_option(erlang:element(2, Builder), <<"to"/utf8>>),
fun(To_val) ->
gleam@result:'try'(
require_option(erlang:element(3, Builder), <<"value"/utf8>>),
fun(Value_val) ->
gleam@result:'try'(
require_option(
erlang:element(4, Builder),
<<"gas_limit"/utf8>>
),
fun(Gas_limit_val) ->
gleam@result:'try'(
require_option(
erlang:element(5, Builder),
<<"max_fee_per_gas"/utf8>>
),
fun(Max_fee_val) ->
gleam@result:'try'(
require_option(
erlang:element(6, Builder),
<<"max_priority_fee_per_gas"/utf8>>
),
fun(Max_priority_fee_val) ->
gleam@result:'try'(
require_option(
erlang:element(7, Builder),
<<"nonce"/utf8>>
),
fun(Nonce_val) ->
gleam@result:'try'(
require_option_int(
erlang:element(
9,
Builder
),
<<"chain_id"/utf8>>
),
fun(Chain_id_val) ->
Data_val = gleam@option:unwrap(
erlang:element(
8,
Builder
),
<<"0x"/utf8>>
),
gleam@result:'try'(
create_eip1559_transaction(
To_val,
Value_val,
Gas_limit_val,
Max_fee_val,
Max_priority_fee_val,
Nonce_val,
Data_val,
Chain_id_val,
erlang:element(
10,
Builder
)
),
fun(Tx) ->
sign_eip1559_transaction(
Tx,
W
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/gleeth/crypto/transaction.gleam", 950).
?DOC(
" Recover the sender address from a signed legacy transaction.\n"
" Reconstructs the EIP-155 signing hash and uses ECDSA recovery.\n"
).
-spec recover_sender(signed_transaction()) -> {ok, binary()} |
{error, transaction_error()}.
recover_sender(Signed) ->
Unsigned = {unsigned_transaction,
erlang:element(2, Signed),
erlang:element(3, Signed),
erlang:element(4, Signed),
erlang:element(5, Signed),
erlang:element(6, Signed),
erlang:element(7, Signed),
erlang:element(8, Signed)},
Signing_hash = create_signing_hash(Unsigned),
gleam@result:'try'(
begin
_pipe = gleeth@utils@hex:to_int(erlang:element(9, Signed)),
gleam@result:map_error(
_pipe,
fun(_) ->
{signing_failed,
<<"Failed to parse v value: "/utf8,
(erlang:element(9, Signed))/binary>>}
end
)
end,
fun(V_int) ->
Recovery_id = (V_int - (2 * erlang:element(8, Signed))) - 35,
gleam@result:'try'(
begin
_pipe@1 = gleeth@utils@hex:decode(
erlang:element(10, Signed)
),
gleam@result:map_error(
_pipe@1,
fun(_) ->
{signing_failed,
<<"Failed to decode r value: "/utf8,
(erlang:element(10, Signed))/binary>>}
end
)
end,
fun(R_bytes) ->
gleam@result:'try'(
begin
_pipe@2 = gleeth@utils@hex:decode(
erlang:element(11, Signed)
),
gleam@result:map_error(
_pipe@2,
fun(_) ->
{signing_failed,
<<"Failed to decode s value: "/utf8,
(erlang:element(11, Signed))/binary>>}
end
)
end,
fun(S_bytes) ->
Signature = {signature,
R_bytes,
S_bytes,
Recovery_id},
gleam@result:'try'(
begin
_pipe@3 = gleeth@crypto@secp256k1:recover_address(
Signing_hash,
Signature
),
gleam@result:map_error(
_pipe@3,
fun(Msg) ->
{signing_failed,
<<"Recovery failed: "/utf8,
Msg/binary>>}
end
)
end,
fun(Address) ->
{ok,
gleeth@crypto@secp256k1:address_to_string(
Address
)}
end
)
end
)
end
)
end
).
-file("src/gleeth/crypto/transaction.gleam", 1000).
?DOC(
" Recover the sender address from a signed EIP-1559 transaction.\n"
" For EIP-1559, v is directly the recovery_id (0 or 1).\n"
).
-spec recover_sender_eip1559(signed_eip1559_transaction()) -> {ok, binary()} |
{error, transaction_error()}.
recover_sender_eip1559(Signed) ->
Unsigned = {eip1559_transaction,
erlang:element(2, Signed),
erlang:element(3, Signed),
erlang:element(4, Signed),
erlang:element(5, Signed),
erlang:element(6, Signed),
erlang:element(7, Signed),
erlang:element(8, Signed),
erlang:element(9, Signed),
erlang:element(10, Signed)},
Signing_hash = create_eip1559_signing_hash(Unsigned),
gleam@result:'try'(
begin
_pipe = gleeth@utils@hex:to_int(erlang:element(11, Signed)),
gleam@result:map_error(
_pipe,
fun(_) ->
{signing_failed,
<<"Failed to parse v value: "/utf8,
(erlang:element(11, Signed))/binary>>}
end
)
end,
fun(V_int) ->
gleam@result:'try'(
begin
_pipe@1 = gleeth@utils@hex:decode(
erlang:element(12, Signed)
),
gleam@result:map_error(
_pipe@1,
fun(_) ->
{signing_failed,
<<"Failed to decode r value: "/utf8,
(erlang:element(12, Signed))/binary>>}
end
)
end,
fun(R_bytes) ->
gleam@result:'try'(
begin
_pipe@2 = gleeth@utils@hex:decode(
erlang:element(13, Signed)
),
gleam@result:map_error(
_pipe@2,
fun(_) ->
{signing_failed,
<<"Failed to decode s value: "/utf8,
(erlang:element(13, Signed))/binary>>}
end
)
end,
fun(S_bytes) ->
Signature = {signature, R_bytes, S_bytes, V_int},
gleam@result:'try'(
begin
_pipe@3 = gleeth@crypto@secp256k1:recover_address(
Signing_hash,
Signature
),
gleam@result:map_error(
_pipe@3,
fun(Msg) ->
{signing_failed,
<<"Recovery failed: "/utf8,
Msg/binary>>}
end
)
end,
fun(Address) ->
{ok,
gleeth@crypto@secp256k1:address_to_string(
Address
)}
end
)
end
)
end
)
end
).
-file("src/gleeth/crypto/transaction.gleam", 1054).
?DOC(" Convert signed transaction to string for display\n").
-spec signed_transaction_to_string(signed_transaction()) -> binary().
signed_transaction_to_string(Tx) ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"SignedTransaction {\n"/utf8,
" to: "/utf8>>/binary,
(erlang:element(
2,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" value: "/utf8>>/binary,
(erlang:element(
3,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" gas_limit: "/utf8>>/binary,
(erlang:element(
4,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" gas_price: "/utf8>>/binary,
(erlang:element(
5,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" nonce: "/utf8>>/binary,
(erlang:element(
6,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" data: "/utf8>>/binary,
(erlang:element(
7,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" chain_id: "/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(
8,
Tx
)
))/binary>>/binary,
"\n"/utf8>>/binary,
" v: "/utf8>>/binary,
(erlang:element(9, Tx))/binary>>/binary,
"\n"/utf8>>/binary,
" r: "/utf8>>/binary,
(erlang:element(10, Tx))/binary>>/binary,
"\n"/utf8>>/binary,
" s: "/utf8>>/binary,
(erlang:element(11, Tx))/binary>>/binary,
"\n"/utf8>>/binary,
" raw: "/utf8>>/binary,
(erlang:element(12, Tx))/binary>>/binary,
"\n"/utf8>>/binary,
"}"/utf8>>.
-file("src/gleeth/crypto/transaction.gleam", 1103).
?DOC(" Hash a raw transaction hex string to get its transaction hash\n").
-spec hash_raw_transaction(binary()) -> binary().
hash_raw_transaction(Raw_transaction) ->
case gleeth@utils@hex:decode(Raw_transaction) of
{ok, Raw_bytes} ->
gleeth@utils@hex:encode(
gleeth@crypto@keccak:keccak256_binary(Raw_bytes)
);
{error, _} ->
<<""/utf8>>
end.
-file("src/gleeth/crypto/transaction.gleam", 1093).
?DOC(" Get transaction hash from a signed legacy transaction\n").
-spec get_transaction_hash(signed_transaction()) -> binary().
get_transaction_hash(Tx) ->
hash_raw_transaction(erlang:element(12, Tx)).
-file("src/gleeth/crypto/transaction.gleam", 1098).
?DOC(" Get transaction hash from a signed EIP-1559 transaction\n").
-spec get_eip1559_transaction_hash(signed_eip1559_transaction()) -> binary().
get_eip1559_transaction_hash(Tx) ->
hash_raw_transaction(erlang:element(14, Tx)).
-file("src/gleeth/crypto/transaction.gleam", 1111).
?DOC(" Convert signed EIP-1559 transaction to string for display\n").
-spec signed_eip1559_transaction_to_string(signed_eip1559_transaction()) -> binary().
signed_eip1559_transaction_to_string(Tx) ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"SignedEip1559Transaction {\n"/utf8,
" to: "/utf8>>/binary,
(erlang:element(
2,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" value: "/utf8>>/binary,
(erlang:element(
3,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" gas_limit: "/utf8>>/binary,
(erlang:element(
4,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" max_fee_per_gas: "/utf8>>/binary,
(erlang:element(
5,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" max_priority_fee_per_gas: "/utf8>>/binary,
(erlang:element(
6,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" nonce: "/utf8>>/binary,
(erlang:element(
7,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" data: "/utf8>>/binary,
(erlang:element(
8,
Tx
))/binary>>/binary,
"\n"/utf8>>/binary,
" chain_id: "/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(
9,
Tx
)
))/binary>>/binary,
"\n"/utf8>>/binary,
" v: "/utf8>>/binary,
(erlang:element(11, Tx))/binary>>/binary,
"\n"/utf8>>/binary,
" r: "/utf8>>/binary,
(erlang:element(12, Tx))/binary>>/binary,
"\n"/utf8>>/binary,
" s: "/utf8>>/binary,
(erlang:element(13, Tx))/binary>>/binary,
"\n"/utf8>>/binary,
" raw: "/utf8>>/binary,
(erlang:element(14, Tx))/binary>>/binary,
"\n"/utf8>>/binary,
"}"/utf8>>.
-file("src/gleeth/crypto/transaction.gleam", 1155).
?DOC(" Convert TransactionError to string\n").
-spec error_to_string(transaction_error()) -> binary().
error_to_string(Error) ->
case Error of
{invalid_address, Msg} ->
<<"Invalid address: "/utf8, Msg/binary>>;
{invalid_amount, Msg@1} ->
<<"Invalid amount: "/utf8, Msg@1/binary>>;
{invalid_gas, Msg@2} ->
<<"Invalid gas: "/utf8, Msg@2/binary>>;
{invalid_nonce, Msg@3} ->
<<"Invalid nonce: "/utf8, Msg@3/binary>>;
{invalid_chain_id, Msg@4} ->
<<"Invalid chain ID: "/utf8, Msg@4/binary>>;
{invalid_data, Msg@5} ->
<<"Invalid data: "/utf8, Msg@5/binary>>;
{signing_failed, Msg@6} ->
<<"Signing failed: "/utf8, Msg@6/binary>>;
{encoding_failed, Msg@7} ->
<<"Encoding failed: "/utf8, Msg@7/binary>>
end.
-file("src/gleeth/crypto/transaction.gleam", 1418).
?DOC(" Convert RLP bytes to an address string (0x-prefixed, full 40 chars)\n").
-spec rlp_to_address(gleeth@encoding@rlp:rlp_item()) -> binary().
rlp_to_address(Item) ->
case Item of
{rlp_bytes, <<>>} ->
<<""/utf8>>;
{rlp_bytes, Bytes} ->
<<"0x"/utf8,
(string:lowercase(gleam_stdlib:base16_encode(Bytes)))/binary>>;
_ ->
<<""/utf8>>
end.
-file("src/gleeth/crypto/transaction.gleam", 1380).
-spec decode_access_list_entry(gleeth@encoding@rlp:rlp_item()) -> {ok,
access_list_entry()} |
{error, nil}.
decode_access_list_entry(Item) ->
case Item of
{rlp_list, [Addr_rlp, Keys_rlp]} ->
Address = rlp_to_address(Addr_rlp),
Storage_keys = case Keys_rlp of
{rlp_list, Keys} ->
gleam@list:map(Keys, fun(K) -> case K of
{rlp_bytes, Bytes} ->
<<"0x"/utf8,
(string:lowercase(
gleam_stdlib:base16_encode(Bytes)
))/binary>>;
_ ->
<<"0x"/utf8>>
end end);
_ ->
[]
end,
{ok, {access_list_entry, Address, Storage_keys}};
_ ->
{error, nil}
end.
-file("src/gleeth/crypto/transaction.gleam", 1373).
-spec decode_access_list(gleeth@encoding@rlp:rlp_item()) -> list(access_list_entry()).
decode_access_list(Item) ->
case Item of
{rlp_list, Entries} ->
gleam@list:filter_map(Entries, fun decode_access_list_entry/1);
_ ->
[]
end.
-file("src/gleeth/crypto/transaction.gleam", 1428).
?DOC(" Convert RLP bytes to hex data string (0x-prefixed, preserves all bytes)\n").
-spec rlp_to_hex_data(gleeth@encoding@rlp:rlp_item()) -> binary().
rlp_to_hex_data(Item) ->
case Item of
{rlp_bytes, <<>>} ->
<<"0x"/utf8>>;
{rlp_bytes, Bytes} ->
<<"0x"/utf8,
(string:lowercase(gleam_stdlib:base16_encode(Bytes)))/binary>>;
_ ->
<<"0x"/utf8>>
end.
-file("src/gleeth/crypto/transaction.gleam", 1438).
?DOC(" Convert RLP bytes to hex string preserving all bytes (for r, s values)\n").
-spec rlp_to_hex_bytes(gleeth@encoding@rlp:rlp_item()) -> binary().
rlp_to_hex_bytes(Item) ->
case Item of
{rlp_bytes, Bytes} ->
<<"0x"/utf8,
(string:lowercase(gleam_stdlib:base16_encode(Bytes)))/binary>>;
_ ->
<<"0x"/utf8>>
end.
-file("src/gleeth/crypto/transaction.gleam", 1460).
-spec do_bytes_to_int(bitstring(), integer()) -> integer().
do_bytes_to_int(Data, Acc) ->
case Data of
<<Byte:8, Rest/bitstring>> ->
do_bytes_to_int(Rest, (Acc * 256) + Byte);
_ ->
Acc
end.
-file("src/gleeth/crypto/transaction.gleam", 1456).
?DOC(" Convert big-endian bytes to an integer\n").
-spec bytes_to_int(bitstring()) -> integer().
bytes_to_int(Data) ->
do_bytes_to_int(Data, 0).
-file("src/gleeth/crypto/transaction.gleam", 1447).
?DOC(" Convert RLP bytes to an integer\n").
-spec rlp_to_int(gleeth@encoding@rlp:rlp_item()) -> integer().
rlp_to_int(Item) ->
case Item of
{rlp_bytes, <<>>} ->
0;
{rlp_bytes, Bytes} ->
bytes_to_int(Bytes);
_ ->
0
end.
-file("src/gleeth/crypto/transaction.gleam", 1468).
?DOC(" Drop leading zeros from a hex string (but keep at least one digit)\n").
-spec drop_leading_hex_zeros(binary()) -> binary().
drop_leading_hex_zeros(Hex_str) ->
case Hex_str of
<<"0"/utf8, Rest/binary>> ->
case Rest of
<<""/utf8>> ->
<<"0"/utf8>>;
_ ->
drop_leading_hex_zeros(Rest)
end;
_ ->
Hex_str
end.
-file("src/gleeth/crypto/transaction.gleam", 1406).
?DOC(" Convert RLP bytes to a hex amount string (0x-prefixed, minimal encoding)\n").
-spec rlp_to_hex_amount(gleeth@encoding@rlp:rlp_item()) -> binary().
rlp_to_hex_amount(Item) ->
case Item of
{rlp_bytes, <<>>} ->
<<"0x0"/utf8>>;
{rlp_bytes, Bytes} ->
Hex_str = string:lowercase(gleam_stdlib:base16_encode(Bytes)),
<<"0x"/utf8, (drop_leading_hex_zeros(Hex_str))/binary>>;
_ ->
<<"0x0"/utf8>>
end.
-file("src/gleeth/crypto/transaction.gleam", 1239).
-spec decode_legacy_items(list(gleeth@encoding@rlp:rlp_item()), binary()) -> {ok,
signed_transaction()} |
{error, transaction_error()}.
decode_legacy_items(Items, Raw_hex) ->
case Items of
[Nonce_rlp,
Gas_price_rlp,
Gas_limit_rlp,
To_rlp,
Value_rlp,
Data_rlp,
V_rlp,
R_rlp,
S_rlp] ->
Nonce = rlp_to_hex_amount(Nonce_rlp),
Gas_price = rlp_to_hex_amount(Gas_price_rlp),
Gas_limit = rlp_to_hex_amount(Gas_limit_rlp),
To = rlp_to_address(To_rlp),
Value = rlp_to_hex_amount(Value_rlp),
Data = rlp_to_hex_data(Data_rlp),
V_int = rlp_to_int(V_rlp),
R = rlp_to_hex_bytes(R_rlp),
S = rlp_to_hex_bytes(S_rlp),
Chain_id = (V_int - 35) div 2,
V_hex = <<"0x"/utf8,
(string:lowercase(gleam@int:to_base16(V_int)))/binary>>,
{ok,
{signed_transaction,
To,
Value,
Gas_limit,
Gas_price,
Nonce,
Data,
Chain_id,
V_hex,
R,
S,
Raw_hex}};
_ ->
{error,
{encoding_failed,
<<"Legacy transaction must have 9 RLP items, got "/utf8,
(erlang:integer_to_binary(erlang:length(Items)))/binary>>}}
end.
-file("src/gleeth/crypto/transaction.gleam", 1222).
?DOC(
" Decode a raw signed legacy transaction from its RLP-encoded hex string.\n"
" Recovers chain_id from the EIP-155 v value: chain_id = (v - 35) / 2.\n"
).
-spec decode_legacy(binary()) -> {ok, signed_transaction()} |
{error, transaction_error()}.
decode_legacy(Raw_hex) ->
gleam@result:'try'(
begin
_pipe = gleeth@utils@hex:decode(Raw_hex),
gleam@result:map_error(
_pipe,
fun(_) -> {encoding_failed, <<"Invalid hex string"/utf8>>} end
)
end,
fun(Raw_bytes) ->
gleam@result:'try'(
begin
_pipe@1 = gleeth@encoding@rlp:decode(Raw_bytes),
gleam@result:map_error(
_pipe@1,
fun(_) ->
{encoding_failed, <<"Invalid RLP encoding"/utf8>>}
end
)
end,
fun(Rlp_item) -> case Rlp_item of
{rlp_list, Items} ->
decode_legacy_items(Items, Raw_hex);
_ ->
{error,
{encoding_failed,
<<"Expected RLP list for legacy transaction"/utf8>>}}
end end
)
end
).
-file("src/gleeth/crypto/transaction.gleam", 1316).
-spec decode_eip1559_items(list(gleeth@encoding@rlp:rlp_item()), binary()) -> {ok,
signed_eip1559_transaction()} |
{error, transaction_error()}.
decode_eip1559_items(Items, Raw_hex) ->
case Items of
[Chain_id_rlp,
Nonce_rlp,
Max_priority_fee_rlp,
Max_fee_rlp,
Gas_limit_rlp,
To_rlp,
Value_rlp,
Data_rlp,
Access_list_rlp,
V_rlp,
R_rlp,
S_rlp] ->
Chain_id = rlp_to_int(Chain_id_rlp),
Nonce = rlp_to_hex_amount(Nonce_rlp),
Max_priority_fee = rlp_to_hex_amount(Max_priority_fee_rlp),
Max_fee = rlp_to_hex_amount(Max_fee_rlp),
Gas_limit = rlp_to_hex_amount(Gas_limit_rlp),
To = rlp_to_address(To_rlp),
Value = rlp_to_hex_amount(Value_rlp),
Data = rlp_to_hex_data(Data_rlp),
Access_list = decode_access_list(Access_list_rlp),
V_int = rlp_to_int(V_rlp),
V_hex = <<"0x"/utf8,
(string:lowercase(gleam@int:to_base16(V_int)))/binary>>,
R = rlp_to_hex_bytes(R_rlp),
S = rlp_to_hex_bytes(S_rlp),
{ok,
{signed_eip1559_transaction,
To,
Value,
Gas_limit,
Max_fee,
Max_priority_fee,
Nonce,
Data,
Chain_id,
Access_list,
V_hex,
R,
S,
Raw_hex}};
_ ->
{error,
{encoding_failed,
<<"EIP-1559 transaction must have 12 RLP items, got "/utf8,
(erlang:integer_to_binary(erlang:length(Items)))/binary>>}}
end.
-file("src/gleeth/crypto/transaction.gleam", 1294).
?DOC(
" Decode a raw signed EIP-1559 (Type 2) transaction from its hex string.\n"
" The input must start with 0x02 (the type prefix).\n"
).
-spec decode_eip1559(binary()) -> {ok, signed_eip1559_transaction()} |
{error, transaction_error()}.
decode_eip1559(Raw_hex) ->
gleam@result:'try'(
begin
_pipe = gleeth@utils@hex:decode(Raw_hex),
gleam@result:map_error(
_pipe,
fun(_) -> {encoding_failed, <<"Invalid hex string"/utf8>>} end
)
end,
fun(Raw_bytes) -> case Raw_bytes of
<<16#02, Payload/bitstring>> ->
gleam@result:'try'(
begin
_pipe@1 = gleeth@encoding@rlp:decode(Payload),
gleam@result:map_error(
_pipe@1,
fun(_) ->
{encoding_failed,
<<"Invalid RLP encoding"/utf8>>}
end
)
end,
fun(Rlp_item) -> case Rlp_item of
{rlp_list, Items} ->
decode_eip1559_items(Items, Raw_hex);
_ ->
{error,
{encoding_failed,
<<"Expected RLP list for EIP-1559 transaction"/utf8>>}}
end end
);
_ ->
{error,
{encoding_failed,
<<"EIP-1559 transaction must start with 0x02"/utf8>>}}
end end
).
-file("src/gleeth/crypto/transaction.gleam", 1203).
?DOC(
" Decode a raw transaction hex string, auto-detecting the type.\n"
" Legacy transactions start with an RLP list prefix (0xc0-0xff).\n"
" EIP-1559 transactions start with 0x02.\n"
).
-spec decode(binary()) -> {ok, decoded_transaction()} |
{error, transaction_error()}.
decode(Raw_hex) ->
gleam@result:'try'(
begin
_pipe = gleeth@utils@hex:decode(Raw_hex),
gleam@result:map_error(
_pipe,
fun(_) -> {encoding_failed, <<"Invalid hex string"/utf8>>} end
)
end,
fun(Raw_bytes) -> case Raw_bytes of
<<16#02, _/bitstring>> ->
gleam@result:map(
decode_eip1559(Raw_hex),
fun(Tx) -> {decoded_eip1559, Tx} end
);
_ ->
gleam@result:map(
decode_legacy(Raw_hex),
fun(Tx@1) -> {decoded_legacy, Tx@1} end
)
end end
).