-module(gleeth@ethereum@abi@encode). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/gleeth/ethereum/abi/encode.gleam"). -export([function_selector/2, encode/1, encode_single/2, encode_call/2]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -file("src/gleeth/ethereum/abi/encode.gleam", 28). ?DOC(" Compute the 4-byte function selector: keccak256(name(type1,type2,...))[:4]\n"). -spec function_selector(binary(), list(gleeth@ethereum@abi@types:abi_type())) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. function_selector(Name, Param_types) -> Sig = <<<<<>/binary, (gleam@string:join( gleam@list:map( Param_types, fun gleeth@ethereum@abi@types:to_string/1 ), <<","/utf8>> ))/binary>>/binary, ")"/utf8>>, Hash = gleeth@crypto@keccak:keccak256_binary(gleam_stdlib:identity(Sig)), case gleam_stdlib:bit_array_slice(Hash, 0, 4) of {ok, Selector} -> {ok, Selector}; {error, _} -> {error, {encode_error, <<"Failed to compute function selector"/utf8>>}} end. -file("src/gleeth/ethereum/abi/encode.gleam", 233). -spec build_bytes(integer(), integer(), bitstring()) -> bitstring(). build_bytes(Value, Remaining, Acc) -> case Remaining of 0 -> Acc; _ -> Shift = (Remaining - 1) * 8, Byte = erlang:'band'(erlang:'bsr'(Value, Shift), 16#ff), build_bytes(Value, Remaining - 1, <>) end. -file("src/gleeth/ethereum/abi/encode.gleam", 228). ?DOC( " Convert an integer to a 32-byte big-endian representation.\n" " For values that fit, we use direct byte construction.\n" ). -spec int_to_bytes32(integer()) -> bitstring(). int_to_bytes32(Value) -> build_bytes(Value, 32, <<>>). -file("src/gleeth/ethereum/abi/encode.gleam", 221). -spec encode_uint256(integer()) -> bitstring(). encode_uint256(Value) -> int_to_bytes32(Value). -file("src/gleeth/ethereum/abi/encode.gleam", 117). -spec encode_uint(integer(), integer()) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. encode_uint(Value, Bit_size) -> Max = erlang:'bsl'(1, Bit_size), case (Value >= 0) andalso (Value < Max) of true -> {ok, encode_uint256(Value)}; false -> {error, {encode_error, <<<<<<"Value out of range for uint"/utf8, (erlang:integer_to_binary(Bit_size))/binary>>/binary, ": "/utf8>>/binary, (erlang:integer_to_binary(Value))/binary>>}} end. -file("src/gleeth/ethereum/abi/encode.gleam", 131). -spec encode_int(integer(), integer()) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. encode_int(Value, Bit_size) -> Half = erlang:'bsl'(1, Bit_size - 1), case (Value >= - Half) andalso (Value < Half) of true -> Unsigned = case Value >= 0 of true -> Value; false -> erlang:'bsl'(1, 256) + Value end, {ok, encode_uint256(Unsigned)}; false -> {error, {encode_error, <<<<<<"Value out of range for int"/utf8, (erlang:integer_to_binary(Bit_size))/binary>>/binary, ": "/utf8>>/binary, (erlang:integer_to_binary(Value))/binary>>}} end. -file("src/gleeth/ethereum/abi/encode.gleam", 245). -spec bool_to_int(boolean()) -> integer(). bool_to_int(B) -> case B of true -> 1; false -> 0 end. -file("src/gleeth/ethereum/abi/encode.gleam", 298). -spec make_zero_bytes_acc(integer(), bitstring()) -> bitstring(). make_zero_bytes_acc(N, Acc) -> case N =< 0 of true -> Acc; false -> make_zero_bytes_acc(N - 1, <>) end. -file("src/gleeth/ethereum/abi/encode.gleam", 294). -spec make_zero_bytes(integer()) -> bitstring(). make_zero_bytes(N) -> make_zero_bytes_acc(N, <<>>). -file("src/gleeth/ethereum/abi/encode.gleam", 253). ?DOC(" Left-pad data with zeros to 32 bytes.\n"). -spec left_pad32(bitstring()) -> bitstring(). left_pad32(Data) -> Size = erlang:byte_size(Data), case Size >= 32 of true -> Data; false -> Padding = make_zero_bytes(32 - Size), gleam_stdlib:bit_array_concat([Padding, Data]) end. -file("src/gleeth/ethereum/abi/encode.gleam", 152). -spec encode_address(binary()) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. encode_address(Addr) -> Clean = gleeth@utils@hex:strip_prefix(Addr), case string:length(Clean) of 40 -> case gleeth@utils@hex:decode(<<"0x"/utf8, Clean/binary>>) of {ok, Bytes} -> {ok, left_pad32(Bytes)}; {error, _} -> {error, {encode_error, <<"Invalid hex in address: "/utf8, Addr/binary>>}} end; _ -> {error, {encode_error, <<"Address must be 20 bytes (40 hex chars)"/utf8>>}} end. -file("src/gleeth/ethereum/abi/encode.gleam", 265). ?DOC(" Right-pad data with zeros to 32 bytes.\n"). -spec right_pad32(bitstring()) -> bitstring(). right_pad32(Data) -> Size = erlang:byte_size(Data), case Size >= 32 of true -> Data; false -> Padding = make_zero_bytes(32 - Size), gleam_stdlib:bit_array_concat([Data, Padding]) end. -file("src/gleeth/ethereum/abi/encode.gleam", 165). -spec encode_fixed_bytes(bitstring(), integer()) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. encode_fixed_bytes(Data, Size) -> case erlang:byte_size(Data) =:= Size of true -> {ok, right_pad32(Data)}; false -> {error, {encode_error, <<<<<<"Expected "/utf8, (erlang:integer_to_binary(Size))/binary>>/binary, " bytes, got "/utf8>>/binary, (erlang:integer_to_binary(erlang:byte_size(Data)))/binary>>}} end. -file("src/gleeth/ethereum/abi/encode.gleam", 277). ?DOC(" Right-pad data to next 32-byte boundary.\n"). -spec right_pad_to_32(bitstring()) -> bitstring(). right_pad_to_32(Data) -> Size = erlang:byte_size(Data), case Size of 0 -> <<>>; _ -> Remainder = Size rem 32, case Remainder of 0 -> Data; _ -> Padding = make_zero_bytes(32 - Remainder), gleam_stdlib:bit_array_concat([Data, Padding]) end end. -file("src/gleeth/ethereum/abi/encode.gleam", 178). -spec encode_dynamic_bytes(bitstring()) -> bitstring(). encode_dynamic_bytes(Data) -> Length = erlang:byte_size(Data), Length_slot = encode_uint256(Length), Padded_data = right_pad_to_32(Data), gleam_stdlib:bit_array_concat([Length_slot, Padded_data]). -file("src/gleeth/ethereum/abi/encode.gleam", 185). -spec encode_dynamic_array( gleeth@ethereum@abi@types:abi_type(), list(gleeth@ethereum@abi@types:abi_value()) ) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. encode_dynamic_array(Element_type, Elements) -> Count = erlang:length(Elements), Count_slot = encode_uint256(Count), Pairs = gleam@list:map(Elements, fun(V) -> {Element_type, V} end), gleam@result:'try'( encode_tuple_values(Pairs), fun(Encoded) -> {ok, gleam_stdlib:bit_array_concat([Count_slot, Encoded])} end ). -file("src/gleeth/ethereum/abi/encode.gleam", 60). ?DOC(" Encode values using ABI tuple encoding (head/tail layout).\n"). -spec encode_tuple_values( list({gleeth@ethereum@abi@types:abi_type(), gleeth@ethereum@abi@types:abi_value()}) ) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. encode_tuple_values(Pairs) -> Total_head_size = gleam@list:fold( Pairs, 0, fun(Acc, Pair) -> Acc + gleeth@ethereum@abi@types:head_size(erlang:element(1, Pair)) end ), gleam@result:'try'( gleam@list:try_fold( Pairs, {<<>>, <<>>}, fun(Acc@1, Pair@1) -> {Head_acc, Tail_acc} = Acc@1, {T, V} = Pair@1, case gleeth@ethereum@abi@types:is_dynamic(T) of false -> gleam@result:'try'( encode_value(T, V), fun(Encoded) -> {ok, {gleam_stdlib:bit_array_concat( [Head_acc, Encoded] ), Tail_acc}} end ); true -> gleam@result:'try'( encode_value(T, V), fun(Encoded@1) -> Offset = Total_head_size + erlang:byte_size( Tail_acc ), Offset_bytes = encode_uint256(Offset), {ok, {gleam_stdlib:bit_array_concat( [Head_acc, Offset_bytes] ), gleam_stdlib:bit_array_concat( [Tail_acc, Encoded@1] )}} end ) end end ), fun(_use0) -> {Head, Tail} = _use0, {ok, gleam_stdlib:bit_array_concat([Head, Tail])} end ). -file("src/gleeth/ethereum/abi/encode.gleam", 95). -spec encode_value( gleeth@ethereum@abi@types:abi_type(), gleeth@ethereum@abi@types:abi_value() ) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. encode_value(T, V) -> case {T, V} of {{uint, Size}, {uint_value, N}} -> encode_uint(N, Size); {{int, Size@1}, {int_value, N@1}} -> encode_int(N@1, Size@1); {address, {address_value, Addr}} -> encode_address(Addr); {bool, {bool_value, B}} -> {ok, encode_uint256(bool_to_int(B))}; {{fixed_bytes, Size@2}, {fixed_bytes_value, Data}} -> encode_fixed_bytes(Data, Size@2); {bytes, {bytes_value, Data@1}} -> {ok, encode_dynamic_bytes(Data@1)}; {string, {string_value, S}} -> {ok, encode_dynamic_bytes(gleam_stdlib:identity(S))}; {{array, Element_type}, {array_value, Elements}} -> encode_dynamic_array(Element_type, Elements); {{fixed_array, Element_type@1, _}, {array_value, Elements@1}} -> encode_fixed_array(Element_type@1, Elements@1); {{tuple, Element_types}, {tuple_value, Values}} -> encode_tuple_type(Element_types, Values); {_, _} -> {error, {encode_error, <<"Type/value mismatch: "/utf8, (gleeth@ethereum@abi@types:to_string(T))/binary>>}} end. -file("src/gleeth/ethereum/abi/encode.gleam", 15). ?DOC( " Encode a list of typed values according to the Solidity ABI spec.\n" " This is the top-level encoding for function parameters.\n" ). -spec encode( list({gleeth@ethereum@abi@types:abi_type(), gleeth@ethereum@abi@types:abi_value()}) ) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. encode(Values) -> encode_tuple_values(Values). -file("src/gleeth/ethereum/abi/encode.gleam", 20). ?DOC(" Encode a single typed value.\n"). -spec encode_single( gleeth@ethereum@abi@types:abi_type(), gleeth@ethereum@abi@types:abi_value() ) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. encode_single(Type_, Value) -> encode_value(Type_, Value). -file("src/gleeth/ethereum/abi/encode.gleam", 45). ?DOC(" Build complete call data: 4-byte selector + ABI-encoded parameters.\n"). -spec encode_call( binary(), list({gleeth@ethereum@abi@types:abi_type(), gleeth@ethereum@abi@types:abi_value()}) ) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. encode_call(Name, Params) -> Param_types = gleam@list:map(Params, fun(P) -> erlang:element(1, P) end), gleam@result:'try'( function_selector(Name, Param_types), fun(Selector) -> gleam@result:'try'( encode(Params), fun(Encoded) -> {ok, gleam_stdlib:bit_array_concat([Selector, Encoded])} end ) end ). -file("src/gleeth/ethereum/abi/encode.gleam", 196). -spec encode_fixed_array( gleeth@ethereum@abi@types:abi_type(), list(gleeth@ethereum@abi@types:abi_value()) ) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. encode_fixed_array(Element_type, Elements) -> Pairs = gleam@list:map(Elements, fun(V) -> {Element_type, V} end), encode_tuple_values(Pairs). -file("src/gleeth/ethereum/abi/encode.gleam", 204). -spec encode_tuple_type( list(gleeth@ethereum@abi@types:abi_type()), list(gleeth@ethereum@abi@types:abi_value()) ) -> {ok, bitstring()} | {error, gleeth@ethereum@abi@types:abi_error()}. encode_tuple_type(Element_types, Values) -> case erlang:length(Element_types) =:= erlang:length(Values) of false -> {error, {encode_error, <<"Tuple element count mismatch"/utf8>>}}; true -> Pairs = gleam@list:zip(Element_types, Values), encode_tuple_values(Pairs) end.