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.