diff --git a/lib/ethers/event.ex b/lib/ethers/event.ex index 71cd0ec..0d0cc54 100644 --- a/lib/ethers/event.ex +++ b/lib/ethers/event.ex @@ -65,15 +65,7 @@ defmodule Ethers.Event do sub_topics_raw |> Enum.map(&Utils.hex_decode!/1) |> Enum.zip(ContractHelpers.event_indexed_types(selector)) - |> Enum.map(fn - {data, :string} -> - {Utils.hex_encode(data), :string} - - {data, type} -> - [decoded] = TypeDecoder.decode_raw(data, [type]) - {decoded, type} - end) - |> Enum.map(fn {data, type} -> Utils.human_arg(data, type) end) + |> Enum.map(fn {raw, type} -> decode_sub_topic(raw, type) end) topics = [FunctionSelector.encode(selector) | decoded_topics] @@ -120,4 +112,14 @@ defmodule Ethers.Event do {:ok, decode(log, selector)} end end + + defp decode_sub_topic(raw, type) when type in [:string, :bytes], do: Utils.hex_encode(raw) + defp decode_sub_topic(raw, {:array, _}), do: Utils.hex_encode(raw) + defp decode_sub_topic(raw, {:array, _, _}), do: Utils.hex_encode(raw) + defp decode_sub_topic(raw, {:tuple, _}), do: Utils.hex_encode(raw) + + defp decode_sub_topic(raw, type) do + [decoded] = TypeDecoder.decode_raw(raw, [type]) + Utils.human_arg(decoded, type) + end end diff --git a/test/ethers/event_test.exs b/test/ethers/event_test.exs index 9147c30..66a9fc1 100644 --- a/test/ethers/event_test.exs +++ b/test/ethers/event_test.exs @@ -4,6 +4,211 @@ defmodule Ethers.EventTest do doctest Event describe "decode/2" do + test "indexed bytes topic is returned as a hex-encoded keccak hash" do + selector = %ABI.FunctionSelector{ + function: "SetLiquidityAdapterAndData", + method_id: + <<0x9D, 0xEB, 0x43, 0xD7, 0x14, 0x22, 0xAF, 0x41, 0x85, 0x3C, 0x39, 0x21, 0xFB, 0x36, + 0x4B, 0x76, 0x47, 0xF9, 0xA9, 0xB1, 0x36, 0xE4, 0x6D, 0x66, 0xD4, 0x5C, 0x1B, 0xF7, + 0x07, 0xAF, 0x70, 0x6C>>, + type: :event, + inputs_indexed: [true, true, true], + state_mutability: nil, + input_names: ["sender", "newLiquidityAdapter", "newLiquidityData"], + types: [:address, :address, :bytes], + returns: [] + } + + assert %Ethers.Event{ + topics: [ + _method, + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x607Bca5681cEe20C82cF1D899E60B9eD36bc611C", + "0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64" + ] + } = + Event.decode( + %{ + "address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750", + "blockHash" => + "0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83", + "blockNumber" => "0x1138b39", + "data" => "0x", + "logIndex" => "0x1a1", + "removed" => false, + "topics" => [ + "0x9deb43d71422af41853c3921fb364b7647f9a9b136e46d66d45c1bf707af706c", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "0x000000000000000000000000607bca5681cee20c82cf1d899e60b9ed36bc611c", + "0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64" + ], + "transactionHash" => + "0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d", + "transactionIndex" => "0x83" + }, + selector + ) + end + + test "indexed string topic is returned as a hex-encoded keccak hash" do + selector = %ABI.FunctionSelector{ + function: "E", + method_id: :binary.copy(<<0>>, 32), + type: :event, + inputs_indexed: [true], + state_mutability: nil, + input_names: ["name"], + types: [:string], + returns: [] + } + + assert %Ethers.Event{ + topics: [ + _method, + "0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64" + ] + } = + Event.decode( + %{ + "address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750", + "blockHash" => + "0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83", + "blockNumber" => "0x1138b39", + "data" => "0x", + "logIndex" => "0x1a1", + "removed" => false, + "topics" => [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64" + ], + "transactionHash" => + "0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d", + "transactionIndex" => "0x83" + }, + selector + ) + end + + test "indexed dynamic array topic is returned as a hex-encoded keccak hash" do + selector = %ABI.FunctionSelector{ + function: "E", + method_id: :binary.copy(<<0>>, 32), + type: :event, + inputs_indexed: [true], + state_mutability: nil, + input_names: ["xs"], + types: [{:array, {:uint, 256}}], + returns: [] + } + + assert %Ethers.Event{ + topics: [ + _method, + "0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" + ] + } = + Event.decode( + %{ + "address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750", + "blockHash" => + "0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83", + "blockNumber" => "0x1138b39", + "data" => "0x", + "logIndex" => "0x1a1", + "removed" => false, + "topics" => [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" + ], + "transactionHash" => + "0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d", + "transactionIndex" => "0x83" + }, + selector + ) + end + + test "indexed tuple topic is returned as a hex-encoded keccak hash" do + selector = %ABI.FunctionSelector{ + function: "E", + method_id: :binary.copy(<<0>>, 32), + type: :event, + inputs_indexed: [true], + state_mutability: nil, + input_names: ["s"], + types: [{:tuple, [{:uint, 256}, :address]}], + returns: [] + } + + assert %Ethers.Event{ + topics: [ + _method, + "0x1111111111111111111111111111111111111111111111111111111111111111" + ] + } = + Event.decode( + %{ + "address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750", + "blockHash" => + "0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83", + "blockNumber" => "0x1138b39", + "data" => "0x", + "logIndex" => "0x1a1", + "removed" => false, + "topics" => [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x1111111111111111111111111111111111111111111111111111111111111111" + ], + "transactionHash" => + "0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d", + "transactionIndex" => "0x83" + }, + selector + ) + end + + test "mixed indexed bytes and non-indexed uint256 decodes both" do + selector = %ABI.FunctionSelector{ + function: "E", + method_id: :binary.copy(<<0>>, 32), + type: :event, + inputs_indexed: [true, true, false], + state_mutability: nil, + input_names: ["a", "b", "nonIndexed"], + types: [:address, :bytes, {:uint, 256}], + returns: [uint: 256] + } + + assert %Ethers.Event{ + data: [42], + topics: [ + _method, + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64" + ] + } = + Event.decode( + %{ + "address" => "0xaa107ccfe230a29c345fd97bc6eb9bd2fccd0750", + "blockHash" => + "0xe8885761ec559c5e267c48f44b4b12e4169f7d3a116f5e8f43314147722f0d83", + "blockNumber" => "0x1138b39", + "data" => "0x000000000000000000000000000000000000000000000000000000000000002a", + "logIndex" => "0x1a1", + "removed" => false, + "topics" => [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64" + ], + "transactionHash" => + "0xf6e06e4f3fbd67088e8278843e55862957537760c63bae7b682a0e39da75b45d", + "transactionIndex" => "0x83" + }, + selector + ) + end + test "decode log with no data returns empty list" do selector = %ABI.FunctionSelector{ function: "Approval",