|
|
@ -1,5 +1,7 @@ |
|
|
|
defmodule ExW3 do |
|
|
|
defmodule ExW3 do |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec bytes_to_string(binary()) :: binary() |
|
|
|
|
|
|
|
@doc "converts Ethereum style bytes to string" |
|
|
|
def bytes_to_string bytes do |
|
|
|
def bytes_to_string bytes do |
|
|
|
bytes |
|
|
|
bytes |
|
|
|
|> Base.encode16(case: :lower) |
|
|
|
|> Base.encode16(case: :lower) |
|
|
@ -7,6 +9,8 @@ defmodule ExW3 do |
|
|
|
|> Base.decode16!(case: :lower) |
|
|
|
|> Base.decode16!(case: :lower) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec accounts() :: list() |
|
|
|
|
|
|
|
@doc "returns all available accounts" |
|
|
|
def accounts do |
|
|
|
def accounts do |
|
|
|
case Ethereumex.HttpClient.eth_accounts() do |
|
|
|
case Ethereumex.HttpClient.eth_accounts() do |
|
|
|
{:ok, accounts} -> accounts |
|
|
|
{:ok, accounts} -> accounts |
|
|
@ -14,13 +18,16 @@ defmodule ExW3 do |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
# 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 |
|
|
|
def to_decimal(hex_string) do |
|
|
|
hex_string |
|
|
|
hex_string |
|
|
|
|> String.slice(2..-1) |
|
|
|
|> String.slice(2..-1) |
|
|
|
|> String.to_integer(16) |
|
|
|
|> String.to_integer(16) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec block_number() :: integer() |
|
|
|
|
|
|
|
@doc "Returns the current block number" |
|
|
|
def block_number do |
|
|
|
def block_number do |
|
|
|
case Ethereumex.HttpClient.eth_block_number() do |
|
|
|
case Ethereumex.HttpClient.eth_block_number() do |
|
|
|
{:ok, block_number} -> |
|
|
|
{:ok, block_number} -> |
|
|
@ -31,6 +38,8 @@ defmodule ExW3 do |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec balance(binary()) :: integer() |
|
|
|
|
|
|
|
@doc "Returns current balance of account" |
|
|
|
def balance(account) do |
|
|
|
def balance(account) do |
|
|
|
case Ethereumex.HttpClient.eth_get_balance(account) do |
|
|
|
case Ethereumex.HttpClient.eth_get_balance(account) do |
|
|
|
{:ok, balance} -> |
|
|
|
{:ok, balance} -> |
|
|
@ -41,6 +50,7 @@ defmodule ExW3 do |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec keys_to_decimal(%{}, []) :: %{} |
|
|
|
def keys_to_decimal(map, keys) do |
|
|
|
def keys_to_decimal(map, keys) do |
|
|
|
Map.new( |
|
|
|
Map.new( |
|
|
|
Enum.map(keys, fn k -> |
|
|
|
Enum.map(keys, fn k -> |
|
|
@ -49,6 +59,8 @@ defmodule ExW3 do |
|
|
|
) |
|
|
|
) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec tx_receipt(binary()) :: %{} |
|
|
|
|
|
|
|
@doc "Returns transaction receipt for specified transaction hash(id)" |
|
|
|
def tx_receipt(tx_hash) do |
|
|
|
def tx_receipt(tx_hash) do |
|
|
|
case Ethereumex.HttpClient.eth_get_transaction_receipt(tx_hash) do |
|
|
|
case Ethereumex.HttpClient.eth_get_transaction_receipt(tx_hash) do |
|
|
|
{:ok, receipt} -> |
|
|
|
{:ok, receipt} -> |
|
|
@ -59,6 +71,8 @@ defmodule ExW3 do |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec block(integer()) :: any() |
|
|
|
|
|
|
|
@doc "Returns block data for specified block number" |
|
|
|
def block(block_number) do |
|
|
|
def block(block_number) do |
|
|
|
case Ethereumex.HttpClient.eth_get_block_by_number(block_number, true) do |
|
|
|
case Ethereumex.HttpClient.eth_get_block_by_number(block_number, true) do |
|
|
|
{:ok, block} -> block |
|
|
|
{:ok, block} -> block |
|
|
@ -66,16 +80,22 @@ defmodule ExW3 do |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec mine(integer()) :: any() |
|
|
|
|
|
|
|
@doc "Mines number of blocks specified. Default is 1" |
|
|
|
def mine(num_blocks \\ 1) do |
|
|
|
def mine(num_blocks \\ 1) do |
|
|
|
for _ <- 0..(num_blocks - 1) do |
|
|
|
for _ <- 0..(num_blocks - 1) do |
|
|
|
Ethereumex.HttpClient.request("evm_mine", [], []) |
|
|
|
Ethereumex.HttpClient.request("evm_mine", [], []) |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec encode_event(binary()) :: binary() |
|
|
|
|
|
|
|
@doc "Encodes event based on signature" |
|
|
|
def encode_event(signature) do |
|
|
|
def encode_event(signature) do |
|
|
|
ExthCrypto.Hash.Keccak.kec(signature) |> Base.encode16(case: :lower) |
|
|
|
ExthCrypto.Hash.Keccak.kec(signature) |> Base.encode16(case: :lower) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec decode_event(binary(), binary()) :: any() |
|
|
|
|
|
|
|
@doc "Decodes event based on given data and provided signature" |
|
|
|
def decode_event(data, signature) do |
|
|
|
def decode_event(data, signature) do |
|
|
|
formatted_data = |
|
|
|
formatted_data = |
|
|
|
data |
|
|
|
data |
|
|
@ -87,10 +107,14 @@ defmodule ExW3 do |
|
|
|
ABI.TypeDecoder.decode(formatted_data, fs) |
|
|
|
ABI.TypeDecoder.decode(formatted_data, fs) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec reformat_abi([]) :: %{} |
|
|
|
|
|
|
|
@doc "Reformats abi from list to map with event and function names as keys" |
|
|
|
def reformat_abi(abi) do |
|
|
|
def reformat_abi(abi) do |
|
|
|
Map.new(Enum.map(abi, fn x -> {x["name"], x} end)) |
|
|
|
Map.new(Enum.map(abi, fn x -> {x["name"], x} end)) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec load_abi(binary()) :: [] |
|
|
|
|
|
|
|
@doc "Loads the abi at the file path and reformats it to a map" |
|
|
|
def load_abi(file_path) do |
|
|
|
def load_abi(file_path) do |
|
|
|
file = File.read(Path.join(System.cwd(), file_path)) |
|
|
|
file = File.read(Path.join(System.cwd(), file_path)) |
|
|
|
|
|
|
|
|
|
|
@ -100,6 +124,8 @@ defmodule ExW3 do |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec load_bin(binary()) :: binary() |
|
|
|
|
|
|
|
@doc "Loads the bin ar the file path" |
|
|
|
def load_bin(file_path) do |
|
|
|
def load_bin(file_path) do |
|
|
|
file = File.read(Path.join(System.cwd(), file_path)) |
|
|
|
file = File.read(Path.join(System.cwd(), file_path)) |
|
|
|
|
|
|
|
|
|
|
@ -109,6 +135,8 @@ defmodule ExW3 do |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec decode_output(%{}, binary(), binary()) :: [] |
|
|
|
|
|
|
|
@doc "Decodes output based on specified functions return signature" |
|
|
|
def decode_output(abi, name, output) do |
|
|
|
def decode_output(abi, name, output) do |
|
|
|
{:ok, trim_output} = |
|
|
|
{:ok, trim_output} = |
|
|
|
String.slice(output, 2..String.length(output)) |> Base.decode16(case: :lower) |
|
|
|
String.slice(output, 2..String.length(output)) |> Base.decode16(case: :lower) |
|
|
@ -124,12 +152,16 @@ defmodule ExW3 do |
|
|
|
outputs |
|
|
|
outputs |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec types_signature(%{}, binary()) :: binary() |
|
|
|
|
|
|
|
@doc "Returns the type signature of a given function" |
|
|
|
def types_signature(abi, name) do |
|
|
|
def types_signature(abi, name) do |
|
|
|
input_types = Enum.map(abi[name]["inputs"], fn x -> x["type"] end) |
|
|
|
input_types = Enum.map(abi[name]["inputs"], fn x -> x["type"] end) |
|
|
|
types_signature = Enum.join(["(", Enum.join(input_types, ","), ")"]) |
|
|
|
types_signature = Enum.join(["(", Enum.join(input_types, ","), ")"]) |
|
|
|
types_signature |
|
|
|
types_signature |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@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 |
|
|
|
def method_signature(abi, name) do |
|
|
|
if abi[name] do |
|
|
|
if abi[name] do |
|
|
|
input_signature = "#{name}#{types_signature(abi, name)}" |> ExthCrypto.Hash.Keccak.kec() |
|
|
|
input_signature = "#{name}#{types_signature(abi, name)}" |> ExthCrypto.Hash.Keccak.kec() |
|
|
@ -142,6 +174,8 @@ defmodule ExW3 do |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec encode_data(binary(), []) :: binary() |
|
|
|
|
|
|
|
@doc "Encodes data into Ethereum hex string based on types signature" |
|
|
|
def encode_data(types_signature, data) do |
|
|
|
def encode_data(types_signature, data) do |
|
|
|
ABI.TypeEncoder.encode_raw( |
|
|
|
ABI.TypeEncoder.encode_raw( |
|
|
|
[List.to_tuple(data)], |
|
|
|
[List.to_tuple(data)], |
|
|
@ -149,11 +183,15 @@ defmodule ExW3 do |
|
|
|
) |
|
|
|
) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@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 |
|
|
|
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) |
|
|
|
encoded_method_call |> Base.encode16(case: :lower) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec encode_input(%{}, binary(), []) :: binary() |
|
|
|
|
|
|
|
@doc "Encodes input from a method call based on function signature" |
|
|
|
def encode_input(abi, name, input) do |
|
|
|
def encode_input(abi, name, input) do |
|
|
|
if abi[name]["inputs"] do |
|
|
|
if abi[name]["inputs"] do |
|
|
|
input_types = Enum.map(abi[name]["inputs"], fn x -> x["type"] end) |
|
|
|
input_types = Enum.map(abi[name]["inputs"], fn x -> x["type"] end) |
|
|
@ -181,30 +219,44 @@ defmodule ExW3 do |
|
|
|
|
|
|
|
|
|
|
|
# Client |
|
|
|
# Client |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec start_link(atom(), list()) :: {:ok, pid()} |
|
|
|
|
|
|
|
@doc "Begins a Contract GenServer with specified name and state" |
|
|
|
def start_link(name, state) do |
|
|
|
def start_link(name, state) do |
|
|
|
GenServer.start_link(__MODULE__, state, name: name) |
|
|
|
GenServer.start_link(__MODULE__, state, name: name) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec deploy(pid(), []) :: {:ok, []} |
|
|
|
|
|
|
|
@doc "Deploys contracts with given arguments" |
|
|
|
def deploy(pid, args) do |
|
|
|
def deploy(pid, args) do |
|
|
|
GenServer.call(pid, {:deploy, args}) |
|
|
|
GenServer.call(pid, {:deploy, args}) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec at(pid(), binary()) :: :ok |
|
|
|
|
|
|
|
@doc "Sets the current Contract GenServer's address to given address" |
|
|
|
def at(pid, address) do |
|
|
|
def at(pid, address) do |
|
|
|
GenServer.cast(pid, {:at, address}) |
|
|
|
GenServer.cast(pid, {:at, address}) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec address(pid()) :: {:ok, binary()} |
|
|
|
|
|
|
|
@doc "Returns the current Contract GenServer's address" |
|
|
|
def address(pid) do |
|
|
|
def address(pid) do |
|
|
|
GenServer.call(pid, :address) |
|
|
|
GenServer.call(pid, :address) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec call(pid(), keyword(), []) :: {:ok, any()} |
|
|
|
|
|
|
|
@doc "Use a Contract's method with an eth_call" |
|
|
|
def call(pid, method_name, args \\ []) do |
|
|
|
def call(pid, method_name, args \\ []) do |
|
|
|
GenServer.call(pid, {:call, {method_name, args}}) |
|
|
|
GenServer.call(pid, {:call, {method_name, args}}) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec send(pid(), keyword(), [], %{}) :: {:ok, binary()} |
|
|
|
|
|
|
|
@doc "Use a Contract's method with an eth_sendTransaction" |
|
|
|
def send(pid, method_name, args, options) do |
|
|
|
def send(pid, method_name, args, options) do |
|
|
|
GenServer.call(pid, {:send, {method_name, args, options}}) |
|
|
|
GenServer.call(pid, {:send, {method_name, args, options}}) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@spec tx_receipt(pid(), binary()) :: %{} |
|
|
|
|
|
|
|
@doc "Returns a formatted transaction receipt for the given transaction hash(id)" |
|
|
|
def tx_receipt(pid, tx_hash) do |
|
|
|
def tx_receipt(pid, tx_hash) do |
|
|
|
GenServer.call(pid, {:tx_receipt, tx_hash}) |
|
|
|
GenServer.call(pid, {:tx_receipt, tx_hash}) |
|
|
|
end |
|
|
|
end |
|
|
|