|
|
|
@ -1,16 +1,15 @@ |
|
|
|
|
defmodule ExW3 do |
|
|
|
|
|
|
|
|
|
@spec bytes_to_string(binary()) :: binary() |
|
|
|
|
@doc "converts Ethereum style bytes to string" |
|
|
|
|
def bytes_to_string bytes do |
|
|
|
|
@spec bytes_to_string(binary()) :: binary() |
|
|
|
|
@doc "converts Ethereum style bytes to string" |
|
|
|
|
def bytes_to_string(bytes) do |
|
|
|
|
bytes |
|
|
|
|
|> Base.encode16(case: :lower) |
|
|
|
|
|> String.replace_trailing("0", "") |
|
|
|
|
|> Base.decode16!(case: :lower) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec accounts() :: list() |
|
|
|
|
@doc "returns all available accounts" |
|
|
|
|
@spec accounts() :: list() |
|
|
|
|
@doc "returns all available accounts" |
|
|
|
|
def accounts do |
|
|
|
|
case Ethereumex.HttpClient.eth_accounts() do |
|
|
|
|
{:ok, accounts} -> accounts |
|
|
|
@ -18,16 +17,16 @@ defmodule ExW3 do |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec to_decimal(binary()) :: number() |
|
|
|
|
@doc "Converts ethereum hex string to decimal number" |
|
|
|
|
@spec to_decimal(binary()) :: number() |
|
|
|
|
@doc "Converts ethereum hex string to decimal number" |
|
|
|
|
def to_decimal(hex_string) do |
|
|
|
|
hex_string |
|
|
|
|
|> String.slice(2..-1) |
|
|
|
|
|> String.to_integer(16) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec block_number() :: integer() |
|
|
|
|
@doc "Returns the current block number" |
|
|
|
|
@spec block_number() :: integer() |
|
|
|
|
@doc "Returns the current block number" |
|
|
|
|
def block_number do |
|
|
|
|
case Ethereumex.HttpClient.eth_block_number() do |
|
|
|
|
{:ok, block_number} -> |
|
|
|
@ -38,8 +37,8 @@ defmodule ExW3 do |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec balance(binary()) :: integer() |
|
|
|
|
@doc "Returns current balance of account" |
|
|
|
|
@spec balance(binary()) :: integer() |
|
|
|
|
@doc "Returns current balance of account" |
|
|
|
|
def balance(account) do |
|
|
|
|
case Ethereumex.HttpClient.eth_get_balance(account) do |
|
|
|
|
{:ok, balance} -> |
|
|
|
@ -50,7 +49,7 @@ defmodule ExW3 do |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec keys_to_decimal(%{}, []) :: %{} |
|
|
|
|
@spec keys_to_decimal(%{}, []) :: %{} |
|
|
|
|
def keys_to_decimal(map, keys) do |
|
|
|
|
Map.new( |
|
|
|
|
Enum.map(keys, fn k -> |
|
|
|
@ -59,20 +58,23 @@ defmodule ExW3 do |
|
|
|
|
) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec tx_receipt(binary()) :: %{} |
|
|
|
|
@doc "Returns transaction receipt for specified transaction hash(id)" |
|
|
|
|
@spec tx_receipt(binary()) :: %{} |
|
|
|
|
@doc "Returns transaction receipt for specified transaction hash(id)" |
|
|
|
|
def tx_receipt(tx_hash) do |
|
|
|
|
case Ethereumex.HttpClient.eth_get_transaction_receipt(tx_hash) do |
|
|
|
|
{:ok, receipt} -> |
|
|
|
|
Map.merge receipt, keys_to_decimal(receipt, ["blockNumber", "cumulativeGasUsed", "gasUsed"]) |
|
|
|
|
Map.merge( |
|
|
|
|
receipt, |
|
|
|
|
keys_to_decimal(receipt, ["blockNumber", "cumulativeGasUsed", "gasUsed"]) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
err -> |
|
|
|
|
err |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec block(integer()) :: any() |
|
|
|
|
@doc "Returns block data for specified block number" |
|
|
|
|
@spec block(integer()) :: any() |
|
|
|
|
@doc "Returns block data for specified block number" |
|
|
|
|
def block(block_number) do |
|
|
|
|
case Ethereumex.HttpClient.eth_get_block_by_number(block_number, true) do |
|
|
|
|
{:ok, block} -> block |
|
|
|
@ -80,22 +82,22 @@ defmodule ExW3 do |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec mine(integer()) :: any() |
|
|
|
|
@doc "Mines number of blocks specified. Default is 1" |
|
|
|
|
@spec mine(integer()) :: any() |
|
|
|
|
@doc "Mines number of blocks specified. Default is 1" |
|
|
|
|
def mine(num_blocks \\ 1) do |
|
|
|
|
for _ <- 0..(num_blocks - 1) do |
|
|
|
|
Ethereumex.HttpClient.request("evm_mine", [], []) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec encode_event(binary()) :: binary() |
|
|
|
|
@doc "Encodes event based on signature" |
|
|
|
|
@spec encode_event(binary()) :: binary() |
|
|
|
|
@doc "Encodes event based on signature" |
|
|
|
|
def encode_event(signature) do |
|
|
|
|
ExthCrypto.Hash.Keccak.kec(signature) |> Base.encode16(case: :lower) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec decode_event(binary(), binary()) :: any() |
|
|
|
|
@doc "Decodes event based on given data and provided signature" |
|
|
|
|
@spec decode_event(binary(), binary()) :: any() |
|
|
|
|
@doc "Decodes event based on given data and provided signature" |
|
|
|
|
def decode_event(data, signature) do |
|
|
|
|
formatted_data = |
|
|
|
|
data |
|
|
|
@ -107,14 +109,14 @@ defmodule ExW3 do |
|
|
|
|
ABI.TypeDecoder.decode(formatted_data, fs) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec reformat_abi([]) :: %{} |
|
|
|
|
@doc "Reformats abi from list to map with event and function names as keys" |
|
|
|
|
@spec reformat_abi([]) :: %{} |
|
|
|
|
@doc "Reformats abi from list to map with event and function names as keys" |
|
|
|
|
def reformat_abi(abi) do |
|
|
|
|
Map.new(Enum.map(abi, fn x -> {x["name"], x} end)) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec load_abi(binary()) :: [] |
|
|
|
|
@doc "Loads the abi at the file path and reformats it to a map" |
|
|
|
|
@spec load_abi(binary()) :: [] |
|
|
|
|
@doc "Loads the abi at the file path and reformats it to a map" |
|
|
|
|
def load_abi(file_path) do |
|
|
|
|
file = File.read(Path.join(System.cwd(), file_path)) |
|
|
|
|
|
|
|
|
@ -124,19 +126,19 @@ defmodule ExW3 do |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec load_bin(binary()) :: binary() |
|
|
|
|
@doc "Loads the bin ar the file path" |
|
|
|
|
@spec load_bin(binary()) :: binary() |
|
|
|
|
@doc "Loads the bin ar the file path" |
|
|
|
|
def load_bin(file_path) do |
|
|
|
|
file = File.read(Path.join(System.cwd(), file_path)) |
|
|
|
|
|
|
|
|
|
case file do |
|
|
|
|
{:ok, bin} -> bin |
|
|
|
|
err -> err |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec decode_output(%{}, binary(), binary()) :: [] |
|
|
|
|
@doc "Decodes output based on specified functions return signature" |
|
|
|
|
@spec decode_output(%{}, binary(), binary()) :: [] |
|
|
|
|
@doc "Decodes output based on specified functions return signature" |
|
|
|
|
def decode_output(abi, name, output) do |
|
|
|
|
{:ok, trim_output} = |
|
|
|
|
String.slice(output, 2..String.length(output)) |> Base.decode16(case: :lower) |
|
|
|
@ -144,54 +146,57 @@ defmodule ExW3 do |
|
|
|
|
output_types = Enum.map(abi[name]["outputs"], fn x -> x["type"] end) |
|
|
|
|
types_signature = Enum.join(["(", Enum.join(output_types, ","), ")"]) |
|
|
|
|
output_signature = "#{name}(#{types_signature})" |
|
|
|
|
outputs = |
|
|
|
|
|
|
|
|
|
outputs = |
|
|
|
|
ABI.decode(output_signature, trim_output) |
|
|
|
|
|> List.first |
|
|
|
|
|> Tuple.to_list |
|
|
|
|
|> List.first() |
|
|
|
|
|> Tuple.to_list() |
|
|
|
|
|
|
|
|
|
outputs |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec types_signature(%{}, binary()) :: binary() |
|
|
|
|
@doc "Returns the type signature of a given function" |
|
|
|
|
@spec types_signature(%{}, binary()) :: binary() |
|
|
|
|
@doc "Returns the type signature of a given function" |
|
|
|
|
def types_signature(abi, name) do |
|
|
|
|
input_types = Enum.map(abi[name]["inputs"], fn x -> x["type"] end) |
|
|
|
|
types_signature = Enum.join(["(", Enum.join(input_types, ","), ")"]) |
|
|
|
|
types_signature |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec method_signature(%{}, binary()) :: binary() |
|
|
|
|
@doc "Returns the 4 character method id based on the hash of the method signature" |
|
|
|
|
@spec method_signature(%{}, binary()) :: binary() |
|
|
|
|
@doc "Returns the 4 character method id based on the hash of the method signature" |
|
|
|
|
def method_signature(abi, name) do |
|
|
|
|
if abi[name] do |
|
|
|
|
input_signature = "#{name}#{types_signature(abi, name)}" |> ExthCrypto.Hash.Keccak.kec() |
|
|
|
|
|
|
|
|
|
# Take first four bytes |
|
|
|
|
<<init::binary-size(4), _rest::binary>> = input_signature |
|
|
|
|
init |
|
|
|
|
init |
|
|
|
|
else |
|
|
|
|
raise "#{name} method not found in the given abi" |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec encode_data(binary(), []) :: binary() |
|
|
|
|
@doc "Encodes data into Ethereum hex string based on types signature" |
|
|
|
|
@spec encode_data(binary(), []) :: binary() |
|
|
|
|
@doc "Encodes data into Ethereum hex string based on types signature" |
|
|
|
|
def encode_data(types_signature, data) do |
|
|
|
|
ABI.TypeEncoder.encode_raw( |
|
|
|
|
[List.to_tuple(data)], |
|
|
|
|
[List.to_tuple(data)], |
|
|
|
|
ABI.FunctionSelector.decode_raw(types_signature) |
|
|
|
|
) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec encode_method_call(%{}, binary(), []) :: binary() |
|
|
|
|
@doc "Encodes data and appends it to the encoded method id" |
|
|
|
|
@spec encode_method_call(%{}, binary(), []) :: binary() |
|
|
|
|
@doc "Encodes data and appends it to the encoded method id" |
|
|
|
|
def encode_method_call(abi, name, input) do |
|
|
|
|
encoded_method_call = method_signature(abi, name) <> encode_data(types_signature(abi, name), input) |
|
|
|
|
encoded_method_call = |
|
|
|
|
method_signature(abi, name) <> encode_data(types_signature(abi, name), input) |
|
|
|
|
|
|
|
|
|
encoded_method_call |> Base.encode16(case: :lower) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec encode_input(%{}, binary(), []) :: binary() |
|
|
|
|
@doc "Encodes input from a method call based on function signature" |
|
|
|
|
@spec encode_input(%{}, binary(), []) :: binary() |
|
|
|
|
@doc "Encodes input from a method call based on function signature" |
|
|
|
|
def encode_input(abi, name, input) do |
|
|
|
|
if abi[name]["inputs"] do |
|
|
|
|
input_types = Enum.map(abi[name]["inputs"], fn x -> x["type"] end) |
|
|
|
@ -202,11 +207,11 @@ defmodule ExW3 do |
|
|
|
|
<<init::binary-size(4), _rest::binary>> = input_signature |
|
|
|
|
|
|
|
|
|
encoded_input = |
|
|
|
|
init <> |
|
|
|
|
ABI.TypeEncoder.encode_raw( |
|
|
|
|
[List.to_tuple(input)], |
|
|
|
|
ABI.FunctionSelector.decode_raw(types_signature) |
|
|
|
|
) |
|
|
|
|
init <> |
|
|
|
|
ABI.TypeEncoder.encode_raw( |
|
|
|
|
[List.to_tuple(input)], |
|
|
|
|
ABI.FunctionSelector.decode_raw(types_signature) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
encoded_input |> Base.encode16(case: :lower) |
|
|
|
|
else |
|
|
|
@ -219,50 +224,50 @@ defmodule ExW3 do |
|
|
|
|
|
|
|
|
|
# Client |
|
|
|
|
|
|
|
|
|
@spec start_link(atom(), list()) :: {:ok, pid()} |
|
|
|
|
@doc "Begins a Contract GenServer with specified name and state" |
|
|
|
|
@spec start_link(atom(), list()) :: {:ok, pid()} |
|
|
|
|
@doc "Begins a Contract GenServer with specified name and state" |
|
|
|
|
def start_link(name, state) do |
|
|
|
|
GenServer.start_link(__MODULE__, state, name: name) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec deploy(pid(), []) :: {:ok, []} |
|
|
|
|
@doc "Deploys contracts with given arguments" |
|
|
|
|
@spec deploy(pid(), []) :: {:ok, []} |
|
|
|
|
@doc "Deploys contracts with given arguments" |
|
|
|
|
def deploy(pid, args) do |
|
|
|
|
GenServer.call(pid, {:deploy, args}) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec at(pid(), binary()) :: :ok |
|
|
|
|
@doc "Sets the current Contract GenServer's address to given address" |
|
|
|
|
@spec at(pid(), binary()) :: :ok |
|
|
|
|
@doc "Sets the current Contract GenServer's address to given address" |
|
|
|
|
def at(pid, address) do |
|
|
|
|
GenServer.cast(pid, {:at, address}) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec address(pid()) :: {:ok, binary()} |
|
|
|
|
@doc "Returns the current Contract GenServer's address" |
|
|
|
|
@spec address(pid()) :: {:ok, binary()} |
|
|
|
|
@doc "Returns the current Contract GenServer's address" |
|
|
|
|
def address(pid) do |
|
|
|
|
GenServer.call(pid, :address) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec call(pid(), keyword(), []) :: {:ok, any()} |
|
|
|
|
@doc "Use a Contract's method with an eth_call" |
|
|
|
|
@spec call(pid(), keyword(), []) :: {:ok, any()} |
|
|
|
|
@doc "Use a Contract's method with an eth_call" |
|
|
|
|
def call(pid, method_name, args \\ []) do |
|
|
|
|
GenServer.call(pid, {:call, {method_name, args}}) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec send(pid(), keyword(), [], %{}) :: {:ok, binary()} |
|
|
|
|
@doc "Use a Contract's method with an eth_sendTransaction" |
|
|
|
|
@spec send(pid(), keyword(), [], %{}) :: {:ok, binary()} |
|
|
|
|
@doc "Use a Contract's method with an eth_sendTransaction" |
|
|
|
|
def send(pid, method_name, args, options) do |
|
|
|
|
GenServer.call(pid, {:send, {method_name, args, options}}) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@spec tx_receipt(pid(), binary()) :: %{} |
|
|
|
|
@doc "Returns a formatted transaction receipt for the given transaction hash(id)" |
|
|
|
|
@spec tx_receipt(pid(), binary()) :: %{} |
|
|
|
|
@doc "Returns a formatted transaction receipt for the given transaction hash(id)" |
|
|
|
|
def tx_receipt(pid, tx_hash) do |
|
|
|
|
GenServer.call(pid, {:tx_receipt, tx_hash}) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
# Server |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def init(state) do |
|
|
|
|
if state[:abi] do |
|
|
|
|
{:ok, [{:events, init_events(state[:abi])} | state]} |
|
|
|
@ -272,17 +277,19 @@ defmodule ExW3 do |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp init_events(abi) do |
|
|
|
|
events = Enum.filter abi, fn {_, v} -> |
|
|
|
|
v["type"] == "event" |
|
|
|
|
end |
|
|
|
|
events = |
|
|
|
|
Enum.filter(abi, fn {_, v} -> |
|
|
|
|
v["type"] == "event" |
|
|
|
|
end) |
|
|
|
|
|
|
|
|
|
signature_types_map = Enum.map events, fn {name, v} -> |
|
|
|
|
types = Enum.map v["inputs"], &Map.get(&1, "type") |
|
|
|
|
names = Enum.map v["inputs"], &Map.get(&1, "name") |
|
|
|
|
signature = Enum.join([name, "(", Enum.join(types, ","), ")"]) |
|
|
|
|
signature_types_map = |
|
|
|
|
Enum.map(events, fn {name, v} -> |
|
|
|
|
types = Enum.map(v["inputs"], &Map.get(&1, "type")) |
|
|
|
|
names = Enum.map(v["inputs"], &Map.get(&1, "name")) |
|
|
|
|
signature = Enum.join([name, "(", Enum.join(types, ","), ")"]) |
|
|
|
|
|
|
|
|
|
{"0x#{ExW3.encode_event(signature)}", %{signature: signature, names: names}} |
|
|
|
|
end |
|
|
|
|
{"0x#{ExW3.encode_event(signature)}", %{signature: signature, names: names}} |
|
|
|
|
end) |
|
|
|
|
|
|
|
|
|
Enum.into(signature_types_map, %{}) |
|
|
|
|
end |
|
|
|
@ -290,20 +297,21 @@ defmodule ExW3 do |
|
|
|
|
# Helpers |
|
|
|
|
|
|
|
|
|
def deploy_helper(bin, abi, args) do |
|
|
|
|
|
|
|
|
|
constructor_arg_data = |
|
|
|
|
if args[:args] do |
|
|
|
|
constructor_abi = Enum.find abi, fn {_, v} -> |
|
|
|
|
v["type"] == "constructor" |
|
|
|
|
end |
|
|
|
|
if constructor_abi do |
|
|
|
|
{_, constructor} = constructor_abi |
|
|
|
|
input_types = Enum.map(constructor["inputs"], fn x -> x["type"] end) |
|
|
|
|
types_signature = Enum.join(["(", Enum.join(input_types, ","), ")"]) |
|
|
|
|
bin <> (ExW3.encode_data(types_signature, args[:args]) |> Base.encode16(case: :lower)) |
|
|
|
|
else |
|
|
|
|
bin |
|
|
|
|
end |
|
|
|
|
constructor_abi = |
|
|
|
|
Enum.find(abi, fn {_, v} -> |
|
|
|
|
v["type"] == "constructor" |
|
|
|
|
end) |
|
|
|
|
|
|
|
|
|
if constructor_abi do |
|
|
|
|
{_, constructor} = constructor_abi |
|
|
|
|
input_types = Enum.map(constructor["inputs"], fn x -> x["type"] end) |
|
|
|
|
types_signature = Enum.join(["(", Enum.join(input_types, ","), ")"]) |
|
|
|
|
bin <> (ExW3.encode_data(types_signature, args[:args]) |> Base.encode16(case: :lower)) |
|
|
|
|
else |
|
|
|
|
bin |
|
|
|
|
end |
|
|
|
|
else |
|
|
|
|
bin |
|
|
|
|
end |
|
|
|
@ -315,17 +323,18 @@ defmodule ExW3 do |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
{:ok, tx_receipt_id} = Ethereumex.HttpClient.eth_send_transaction(tx) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{:ok, tx_receipt} = Ethereumex.HttpClient.eth_get_transaction_receipt(tx_receipt_id) |
|
|
|
|
|
|
|
|
|
tx_receipt["contractAddress"] |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def eth_call_helper(address, abi, method_name, args) do |
|
|
|
|
result = Ethereumex.HttpClient.eth_call(%{ |
|
|
|
|
to: address, |
|
|
|
|
data: ExW3.encode_method_call(abi, method_name, args) |
|
|
|
|
}) |
|
|
|
|
def eth_call_helper(address, abi, method_name, args) do |
|
|
|
|
result = |
|
|
|
|
Ethereumex.HttpClient.eth_call(%{ |
|
|
|
|
to: address, |
|
|
|
|
data: ExW3.encode_method_call(abi, method_name, args) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
case result do |
|
|
|
|
{:ok, data} -> ([:ok] ++ ExW3.decode_output(abi, method_name, data)) |> List.to_tuple() |
|
|
|
@ -367,6 +376,7 @@ defmodule ExW3 do |
|
|
|
|
|
|
|
|
|
def handle_call({:call, {method_name, args}}, _from, state) do |
|
|
|
|
address = state[:address] |
|
|
|
|
|
|
|
|
|
if address do |
|
|
|
|
result = eth_call_helper(address, state[:abi], Atom.to_string(method_name), args) |
|
|
|
|
{:reply, result, state} |
|
|
|
@ -377,6 +387,7 @@ defmodule ExW3 do |
|
|
|
|
|
|
|
|
|
def handle_call({:send, {method_name, args, options}}, _from, state) do |
|
|
|
|
address = state[:address] |
|
|
|
|
|
|
|
|
|
if address do |
|
|
|
|
result = eth_send_helper(address, state[:abi], Atom.to_string(method_name), args, options) |
|
|
|
|
{:reply, result, state} |
|
|
|
@ -386,21 +397,24 @@ defmodule ExW3 do |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def handle_call({:tx_receipt, tx_hash}, _from, state) do |
|
|
|
|
receipt = ExW3.tx_receipt tx_hash |
|
|
|
|
receipt = ExW3.tx_receipt(tx_hash) |
|
|
|
|
events = state[:events] |
|
|
|
|
logs = receipt["logs"] |
|
|
|
|
formatted_logs = Enum.map logs, fn log -> |
|
|
|
|
topic = Enum.at log["topics"], 0 |
|
|
|
|
event = Map.get events, topic |
|
|
|
|
if event do |
|
|
|
|
Enum.zip(event[:names], ExW3.decode_event(log["data"], event[:signature])) |> Enum.into(%{}) |
|
|
|
|
else |
|
|
|
|
nil |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
formatted_logs = |
|
|
|
|
Enum.map(logs, fn log -> |
|
|
|
|
topic = Enum.at(log["topics"], 0) |
|
|
|
|
event = Map.get(events, topic) |
|
|
|
|
|
|
|
|
|
if event do |
|
|
|
|
Enum.zip(event[:names], ExW3.decode_event(log["data"], event[:signature])) |
|
|
|
|
|> Enum.into(%{}) |
|
|
|
|
else |
|
|
|
|
nil |
|
|
|
|
end |
|
|
|
|
end) |
|
|
|
|
|
|
|
|
|
{:reply, {:ok, {receipt, formatted_logs}}, state} |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|