Skip to content

Commit 2be9964

Browse files
authored
Add Ethers.Utils.find_and_decode/2 function (#156)
1 parent db5090a commit 2be9964

File tree

4 files changed

+94
-38
lines changed

4 files changed

+94
-38
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- Move and export abi decode functionality to `Ethers.TxData` module
88
- Export `Ethers.TxData.to_map/2` in docs
9+
- Add `Ethers.Event.find_and_decode/2` function
910

1011
## v0.5.4 (2024-10-22)
1112

lib/ethers/contract.ex

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,18 +119,20 @@ defmodule Ethers.Contract do
119119

120120
events_mod_name = Module.concat(module, EventFilters)
121121

122-
events =
123-
function_selectors_with_meta
124-
|> Enum.filter(&(&1.type == :event))
125-
|> Enum.map(&impl(&1, module, impl_opts))
122+
events = Enum.filter(function_selectors_with_meta, &(&1.type == :event))
123+
124+
events_impl = Enum.map(events, &impl(&1, module, impl_opts))
125+
event_selectors = Enum.flat_map(events, & &1.selectors)
126126

127127
events_module_ast =
128128
quote context: module do
129129
defmodule unquote(events_mod_name) do
130130
@moduledoc "Events for `#{Macro.to_string(unquote(module))}`"
131131

132132
defdelegate __default_address__, to: unquote(module)
133-
unquote(events)
133+
unquote(events_impl)
134+
135+
def __events__, do: unquote(Macro.escape(event_selectors))
134136
end
135137
end
136138

lib/ethers/event.ex

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,29 @@ defmodule Ethers.Event do
9292
transaction_index: transaction_index
9393
}
9494
end
95+
96+
@doc """
97+
Given a log entry and an EventFilters module (e.g. `Ethers.Contracts.ERC20.EventFilters`)
98+
will find a matching event filter in the given module and decodes the log using the event selector.
99+
"""
100+
@spec find_and_decode(map(), module()) :: {:ok, t()} | {:error, :not_found}
101+
def find_and_decode(log, event_filters_module) do
102+
[topic | _] = Map.fetch!(log, "topics")
103+
104+
topic_raw = Utils.hex_decode!(topic)
105+
106+
selector =
107+
Enum.find(
108+
event_filters_module.__events__(),
109+
fn %ABI.FunctionSelector{method_id: method_id} -> method_id == topic_raw end
110+
)
111+
112+
case selector do
113+
nil ->
114+
{:error, :not_found}
115+
116+
%ABI.FunctionSelector{} ->
117+
{:ok, decode(log, selector)}
118+
end
119+
end
95120
end

test/ethers/event_test.exs

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,67 @@ defmodule Ethers.EventTest do
33
alias Ethers.Event
44
doctest Event
55

6-
test "decode log with no data returns empty list" do
7-
selector = %ABI.FunctionSelector{
8-
function: "Approval",
9-
method_id: <<140, 91, 225, 229>>,
10-
type: :event,
11-
inputs_indexed: [true, true, false],
12-
state_mutability: nil,
13-
input_names: ["owner", "spender", "value"],
14-
types: [:address, :address, {:uint, 256}],
15-
returns: [uint: 256]
16-
}
6+
describe "decode/2" do
7+
test "decode log with no data returns empty list" do
8+
selector = %ABI.FunctionSelector{
9+
function: "Approval",
10+
method_id: <<140, 91, 225, 229>>,
11+
type: :event,
12+
inputs_indexed: [true, true, true],
13+
state_mutability: nil,
14+
input_names: ["owner", "spender", "value"],
15+
types: [:address, :address, {:uint, 256}],
16+
returns: [uint: 256]
17+
}
1718

18-
assert %Ethers.Event{data: []} =
19-
Event.decode(
20-
%{
21-
"address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750",
22-
"blockHash" =>
23-
"0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83",
24-
"blockNumber" => "0x1138b39",
25-
"data" => "0x",
26-
"logIndex" => "0x1a1",
27-
"removed" => false,
28-
"topics" => [
29-
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
30-
"0x00000000000000000000000023c5d7a16cf2e14a00f1c81be9443259f3cbc4ce",
31-
"0x0000000000000000000000000000000000000000000000000000000000000000",
32-
"0x0000000000000000000000000000000000000000000000000000000000000ef7"
33-
],
34-
"transactionHash" =>
35-
"0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d",
36-
"transactionIndex" => "0x83"
37-
},
38-
selector
39-
)
19+
assert %Ethers.Event{data: []} =
20+
Event.decode(
21+
%{
22+
"address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750",
23+
"blockHash" =>
24+
"0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83",
25+
"blockNumber" => "0x1138b39",
26+
"data" => "0x",
27+
"logIndex" => "0x1a1",
28+
"removed" => false,
29+
"topics" => [
30+
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
31+
"0x00000000000000000000000023c5d7a16cf2e14a00f1c81be9443259f3cbc4ce",
32+
"0x0000000000000000000000000000000000000000000000000000000000000000",
33+
"0x0000000000000000000000000000000000000000000000000000000000000ef7"
34+
],
35+
"transactionHash" =>
36+
"0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d",
37+
"transactionIndex" => "0x83"
38+
},
39+
selector
40+
)
41+
end
42+
end
43+
44+
describe "find_and_decode/2" do
45+
test "finds correct selector and decodes log" do
46+
assert {:ok, %Ethers.Event{data: [3831]}} =
47+
Event.find_and_decode(
48+
%{
49+
"address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750",
50+
"blockHash" =>
51+
"0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83",
52+
"blockNumber" => "0x1138b39",
53+
"data" => "0x0000000000000000000000000000000000000000000000000000000000000ef7",
54+
"logIndex" => "0x1a1",
55+
"removed" => false,
56+
"topics" => [
57+
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
58+
"0x00000000000000000000000023c5d7a16cf2e14a00f1c81be9443259f3cbc4ce",
59+
"0x0000000000000000000000000000000000000000000000000000000000000000"
60+
],
61+
"transactionHash" =>
62+
"0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d",
63+
"transactionIndex" => "0x83"
64+
},
65+
Ethers.Contracts.ERC20.EventFilters
66+
)
67+
end
4068
end
4169
end

0 commit comments

Comments
 (0)