From 6672c4635ae806d94a120de64a8fe2a10144b37e Mon Sep 17 00:00:00 2001 From: hswick Date: Fri, 4 May 2018 23:38:40 -0500 Subject: [PATCH] Reformatted code --- lib/exw3.ex | 232 ++++++++++++++++++++++++--------------------- mix.exs | 2 +- test/exw3_test.exs | 105 ++++++++++---------- 3 files changed, 177 insertions(+), 162 deletions(-) diff --git a/lib/exw3.ex b/lib/exw3.ex index a9b6da2..07e6586 100644 --- a/lib/exw3.ex +++ b/lib/exw3.ex @@ -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 <> = 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 <> = 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 \ No newline at end of file +end diff --git a/mix.exs b/mix.exs index 36c62b5..43aa772 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule ExW3.MixProject do def project do [app: :exw3, - version: "0.1.0", + version: "0.1.1", elixir: "~> 1.6.4", build_embedded: Mix.env == :prod, start_permanent: Mix.env == :prod, diff --git a/test/exw3_test.exs b/test/exw3_test.exs index ea95b15..ea0badc 100644 --- a/test/exw3_test.exs +++ b/test/exw3_test.exs @@ -8,20 +8,20 @@ defmodule EXW3Test do array_tester_abi: ExW3.load_abi("test/examples/build/ArrayTester.abi"), event_tester_abi: ExW3.load_abi("test/examples/build/EventTester.abi"), complex_abi: ExW3.load_abi("test/examples/build/Complex.abi"), - accounts: ExW3.accounts + accounts: ExW3.accounts() } end test "gets accounts" do - assert ExW3.accounts |> is_list + assert ExW3.accounts() |> is_list end test "gets balance", context do - assert ExW3.balance(Enum.at context[:accounts], 0 ) |> is_integer + assert ExW3.balance(Enum.at(context[:accounts], 0)) |> is_integer end test "gets block number" do - assert ExW3.block_number |> is_integer + assert ExW3.block_number() |> is_integer end test "loads abi", context do @@ -29,29 +29,29 @@ defmodule EXW3Test do end test "mines a block" do - block_number = ExW3.block_number - ExW3.mine - assert ExW3.block_number == block_number + 1 + block_number = ExW3.block_number() + ExW3.mine() + assert ExW3.block_number() == block_number + 1 end test "mines multiple blocks" do - block_number = ExW3.block_number - ExW3.mine 5 - assert ExW3.block_number == block_number + 5 + block_number = ExW3.block_number() + ExW3.mine(5) + assert ExW3.block_number() == block_number + 5 end test "starts a Contract GenServer for simple storage contract", context do - ExW3.Contract.start_link(SimpleStorage, abi: context[:simple_storage_abi]) - {:ok, address} = ExW3.Contract.deploy( - SimpleStorage, - bin: ExW3.load_bin("test/examples/build/SimpleStorage.bin"), - options: %{ - gas: 300000, - from: Enum.at(context[:accounts], 0) - } - ) + {:ok, address} = + ExW3.Contract.deploy( + SimpleStorage, + bin: ExW3.load_bin("test/examples/build/SimpleStorage.bin"), + options: %{ + gas: 300_000, + from: Enum.at(context[:accounts], 0) + } + ) ExW3.Contract.at(SimpleStorage, address) @@ -66,20 +66,20 @@ defmodule EXW3Test do {:ok, data} = ExW3.Contract.call(SimpleStorage, :get) assert data == 1 - end test "starts a Contract GenServer for array tester contract", context do ExW3.Contract.start_link(ArrayTester, abi: context[:array_tester_abi]) - {:ok, address} = ExW3.Contract.deploy( - ArrayTester, - bin: ExW3.load_bin("test/examples/build/ArrayTester.bin"), - options: %{ - gas: 300000, - from: Enum.at(context[:accounts], 0) - } - ) + {:ok, address} = + ExW3.Contract.deploy( + ArrayTester, + bin: ExW3.load_bin("test/examples/build/ArrayTester.bin"), + options: %{ + gas: 300_000, + from: Enum.at(context[:accounts], 0) + } + ) ExW3.Contract.at(ArrayTester, address) @@ -99,20 +99,24 @@ defmodule EXW3Test do test "starts a Contract GenServer for event tester contract", context do ExW3.Contract.start_link(EventTester, abi: context[:event_tester_abi]) - {:ok, address} = ExW3.Contract.deploy( - EventTester, - bin: ExW3.load_bin("test/examples/build/EventTester.bin"), - options: %{ - gas: 300000, - from: Enum.at(context[:accounts], 0) - } - ) + {:ok, address} = + ExW3.Contract.deploy( + EventTester, + bin: ExW3.load_bin("test/examples/build/EventTester.bin"), + options: %{ + gas: 300_000, + from: Enum.at(context[:accounts], 0) + } + ) ExW3.Contract.at(EventTester, address) assert address == ExW3.Contract.address(EventTester) - {:ok, tx_hash} = ExW3.Contract.send(EventTester, :simple, ["Hello, World!"], %{from: Enum.at(context[:accounts], 0)}) + {:ok, tx_hash} = + ExW3.Contract.send(EventTester, :simple, ["Hello, World!"], %{ + from: Enum.at(context[:accounts], 0) + }) {:ok, {receipt, logs}} = ExW3.Contract.tx_receipt(EventTester, tx_hash) @@ -124,23 +128,22 @@ defmodule EXW3Test do |> Map.get("data") |> ExW3.bytes_to_string() - assert data == "Hello, World!" - + assert data == "Hello, World!" end test "starts a Contract GenServer for Complex contract", context do - ExW3.Contract.start_link(Complex, abi: context[:complex_abi]) - {:ok, address} = ExW3.Contract.deploy( - Complex, - bin: ExW3.load_bin("test/examples/build/Complex.bin"), - args: [42, "Hello, world!"], - options: %{ - from: Enum.at(context[:accounts], 0), - gas: 300000 - } - ) + {:ok, address} = + ExW3.Contract.deploy( + Complex, + bin: ExW3.load_bin("test/examples/build/Complex.bin"), + args: [42, "Hello, world!"], + options: %{ + from: Enum.at(context[:accounts], 0), + gas: 300_000 + } + ) ExW3.Contract.at(Complex, address) @@ -151,7 +154,5 @@ defmodule EXW3Test do assert foo == 42 assert ExW3.bytes_to_string(foobar) == "Hello, world!" - end - -end \ No newline at end of file +end