From c22899fb09dd10ba3ffa6ff015f2f68698b47ebc Mon Sep 17 00:00:00 2001 From: hswick Date: Thu, 27 Sep 2018 15:38:58 -0500 Subject: [PATCH] Updated version number, and ran mix format --- README.md | 2 +- lib/exw3.ex | 484 ++++++++++++++++++++++++--------------------- mix.exs | 2 +- test/exw3_test.exs | 224 +++++++++++---------- 4 files changed, 389 insertions(+), 323 deletions(-) diff --git a/README.md b/README.md index 3c71f63..a22bc3b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ```elixir def deps do - [{:exw3, "~> 0.1.6"}] + [{:exw3, "~> 0.2.0"}] end ``` ## Overview diff --git a/lib/exw3.ex b/lib/exw3.ex index 5e98c04..a316bcb 100644 --- a/lib/exw3.ex +++ b/lib/exw3.ex @@ -1,7 +1,6 @@ defmodule ExW3 do + Module.register_attribute(__MODULE__, :unit_map, persist: true, accumulate: false) - Module.register_attribute __MODULE__, :unit_map, persist: true, accumulate: false - @unit_map %{ :noether => 0, :wei => 1, @@ -42,7 +41,7 @@ defmodule ExW3 do if @unit_map[key] do num * @unit_map[key] else - throw "#{key} not valid unit" + throw("#{key} not valid unit") end end @@ -50,7 +49,7 @@ defmodule ExW3 do if @unit_map[key] do num / @unit_map[key] else - throw "#{key} not valid unit" + throw("#{key} not valid unit") end end @@ -59,7 +58,7 @@ defmodule ExW3 do def keccak256(string) do Enum.join(["0x", ExthCrypto.Hash.Keccak.kec(string) |> Base.encode16(case: :lower)], "") end - + @spec bytes_to_string(binary()) :: binary() @doc "converts Ethereum style bytes to string" def bytes_to_string(bytes) do @@ -75,7 +74,7 @@ defmodule ExW3 do address |> String.slice(2..-1) |> Base.decode16!(case: :lower) - |> :binary.decode_unsigned + |> :binary.decode_unsigned() end @spec to_address(binary()) :: binary() @@ -89,25 +88,29 @@ defmodule ExW3 do def to_checksum_address(address) do address = String.replace(address, ~r/^0x/, "") - hash = ExthCrypto.Hash.Keccak.kec(String.downcase(address)) - |> Base.encode16(case: :lower) - |> String.replace(~r/^0x/, "") + hash = + ExthCrypto.Hash.Keccak.kec(String.downcase(address)) + |> Base.encode16(case: :lower) + |> String.replace(~r/^0x/, "") + + keccak_hash_list = + hash + |> String.split("", trim: true) + |> Enum.map(fn x -> elem(Integer.parse(x, 16), 0) end) - keccak_hash_list = hash - |> String.split("", trim: true) - |> Enum.map(fn (x) -> elem(Integer.parse(x, 16),0) end) + list_arr = + for n <- 0..(String.length(address) - 1) do + number = Enum.at(keccak_hash_list, n) - list_arr = for n <- 0..String.length(address)-1 do - number = Enum.at(keccak_hash_list, n) - cond do - number >= 8 -> String.upcase(String.at(address, n)) - true -> String.downcase(String.at(address, n)) + cond do + number >= 8 -> String.upcase(String.at(address, n)) + true -> String.downcase(String.at(address, n)) + end end - end "0x" <> List.to_string(list_arr) end - + @doc "checks if the address is a valid checksummed address" @spec is_valid_checksum_address(binary()) :: boolean() def is_valid_checksum_address(address) do @@ -242,11 +245,9 @@ defmodule ExW3 do @spec reformat_abi([]) :: %{} @doc "Reformats abi from list to map with event and function names as keys" def reformat_abi(abi) do - abi |> Enum.map(&map_abi/1) - |> Map.new - + |> Map.new() end @spec load_abi(binary()) :: [] @@ -274,8 +275,7 @@ defmodule ExW3 do @spec decode_data(binary(), binary()) :: any() @doc "Decodes data based on given type signature" def decode_data(types_signature, data) do - {:ok, trim_data} = - String.slice(data, 2..String.length(data)) |> Base.decode16(case: :lower) + {:ok, trim_data} = String.slice(data, 2..String.length(data)) |> Base.decode16(case: :lower) ABI.decode(types_signature, trim_data) |> List.first() end @@ -334,7 +334,11 @@ defmodule ExW3 do def encode_option(0), do: "0x0" def encode_option(value) do - "0x" <> (value |> :binary.encode_unsigned() |> Base.encode16(case: :lower) |> String.trim_leading("0")) + "0x" <> + (value + |> :binary.encode_unsigned() + |> Base.encode16(case: :lower) + |> String.trim_leading("0")) end @spec encode_method_call(%{}, binary(), []) :: binary() @@ -393,7 +397,8 @@ defmodule ExW3 do @impl true def init(state) do - schedule_work() # Schedule work to be performed on start + # Schedule work to be performed on start + schedule_work() {:ok, state} end @@ -405,19 +410,21 @@ defmodule ExW3 do @impl true def handle_info(:work, state) do # Do the desired work here - Enum.each state, fn filter_id -> - send Listener, {:event, filter_id, ExW3.get_filter_changes(filter_id)} - end - - schedule_work() # Reschedule once more + Enum.each(state, fn filter_id -> + send(Listener, {:event, filter_id, ExW3.get_filter_changes(filter_id)}) + end) + + # Reschedule once more + schedule_work() {:noreply, state} end defp schedule_work() do - Process.send_after(self(), :work, 500) # In 1/2 sec + # In 1/2 sec + Process.send_after(self(), :work, 500) end end - + defmodule EventListener do def start_link do Poller.start_link() @@ -428,13 +435,14 @@ defmodule ExW3 do def filter(filter_id, event_fields, pid) do Poller.filter(filter_id) - send Listener, {:filter, filter_id, event_fields, pid} + send(Listener, {:filter, filter_id, event_fields, pid}) end def listen(callback) do receive do - {:event, result} -> apply callback, [result] + {:event, result} -> apply(callback, [result]) end + listen(callback) end @@ -444,58 +452,68 @@ defmodule ExW3 do defp format_log_data(log, event_attributes) do non_indexed_fields = - extract_non_indexed_fields( - Map.get(log, "data"), - event_attributes[:non_indexed_names], - event_attributes[:signature] - ) + extract_non_indexed_fields( + Map.get(log, "data"), + event_attributes[:non_indexed_names], + event_attributes[:signature] + ) indexed_fields = - if length(log["topics"]) > 1 do - [ _head | tail ] = log["topics"] - decoded_topics = Enum.map(0..length(tail) - 1, fn i -> - topic_type = Enum.at(event_attributes[:topic_types], i) - topic_data = Enum.at(tail, i) + if length(log["topics"]) > 1 do + [_head | tail] = log["topics"] - {decoded} = ExW3.decode_data(topic_type, topic_data) + decoded_topics = + Enum.map(0..(length(tail) - 1), fn i -> + topic_type = Enum.at(event_attributes[:topic_types], i) + topic_data = Enum.at(tail, i) - decoded - end) + {decoded} = ExW3.decode_data(topic_type, topic_data) - Enum.zip(event_attributes[:topic_names], decoded_topics) |> Enum.into(%{}) - else - %{} - end + decoded + end) + + Enum.zip(event_attributes[:topic_names], decoded_topics) |> Enum.into(%{}) + else + %{} + end new_data = Map.merge(indexed_fields, non_indexed_fields) Map.put(log, "data", new_data) end - + defp loop(state) do receive do - {:filter, filter_id, event_attributes, pid} -> - loop(Map.put(state, filter_id, %{pid: pid, event_attributes: event_attributes})) - {:event, filter_id, logs} -> - filter_attributes = Map.get(state, filter_id) - event_attributes = filter_attributes[:event_attributes] - - unless logs == [] do - Enum.each(logs, fn log -> - formatted_log = - Enum.reduce([ - ExW3.keys_to_decimal(log, ["blockNumber", "logIndex", "transactionIndex", "transactionLogIndex"]), - format_log_data(log, event_attributes) - ], - &Map.merge/2) - - send filter_attributes[:pid], {:event, {filter_id, formatted_log}} - end) - end - loop(state) + {:filter, filter_id, event_attributes, pid} -> + loop(Map.put(state, filter_id, %{pid: pid, event_attributes: event_attributes})) + + {:event, filter_id, logs} -> + filter_attributes = Map.get(state, filter_id) + event_attributes = filter_attributes[:event_attributes] + + unless logs == [] do + Enum.each(logs, fn log -> + formatted_log = + Enum.reduce( + [ + ExW3.keys_to_decimal(log, [ + "blockNumber", + "logIndex", + "transactionIndex", + "transactionLogIndex" + ]), + format_log_data(log, event_attributes) + ], + &Map.merge/2 + ) + + send(filter_attributes[:pid], {:event, {filter_id, formatted_log}}) + end) + end + + loop(state) end end - end defmodule Contract do @@ -549,7 +567,7 @@ defmodule ExW3 do @doc "Use a Contract's method with an eth_call. Returns a Task to be awaited." def call_async(contract_name, method_name, args \\ []) do Task.async(fn -> - GenServer.call(ContractManager, {:call, {contract_name, method_name, args}}) + GenServer.call(ContractManager, {:call, {contract_name, method_name, args}}) end) end @@ -557,10 +575,10 @@ defmodule ExW3 do @doc "Use a Contract's method with an eth_sendTransaction. Returns a Task to be awaited." def send_async(contract_name, method_name, args, options) do Task.async(fn -> - GenServer.call(ContractManager, {:send, {contract_name, method_name, args, options}}) + GenServer.call(ContractManager, {:send, {contract_name, method_name, args, options}}) end) - end - + end + @spec tx_receipt(keyword(), binary()) :: %{} @doc "Returns a formatted transaction receipt for the given transaction hash(id)" def tx_receipt(contract_name, tx_hash) do @@ -568,28 +586,31 @@ defmodule ExW3 do end def filter(contract_name, event_name, other_pid, event_data \\ %{}) do - GenServer.call(ContractManager, {:filter, {contract_name, event_name, other_pid, event_data}}) + GenServer.call( + ContractManager, + {:filter, {contract_name, event_name, other_pid, event_data}} + ) end # Server def init(state) do - {:ok, state} + {:ok, state} end defp data_signature_helper(name, fields) do non_indexed_types = Enum.map(fields, &Map.get(&1, "type")) - Enum.join([name, "(", Enum.join(non_indexed_types, ","), ")"]) + Enum.join([name, "(", Enum.join(non_indexed_types, ","), ")"]) end defp topic_types_helper(fields) do if length(fields) > 0 do - Enum.map(fields, fn field -> - "(#{field["type"]})" - end) + Enum.map(fields, fn field -> + "(#{field["type"]})" + end) else - [] - end + [] + end end defp init_events(abi) do @@ -600,48 +621,52 @@ defmodule ExW3 do names_and_signature_types_map = Enum.map(events, fn {name, v} -> - types = Enum.map(v["inputs"], &Map.get(&1, "type")) + types = Enum.map(v["inputs"], &Map.get(&1, "type")) signature = Enum.join([name, "(", Enum.join(types, ","), ")"]) - encoded_event_signature = "0x#{ExW3.encode_event(signature)}" + encoded_event_signature = "0x#{ExW3.encode_event(signature)}" - indexed_fields = Enum.filter(v["inputs"], fn input -> - input["indexed"] - end) + indexed_fields = + Enum.filter(v["inputs"], fn input -> + input["indexed"] + end) - indexed_names = Enum.map(indexed_fields, fn field -> - field["name"] - end) + indexed_names = + Enum.map(indexed_fields, fn field -> + field["name"] + end) - non_indexed_fields = Enum.filter(v["inputs"], fn input -> - !input["indexed"] - end) + non_indexed_fields = + Enum.filter(v["inputs"], fn input -> + !input["indexed"] + end) - non_indexed_names = Enum.map(non_indexed_fields, fn field -> - field["name"] - end) + non_indexed_names = + Enum.map(non_indexed_fields, fn field -> + field["name"] + end) - data_signature = data_signature_helper(name, non_indexed_fields) + data_signature = data_signature_helper(name, non_indexed_fields) - event_attributes = %{ - signature: data_signature, - non_indexed_names: non_indexed_names, - topic_types: topic_types_helper(indexed_fields), - topic_names: indexed_names - } + event_attributes = %{ + signature: data_signature, + non_indexed_names: non_indexed_names, + topic_types: topic_types_helper(indexed_fields), + topic_names: indexed_names + } {{encoded_event_signature, event_attributes}, {name, encoded_event_signature}} end) signature_types_map = - Enum.map(names_and_signature_types_map, fn {signature_types, _} -> - signature_types - end) + Enum.map(names_and_signature_types_map, fn {signature_types, _} -> + signature_types + end) names_map = - Enum.map(names_and_signature_types_map, fn {_, names} -> - names - end) + Enum.map(names_and_signature_types_map, fn {_, names} -> + names + end) [events: Enum.into(signature_types_map, %{}), event_names: Enum.into(names_map, %{})] end @@ -661,15 +686,18 @@ defmodule ExW3 do input_types = Enum.map(constructor["inputs"], fn x -> x["type"] end) types_signature = Enum.join(["(", Enum.join(input_types, ","), ")"]) - arg_count = Enum.count(arguments) - input_types_count = Enum.count(input_types) + arg_count = Enum.count(arguments) + input_types_count = Enum.count(input_types) + if input_types_count != arg_count do - raise "Number of provided arguments to constructor is incorrect. Was given #{arg_count} args, looking for #{input_types_count}." + raise "Number of provided arguments to constructor is incorrect. Was given #{ + arg_count + } args, looking for #{input_types_count}." end bin <> (ExW3.encode_data(types_signature, arguments) |> Base.encode16(case: :lower)) else - #IO.warn("Could not find a constructor") + # IO.warn("Could not find a constructor") bin end else @@ -706,6 +734,7 @@ defmodule ExW3 do def eth_send_helper(address, abi, method_name, args, options) do gas = ExW3.encode_option(options[:gas]) + Ethereumex.HttpClient.eth_send_transaction( Map.merge( %{ @@ -719,7 +748,7 @@ defmodule ExW3 do defp add_helper(contract_info) do if contract_info[:abi] do - contract_info ++ init_events(contract_info[:abi]) + contract_info ++ init_events(contract_info[:abi]) else raise "ABI not provided upon initialization" end @@ -729,7 +758,7 @@ defmodule ExW3 do defp check_option(nil, error_atom), do: {:error, error_atom} defp check_option([], error_atom), do: {:error, error_atom} - defp check_option([head | _tail], _atom) when head != nil, do: {:ok, head} + defp check_option([head | _tail], _atom) when head != nil, do: {:ok, head} defp check_option([_head | tail], atom), do: check_option(tail, atom) defp check_option(value, _atom), do: {:ok, value} @@ -737,7 +766,7 @@ defmodule ExW3 do def handle_cast({:at, {name, address}}, state) do contract_info = state[name] - {:noreply, Map.put(state, name, contract_info ++ [address: address])} + {:noreply, Map.put(state, name, contract_info ++ [address: address])} end def handle_cast({:register, {name, contract_info}}, state) do @@ -747,65 +776,70 @@ defmodule ExW3 do # Calls defp filter_topics_helper(event_signature, event_data, topic_types, topic_names) do - topics = - if is_map(event_data[:topics]) do - Enum.map(topic_names, fn name -> - event_data[:topics][String.to_atom(name)] - end) - else - event_data[:topics] - end - + if is_map(event_data[:topics]) do + Enum.map(topic_names, fn name -> + event_data[:topics][String.to_atom(name)] + end) + else + event_data[:topics] + end + if topics do - formatted_topics = - Enum.map(0..length(topics) - 1, fn i -> - topic = Enum.at(topics, i) - if topic do - if is_list(topic) do - topic_type = Enum.at(topic_types, i) - Enum.map(topic, fn t -> - "0x" <> (ExW3.encode_data(topic_type, [t]) |> Base.encode16(case: :lower)) - end) - else - topic_type = Enum.at(topic_types, i) - "0x" <> (ExW3.encode_data(topic_type, [topic]) |> Base.encode16(case: :lower)) - end - else - topic - end - end) - [event_signature] ++ formatted_topics + formatted_topics = + Enum.map(0..(length(topics) - 1), fn i -> + topic = Enum.at(topics, i) + + if topic do + if is_list(topic) do + topic_type = Enum.at(topic_types, i) + + Enum.map(topic, fn t -> + "0x" <> (ExW3.encode_data(topic_type, [t]) |> Base.encode16(case: :lower)) + end) + else + topic_type = Enum.at(topic_types, i) + "0x" <> (ExW3.encode_data(topic_type, [topic]) |> Base.encode16(case: :lower)) + end + else + topic + end + end) + + [event_signature] ++ formatted_topics else - [event_signature] + [event_signature] end end def from_block_helper(event_data) do if event_data[:fromBlock] do - new_from_block = - if Enum.member?(["latest", "earliest", "pending"], event_data[:fromBlock]) do - event_data[:fromBlock] - else - ExW3.encode_data("(uint256)", [event_data[:fromBlock]]) - end - Map.put(event_data, :fromBlock, new_from_block) + new_from_block = + if Enum.member?(["latest", "earliest", "pending"], event_data[:fromBlock]) do + event_data[:fromBlock] + else + ExW3.encode_data("(uint256)", [event_data[:fromBlock]]) + end + + Map.put(event_data, :fromBlock, new_from_block) else - event_data - end + event_data + end end defp param_helper(event_data, key) do if event_data[key] do - new_param = - if Enum.member?(["latest", "earliest", "pending"], event_data[key]) do - event_data[key] - else - "0x" <> (ExW3.encode_data("(uint256)", [event_data[key]]) |> Base.encode16(case: :lower)) - end - Map.put(event_data, key, new_param) + new_param = + if Enum.member?(["latest", "earliest", "pending"], event_data[key]) do + event_data[key] + else + "0x" <> + (ExW3.encode_data("(uint256)", [event_data[key]]) |> Base.encode16(case: :lower)) + end + + Map.put(event_data, key, new_param) else - event_data + event_data end end @@ -817,11 +851,10 @@ defmodule ExW3 do end def handle_call({:filter, {contract_name, event_name, other_pid, event_data}}, _from, state) do - contract_info = state[contract_name] - + unless Process.whereis(Listener) do - raise "EventListener process not alive. Call ExW3.EventListener.start_link before using ExW3.Contract.subscribe" + raise "EventListener process not alive. Call ExW3.EventListener.start_link before using ExW3.Contract.subscribe" end event_signature = contract_info[:event_names][event_name] @@ -829,68 +862,74 @@ defmodule ExW3 do topic_names = contract_info[:events][event_signature][:topic_names] topics = filter_topics_helper(event_signature, event_data, topic_types, topic_names) - - payload = Map.merge( - %{address: contract_info[:address], topics: topics}, - event_data_format_helper(event_data) - ) - + + payload = + Map.merge( + %{address: contract_info[:address], topics: topics}, + event_data_format_helper(event_data) + ) + filter_id = ExW3.new_filter(payload) event_attributes = contract_info[:events][contract_info[:event_names][event_name]] - + EventListener.filter(filter_id, event_attributes, other_pid) - + {:reply, filter_id, Map.put(state, contract_name, contract_info ++ [event_name, filter_id])} end def handle_call({:deploy, {name, args}}, _from, state) do - contract_info = state[name] - + with {:ok, _} <- check_option(args[:options][:from], :missing_sender), - {:ok,_} <- check_option(args[:options][:gas], :missing_gas), - {:ok, bin} <- check_option([state[:bin], args[:bin]], :missing_binary) - do + {:ok, _} <- check_option(args[:options][:gas], :missing_gas), + {:ok, bin} <- check_option([state[:bin], args[:bin]], :missing_binary) do {contract_addr, tx_hash} = deploy_helper(bin, contract_info[:abi], args) result = {:ok, contract_addr, tx_hash} - {:reply, result , state} - else - err -> {:reply, err, state} + {:reply, result, state} + else + err -> {:reply, err, state} end end - def handle_call({:address, name}, _from, state) do + def handle_call({:address, name}, _from, state) do {:reply, state[name][:address], state} end def handle_call({:call, {contract_name, method_name, args}}, _from, state) do contract_info = state[contract_name] - - with {:ok, address} <- check_option(contract_info[:address], :missing_address) - do - result = eth_call_helper(address, contract_info[:abi], Atom.to_string(method_name), args) - {:reply, result, state} - else - err -> {:reply, err, state} + + with {:ok, address} <- check_option(contract_info[:address], :missing_address) do + result = eth_call_helper(address, contract_info[:abi], Atom.to_string(method_name), args) + {:reply, result, state} + else + err -> {:reply, err, state} end end def handle_call({:send, {contract_name, method_name, args, options}}, _from, state) do contract_info = state[contract_name] + with {:ok, address} <- check_option(contract_info[:address], :missing_address), {:ok, _} <- check_option(options[:from], :missing_sender), - {:ok, _} <- check_option(options[:gas], :missing_gas) - do - result = eth_send_helper(address, contract_info[:abi], Atom.to_string(method_name), args, options) - {:reply, result, state} - else - err -> {:reply, err, state} + {:ok, _} <- check_option(options[:gas], :missing_gas) do + result = + eth_send_helper( + address, + contract_info[:abi], + Atom.to_string(method_name), + args, + options + ) + + {:reply, result, state} + else + err -> {:reply, err, state} end end def handle_call({:tx_receipt, {contract_name, tx_hash}}, _from, state) do contract_info = state[contract_name] - + receipt = ExW3.tx_receipt(tx_hash) events = contract_info[:events] @@ -902,27 +941,33 @@ defmodule ExW3 do event_attributes = Map.get(events, topic) if event_attributes do - non_indexed_fields = Enum.zip(event_attributes[:non_indexed_names], ExW3.decode_event(log["data"], event_attributes[:signature])) |> Enum.into(%{}) - - - if length(log["topics"]) > 1 do - [ _head | tail ] = log["topics"] - decoded_topics = Enum.map(0..length(tail) - 1, fn i -> - - topic_type = Enum.at(event_attributes[:topic_types], i) - topic_data = Enum.at(tail, i) - - {decoded} = ExW3.decode_data(topic_type, topic_data) - - decoded - end) - - indexed_fields = Enum.zip(event_attributes[:topic_names], decoded_topics) |> Enum.into(%{}) - Map.merge(indexed_fields, non_indexed_fields) - else - non_indexed_fields - end - + non_indexed_fields = + Enum.zip( + event_attributes[:non_indexed_names], + ExW3.decode_event(log["data"], event_attributes[:signature]) + ) + |> Enum.into(%{}) + + if length(log["topics"]) > 1 do + [_head | tail] = log["topics"] + + decoded_topics = + Enum.map(0..(length(tail) - 1), fn i -> + topic_type = Enum.at(event_attributes[:topic_types], i) + topic_data = Enum.at(tail, i) + + {decoded} = ExW3.decode_data(topic_type, topic_data) + + decoded + end) + + indexed_fields = + Enum.zip(event_attributes[:topic_names], decoded_topics) |> Enum.into(%{}) + + Map.merge(indexed_fields, non_indexed_fields) + else + non_indexed_fields + end else nil end @@ -931,5 +976,4 @@ defmodule ExW3 do {:reply, {:ok, {receipt, formatted_logs}}, state} end end - end diff --git a/mix.exs b/mix.exs index 17c4d93..4778495 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule ExW3.MixProject do def project do [ app: :exw3, - version: "0.1.6", + version: "0.2.0", elixir: "~> 1.7.2", build_embedded: Mix.env == :prod, start_permanent: Mix.env == :prod, diff --git a/test/exw3_test.exs b/test/exw3_test.exs index d4caf78..8081f9a 100644 --- a/test/exw3_test.exs +++ b/test/exw3_test.exs @@ -3,8 +3,8 @@ defmodule EXW3Test do doctest ExW3 setup_all do - ExW3.Contract.start_link - + ExW3.Contract.start_link() + %{ simple_storage_abi: ExW3.load_abi("test/examples/build/SimpleStorage.abi"), array_tester_abi: ExW3.load_abi("test/examples/build/ArrayTester.abi"), @@ -32,7 +32,7 @@ defmodule EXW3Test do end # Only works with ganache-cli - + # test "mines a block" do # block_number = ExW3.block_number() # ExW3.mine() @@ -52,21 +52,19 @@ defmodule EXW3Test do assert hash == "0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d" num_bytes = - hash |> - String.slice(2..-1) |> - byte_size + hash + |> String.slice(2..-1) + |> byte_size assert trunc(num_bytes / 2) == 32 end test "starts a Contract GenServer for simple storage contract", context do - - ExW3.Contract.register(:SimpleStorage, abi: context[:simple_storage_abi]) {:ok, address, _} = ExW3.Contract.deploy( - :SimpleStorage, + :SimpleStorage, bin: ExW3.load_bin("test/examples/build/SimpleStorage.bin"), args: [], options: %{ @@ -83,7 +81,10 @@ defmodule EXW3Test do assert data == 0 - ExW3.Contract.send(:SimpleStorage, :set, [1], %{from: Enum.at(context[:accounts], 0), gas: 50_000}) + ExW3.Contract.send(:SimpleStorage, :set, [1], %{ + from: Enum.at(context[:accounts], 0), + gas: 50_000 + }) {:ok, data} = ExW3.Contract.call(:SimpleStorage, :get) @@ -137,8 +138,8 @@ defmodule EXW3Test do {:ok, tx_hash} = ExW3.Contract.send(:EventTester, :simple, ["Hello, World!"], %{ - from: Enum.at(context[:accounts], 0), - gas: 30_000 + from: Enum.at(context[:accounts], 0), + gas: 30_000 }) {:ok, {receipt, logs}} = ExW3.Contract.tx_receipt(:EventTester, tx_hash) @@ -155,9 +156,9 @@ defmodule EXW3Test do {:ok, tx_hash2} = ExW3.Contract.send(:EventTester, :simpleIndex, ["Hello, World!"], %{ - from: Enum.at(context[:accounts], 0), - gas: 30_000 - }) + from: Enum.at(context[:accounts], 0), + gas: 30_000 + }) {:ok, {_receipt, logs}} = ExW3.Contract.tx_receipt(:EventTester, tx_hash2) @@ -179,7 +180,7 @@ defmodule EXW3Test do logs |> Enum.at(0) |> Map.get("data") - |> ExW3.bytes_to_string + |> ExW3.bytes_to_string() assert data == "Hello, World!" end @@ -200,7 +201,7 @@ defmodule EXW3Test do ExW3.Contract.at(:EventTester, address) {:ok, agent} = Agent.start_link(fn -> [] end) - + ExW3.EventListener.start_link() # Test non indexed events @@ -209,17 +210,18 @@ defmodule EXW3Test do {:ok, _tx_hash} = ExW3.Contract.send( - :EventTester, - :simple, - ["Hello, World!"], - %{from: Enum.at(context[:accounts], 0), gas: 30_000} + :EventTester, + :simple, + ["Hello, World!"], + %{from: Enum.at(context[:accounts], 0), gas: 30_000} ) - + receive do {:event, {_filter_id, data}} -> - Agent.update(agent, fn list -> [data | list] end) - after 3_000 -> - raise "Never received event" + Agent.update(agent, fn list -> [data | list] end) + after + 3_000 -> + raise "Never received event" end state = Agent.get(agent, fn list -> list end) @@ -241,17 +243,18 @@ defmodule EXW3Test do {:ok, _tx_hash} = ExW3.Contract.send( - :EventTester, - :simpleIndex, - ["Hello, World!"], - %{from: Enum.at(context[:accounts], 0), gas: 30_000} + :EventTester, + :simpleIndex, + ["Hello, World!"], + %{from: Enum.at(context[:accounts], 0), gas: 30_000} ) receive do {:event, {_filter_id, data}} -> - Agent.update(agent, fn list -> [data | list] end) - after 3_000 -> - raise "Never received event" + Agent.update(agent, fn list -> [data | list] end) + after + 3_000 -> + raise "Never received event" end state = Agent.get(agent, fn list -> list end) @@ -268,30 +271,32 @@ defmodule EXW3Test do {:ok, agent} = Agent.start_link(fn -> [] end) - indexed_filter_id = ExW3.Contract.filter( - :EventTester, - "SimpleIndex", - self(), - %{ - topics: [nil, ["Hello, World", "Hello, World!"]], - fromBlock: 1, - toBlock: "latest" - } - ) + indexed_filter_id = + ExW3.Contract.filter( + :EventTester, + "SimpleIndex", + self(), + %{ + topics: [nil, ["Hello, World", "Hello, World!"]], + fromBlock: 1, + toBlock: "latest" + } + ) {:ok, _tx_hash} = ExW3.Contract.send( - :EventTester, - :simpleIndex, - ["Hello, World!"], - %{from: Enum.at(context[:accounts], 0), gas: 30_000} + :EventTester, + :simpleIndex, + ["Hello, World!"], + %{from: Enum.at(context[:accounts], 0), gas: 30_000} ) receive do {:event, {_filter_id, data}} -> - Agent.update(agent, fn list -> [data | list] end) - after 3_000 -> - raise "Never received event" + Agent.update(agent, fn list -> [data | list] end) + after + 3_000 -> + raise "Never received event" end state = Agent.get(agent, fn list -> list end) @@ -302,7 +307,7 @@ defmodule EXW3Test do assert Map.get(log_data, "num") == 46 assert ExW3.bytes_to_string(Map.get(log_data, "data")) == "Hello, World!" assert Map.get(log_data, "otherNum") == 42 - ExW3.uninstall_filter(indexed_filter_id) + ExW3.uninstall_filter(indexed_filter_id) end test "starts a EventTester", context do @@ -320,34 +325,36 @@ defmodule EXW3Test do ExW3.Contract.at(:EventTester, address) - # Test Indexing Indexed Events with Map params + # Test Indexing Indexed Events with Map params - ExW3.EventListener.start_link() + ExW3.EventListener.start_link() {:ok, agent} = Agent.start_link(fn -> [] end) - indexed_filter_id = ExW3.Contract.filter( - :EventTester, - "SimpleIndex", - self(), - %{ - topics: %{num: 46, data: "Hello, World!"}, - } - ) + indexed_filter_id = + ExW3.Contract.filter( + :EventTester, + "SimpleIndex", + self(), + %{ + topics: %{num: 46, data: "Hello, World!"} + } + ) {:ok, _tx_hash} = ExW3.Contract.send( - :EventTester, - :simpleIndex, - ["Hello, World!"], - %{from: Enum.at(context[:accounts], 0), gas: 30_000} + :EventTester, + :simpleIndex, + ["Hello, World!"], + %{from: Enum.at(context[:accounts], 0), gas: 30_000} ) receive do {:event, {_filter_id, data}} -> - Agent.update(agent, fn list -> [data | list] end) - after 3_000 -> - raise "Never received event" + Agent.update(agent, fn list -> [data | list] end) + after + 3_000 -> + raise "Never received event" end state = Agent.get(agent, fn list -> list end) @@ -359,7 +366,6 @@ defmodule EXW3Test do assert ExW3.bytes_to_string(Map.get(log_data, "data")) == "Hello, World!" assert Map.get(log_data, "otherNum") == 42 ExW3.uninstall_filter(indexed_filter_id) - end test "starts a Contract GenServer for Complex contract", context do @@ -405,8 +411,8 @@ defmodule EXW3Test do assert address == ExW3.Contract.address(:AddressTester) formatted_address = - Enum.at(context[:accounts], 0) - |> ExW3.format_address + Enum.at(context[:accounts], 0) + |> ExW3.format_address() {:ok, same_address} = ExW3.Contract.call(:AddressTester, :get, [formatted_address]) @@ -414,21 +420,33 @@ defmodule EXW3Test do end test "returns checksum for all caps address" do - assert ExW3.to_checksum_address(String.downcase("0x52908400098527886E0F7030069857D2E4169EE7")) == "0x52908400098527886E0F7030069857D2E4169EE7" - assert ExW3.to_checksum_address(String.downcase("0x8617E340B3D01FA5F11F306F4090FD50E238070D")) == "0x8617E340B3D01FA5F11F306F4090FD50E238070D" - end + assert ExW3.to_checksum_address(String.downcase("0x52908400098527886E0F7030069857D2E4169EE7")) == + "0x52908400098527886E0F7030069857D2E4169EE7" + assert ExW3.to_checksum_address(String.downcase("0x8617E340B3D01FA5F11F306F4090FD50E238070D")) == + "0x8617E340B3D01FA5F11F306F4090FD50E238070D" + end test "returns checksumfor all lowercase address" do - assert ExW3.to_checksum_address(String.downcase("0xde709f2102306220921060314715629080e2fb77")) == "0xde709f2102306220921060314715629080e2fb77" - assert ExW3.to_checksum_address(String.downcase("0x27b1fdb04752bbc536007a920d24acb045561c26")) == "0x27b1fdb04752bbc536007a920d24acb045561c26" + assert ExW3.to_checksum_address(String.downcase("0xde709f2102306220921060314715629080e2fb77")) == + "0xde709f2102306220921060314715629080e2fb77" + + assert ExW3.to_checksum_address(String.downcase("0x27b1fdb04752bbc536007a920d24acb045561c26")) == + "0x27b1fdb04752bbc536007a920d24acb045561c26" end test "returns checksum for normal addresses" do - assert ExW3.to_checksum_address(String.downcase("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")) == "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed" - assert ExW3.to_checksum_address(String.downcase("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359")) == "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359" - assert ExW3.to_checksum_address(String.downcase("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB")) == "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB" - assert ExW3.to_checksum_address(String.downcase("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb")) == "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb" + assert ExW3.to_checksum_address(String.downcase("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")) == + "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed" + + assert ExW3.to_checksum_address(String.downcase("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359")) == + "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359" + + assert ExW3.to_checksum_address(String.downcase("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB")) == + "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB" + + assert ExW3.to_checksum_address(String.downcase("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb")) == + "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb" end test "returns valid check for is_valid_checksum_address()" do @@ -447,18 +465,17 @@ defmodule EXW3Test do end test "returns proper error messages at contract deployment", context do - ExW3.Contract.register(:SimpleStorage, abi: context[:simple_storage_abi]) assert {:error, :missing_gas} == - ExW3.Contract.deploy( - :SimpleStorage, - bin: ExW3.load_bin("test/examples/build/SimpleStorage.bin"), - args: [], - options: %{ - from: Enum.at(context[:accounts], 0) - } - ) + ExW3.Contract.deploy( + :SimpleStorage, + bin: ExW3.load_bin("test/examples/build/SimpleStorage.bin"), + args: [], + options: %{ + from: Enum.at(context[:accounts], 0) + } + ) assert {:error, :missing_sender} == ExW3.Contract.deploy( @@ -466,7 +483,7 @@ defmodule EXW3Test do bin: ExW3.load_bin("test/examples/build/SimpleStorage.bin"), args: [], options: %{ - gas: 300_000, + gas: 300_000 } ) @@ -479,7 +496,6 @@ defmodule EXW3Test do from: Enum.at(context[:accounts], 0) } ) - end test "return proper error messages at send and call", context do @@ -497,17 +513,23 @@ defmodule EXW3Test do ) assert {:error, :missing_address} == ExW3.Contract.call(:SimpleStorage, :get) - assert {:error, :missing_address} == ExW3.Contract.send(:SimpleStorage, :set, [1], %{from: Enum.at(context[:accounts], 0), gas: 50_000}) + + assert {:error, :missing_address} == + ExW3.Contract.send(:SimpleStorage, :set, [1], %{ + from: Enum.at(context[:accounts], 0), + gas: 50_000 + }) ExW3.Contract.at(:SimpleStorage, address) - assert {:error, :missing_sender} == ExW3.Contract.send(:SimpleStorage, :set, [1], %{gas: 50_000}) - assert {:error, :missing_gas} == ExW3.Contract.send(:SimpleStorage, :set, [1], %{from: Enum.at(context[:accounts], 0)}) + assert {:error, :missing_sender} == + ExW3.Contract.send(:SimpleStorage, :set, [1], %{gas: 50_000}) + assert {:error, :missing_gas} == + ExW3.Contract.send(:SimpleStorage, :set, [1], %{from: Enum.at(context[:accounts], 0)}) end test "unit conversion to_wei" do - assert ExW3.to_wei(1, :wei) == 1 assert ExW3.to_wei(1, :kwei) == 1_000 assert ExW3.to_wei(1, :Kwei) == 1_000 @@ -538,7 +560,7 @@ defmodule EXW3Test do try do ExW3.to_wei(1, :wei1) catch - _ -> Agent.update(agent, fn _ -> true end) + _ -> Agent.update(agent, fn _ -> true end) end assert Agent.get(agent, fn state -> state end) @@ -560,13 +582,11 @@ defmodule EXW3Test do end test "send and call sync with SimpleStorage", context do - - ExW3.Contract.register(:SimpleStorage, abi: context[:simple_storage_abi]) {:ok, address, _} = ExW3.Contract.deploy( - :SimpleStorage, + :SimpleStorage, bin: ExW3.load_bin("test/examples/build/SimpleStorage.bin"), args: [], options: %{ @@ -585,7 +605,11 @@ defmodule EXW3Test do assert data == 0 - t = ExW3.Contract.send_async(:SimpleStorage, :set, [1], %{from: Enum.at(context[:accounts], 0), gas: 50_000}) + t = + ExW3.Contract.send_async(:SimpleStorage, :set, [1], %{ + from: Enum.at(context[:accounts], 0), + gas: 50_000 + }) Task.await(t) @@ -595,6 +619,4 @@ defmodule EXW3Test do assert data == 1 end - - end