Skip to content

Commit 0afe239

Browse files
authored
fix indexed dynamic types event decoding (#250)
1 parent e08fe17 commit 0afe239

2 files changed

Lines changed: 216 additions & 9 deletions

File tree

lib/ethers/event.ex

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,7 @@ defmodule Ethers.Event do
6565
sub_topics_raw
6666
|> Enum.map(&Utils.hex_decode!/1)
6767
|> Enum.zip(ContractHelpers.event_indexed_types(selector))
68-
|> Enum.map(fn
69-
{data, :string} ->
70-
{Utils.hex_encode(data), :string}
71-
72-
{data, type} ->
73-
[decoded] = TypeDecoder.decode_raw(data, [type])
74-
{decoded, type}
75-
end)
76-
|> Enum.map(fn {data, type} -> Utils.human_arg(data, type) end)
68+
|> Enum.map(fn {raw, type} -> decode_sub_topic(raw, type) end)
7769

7870
topics = [FunctionSelector.encode(selector) | decoded_topics]
7971

@@ -120,4 +112,14 @@ defmodule Ethers.Event do
120112
{:ok, decode(log, selector)}
121113
end
122114
end
115+
116+
defp decode_sub_topic(raw, type) when type in [:string, :bytes], do: Utils.hex_encode(raw)
117+
defp decode_sub_topic(raw, {:array, _}), do: Utils.hex_encode(raw)
118+
defp decode_sub_topic(raw, {:array, _, _}), do: Utils.hex_encode(raw)
119+
defp decode_sub_topic(raw, {:tuple, _}), do: Utils.hex_encode(raw)
120+
121+
defp decode_sub_topic(raw, type) do
122+
[decoded] = TypeDecoder.decode_raw(raw, [type])
123+
Utils.human_arg(decoded, type)
124+
end
123125
end

test/ethers/event_test.exs

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,211 @@ defmodule Ethers.EventTest do
44
doctest Event
55

66
describe "decode/2" do
7+
test "indexed bytes topic is returned as a hex-encoded keccak hash" do
8+
selector = %ABI.FunctionSelector{
9+
function: "SetLiquidityAdapterAndData",
10+
method_id:
11+
<<0x9D, 0xEB, 0x43, 0xD7, 0x14, 0x22, 0xAF, 0x41, 0x85, 0x3C, 0x39, 0x21, 0xFB, 0x36,
12+
0x4B, 0x76, 0x47, 0xF9, 0xA9, 0xB1, 0x36, 0xE4, 0x6D, 0x66, 0xD4, 0x5C, 0x1B, 0xF7,
13+
0x07, 0xAF, 0x70, 0x6C>>,
14+
type: :event,
15+
inputs_indexed: [true, true, true],
16+
state_mutability: nil,
17+
input_names: ["sender", "newLiquidityAdapter", "newLiquidityData"],
18+
types: [:address, :address, :bytes],
19+
returns: []
20+
}
21+
22+
assert %Ethers.Event{
23+
topics: [
24+
_method,
25+
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
26+
"0x607Bca5681cEe20C82cF1D899E60B9eD36bc611C",
27+
"0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64"
28+
]
29+
} =
30+
Event.decode(
31+
%{
32+
"address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750",
33+
"blockHash" =>
34+
"0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83",
35+
"blockNumber" => "0x1138b39",
36+
"data" => "0x",
37+
"logIndex" => "0x1a1",
38+
"removed" => false,
39+
"topics" => [
40+
"0x9deb43d71422af41853c3921fb364b7647f9a9b136e46d66d45c1bf707af706c",
41+
"0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266",
42+
"0x000000000000000000000000607bca5681cee20c82cf1d899e60b9ed36bc611c",
43+
"0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64"
44+
],
45+
"transactionHash" =>
46+
"0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d",
47+
"transactionIndex" => "0x83"
48+
},
49+
selector
50+
)
51+
end
52+
53+
test "indexed string topic is returned as a hex-encoded keccak hash" do
54+
selector = %ABI.FunctionSelector{
55+
function: "E",
56+
method_id: :binary.copy(<<0>>, 32),
57+
type: :event,
58+
inputs_indexed: [true],
59+
state_mutability: nil,
60+
input_names: ["name"],
61+
types: [:string],
62+
returns: []
63+
}
64+
65+
assert %Ethers.Event{
66+
topics: [
67+
_method,
68+
"0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64"
69+
]
70+
} =
71+
Event.decode(
72+
%{
73+
"address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750",
74+
"blockHash" =>
75+
"0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83",
76+
"blockNumber" => "0x1138b39",
77+
"data" => "0x",
78+
"logIndex" => "0x1a1",
79+
"removed" => false,
80+
"topics" => [
81+
"0x0000000000000000000000000000000000000000000000000000000000000000",
82+
"0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64"
83+
],
84+
"transactionHash" =>
85+
"0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d",
86+
"transactionIndex" => "0x83"
87+
},
88+
selector
89+
)
90+
end
91+
92+
test "indexed dynamic array topic is returned as a hex-encoded keccak hash" do
93+
selector = %ABI.FunctionSelector{
94+
function: "E",
95+
method_id: :binary.copy(<<0>>, 32),
96+
type: :event,
97+
inputs_indexed: [true],
98+
state_mutability: nil,
99+
input_names: ["xs"],
100+
types: [{:array, {:uint, 256}}],
101+
returns: []
102+
}
103+
104+
assert %Ethers.Event{
105+
topics: [
106+
_method,
107+
"0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
108+
]
109+
} =
110+
Event.decode(
111+
%{
112+
"address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750",
113+
"blockHash" =>
114+
"0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83",
115+
"blockNumber" => "0x1138b39",
116+
"data" => "0x",
117+
"logIndex" => "0x1a1",
118+
"removed" => false,
119+
"topics" => [
120+
"0x0000000000000000000000000000000000000000000000000000000000000000",
121+
"0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
122+
],
123+
"transactionHash" =>
124+
"0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d",
125+
"transactionIndex" => "0x83"
126+
},
127+
selector
128+
)
129+
end
130+
131+
test "indexed tuple topic is returned as a hex-encoded keccak hash" do
132+
selector = %ABI.FunctionSelector{
133+
function: "E",
134+
method_id: :binary.copy(<<0>>, 32),
135+
type: :event,
136+
inputs_indexed: [true],
137+
state_mutability: nil,
138+
input_names: ["s"],
139+
types: [{:tuple, [{:uint, 256}, :address]}],
140+
returns: []
141+
}
142+
143+
assert %Ethers.Event{
144+
topics: [
145+
_method,
146+
"0x1111111111111111111111111111111111111111111111111111111111111111"
147+
]
148+
} =
149+
Event.decode(
150+
%{
151+
"address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750",
152+
"blockHash" =>
153+
"0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83",
154+
"blockNumber" => "0x1138b39",
155+
"data" => "0x",
156+
"logIndex" => "0x1a1",
157+
"removed" => false,
158+
"topics" => [
159+
"0x0000000000000000000000000000000000000000000000000000000000000000",
160+
"0x1111111111111111111111111111111111111111111111111111111111111111"
161+
],
162+
"transactionHash" =>
163+
"0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d",
164+
"transactionIndex" => "0x83"
165+
},
166+
selector
167+
)
168+
end
169+
170+
test "mixed indexed bytes and non-indexed uint256 decodes both" do
171+
selector = %ABI.FunctionSelector{
172+
function: "E",
173+
method_id: :binary.copy(<<0>>, 32),
174+
type: :event,
175+
inputs_indexed: [true, true, false],
176+
state_mutability: nil,
177+
input_names: ["a", "b", "nonIndexed"],
178+
types: [:address, :bytes, {:uint, 256}],
179+
returns: [uint: 256]
180+
}
181+
182+
assert %Ethers.Event{
183+
data: [42],
184+
topics: [
185+
_method,
186+
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
187+
"0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64"
188+
]
189+
} =
190+
Event.decode(
191+
%{
192+
"address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750",
193+
"blockHash" =>
194+
"0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83",
195+
"blockNumber" => "0x1138b39",
196+
"data" => "0x000000000000000000000000000000000000000000000000000000000000002a",
197+
"logIndex" => "0x1a1",
198+
"removed" => false,
199+
"topics" => [
200+
"0x0000000000000000000000000000000000000000000000000000000000000000",
201+
"0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266",
202+
"0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64"
203+
],
204+
"transactionHash" =>
205+
"0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d",
206+
"transactionIndex" => "0x83"
207+
},
208+
selector
209+
)
210+
end
211+
7212
test "decode log with no data returns empty list" do
8213
selector = %ABI.FunctionSelector{
9214
function: "Approval",

0 commit comments

Comments
 (0)