b0VIM 8.0 [:x>tzumbyRazvans-MacBook-Pro.local~tzumby/code/projects/sqrly/eth_contract/lib/eth_contract.exutf-8 3210#"! Utpzy{>adzVPO54Z9 ~ q Y A '  | { m l   w Z R L K @  w v p 3 k`B7. F{slk`reI2 "nextActionAt" => 0, "matronId" => 0, "isReady" => true, "isGestating" => false, "genes" => 626837621154801616088980922659877168609154386318304496692374110716999053, "generation" => 0, "cooldownIndex" => 0, "birthTime" => 1511417999, %{ iex> EthContract.meta(%{token_id: 45, method: "getKitty", contract: "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d", abi: abi}) iex> abi = EthContract.parse_abi("test/support/crypto_kitties.json") ## Examples ERC721 Meta. This will return a Map with the meta information associated with a token. You have to provide the name of the method to use (ie. CryptoKitties uses getKitty) @doc """ end end err -> {:error, err } else {:ok, total_supply |> bytes_to_int } with {:ok, total_supply } <- @json_rpc_client.eth_call(%{ data: "0x" <> total_supply_hex(), to: contract}) do def total_supply(%{contract: contract}) do """ :ok iex> EthContract.total_supply(%{contract: '0x234'}) ## Examples Get the total supply given a contract address. This was tested against ERC20 and ERC721 standard contracts. @doc """ end end {:error, err} err -> else end err -> {:error, err } else {:ok, balance } with {balance, _ } <- balance |> String.slice(2..-1) |> Integer.parse(16) do with {:ok, balance } <- @json_rpc_client.eth_get_balance(address) do def account_balance(%{ address: address }) do """ Get the account balance given a wallet address. @doc """ end end err -> {:error, err } else end err -> {:error, err } else {:ok, balance |> bytes_to_int } with {:ok, balance } <- @json_rpc_client.eth_call(%{ data: "0x" <> signature, to: contract }) do with {:ok, signature } <- balance_of_hex(address) do def balance_of(%{address: address, contract: contract}) do """ 2 iex> EthContract.balance_of(%{address: '0x123', contract: '0x234'}) ## Examples Get the balance given a wallet address. This was tested against ERC20 and ERC721 standard contracts. @doc """ end end err -> { :error, err } else end err -> { :error, err } else {:ok, address} with {:ok, address } <- address |> String.slice(2..-1) |> decode_address do with {:ok, address } <- @json_rpc_client.eth_call(%{ data: "0x" <> owner_of_hex(token_id), to: contract }) do def owner_of(%{token_id: token_id, contract: contract}) do """ 0x79bd592415ff6c91cfe69a7f9cd091354fc65a18 iex> EthContract.owner_of(%{token_id: 1, contract: "0x06012c8cf97bead5deae237070f9587f8e7a266d"}) ## Examples Get the wallet address given a contract and a token id @doc """ @callback total_supply(map()) :: map() ) :: map() method :: String.t() abi :: String.t(), topics :: String.t(), data :: String.t(), @callback decode_log( ) :: map() signatures_key :: String.t() method :: String.t(), abi :: String.t(), data :: String.t(), @callback decode_data( @callback parse_abi(file_path :: String.t()) :: map() @callback meta(map()) :: map() @callback owner_of(map()) :: map() @callback balance_of(map()) :: map() @json_rpc_client Application.get_env(:eth_contract, :json_rpc_client) || HttpClient alias ABI.TypeDecoder alias Ethereumex.HttpClient import EthContract.Util """ Provides a few convenience methods for getting information about tokens from ETH smart contracts. Uses JSON-RPC for requests. @moduledoc """defmodule EthContract doad >x^32b. u t ^ T < ;  o i h J 8 2 1  g ] B  end end end end |> List.first |> TypeDecoder.decode_raw([{:uint, 256}]) |> Base.decode16!(case: :lower) |> String.slice(2..-1) bytes defp bytes_to_int(bytes) do end 0 defp bytes_to_int("0x") do end {:ok, "0x" <> address } {address, _} = TypeDecoder.decode_bytes(bytes, 40, :left) defp decode_address(bytes) do end {:ok, "0x" } defp decode_address("") do end decode_data(data, abi, method) def decode_log(data, _topics, abi, method) do """ Decodes events with no indexes. @doc """ end {:ok, Map.merge(indexed_result, non_indexed_result) } |> Enum.into(%{}) res indexed_result = end) {map["name"], decoded} end |> :binary.decode_unsigned |> trim_data data :uint256 -> "0x" <> result |> Base.encode16(case: :lower) |> List.first |> ABI.TypeDecoder.decode_raw([type]) |> trim_data result = data :address -> decoded = case type do type = String.to_atom(map["type"]) { map, data } = x |> Enum.map(fn x -> Enum.zip(indexed, topics) res = non_indexed_result = combine_data(decoded_names, decoded_data)ad--yw]A2 } | v u *   a @  o ; W   } | 4 . - " we_'yF0V2,jicBA\[?>f.-, |> Enum.map( fn x -> x["name"] end) decoded_names = not_indexed |> Tuple.to_list |> List.first |> ABI.TypeDecoder.decode(ABI.Parser.parse!(output_signature)) decoded_data = trimmed_data [ _ | topics ] = topics output_signature = "#{method}(#{types_signature})" types_signature = Enum.join(["(", Enum.join(output_types_no_index, ","), ")"]) output_types_no_index = Enum.map(not_indexed, fn x -> x["type"] end) # ["uint256"] indexed = Enum.reject(abi[method]["inputs"], fn x -> x["indexed"] == false end) not_indexed = Enum.reject(abi[method]["inputs"], fn x -> x["indexed"] == true end) trimmed_data = trim_data(data) def decode_log(data, topics, abi, method) when Kernel.length(topics) > 1 do """ Decodes events that have indexed elements. @doc """ end |> Enum.into(%{}) Enum.zip(names, items) defp combine_data(names, items) do defp maybe_encode_binary(data), do: data end "0x" <> enc enc = Base.encode16(data, case: :lower) defp maybe_encode_binary( << data :: binary>>) do end {:ok, combine_data(output_names, outputs)} |> Enum.map(&maybe_encode_binary/1) |> Tuple.to_list() |> List.first() ABI.decode(output_signature, trimmed_output) outputs = %{ output_signature: output_signature, output_names: output_names } = output_signature(abi, method, signatures_key) trimmed_output = trim_data(data) def decode_data(data, abi, method, signatures_key) do end {:error, %{}} def decode_data("0x", _abi, _method, _signatures_key) do def decode_data(data, abi, method, signatures_key \\ "inputs") """ Decodes general smart contract calls @doc """ end %{ output_signature: output_signature, output_names: output_names } output_signature = "#{method}(#{types_signature})" types_signature = Enum.join(["(", Enum.join(output_types, ","), ")"]) output_names = Enum.map(abi[method][signatures_key], fn x -> x["name"] end) output_types = Enum.map(abi[method][signatures_key], fn x -> x["type"] end) defp output_signature(abi, method, signatures_key) do end trimmed_output |> Base.decode16(case: :lower) |> String.slice(2..-1) {:ok, trimmed_output } = data defp trim_data(data) do end end err -> err |> Enum.into(%{}) |> Enum.map(fn x -> {x["name"], x} end) {:ok, abi } -> Poison.Parser.parse!(abi) case File.read(file_path) do def parse_abi(file_path) do """ %{} iex> abi = EthContract.parse_abi("test/support/crypto_kitties.json") ## Examples Parses abi into a map with the name of the method as key. @doc """ # Taken from https://github.com/hswick/exw3/blob/master/lib/exw3.ex#L159 end end {:error, "Error in eth_call" } _err -> else end end {:error, "Error in decode_data"} _err -> else {:ok, output } with {:ok, output } <- decode_data(meta, abi, method, "outputs") do meta -> {:error, "Meta is 0x" } "0x" -> case meta do with {:ok, meta } <- @json_rpc_client.eth_call(%{ data: "0x" <> meta_for_hex(token_id, method), to: contract }) do def meta(%{token_id: token_id, method: method, contract: contract, abi: abi}) do """ } "siringWithId" => 0 "sireId" => 0,