Current section
Files
Jump to
Current section
Files
src/gleeth@ethereum@abi@events.erl
-module(gleeth@ethereum@abi@events).-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).-define(FILEPATH, "src/gleeth/ethereum/abi/events.gleam").-export([event_topic/2, event_topic_hex/2, decode_log/2]).-export_type([decoded_log/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 decoded_log() :: {decoded_log, binary(), list({binary(), gleeth@ethereum@abi@types:abi_value()})}.-file("src/gleeth/ethereum/abi/events.gleam", 17).?DOC(" Compute the event topic hash: keccak256(\"EventName(type1,type2,...)\")\n").-spec event_topic(binary(), list(gleeth@ethereum@abi@types:abi_type())) -> bitstring().event_topic(Name, Param_types) -> Sig = <<<<<<Name/binary, "("/utf8>>/binary, (gleam@string:join( gleam@list:map( Param_types, fun gleeth@ethereum@abi@types:to_string/1 ), <<","/utf8>> ))/binary>>/binary, ")"/utf8>>, gleeth@crypto@keccak:keccak256_binary(gleam_stdlib:identity(Sig)).-file("src/gleeth/ethereum/abi/events.gleam", 27).?DOC(" Compute the event topic hash as a hex string with 0x prefix.\n").-spec event_topic_hex(binary(), list(gleeth@ethereum@abi@types:abi_type())) -> binary().event_topic_hex(Name, Param_types) -> gleeth@utils@hex:encode(event_topic(Name, Param_types)).-file("src/gleeth/ethereum/abi/events.gleam", 57).-spec find_event_by_topic(binary(), list(gleeth@ethereum@abi@json:abi_entry())) -> {ok, gleeth@ethereum@abi@json:abi_entry()} | {error, gleeth@ethereum@abi@types:abi_error()}.find_event_by_topic(Topic0, Events) -> Topic0_clean = string:lowercase(gleeth@utils@hex:strip_prefix(Topic0)), Matching = gleam@list:find(Events, fun(Entry) -> case Entry of {event_entry, Name, Inputs} -> Param_types = gleam@list:map( Inputs, fun(P) -> erlang:element(3, P) end ), Computed = event_topic(Name, Param_types), Computed_hex = string:lowercase( gleam_stdlib:base16_encode(Computed) ), Computed_hex =:= Topic0_clean; _ -> false end end), case Matching of {ok, Entry@1} -> {ok, Entry@1}; {error, _} -> {error, {decode_error, <<"No matching event for topic: 0x"/utf8, Topic0_clean/binary>>}} end.-file("src/gleeth/ethereum/abi/events.gleam", 112).-spec decode_indexed_params( list(gleeth@ethereum@abi@json:event_param()), list(binary())) -> {ok, list({binary(), gleeth@ethereum@abi@types:abi_value()})} | {error, gleeth@ethereum@abi@types:abi_error()}.decode_indexed_params(Indexed_inputs, Topics) -> _pipe = gleam@list:zip(Indexed_inputs, Topics), gleam@list:try_map( _pipe, fun(Pair) -> {Input, Topic_hex} = Pair, case gleeth@ethereum@abi@types:is_dynamic(erlang:element(3, Input)) of true -> case gleeth@utils@hex:decode(Topic_hex) of {ok, Bytes} -> {ok, {erlang:element(2, Input), {fixed_bytes_value, Bytes}}}; {error, _} -> {error, {decode_error, <<"Invalid hex in topic: "/utf8, Topic_hex/binary>>}} end; false -> case gleeth@utils@hex:decode(Topic_hex) of {ok, Bytes@1} -> gleam@result:'try'( gleeth@ethereum@abi@decode:decode_single( erlang:element(3, Input), Bytes@1 ), fun(Value) -> {ok, {erlang:element(2, Input), Value}} end ); {error, _} -> {error, {decode_error, <<"Invalid hex in topic: "/utf8, Topic_hex/binary>>}} end end end ).-file("src/gleeth/ethereum/abi/events.gleam", 144).-spec decode_non_indexed_params( list(gleeth@ethereum@abi@json:event_param()), binary()) -> {ok, list({binary(), gleeth@ethereum@abi@types:abi_value()})} | {error, gleeth@ethereum@abi@types:abi_error()}.decode_non_indexed_params(Non_indexed_inputs, Data_hex) -> Non_indexed_types = gleam@list:map( Non_indexed_inputs, fun(P) -> erlang:element(3, P) end ), Non_indexed_names = gleam@list:map( Non_indexed_inputs, fun(P@1) -> erlang:element(2, P@1) end ), case Data_hex of <<"0x"/utf8>> -> case Non_indexed_types of [] -> {ok, []}; _ -> {error, {decode_error, <<"Expected data for non-indexed params"/utf8>>}} end; <<""/utf8>> -> case Non_indexed_types of [] -> {ok, []}; _ -> {error, {decode_error, <<"Expected data for non-indexed params"/utf8>>}} end; _ -> case gleeth@utils@hex:decode(Data_hex) of {ok, Data_bytes} -> gleam@result:'try'( gleeth@ethereum@abi@decode:decode( Non_indexed_types, Data_bytes ), fun(Values) -> {ok, gleam@list:zip(Non_indexed_names, Values)} end ); {error, _} -> {error, {decode_error, <<"Invalid hex in log data"/utf8>>}} end end.-file("src/gleeth/ethereum/abi/events.gleam", 182).-spec merge_impl( list(gleeth@ethereum@abi@json:event_param()), list({binary(), gleeth@ethereum@abi@types:abi_value()}), list({binary(), gleeth@ethereum@abi@types:abi_value()}), list({binary(), gleeth@ethereum@abi@types:abi_value()})) -> list({binary(), gleeth@ethereum@abi@types:abi_value()}).merge_impl(Inputs, Indexed, Non_indexed, Acc) -> case Inputs of [] -> lists:reverse(Acc); [Input | Rest_inputs] -> case erlang:element(4, Input) of true -> case Indexed of [Param | Rest_indexed] -> merge_impl( Rest_inputs, Rest_indexed, Non_indexed, [Param | Acc] ); [] -> merge_impl(Rest_inputs, [], Non_indexed, Acc) end; false -> case Non_indexed of [Param@1 | Rest_non_indexed] -> merge_impl( Rest_inputs, Indexed, Rest_non_indexed, [Param@1 | Acc] ); [] -> merge_impl(Rest_inputs, Indexed, [], Acc) end end end.-file("src/gleeth/ethereum/abi/events.gleam", 174).?DOC(" Merge indexed and non-indexed params back into the original order.\n").-spec merge_params_in_order( list(gleeth@ethereum@abi@json:event_param()), list({binary(), gleeth@ethereum@abi@types:abi_value()}), list({binary(), gleeth@ethereum@abi@types:abi_value()})) -> list({binary(), gleeth@ethereum@abi@types:abi_value()}).merge_params_in_order(All_inputs, Indexed_params, Non_indexed_params) -> merge_impl(All_inputs, Indexed_params, Non_indexed_params, []).-file("src/gleeth/ethereum/abi/events.gleam", 83).-spec decode_event_params( binary(), list(gleeth@ethereum@abi@json:event_param()), list(binary()), binary()) -> {ok, decoded_log()} | {error, gleeth@ethereum@abi@types:abi_error()}.decode_event_params(Event_name, Inputs, Indexed_topics, Data_hex) -> Indexed_inputs = gleam@list:filter( Inputs, fun(P) -> erlang:element(4, P) end ), Non_indexed_inputs = gleam@list:filter( Inputs, fun(P@1) -> not erlang:element(4, P@1) end ), gleam@result:'try'( decode_indexed_params(Indexed_inputs, Indexed_topics), fun(Indexed_params) -> gleam@result:'try'( decode_non_indexed_params(Non_indexed_inputs, Data_hex), fun(Non_indexed_params) -> All_params = merge_params_in_order( Inputs, Indexed_params, Non_indexed_params ), {ok, {decoded_log, Event_name, All_params}} end ) end ).-file("src/gleeth/ethereum/abi/events.gleam", 34).?DOC( " Decode an event log using known ABI event definitions.\n" " Matches topics[0] against event signatures, then decodes indexed\n" " and non-indexed parameters.\n").-spec decode_log( gleeth@ethereum@types:log(), list(gleeth@ethereum@abi@json:abi_entry())) -> {ok, decoded_log()} | {error, gleeth@ethereum@abi@types:abi_error()}.decode_log(Log, Events) -> case erlang:element(3, Log) of [] -> {error, {decode_error, <<"Log has no topics (anonymous event)"/utf8>>}}; [Topic0 | Indexed_topics] -> gleam@result:'try'( find_event_by_topic(Topic0, Events), fun(Event) -> case Event of {event_entry, Name, Inputs} -> decode_event_params( Name, Inputs, Indexed_topics, erlang:element(4, Log) ); _ -> {error, {decode_error, <<"Matched entry is not an event"/utf8>>}} end end ) end.