4
0
Fork 0

Added bytes to string method

contracts
hswick 7 years ago
parent df0bbd35d5
commit 6cf9295896
  1. 170
      lib/exw3.ex
  2. 4
      mix.exs
  3. 1
      mix.lock
  4. 31
      test/exw3_test.exs

@ -1,78 +1,96 @@
defmodule ExW3 do defmodule ExW3 do
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
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))
case file do case file do
{:ok, abi} -> reformat_abi Poison.Parser.parse! abi {:ok, abi} -> reformat_abi(Poison.Parser.parse!(abi))
err -> err err -> err
end end
end end
def decode_output abi, name, output do def decode_output(abi, name, output) do
{:ok, trim_output} = String.slice(output, 2..String.length(output)) |> Base.decode16(case: :lower) {:ok, trim_output} =
output_types = Enum.map abi[name]["outputs"], fn x -> x["type"] end String.slice(output, 2..String.length(output)) |> Base.decode16(case: :lower)
output_signature = Enum.join [name, "(", Enum.join(output_types, ")"), ")"]
output_types = Enum.map(abi[name]["outputs"], fn x -> x["type"] end)
output_signature = Enum.join([name, "(", Enum.join(output_types, ")"), ")"])
ABI.decode(output_signature, trim_output) ABI.decode(output_signature, trim_output)
end end
def encode_inputs abi, name, inputs do def encode_input(abi, name, input) do
input_types = Enum.map abi[name]["inputs"], fn x -> x["type"] end if abi[name]["inputs"] do
input_signature = Enum.join [name, "(", Enum.join(input_types, ","), ")"] input_types = Enum.map(abi[name]["inputs"], fn x -> x["type"] end)
ABI.encode(input_signature, inputs) |> Base.encode16(case: :lower) input_signature = Enum.join([name, "(", Enum.join(input_types, ","), ")"])
ABI.encode(input_signature, input) |> Base.encode16(case: :lower)
else
raise "#{name} method not found with the given abi"
end
end
def bytes_to_string bytes do
bytes
|> Base.encode16(case: :lower)
|> String.replace_trailing("0", "")
|> Hexate.decode
end end
def accounts do def accounts do
case Ethereumex.HttpClient.eth_accounts do case Ethereumex.HttpClient.eth_accounts() do
{:ok, accounts} -> accounts {:ok, accounts} -> accounts
err -> err err -> err
end end
end end
#Converts ethereum hex string to decimal number # 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
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} ->
block_number |> to_decimal block_number |> to_decimal
err -> err
err ->
err
end end
end end
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} ->
balance |> to_decimal balance |> to_decimal
err -> err
err ->
err
end end
end end
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 ->
{ k, Map.get(map, k) |> to_decimal } {k, Map.get(map, k) |> to_decimal}
end end)
) )
end end
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} ->
receipt Map.merge receipt, keys_to_decimal(receipt, ["blockNumber", "cumulativeGasUsed", "gasUsed"])
#Map.merge receipt, keys_to_decimal(receipt, ["blockNumber", "cumulativeGasUsed", "gasUsed"])
err -> err err ->
err
end end
end end
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
err -> err err -> err
@ -91,9 +109,9 @@ defmodule ExW3 do
defmodule Contract do defmodule Contract do
use Agent use Agent
def at(abi, address) do def at(abi, address) do
{ :ok, pid } = Agent.start_link(fn -> %{abi: abi, address: address} end) {:ok, pid} = Agent.start_link(fn -> %{abi: abi, address: address} end)
pid pid
end end
@ -110,40 +128,55 @@ defmodule ExW3 do
def deploy(bin_filename, options) do def deploy(bin_filename, options) do
{:ok, bin} = File.read(Path.join(System.cwd(), bin_filename)) {:ok, bin} = File.read(Path.join(System.cwd(), bin_filename))
tx = %{ tx = %{
from: options[:from], from: options[:from],
data: bin, data: bin,
gas: options[:gas] gas: options[:gas]
} }
{:ok, tx_receipt_id} = Ethereumex.HttpClient.eth_send_transaction(tx) {:ok, tx_receipt_id} = Ethereumex.HttpClient.eth_send_transaction(tx)
{:ok, tx_receipt} = Ethereumex.HttpClient.eth_get_transaction_receipt(tx_receipt_id) {:ok, tx_receipt} = Ethereumex.HttpClient.eth_get_transaction_receipt(tx_receipt_id)
tx_receipt["contractAddress"] tx_receipt["contractAddress"]
end end
def method(contract_agent, name, args \\ [], options \\ %{}) do def method(contract_agent, method_name, args \\ [], options \\ %{}) do
if get(contract_agent, :abi)[name]["constant"] do method_name =
data = ExW3.encode_inputs(get(contract_agent, :abi), name, args) case method_name |> is_atom do
{:ok, output } = Ethereumex.HttpClient.eth_call(%{ true -> Inflex.camelize(method_name, :lower)
to: get(contract_agent, :address), false -> method_name
data: data end
})
[ :ok ] ++ ExW3.decode_output(get(contract_agent, :abi), name, output) |> List.to_tuple input = ExW3.encode_input(get(contract_agent, :abi), method_name, args)
if get(contract_agent, :abi)[method_name]["constant"] do
{:ok, output} =
Ethereumex.HttpClient.eth_call(%{
to: get(contract_agent, :address),
data: input
})
([:ok] ++ ExW3.decode_output(get(contract_agent, :abi), method_name, output)) |> List.to_tuple()
else else
Ethereumex.HttpClient.eth_send_transaction(Map.merge(%{ Ethereumex.HttpClient.eth_send_transaction(
to: get(contract_agent, :address), Map.merge(
data: ExW3.encode_inputs(get(contract_agent, :abi), name, args) %{
}, options)) to: get(contract_agent, :address),
data: input
},
options
)
)
end end
end end
end end
defmodule EventPublisher do defmodule EventPublisher do
use GenServer use GenServer
def start_link do def start_link do
GenServer.start_link(__MODULE__, %{ block_number: ExW3.block_number}) GenServer.start_link(__MODULE__, %{block_number: ExW3.block_number()})
end end
def init(state) do def init(state) do
@ -157,16 +190,15 @@ defmodule ExW3 do
end end
def handle_info(:block, state) do def handle_info(:block, state) do
block_number = ExW3.block_number block_number = ExW3.block_number()
block = ExW3.block block_number block = ExW3.block(block_number)
tx_receipts = Enum.map block["transactions"], fn tx -> ExW3.tx_receipt(tx["hash"]) end tx_receipts = Enum.map(block["transactions"], fn tx -> ExW3.tx_receipt(tx["hash"]) end)
for logs <- Enum.map(tx_receipts, fn receipt -> receipt["logs"] end) do for logs <- Enum.map(tx_receipts, fn receipt -> receipt["logs"] end) do
for log <- logs do for log <- logs do
for topic <- log["topics"] do for topic <- log["topics"] do
PubSub.publish(String.slice(topic, 2..-1), log["data"])
PubSub.publish String.slice(topic, 2..-1), log["data"]
end end
end end
end end
@ -178,30 +210,32 @@ defmodule ExW3 do
defp schedule_block() do defp schedule_block() do
Process.send_after(self(), :block, 1000) Process.send_after(self(), :block, 1000)
end end
end
def decode_event(data, signature) do
fs = ABI.FunctionSelector.decode(signature)
data
|> Base.decode16!(case: :lower)
|> ABI.TypeDecoder.decode(fs)
end end
defmodule EventSubscriber do defmodule EventSubscriber do
def start_link(signature, callback) do
def start(state) do pid = spawn(fn -> loop(%{callback: callback, signature: signature}) end)
spawn(fn -> loop(state) end) ExW3.EventPublisher.subscribe(pid, ExW3.encode_event(signature))
{:ok, pid}
end end
def loop(state) do def loop(state) do
receive do receive do
message -> message ->
apply(state[:callback], [message]) apply(state[:callback], [
ExW3.decode_event(String.slice(message, 2..-1), state[:signature])
])
loop(state) loop(state)
end end
end end
end
def subscribe signature, callback do
pid = ExW3.EventSubscriber.start %{callback: callback}
ExW3.EventPublisher.subscribe pid, ExW3.encode_event(signature)
end end
end end

@ -31,7 +31,9 @@ defmodule ExW3.Mixfile do
{:ethereumex, "~> 0.3.2"}, {:ethereumex, "~> 0.3.2"},
{:abi, "~> 0.1.8"}, {:abi, "~> 0.1.8"},
{:poison, "~> 3.1"}, {:poison, "~> 3.1"},
{:pubsub, "~> 1.0"} {:pubsub, "~> 1.0"},
{:inflex, "~> 1.10.0" },
{:hexate, ">= 0.6.0"}
] ]
end end
end end

@ -8,6 +8,7 @@
"hexate": {:hex, :hexate, "0.6.1", "1cea42e462c1daa32223127d4752e71016c3d933d492b9bb7fa4709a4a0fd50d", [:mix], [], "hexpm"}, "hexate": {:hex, :hexate, "0.6.1", "1cea42e462c1daa32223127d4752e71016c3d933d492b9bb7fa4709a4a0fd50d", [:mix], [], "hexpm"},
"httpoison": {:hex, :httpoison, "1.0.0", "1f02f827148d945d40b24f0b0a89afe40bfe037171a6cf70f2486976d86921cd", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, "httpoison": {:hex, :httpoison, "1.0.0", "1f02f827148d945d40b24f0b0a89afe40bfe037171a6cf70f2486976d86921cd", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, "idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"inflex": {:hex, :inflex, "1.10.0", "8366a7696e70e1813aca102e61274addf85d99f4a072b2f9c7984054ea1b9d29", [:mix], [], "hexpm"},
"keccakf1600": {:hex, :keccakf1600_orig, "2.0.0", "0a7217ddb3ee8220d449bbf7575ec39d4e967099f220a91e3dfca4dbaef91963", [:rebar3], [], "hexpm"}, "keccakf1600": {:hex, :keccakf1600_orig, "2.0.0", "0a7217ddb3ee8220d449bbf7575ec39d4e967099f220a91e3dfca4dbaef91963", [:rebar3], [], "hexpm"},
"libsecp256k1": {:hex, :libsecp256k1, "0.1.3", "57468b986af7c9633527875f71c7ca08bf4150b07b38a60d5bd48fba299ff6c1", [:rebar3], [], "hexpm"}, "libsecp256k1": {:hex, :libsecp256k1, "0.1.3", "57468b986af7c9633527875f71c7ca08bf4150b07b38a60d5bd48fba299ff6c1", [:rebar3], [], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},

@ -50,17 +50,17 @@ defmodule EXW3Test do
storage = ExW3.Contract.at context[:simple_storage_abi], contract_address storage = ExW3.Contract.at context[:simple_storage_abi], contract_address
{:ok, result} = ExW3.Contract.method(storage, "get") {:ok, result} = ExW3.Contract.method(storage, :get)
assert result == 0 assert result == 0
{:ok, tx_hash} = ExW3.Contract.method(storage, "set", [1], %{from: Enum.at(context[:accounts], 0)}) {:ok, tx_hash} = ExW3.Contract.method(storage, :set, [1], %{from: Enum.at(context[:accounts], 0)})
receipt = ExW3.tx_receipt tx_hash receipt = ExW3.tx_receipt tx_hash
#IO.inspect ExW3.block receipt["blockNumber"] #IO.inspect ExW3.block receipt["blockNumber"]
{:ok, result} = ExW3.Contract.method(storage, "get") {:ok, result} = ExW3.Contract.method(storage, :get)
assert result == 1 assert result == 1
@ -79,9 +79,24 @@ defmodule EXW3Test do
{:ok, event_pub} = ExW3.EventPublisher.start_link {:ok, event_pub} = ExW3.EventPublisher.start_link
ExW3.subscribe("Simple(uint256,bytes32)", fn event -> IO.inspect event end) {:ok, pid} = ExW3.EventSubscriber.start_link(
"Simple(uint256,bytes32)",
fn event_data ->
str =
event_data
|> Enum.at(1)
|> ExW3.bytes_to_string
{:ok, tx_hash} = ExW3.Contract.method(event_tester, "simple", ["hello, there!"], %{from: Enum.at(context[:accounts], 0)}) assert str == "Hello, World!"
end
)
{:ok, tx_hash} = ExW3.Contract.method(
event_tester,
:simple,
["Hello, World!"],
%{from: Enum.at(context[:accounts], 0)}
)
receipt = ExW3.tx_receipt tx_hash receipt = ExW3.tx_receipt tx_hash
@ -91,7 +106,7 @@ defmodule EXW3Test do
assert String.slice(Enum.at(topic, 0), 2..-1) == ExW3.encode_event("Simple(uint256,bytes32)") assert String.slice(Enum.at(topic, 0), 2..-1) == ExW3.encode_event("Simple(uint256,bytes32)")
:timer.sleep(5000) :timer.sleep(2000)
end end
@ -108,12 +123,12 @@ defmodule EXW3Test do
arr = [1, 2, 3, 4, 5] arr = [1, 2, 3, 4, 5]
{:ok, result} = ExW3.Contract.method(array_tester, "staticUint", [arr]) {:ok, result} = ExW3.Contract.method array_tester, :static_int, [arr]
assert result == arr assert result == arr
#0x5d4e0342 #0x5d4e0342
{:ok, result} = ExW3.Contract.method(array_tester, "dynamicUint", [arr]) {:ok, result} = ExW3.Contract.method array_tester, :dynamic_uint, [arr]
assert result == arr assert result == arr

Loading…
Cancel
Save