4
0
Fork 0

Updated version number, and ran mix format

indexed
hswick 6 years ago
parent 18b4d34bb6
commit c22899fb09
  1. 2
      README.md
  2. 484
      lib/exw3.ex
  3. 2
      mix.exs
  4. 224
      test/exw3_test.exs

@ -6,7 +6,7 @@
```elixir
def deps do
[{:exw3, "~> 0.1.6"}]
[{:exw3, "~> 0.2.0"}]
end
```
## Overview

@ -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

@ -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,

@ -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

Loading…
Cancel
Save