Code Monkey home page Code Monkey logo

ex_abi's Introduction

ExABI

The Application Binary Interface (ABI) of Solidity describes how to transform binary data to types which the Solidity programming language understands. For instance, if we want to call a function bark(uint32,bool) on a Solidity-created contract contract Dog, what data parameter do we pass into our Ethereum transaction? This project allows us to encode such function calls.

Installation

If available in Hex, the package can be installed by adding ex_abi and ex_keccak to your list of dependencies in mix.exs:

def deps do
  [
    {:ex_abi, "~> 0.8.0"},
    {:ex_keccak, "~> 0.7.5"}
  ]
end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/ex_abi.

Confiiguration

The default keccak library is set to ex_keccak but that can be overridden with a different module. The module should implement one function hash_256/1.

config :ex_abi, keccak_module: MyCustomKeccak

If you're going to use a custom module, you should remove ex_keccak from deps in mix.exs.

Usage

Encoding

To encode a function call, pass the ABI spec and the data to pass in to ABI.encode/1.

iex> ABI.encode("baz(uint,address)", [50, <<1::160>> |> :binary.decode_unsigned])
<<162, 145, 173, 214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, ...>>

That transaction can then be sent via JSON-RPC Client ethereumex.

Decoding

Decode is generally the opposite of encoding, though we generally leave off the function signature from the start of the data. E.g. from above:

iex> ABI.decode("baz(uint,address)", "00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001" |> Base.decode16!(case: :lower))
[50, <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1>>]

Function selectors

Both ABI.encode/2 and ABI.decode/2 can accept a function selector as the first parameter. For example:

selector = %ABI.FunctionSelector{
          function: "startInFlightExit",
          input_names: [
            "inFlightTx",
            "inputTxs",
            "inputUtxosPos",
            "inputTxsInclusionProofs",
            "inFlightTxWitnesses"
          ],
          inputs_indexed: nil,
          method_id: <<90, 82, 133, 20>>,
          returns: [],
          type: :function,
          types: [
            tuple: [
              :bytes,
              {:array, :bytes},
              {:array, {:uint, 256}},
              {:array, :bytes},
              {:array, :bytes}
            ]
          ]
        }

ABI.encode(selector, params)

To parse function selector from the abi json, use ABI.parse_specification/2:

iex> [%{
...>   "inputs" => [
...>      %{"name" => "_numProposals", "type" => "uint8"}
...>   ],
...>   "payable" => false,
...>   "stateMutability" => "nonpayable",
...>   "type" => "constructor"
...> }]
...> |> ABI.parse_specification
[%ABI.FunctionSelector{function: nil, input_names: ["_numProposals"], inputs_indexed: nil, method_id: <<99, 53, 230, 34>>, returns: [], type: :constructor, types: [uint: 8]}]

Decoding output

By default, decode and encode functions try to decode/encode input (params that passed to the function). To decode/encode output pass :output as the third parameter:

      selector = %FunctionSelector{
        function: "getVersion",
        input_names: [],
        inputs_indexed: nil,
        method_id: <<13, 142, 110, 44>>,
        returns: [:string],
        type: :function,
        types: []
      }

      data =
        "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d312e302e342b6136396337363300000000000000000000000000000000000000"
        |> Base.decode16!(case: :lower)

      expected_result = ["1.0.4+a69c763"]

      assert expected_result == ABI.decode(selector, data, :output)
      assert data == ABI.encode(selector, expected_result, :output)

Support

Currently supports:

  • uint<M>
  • int<M>
  • address
  • uint
  • int
  • bool
  • fixed<M>x<N>
  • ufixed<M>x<N>
  • fixed
  • bytes<M>
  • <type>[M]
  • bytes
  • string
  • <type>[]
  • (T1,T2,...,Tn)

Docs

ex_abi's People

Contributors

alexgaribay avatar alisinabh avatar ayrat555 avatar dependabot[bot] avatar dominicletz avatar fedor-ivn avatar hayesgm avatar igorbarinov avatar k1rill-fedoseev avatar kianmeng avatar lucasnar avatar nikitosing avatar paulperegud avatar pthomalla avatar rupurt avatar saneery avatar steffenix avatar tsutsu avatar vbaranov avatar yashh avatar zachdaniel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

ex_abi's Issues

ABI.FunctionSelector.decode_type/1 doesn't recognize tuple type

iex(2)> ABI.FunctionSelector.decode_type("tuple[address,bytes32[],bytes][]")
** (MatchError) no match of right hand side value: {:error, {1, :ethereum_abi_parser, ['syntax error before: ', ['"address"']]}}
    (ex_abi 0.7.2) lib/abi/parser.ex:15: ABI.Parser.parse!/2
    iex:2: (file)

Error when decoding array

When I try to decode an array of addresses, I get an error

iex(1)> data = Base.decode16!("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6000000000000000000000000aa94b687d3f9552a453b81b2834ca53778980dc0000000000000000000000000312c230e7d6db05224f60208a656e3541c5c42ba", case: :lower)
<<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...>>
iex(2)> selector = %ABI.FunctionSelector{function: nil, types: [array: :address]}                                
%ABI.FunctionSelector{
  function: nil,
  input_names: [],
  inputs_indexed: nil,
  method_id: nil,
  returns: [],
  type: nil,
  types: [array: :address]
}
iex(3)> ABI.TypeDecoder.decode(data, selector)
** (MatchError) no match of right hand side value: ""
    (ex_abi) lib/abi/type_decoder.ex:195: ABI.TypeDecoder.decode_bytes/3
    (ex_abi) lib/abi/type_decoder.ex:233: anonymous fn/2 in ABI.TypeDecoder.decode_type/2
    (elixir) lib/enum.ex:1940: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ex_abi) lib/abi/type_decoder.ex:228: ABI.TypeDecoder.decode_type/2
    (ex_abi) lib/abi/type_decoder.ex:222: ABI.TypeDecoder.decode_type/2
    (ex_abi) lib/abi/type_decoder.ex:173: anonymous fn/2 in ABI.TypeDecoder.do_decode_raw/2
    (elixir) lib/enum.ex:1940: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ex_abi) lib/abi/type_decoder.ex:172: ABI.TypeDecoder.do_decode_raw/2

On the 0.1.18 version all right:

...
iex(3)> ABI.TypeDecoder.decode(data, selector)
[
  [
    <<11, 47, 94, 47, 60, 189, 134, 78, 170, 44, 100, 46, 55, 105, 193, 88, 35,
      97, 202, 246>>,
    <<170, 148, 182, 135, 211, 249, 85, 42, 69, 59, 129, 178, 131, 76, 165, 55,
      120, 152, 13, 192>>,
    <<49, 44, 35, 14, 125, 109, 176, 82, 36, 246, 2, 8, 166, 86, 227, 84, 28,
      92, 66, 186>>
  ]
]

ABI.find_and_decode function doesn't validate input arguments

Let's consider function ABI like this: claim() function wit no arguments

abi = %ABI.FunctionSelector{
     function: "claim",
     method_id: <<78, 113, 217, 45>>,
     type: :function,
     inputs_indexed: nil,
     state_mutability: :non_payable,
     input_names: [],
     types: [],
     returns: [],
     return_names: []
   }

Shouldn't input data <<78, 113, 217, 45, 18, 36>> be considered as wrong input with such ABI since claim() method has no arguments and arbitrary <<18, 36>> are sent at the end of input data?

The actual response is:

iex(2)> ABI.find_and_decode([abi], <<78, 113, 217, 45, 18, 36>>)
{%ABI.FunctionSelector{
   function: "claim",
   method_id: <<78, 113, 217, 45>>,
   type: :function,
   inputs_indexed: nil,
   state_mutability: :non_payable,
   input_names: [],
   types: [],
   returns: [],
   return_names: []
 }, []}

Problem with ABI.TypeDecoder

Hi!

I have a problem with ExW3.Contract.call method and ABI.TypeDecoder.decode_type.

Solidity function:
function get_DFA(uint dfaID) public returns (DFA memory) { DFA storage dfa = dfa_pool[dfaID]; return dfa; }

Elixir function:

def sc_call(sc_abi_path, sc_address, sc_name, sc_method, sc_method_args \\ []) do
    ExW3.Contract.start_link()
    sc_abi = ExW3.Abi.load_abi(sc_abi_path)
    ExW3.Contract.register(String.to_atom(sc_name), abi: sc_abi)
    ExW3.Contract.at(String.to_atom(sc_name), sc_address)
    ExW3.Contract.call(String.to_atom(sc_name), String.to_atom(sc_method), sc_method_args)
end

When I call this method from ElixirApp, I get a error

ElixirApp.Utils.Ethereum.sc_call("data/DFAPool.abi", "0x39Df1178CCBeC6d48A81969b6f67f13F3B4CC6E3", "DFAPool", "get_DFA", [1])

00:35:02.056 [error] GenServer ContractManager terminating
** (FunctionClauseError) no function clause matching in ABI.TypeDecoder.decode_type/2
(ex_abi 0.5.3) lib/abi/type_decoder.ex:244: ABI.TypeDecoder.decode_type(:tuple, <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...>>)
(ex_abi 0.5.3) lib/abi/type_decoder.ex:270: anonymous fn/3 in ABI.TypeDecoder.decode_type/2
(elixir 1.11.4) lib/enum.ex:2193: Enum."-reduce/3-lists^foldl/2-0-"/3
(ex_abi 0.5.3) lib/abi/type_decoder.ex:265: ABI.TypeDecoder.decode_type/2
(ex_abi 0.5.3) lib/abi/type_decoder.ex:189: anonymous fn/3 in ABI.TypeDecoder.do_decode_raw/2
(elixir 1.11.4) lib/enum.ex:2193: Enum."-reduce/3-lists^foldl/2-0-"/3
(ex_abi 0.5.3) lib/abi/type_decoder.ex:184: ABI.TypeDecoder.do_decode_raw/2
(ex_abi 0.5.3) lib/abi/type_decoder.ex:178: ABI.TypeDecoder.decode_raw/2
Last message (from #PID<0.255.0>): {:call, {:DFAPool, :get_DFA, [1]}}

I don't understand what's happened.

Incorrect method_id definition for events ABI

ABI spec stores full 32-byte event selectors in topic0 - https://docs.soliditylang.org/en/v0.8.23/abi-spec.html#events, which is a slightly different behavior than the 4-byte selectors used for methods and errors.

Using shorter 4-byte event ids for event signature matching is error-prone and can lead to unnecessary collisions:

$ cast sig 'Transfer(address,address,uint256)'                              
0xddf252ad
$ cast sig 'BeckerrJon(bytes16,bytes4,bytes23,bytes16,bytes1,bytes7,bytes1)'
0xddf252ad
$ cast sig-event 'Transfer(address,address,uint256)'                        
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
$ cast sig-event 'BeckerrJon(bytes16,bytes4,bytes23,bytes16,bytes1,bytes7,bytes1)'
0xddf252ade8bdc6f18de3868ae50ab6e67ee261b7136b3141cd791f1ad4786a79

At last, 4-byte method_id definition in FunctionSelector struct does not make a lot of sense for events, a full 32-byte selector would be more useful in practice.

encoding of multiple dynamic size arrays is incorrect

ABI.encode("test(uint[], uint[])", [[1], [2]]) |> Base.encode16(case: :lower)

we got

"f0d7f6eb00000000000000000000000000000000000000000000000000000000000000 6 000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"

but web3js generates:

"f0d7f6eb00000000000000000000000000000000000000000000000000000000000000 4 000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"

look at the numbers emphasized, one is 6, the other one is 4.

it looks like a problem, cause when i send the transaction, it fails and reverted on "Error encountered".

thank you so much

incorrect usage of types in function selectors

Based on:
https://github.com/poanetwork/ex_abi/blob/master/lib/abi/type_decoder.ex#L140
and
https://github.com/poanetwork/ex_abi/blob/master/lib/abi/function_selector.ex#L29

a function selector return types get completely ignored - so even basic function return data doesn't get parsed.
For example:

selector = %ABI.FunctionSelector{
  function: "getVersion",
  input_names: [],
  inputs_indexed: nil,
  method_id: <<13, 142, 110, 44>>,
  returns: [:string],
  type: :function,
  types: []
}

was parsed directly from our solidity contract abi (json):

abi_path
      |> File.read!()
      |> Jason.decode!()
      |> Map.fetch!("abi")
      |> ABI.parse_specification(include_events?: true)
data2 = method_id |> Encoding.to_hex() |> Kernel.<>(data) |> Encoding.from_hex 
ABI.find_and_decode([selector], data2)

where

iex(196)> method_id
<<13, 142, 110, 44>>
iex(197)> data
"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d312e302e342b6136396337363300000000000000000000000000000000000000"
iex(198)>

and returns this:

{%ABI.FunctionSelector{
   function: "getVersion",
   input_names: [],
   inputs_indexed: nil,
   method_id: <<13, 142, 110, 44>>,
   returns: [:string],
   type: :function,
   types: []
 }, []}

if we modify the selector to (returns and types were swaped):

selector = %ABI.FunctionSelector{
  function: "getVersion",
  input_names: [],
  inputs_indexed: nil,
  method_id: <<13, 142, 110, 44>>,
  returns: [],
  type: :function,
  types: [:string],
}
ABI.find_and_decode([selector], data2)
{%ABI.FunctionSelector{
   function: "getVersion",
   input_names: [],
   inputs_indexed: nil,
   method_id: <<13, 142, 110, 44>>,
   returns: [],
   type: :function,
   types: [:string]
 }, ["1.0.4+a69c763"]}

Error when decoding a function with two argument of type array or bytes

example use:

<<_, _, _, _, data::bytes>> =
      [[1, 2, 3, 4], [4, 5, 6]]
      |> ABI.TypeEncoder.encode(%ABI.FunctionSelector{
        function: "baz",
        types: [{:array, {:uint, 32}}, {:array, {:uint, 32}}]
      })
data
    |> ABI.TypeDecoder.decode(%ABI.FunctionSelector{
      function: "baz",
      types: [{:array, {:uint, 32}}, {:array, {:uint, 32}}]
    })

received error:

** (CompileError) iex:1: ABI.FunctionSelector.__struct__/1 is undefined, cannot expand struct ABI.FunctionSelector
    (stdlib) lists.erl:1354: :lists.mapfoldl/3
    (stdlib) lists.erl:1355: :lists.mapfoldl/3
    (elixir) expanding macro: Kernel.|>/2
    iex:1: (file)

parsing regression

regression in master ef450816ef4ca98648125577b675ab584251a883

function_specs = [%ABI.FunctionSelector{
      function: "startInFlightExit",
      input_names: ["inFlightTx", "inputTxs", "inputUtxosPos", "inputTxsInclusionProofs", "inFlightTxWitnesses"],
      inputs_indexed: nil,
      method_id: <<90, 82, 133, 20>>,
      returns: [],
      type: :function,
      types: [
        tuple: [
          :bytes,
          {:array, :bytes},
          {:array, {:uint, 256}},
          {:array, :bytes},
          {:array, :bytes}
        ]
      ]
    }]

data =
      <<90, 82, 133, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 126, 248, 124, 1, 225, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 1, 210, 32, 127, 180, 0, 246, 245, 1, 243, 148, 118, 78, 248, 3, 28, 17, 248, 220, 42, 92, 18,
        141, 145, 248, 79, 186, 190, 47, 160, 172, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136,
        69, 99, 145, 130, 68, 244, 0, 0, 128, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 248, 91, 1,
        192, 246, 245, 1, 243, 148, 118, 78, 248, 3, 28, 17, 248, 220, 42, 92, 18, 141, 145, 248, 79, 186, 190, 47, 160,
        172, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 138, 199, 35, 4, 137, 232, 0, 0, 128,
        160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 210, 32, 127, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 243, 154, 134, 159, 98, 231, 92, 245, 240, 191, 145, 70, 136, 166, 178,
        137, 202, 242, 4, 148, 53, 216, 230, 140, 92, 94, 109, 5, 228, 73, 19, 243, 78, 213, 192, 45, 109, 72, 200, 147,
        36, 134, 201, 157, 58, 217, 153, 229, 216, 148, 157, 195, 190, 59, 48, 88, 204, 41, 121, 105, 12, 62, 58, 98,
        28, 121, 43, 20, 191, 102, 248, 42, 243, 111, 0, 245, 251, 167, 1, 79, 160, 193, 226, 255, 60, 124, 39, 59, 254,
        82, 60, 26, 207, 103, 220, 63, 95, 160, 128, 166, 134, 165, 160, 208, 92, 61, 72, 34, 253, 84, 214, 50, 220,
        156, 192, 75, 22, 22, 4, 110, 186, 44, 228, 153, 235, 154, 247, 159, 94, 185, 73, 105, 10, 4, 4, 171, 244, 206,
        186, 252, 124, 255, 250, 56, 33, 145, 183, 221, 158, 125, 247, 120, 88, 30, 111, 183, 142, 250, 179, 95, 211,
        100, 201, 213, 218, 218, 212, 86, 155, 109, 212, 127, 127, 234, 186, 250, 53, 113, 248, 66, 67, 68, 37, 84, 131,
        53, 172, 110, 105, 13, 208, 113, 104, 216, 188, 91, 119, 151, 156, 26, 103, 2, 51, 79, 82, 159, 87, 131, 247,
        158, 148, 47, 210, 205, 3, 246, 229, 90, 194, 207, 73, 110, 132, 159, 222, 156, 68, 111, 171, 70, 168, 210, 125,
        177, 227, 16, 15, 39, 90, 119, 125, 56, 91, 68, 227, 203, 192, 69, 202, 186, 201, 218, 54, 202, 224, 64, 173,
        81, 96, 130, 50, 76, 150, 18, 124, 242, 159, 69, 53, 235, 91, 126, 186, 207, 226, 161, 214, 211, 170, 184, 236,
        4, 131, 211, 32, 121, 168, 89, 255, 112, 249, 33, 89, 112, 168, 190, 235, 177, 193, 100, 196, 116, 232, 36, 56,
        23, 76, 142, 235, 111, 188, 140, 180, 89, 75, 136, 201, 68, 143, 29, 64, 176, 155, 234, 236, 172, 91, 69, 219,
        110, 65, 67, 74, 18, 43, 105, 92, 90, 133, 134, 45, 142, 174, 64, 179, 38, 143, 111, 55, 228, 20, 51, 123, 227,
        142, 186, 122, 181, 187, 243, 3, 208, 31, 75, 122, 224, 127, 215, 62, 220, 47, 59, 224, 94, 67, 148, 138, 52,
        65, 138, 50, 114, 80, 156, 67, 194, 129, 26, 130, 30, 92, 152, 43, 165, 24, 116, 172, 125, 201, 221, 121, 168,
        12, 194, 240, 95, 111, 102, 76, 157, 187, 46, 69, 68, 53, 19, 125, 160, 108, 228, 77, 228, 85, 50, 165, 106, 58,
        112, 7, 162, 208, 198, 180, 53, 247, 38, 249, 81, 4, 191, 166, 231, 7, 4, 111, 193, 84, 186, 233, 24, 152, 208,
        58, 26, 10, 198, 249, 180, 94, 71, 22, 70, 226, 85, 90, 199, 158, 63, 232, 126, 177, 120, 30, 38, 242, 5, 0, 36,
        12, 55, 146, 116, 254, 145, 9, 110, 96, 209, 84, 90, 128, 69, 87, 31, 218, 185, 181, 48, 208, 214, 231, 232,
        116, 110, 120, 191, 159, 32, 244, 232, 111, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65,
        52, 191, 197, 222, 130, 0, 246, 100, 25, 133, 115, 123, 250, 19, 77, 122, 226, 50, 133, 34, 71, 195, 27, 188,
        147, 104, 200, 235, 121, 231, 64, 251, 107, 58, 88, 55, 118, 117, 53, 9, 224, 81, 93, 0, 167, 62, 195, 202, 233,
        207, 237, 254, 185, 95, 207, 246, 144, 69, 242, 160, 58, 161, 96, 70, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>

{function_spec, data} = ABI.find_and_decode(function_specs, data)


** (MatchError) no match of right hand side value: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 248, 124, 1, 225, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...>>
    lib/abi/type_decoder.ex:312: ABI.TypeDecoder.decode_type/3
    lib/abi/type_decoder.ex:342: anonymous fn/3 in ABI.TypeDecoder.decode_type/3
    (elixir 1.10.2) lib/enum.ex:2111: Enum."-reduce/3-lists^foldl/2-0-"/3
    lib/abi/type_decoder.ex:340: ABI.TypeDecoder.decode_type/3
    lib/abi/type_decoder.ex:342: anonymous fn/3 in ABI.TypeDecoder.decode_type/3
    (elixir 1.10.2) lib/enum.ex:2111: Enum."-reduce/3-lists^foldl/2-0-"/3
    lib/abi/type_decoder.ex:340: ABI.TypeDecoder.decode_type/3
    lib/abi/type_decoder.ex:175: anonymous fn/3 in ABI.TypeDecoder.do_decode_raw/2

the output expected (works in ex abi 0.2):


%{
             in_flight_tx:
               <<248, 124, 1, 225, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                 210, 32, 127, 180, 0, 246, 245, 1, 243, 148, 118, 78, 248, 3, 28, 17, 248, 220, 42, 92, 18, 141, 145,
                 248, 79, 186, 190, 47, 160, 172, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136,
                 69, 99, 145, 130, 68, 244, 0, 0, 128, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>,
             in_flight_tx_sigs: [
               <<52, 191, 197, 222, 130, 0, 246, 100, 25, 133, 115, 123, 250, 19, 77, 122, 226, 50, 133, 34, 71, 195,
                 27, 188, 147, 104, 200, 235, 121, 231, 64, 251, 107, 58, 88, 55, 118, 117, 53, 9, 224, 81, 93, 0, 167,
                 62, 195, 202, 233, 207, 237, 254, 185, 95, 207, 246, 144, 69, 242, 160, 58, 161, 96, 70, 28>>
             ],
             input_inclusion_proofs: [
               <<243, 154, 134, 159, 98, 231, 92, 245, 240, 191, 145, 70, 136, 166, 178, 137, 202, 242, 4, 148, 53, 216,
                 230, 140, 92, 94, 109, 5, 228, 73, 19, 243, 78, 213, 192, 45, 109, 72, 200, 147, 36, 134, 201, 157, 58,
                 217, 153, 229, 216, 148, 157, 195, 190, 59, 48, 88, 204, 41, 121, 105, 12, 62, 58, 98, 28, 121, 43, 20,
                 191, 102, 248, 42, 243, 111, 0, 245, 251, 167, 1, 79, 160, 193, 226, 255, 60, 124, 39, 59, 254, 82, 60,
                 26, 207, 103, 220, 63, 95, 160, 128, 166, 134, 165, 160, 208, 92, 61, 72, 34, 253, 84, 214, 50, 220,
                 156, 192, 75, 22, 22, 4, 110, 186, 44, 228, 153, 235, 154, 247, 159, 94, 185, 73, 105, 10, 4, 4, 171,
                 244, 206, 186, 252, 124, 255, 250, 56, 33, 145, 183, 221, 158, 125, 247, 120, 88, 30, 111, 183, 142,
                 250, 179, 95, 211, 100, 201, 213, 218, 218, 212, 86, 155, 109, 212, 127, 127, 234, 186, 250, 53, 113,
                 248, 66, 67, 68, 37, 84, 131, 53, 172, 110, 105, 13, 208, 113, 104, 216, 188, 91, 119, 151, 156, 26,
                 103, 2, 51, 79, 82, 159, 87, 131, 247, 158, 148, 47, 210, 205, 3, 246, 229, 90, 194, 207, 73, 110, 132,
                 159, 222, 156, 68, 111, 171, 70, 168, 210, 125, 177, 227, 16, 15, 39, 90, 119, 125, 56, 91, 68, 227,
                 203, 192, 69, 202, 186, 201, 218, 54, 202, 224, 64, 173, 81, 96, 130, 50, 76, 150, 18, 124, 242, 159,
                 69, 53, 235, 91, 126, 186, 207, 226, 161, 214, 211, 170, 184, 236, 4, 131, 211, 32, 121, 168, 89, 255,
                 112, 249, 33, 89, 112, 168, 190, 235, 177, 193, 100, 196, 116, 232, 36, 56, 23, 76, 142, 235, 111, 188,
                 140, 180, 89, 75, 136, 201, 68, 143, 29, 64, 176, 155, 234, 236, 172, 91, 69, 219, 110, 65, 67, 74, 18,
                 43, 105, 92, 90, 133, 134, 45, 142, 174, 64, 179, 38, 143, 111, 55, 228, 20, 51, 123, 227, 142, 186,
                 122, 181, 187, 243, 3, 208, 31, 75, 122, 224, 127, 215, 62, 220, 47, 59, 224, 94, 67, 148, 138, 52, 65,
                 138, 50, 114, 80, 156, 67, 194, 129, 26, 130, 30, 92, 152, 43, 165, 24, 116, 172, 125, 201, 221, 121,
                 168, 12, 194, 240, 95, 111, 102, 76, 157, 187, 46, 69, 68, 53, 19, 125, 160, 108, 228, 77, 228, 85, 50,
                 165, 106, 58, 112, 7, 162, 208, 198, 180, 53, 247, 38, 249, 81, 4, 191, 166, 231, 7, 4, 111, 193, 84,
                 186, 233, 24, 152, 208, 58, 26, 10, 198, 249, 180, 94, 71, 22, 70, 226, 85, 90, 199, 158, 63, 232, 126,
                 177, 120, 30, 38, 242, 5, 0, 36, 12, 55, 146, 116, 254, 145, 9, 110, 96, 209, 84, 90, 128, 69, 87, 31,
                 218, 185, 181, 48, 208, 214, 231, 232, 116, 110, 120, 191, 159, 32, 244, 232, 111, 6>>
             ],
             input_txs: [
               <<248, 91, 1, 192, 246, 245, 1, 243, 148, 118, 78, 248, 3, 28, 17, 248, 220, 42, 92, 18, 141, 145, 248,
                 79, 186, 190, 47, 160, 172, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 138,
                 199, 35, 4, 137, 232, 0, 0, 128, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>
             ],
             input_utxos_pos: [2_002_000_000_000]
           }

Support for the "int" type

Description

The following code raises an error:

abi = [
  %{
    "constant" => true,
    "inputs" => [%{"name" => "x", "type" => "int256"}],
    "name" => "with_arguments",
    "outputs" => [%{"name" => "", "type" => "bool"}],
    "payable" => false,
    "stateMutability" => "view",
    "type" => "function"
  }
]
selector = abi |> ABI.parse_specification() |> hd
selector |> ABI.encode([42])

Output:

** (RuntimeError) Unsupported encoding type: {:int, 256}
    (abi) lib/abi/type_encoder.ex:215: ABI.TypeEncoder.encode_type/2
    (abi) lib/abi/type_encoder.ex:139: ABI.TypeEncoder.do_encode/3
    (abi) lib/abi/type_encoder.ex:102: ABI.TypeEncoder.encode/2

Expected output:

"7df46666000000000000000000000000000000000000000000000000000000000000002a"

From blockscout/blockscout#363.

Uning array of complex types

I am trying to use the ABI with the https://github.com/makerdao/multicall contract.

aggregate(Call[] memory calls)

    struct Call {
        address target;
        bytes callData;
    }

So I am using the encode function with aggregate((address,bytes)[])

I am getting the following error:

        # 1
        {:tuple, [:address, :bytes]}
        # 2
        <<238, 251, 161, 230, 57, 5, 239, 29, 122, 203, 165, 168, 81, 60, 112, 48, 124, 28, 228, 65>>
        # 3
        []
        # 4
        []

I have also tried to do:

      function = %ABI.FunctionSelector{
        function: "aggregate",
        input_names: ["calls"],
        type: :function,
        types: [array: {:tuple, [:address, :bytes]}]
      }
    ABI.encode(function, values)

The issue is the binary isn't passing the Kernel.is_tupple condition.

The second parameter passed to the function is:

[
[address, data]
]

Inconsistency in enconde/decode address?

I think that adresses are encoded as uin160 (ABI.TypeEncoder.do_encode_type) but are decoded as binary (ABI.TypeDecoder.decode_type). This looks like inconsistent. Am I wrong?

ex_abi can not parse complex types

For example

function = %{
  "constant" => true,
  "inputs" => [
    %{"internalType" => "uint160[]", "name" => "exitIds", "type" => "uint160[]"}
  ],
  "name" => "standardExits",
  "outputs" => [
    %{
      "components" => [
        %{"internalType" => "bool", "name" => "exitable", "type" => "bool"},
        %{"internalType" => "uint256", "name" => "utxoPos", "type" => "uint256"},
        %{
          "internalType" => "bytes32",
          "name" => "outputId",
          "type" => "bytes32"
        },
        %{
          "internalType" => "address payable",
          "name" => "exitTarget",
          "type" => "address"
        },
        %{"internalType" => "uint256", "name" => "amount", "type" => "uint256"},
        %{
          "internalType" => "uint256",
          "name" => "bondSize",
          "type" => "uint256"
        }
      ],
      "internalType" => "struct PaymentExitDataModel.StandardExit[]",
      "name" => "",
      "type" => "tuple[]"
    }
  ],
  "payable" => false,
  "stateMutability" => "view",
  "type" => "function"
}

is parsed into

ABI.parse_specification([function])
[
  %ABI.FunctionSelector{
    function: "standardExits",
    input_names: ["exitIds"],
    inputs_indexed: nil,
    method_id: <<12, 165, 182, 118>>,
    returns: [array: :tuple],
    type: :function,
    types: [array: {:uint, 160}]
  }
]

Error on decoding

Hi,

when I try to decode this piece of data from I get the following error:

ABI.decode("Transfer(address,address,uint256)", "00000000000000000000000000000000000000000000001158e460913d000000")
** (MatchError) no match of right hand side value: ""
    (ex_abi 0.5.11) lib/abi/type_decoder.ex:371: ABI.TypeDecoder.decode_uint/2

Invalid result when encoding payload for 0x V3 proxy.

We are using ExABI to encode calls to 0x V3 proxy. It requires us to encode following piece of data and prepend some prefix manually. Only after manually removing the 32 padding it ate it.

ABI.TypeEncoder.encode_raw(
      [{631839923370317324987270211814122349984664846147, [2], [1], 0}],
      [tuple: [:address, {:array, {:uint, 256}}, {:array, {:uint, 256}}, :bytes]]
    )

It is: 00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006eacae9b5d4f0f952694b2b7eef49bebb210ff43000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000
Should be: 0000000000000000000000006eacae9b5d4f0f952694b2b7eef49bebb210ff43000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000

Only after removing the padding blockchain ate this.

Mix complile fails on windows

Get the following error when trying to compile on windows 10:

D:\ex_abi_test>mix compile
==> libsecp256k1
could not compile dependency :libsecp256k1, "mix compile" failed. You can recompile this dependency with "mix deps.compile libsecp256k1", update it with "mix deps.update libsecp256k1" or clean it with "mix deps.clean libsecp256k1"
** (ErlangError) Erlang error: :enoent
    (elixir) lib/system.ex:632: System.cmd("make", [], [into: %IO.Stream{device: :standard_io, line_or_bytes: :line, raw: false}])
    d:/Workspaces/Project/Trixta/other/ex_abi/deps/libsecp256k1/mix.exs:3: Mix.Tasks.Compile.MakeBindings.run/1
    (mix) lib/mix/task.ex:316: Mix.Task.run_task/3
    (mix) lib/mix/tasks/compile.all.ex:68: Mix.Tasks.Compile.All.run_compiler/2
    (mix) lib/mix/tasks/compile.all.ex:52: Mix.Tasks.Compile.All.do_compile/4
    (mix) lib/mix/tasks/compile.all.ex:23: anonymous fn/1 in Mix.Tasks.Compile.All.run/1
    (mix) lib/mix/tasks/compile.all.ex:39: Mix.Tasks.Compile.All.with_logger_app/1
    (mix) lib/mix/task.ex:316: Mix.Task.run_task/3mix compile

Erlang/OTP 21 [erts-10.2] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1]

Elixir 1.7.1 (compiled with Erlang/OTP 19)

`ABI.parse_specification` ignores function objects without `outputs` field

Recently we came across such a contract, where one of the methods (assumeLastTokenIdMatches) does not have an array of outputs in its ABI:

// ...
    {
        "type": "function",
        "stateMutability": "view",
        "name": "assumeLastTokenIdMatches",
        "inputs": [
            {
                "type": "uint256",
                "name": "lastTokenId",
                "internalType": "uint256"
            }
        ]
    },
// ...

If I try to parse this ABI using ABI.parse_specification, the function assumeLastTokenIdMatches is skipped:

iex(1)> File.read!("contract-abi.json") |> Jason.decode! |> ABI.parse_specification |> Enum.filter(fn %ABI.FunctionSelector{function: name} -> name == "assumeLastTokenIdMatches" end)
[]

contract-abi.json

Possibly, the problem is somewhere here:

with %{
"name" => function_name,
"inputs" => named_inputs,
"outputs" => named_outputs
} <- item,

abi parsing issue

abi:

abi = [
  %{
    "constant" => true,
    "inputs" => [
      %{"internalType" => "uint160", "name" => "exitId", "type" => "uint160"}
    ],
    "name" => "inFlightExits",
    "outputs" => [
      %{
        "components" => [
          %{"internalType" => "bool", "name" => "isCanonical", "type" => "bool"},
          %{
            "internalType" => "uint64",
            "name" => "exitStartTimestamp",
            "type" => "uint64"
          },
          %{
            "internalType" => "uint256",
            "name" => "exitMap",
            "type" => "uint256" 
          },
          %{
            "internalType" => "uint256",
            "name" => "position",
            "type" => "uint256"
          },
          %{
            "components" => [
              %{
                "internalType" => "bytes32",
                "name" => "outputId",
                "type" => "bytes32"
              },
              %{
                "internalType" => "address payable",
                "name" => "exitTarget",
                "type" => "address"
              },
              %{
                "internalType" => "address",
                "name" => "token",
                "type" => "address"
              },
              %{
                "internalType" => "uint256",
                "name" => "amount",
                "type" => "uint256"
              },
              %{
                "internalType" => "uint256",
                "name" => "piggybackBondSize",
                "type" => "uint256"
              }
            ],
            "internalType" => "struct PaymentExitDataModel.WithdrawData[4]",
            "name" => "inputs",
            "type" => "tuple[4]"
          },
          %{
            "components" => [
              %{
                "internalType" => "bytes32",
                "name" => "outputId",
                "type" => "bytes32"
              },
              %{
                "internalType" => "address payable",
                "name" => "exitTarget",
                "type" => "address"
              },
              %{
                "internalType" => "address",
                "name" => "token",
                "type" => "address"
              },
              %{
                "internalType" => "uint256",
                "name" => "amount",
                "type" => "uint256"
              },
              %{
                "internalType" => "uint256",
                "name" => "piggybackBondSize",
                "type" => "uint256"
              }
            ],
            "internalType" => "struct PaymentExitDataModel.WithdrawData[4]",
            "name" => "outputs",
            "type" => "tuple[4]"
          },
          %{
            "internalType" => "address payable",
            "name" => "bondOwner",
            "type" => "address"
          },
          %{
            "internalType" => "uint256",
            "name" => "bondSize",
            "type" => "uint256"
          },
          %{
            "internalType" => "uint256",
            "name" => "oldestCompetitorPosition",
            "type" => "uint256"
          }
        ],
        "internalType" => "struct PaymentExitDataModel.InFlightExit",
        "name" => "",
        "type" => "tuple"
      }
    ],
    "payable" => false,
    "stateMutability" => "view",
    "type" => "function"
  }
]
ABI.parse_specification(abi, include_events?: true)

code (abi from above)

ABI.parse_specification(abi, include_events?: true)

errors:

     ** (MatchError) no match of right hand side value: {:error, {1, :ethereum_abi_parser, ['syntax error before: ', ['"tuple"']]}}

Support multidimensional tuple arrays

Currently there are not support for multidimensional tuple arrays, so when an abi contents a type like this tuple[][] is trying to parse it as if having a fixed length https://github.com/poanetwork/ex_abi/blob/master/lib/abi/function_selector.ex#L318 and generating an error.

It's posible to reproduce running this test:

test "parse multidimensional tuple" do
      abi = %{
        "constant" => true,
        "inputs" => [%{"name" => "swaps", "type" => "tuple[][]", "interalType" => "struct ExchangeProxy.Swap[][]", "components" => [
          %{
            "name" => "foo",
            "type" => "uint256"
          },
          %{
            "name" => "bar",
            "type" => "uint256"
          }
        ]}],
        "name" => "batchSwapExactOut",
        "outputs" => [%{"name" => "totalAmountIn", "type" => "uint256"}],
        "payable" => true,
        "stateMutability" => "payable",
        "type" => "function"
      }

      IO.inspect(ABI.parse_specification([abi])) 
    end

The error produced is:

  1) test parse multidimensional tuple (Explorer.SmartContract.ReaderTest)
     apps/explorer/test/explorer/smart_contract/reader_test.exs:385
     ** (ArgumentError) errors were found at the given arguments:
     
       * 1st argument: not a textual representation of an integer
     
     code: IO.inspect(ABI.parse_specification([abi]))
     stacktrace:
       :erlang.binary_to_integer("[")
       (ex_abi 0.5.9) lib/abi/function_selector.ex:318: ABI.FunctionSelector.parse_specification_type/1
       (elixir 1.12.3) lib/enum.ex:1582: Enum."-map/2-lists^map/1-0-"/2
       (ex_abi 0.5.9) lib/abi/function_selector.ex:174: ABI.FunctionSelector.parse_specification_item/1
       (elixir 1.12.3) lib/enum.ex:1582: Enum."-map/2-lists^map/1-0-"/2
       (ex_abi 0.5.9) lib/abi.ex:198: ABI.parse_specification/2
       test/explorer/smart_contract/reader_test.exs:405: (test)

Encoding maps as structs

Hey! First of all, thanks everyone for the great work! One pain point I've been dealing with while using this library is encoding structs. Take, for instance, Multicall3's aggregate function. It requires as input a Call object.

  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "target",
            "type": "address"
          },
          {
            "internalType": "bytes",
            "name": "callData",
            "type": "bytes"
          }
        ],
        "internalType": "struct Multicall3.Call[]",
        "name": "calls",
        "type": "tuple[]"
      }
    ],
    "name": "aggregate",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "blockNumber",
        "type": "uint256"
      },
      {
        "internalType": "bytes[]",
        "name": "returnData",
        "type": "bytes[]"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  }

We need to manually create the tuples with correct field order (first target, then callData) in order to encode our calls. This is quite inconvenient because it means the client code needs to know well each struct type and the precise order of its fields. I believe we can improve this condition by allowing us to pass not only tuples but maps too to the encoding functions. The encoding functions would then transform the maps into tuples with the right field ordering. I'm happy to do this work 💪

If we are going to this, one issue we need to deal with is that the FunctionSelector struct doesn't hold all the information we need. The function selector for aggregate is:

%ABI.FunctionSelector{
      function: "aggregate",
      input_names: ["calls"],
      inputs_indexed: nil,
      method_id: <<37, 45, 186, 66>>,
      returns: [uint: 256, array: :bytes],
      type: :function,
      types: [array: {:tuple, [:address, :bytes]}]
}

We'd need to hold not only the types of each field but the names too. So instead of {:tuple, [:address, :bytes]} it could be {:tuple, [:address, :bytes], [:target, :callData]} or {:tuple, [target: :address, callData: :bytes]}.

This should work fine for encoding, but decoding is another issue: we likely have existing code that assumes the result of decoding is a tuple, so perhaps we'd need to create a new opt-in option to decode into a map.

Any thoughts? Feedback much appreciated.

Type parsing error

    ** (ArgumentError) errors were found at the given arguments:

  * 1st argument: not a textual representation of an integer

        (erts 13.2.2.5) :erlang.binary_to_integer("2[")
        (ex_abi 0.6.4) lib/abi/function_selector.ex:339: ABI.FunctionSelector.parse_specification_type/1
        (elixir 1.14.5) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
        (ex_abi 0.6.4) lib/abi/function_selector.ex:189: ABI.FunctionSelector.parse_specification_item/1
        (elixir 1.14.5) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
        (ex_abi 0.6.4) lib/abi.ex:228: ABI.parse_specification/2

Contract and ABI example:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

contract StorageB {
    function retrieve(uint256 a) public virtual returns (uint256) {
        return 1 + (a * 23) / 10;
    }

    struct s {
        uint256 a;
        uint256 b;
    }

    function test() public pure returns (bytes20[] memory) {
        // Initialize a two-dimensional dynamic array in memory
        bytes20[][] memory array = new bytes20[][](2); // Create 2 rows

        array[0] = new bytes20[](2); // Create 2 columns for the first row
        array[1] = new bytes20[](3); // Create 3 columns for the second row

        // Assign values to the array elements
        array[0][0] = bytes20(0x1111111111111111111111111111111111111111);
        array[0][1] = bytes20(0x2222222222222222222222222222222222222222);

        array[1][0] = bytes20(0x3333333333333333333333333333333333333333);
        array[1][1] = bytes20(0x4444444444444444444444444444444444444444);
        array[1][2] = bytes20(0x5555555555555555555555555555555555555555);

        return array[0];
    }

    struct MyTuple {
        uint256 element1;
        bool element2;
    }

    function createTupleArray() public pure returns (MyTuple[2][] memory) {
        // Initialize a two-dimensional dynamic array in memory
        MyTuple[2][] memory tupleArray = new MyTuple[2][](2); // 2 rows

        tupleArray[0] = [MyTuple(123, true), MyTuple(456, false)]; // 2 columns for the first row
        tupleArray[1] = [MyTuple(101112, false), MyTuple(131415, true)]; // 2 columns for the second row

        return tupleArray;
    }
}
[
	{
		"inputs": [
			{
				"internalType": "uint256",
				"name": "a",
				"type": "uint256"
			}
		],
		"name": "retrieve",
		"outputs": [
			{
				"internalType": "uint256",
				"name": "",
				"type": "uint256"
			}
		],
		"stateMutability": "nonpayable",
		"type": "function"
	},
	{
		"inputs": [],
		"name": "createTupleArray",
		"outputs": [
			{
				"components": [
					{
						"internalType": "uint256",
						"name": "element1",
						"type": "uint256"
					},
					{
						"internalType": "bool",
						"name": "element2",
						"type": "bool"
					}
				],
				"internalType": "struct StorageB.MyTuple[2][]",
				"name": "",
				"type": "tuple[2][]"
			}
		],
		"stateMutability": "pure",
		"type": "function"
	},
	{
		"inputs": [],
		"name": "test",
		"outputs": [
			{
				"internalType": "bytes20[]",
				"name": "",
				"type": "bytes20[]"
			}
		],
		"stateMutability": "pure",
		"type": "function"
	}
]

could not compile dependency :ex_keccak, "mix compile" failed

** (ErlangError) Erlang error: :enoent
(elixir 1.13.0) lib/system.ex:1044: System.cmd("cargo", ["metadata", "--format-version=1"], [cd: "native/exkeccak"])
(rustler 0.23.0) lib/rustler/compiler/config.ex:81: Rustler.Compiler.Config.external_resources/2
(rustler 0.23.0) lib/rustler/compiler/config.ex:69: Rustler.Compiler.Config.build/1
(rustler 0.23.0) lib/rustler/compiler.ex:9: Rustler.Compiler.compile_crate/2
lib/ex_keccak.ex:6: (module)
could not compile dependency :ex_keccak, "mix compile" failed. Errors may have been logged above. You can recompile this dependency with "mix deps.compile ex_keccak", update it with "mix deps.update ex_keccak" or clean it with "mix deps.clean ex_keccak"

question/comments regarding names

This is a question to gain some further understanding on my part.

[
  %{
    "inputs" => [],
    "name" => "getItems",
    "outputs" => [
      %{
        "components" => [
          %{"internalType" => "uint256", "name" => "price", "type" => "uint256"},
          %{
            "internalType" => "uint256",
            "name" => "itemId",
            "type" => "uint256"
          }
        ],
        "internalType" => "struct StructTester.Item[]",
        "name" => "",
        "type" => "tuple[]"
      }
    ],
    "stateMutability" => "view",
    "type" => "function"
  }
]

# running through ABI.parse_specification we get

[
  %ABI.FunctionSelector{
    function: "getItems",
    input_names: [],
    inputs_indexed: nil,
    method_id: <<65, 13, 89, 204>>,
    returns: [array: {:tuple, [uint: 256, uint: 256]}],
    type: :function,
    types: []
  }
]

Since the outputs had a component with names (price and itemId) for the internal struct should there be an output_names list in addition to input_names? Would that list have those internal names? Or could we add another field that looks up components? The end goal for me is to unwrap the tuple into a key/value pair.

abi parsing from json

22:26:07.792 [warn]  couldn't parse! PaymentChallengeIFENotCanonical.json

 because of %MatchError{term: {:error, {1, :ethereum_abi_parser, ['syntax error before: ', ['"PlasmaFramework"']]}}}

22:26:07.823 [warn]  couldn't parse! PaymentPiggybackInFlightExit.json because of %MatchError{term: {:error, {1, :ethereum_abi_parser, ['syntax error before: ', ['"PlasmaFramework"']]}}}

22:26:07.888 [warn]  couldn't parse! PaymentStartStandardExit.json because of %MatchError{term: {:error, {1, :ethereum_abi_parser, ['syntax error before: ', ['"IExitProcessor"']]}}}

22:26:08.069 [warn]  couldn't parse! PaymentChallengeStandardExit.json because of %MatchError{term: {:error, {1, :ethereum_abi_parser, ['syntax error before: ', ['"PlasmaFramework"']]}}}

22:26:08.471 [warn]  couldn't parse! PaymentStartInFlightExit.json because of %MatchError{term: {:error, {1, :ethereum_abi_parser, ['syntax error before: ', ['"PlasmaFramework"']]}}}

22:26:08.543 [warn]  couldn't parse! PaymentInFlightExitRouterMock.json because of %RuntimeError{message: "Unsupported type: :tuple"}

22:26:08.651 [warn]  couldn't parse! PaymentChallengeIFEInputSpent.json
 because of %MatchError{term: {:error, {1, :ethereum_abi_parser, ['syntax error before: ', ['"PlasmaFramework"']]}}}

Code to reproduce:

defmodule AbiEvents do
  @moduledoc """
  Extract ABI event definitions from contract ABIs
  """
  require Logger

  def get(abi_path) do
    abi_path
    |> File.ls!()
    |> Enum.map(fn file ->
      try do
        [abi_path, file]
        |> Path.join()
        |> File.read!()
        |> Jason.decode!()
        |> Map.fetch!("abi")
        |> ABI.parse_specification(include_events?: true)
      rescue
        x in [MatchError, RuntimeError] ->
          _ = Logger.warn("couldn't parse! #{file} because of #{inspect(x)}")
          []
      end
    end)
    |> List.flatten()
    |> Enum.filter(fn fs -> fs.type == :event end)
  end
end

ABIS: Archive.zip

Error when decoding a string with 'ABI.TypeDecoder.decode'

Description

Trying to decode a string using the decode/2 function we receive an error:

iex> "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000441494f4e00000000000000000000000000000000000000000000000000000000"
...> |> Base.decode16!(case: :lower) 
...> |> ABI.TypeDecoder.decode(%ABI.FunctionSelector{function: "name", types: [], returns: :string})
** (RuntimeError) Found extra binary data: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...>>
    (abi) lib/abi/type_decoder.ex:155: ABI.TypeDecoder.do_decode/3

It also happens with the decode_raw/2 function

iex> "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000441494f4e00000000000000000000000000000000000000000000000000000000"
...> |> Base.decode16!(case: :lower) 
...> |> ABI.TypeDecoder.decode_raw([:string])
** (RuntimeError) Found extra binary data: <<65, 73, 79, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>
    (abi) lib/abi/type_decoder.ex:155: ABI.TypeDecoder.do_decode/3

Problem Decoding input

As a test, I am trying to decode input from this txn: https://etherscan.io/tx/0x5b5e8bef3fe643a2cd57dcb4fd4985aeeed87b1b78b7d2844a72d58b759f645a

I am pretty sure I have the proper signature and abi, data etc:
image

The input data is:

0x7ff36ab5000000000000000000000000000000000000000000000000050988c69102478c000000000000000000000000000000000000000000000000000000000000008000000000000000000000000038f7c6dc001ed4c5c163be7ed8de76e7b5e32490000000000000000000000000000000000000000000000000000000005f70ce010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000cc4304a31d09258b0029ea7fe63d032f52e44efe

The function signature is: swapExactETHForTokens(uint256,address[],address,uint256)

I am trying to use it like:

iex(2)> decoded_string = "7ff36ab5000000000000000000000000000000000000000000000000050988c69102478c000000000000000000000000000000000000000000000000000000000000008000000000000000000000000038f7c6dc001ed4c5c163be7ed8de76e7b5e32490000000000000000000000000000000000000000000000000000000005f70ce010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000cc4304a31d09258b0029ea7fe63d032f52e44efe" |> Base.decode16!(case: :lower)
output: <<127, 243, 106, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 5, 9, 136, 198, 145, 2, 71, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, ...>>

The command:

iex(3)> ABI.decode("swapExactETHForTokens(uint256,address[],address,uint256)", decoded_string)

gives me an error:

** (MatchError) no match of right hand side value: <<127, 243, 106, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 9, 136, 198, 145, 2, 71, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...>>
    (ex_abi 0.5.1) lib/abi/type_decoder.ex:299: ABI.TypeDecoder.decode_type/3
    (ex_abi 0.5.1) lib/abi/type_decoder.ex:180: anonymous fn/3 in ABI.TypeDecoder.do_decode_raw/2
    (elixir 1.11.0) lib/enum.ex:2181: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ex_abi 0.5.1) lib/abi/type_decoder.ex:177: ABI.TypeDecoder.do_decode_raw/2
    (ex_abi 0.5.1) lib/abi/type_decoder.ex:171: ABI.TypeDecoder.decode_raw/2

I'm not sure what I am doing wrong but if it's something obvious please let me know.
Thanks!

Issue decoding Seaport Zone getSeaportMetadata

data = "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008546573745a6f6e6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100794b048e2bcbab215c7b626b47c0b97d0c78b2a3ca9da47d2b3094ae99b0cb37000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000"

abi = %{
      "getSeaportMetadata" => %{
        "inputs" => [],
        "name" => "getSeaportMetadata",
        "outputs" => [
          %{"internalType" => "string", "name" => "name", "type" => "string"},
          %{
            "components" => [
              %{
                "internalType" => "uint256",
                "name" => "id",
                "type" => "uint256"
              },
              %{
                "internalType" => "bytes",
                "name" => "metadata",
                "type" => "bytes"
              }
            ],
            "internalType" => "struct Schema[]",
            "name" => "schemas",
            "type" => "tuple[]"
          }
        ],
        "stateMutability" => "view",
        "type" => "function"
      }
    }

method_name = "getSeaportMetadata"


ExW3.Abi.decode_output(abi, method_name, data)

Library version: 0.6.0

** (MatchError) no match of right hand side value: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...>>
    (ex_abi 0.6.0) lib/abi/type_decoder.ex:238: ABI.TypeDecoder.decode_bytes/4
    (ex_abi 0.6.0) lib/abi/type_decoder.ex:327: anonymous fn/3 in ABI.TypeDecoder.decode_type/3
    (elixir 1.14.4) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ex_abi 0.6.0) lib/abi/type_decoder.ex:325: ABI.TypeDecoder.decode_type/3
    (ex_abi 0.6.0) lib/abi/type_decoder.ex:188: anonymous fn/3 in ABI.TypeDecoder.do_decode_raw/2
    (elixir 1.14.4) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ex_abi 0.6.0) lib/abi/type_decoder.ex:185: ABI.TypeDecoder.do_decode_raw/2
    (ex_abi 0.6.0) lib/abi/type_decoder.ex:179: ABI.TypeDecoder.decode_raw/2
    (exw3 0.6.1) lib/exw3/abi.ex:52: ExW3.Abi.decode_output/3
    (hawku 0.1.0) lib/hawku/blockchain/ethereum/queries/blockchain_queries.ex:325: Hawku.Blockchain.Ethereum.Queries.ExW3Impl.call_eth_chain/5
    iex:5: (file)

Dynamic types encoding doesn't conform to spec

By spec I mean this: https://solidity.readthedocs.io/en/v0.5.13/abi-spec.html#use-of-dynamic-types

In particular:

whereas for the dynamic types uint32[] and bytes, we use the offset in bytes to the start of their data area, measured from the start of the value encoding

There was one test that touched this encoding/decoding, but it used the "decodes to binary that it encoded to" assertion, so it passed tests.

I suspect this worked before #14, but got missed there and removed. Now decoding "forgets" the "start of their data area" so it is not possible with the decode_... functions currently existing.

To spin this up, I added the tests for type_decoder to cover this and I did some tidies to facilitate that. Now there's a separation between tests that only use encode |> decode to test, tests leveraging examples from the spec and tests running on own data examples. New tests fail with exceptions, because things don't pattern match inside the guts of decoding. These tests are in a branch:

https://github.com/omisego/ex_abi/tree/fix_dynamic_arrays_decoding

It would be ideal to have the fix for the problem branch off of this branch/commit, so that the tests listed can start to pass and be kept.

(this branch with tests doesn't remove any of the existing tests, at most does some cosmetic refactors and moves them around)

Steps to reproduce

Checkout the branch ☝️ and mix test

cannot decode log data if the destination data size > 32 bytes?

to docode the log data,

raw_data = "00000000000000000000000097ab3d4f7f5051f127b0e9f8d10772125d94d65b0000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000407c278b7e4320528bdbf5c02db611282b431dbaec2509b1fbebe3de4be7442a4114f1c9970ffd0c589afa8ef6262692efd4edae5d77c87641f21f44b1d082c3480000000000000000000000000000000000000000000000000000000000000030b969678ef2cf458b49b8c568d95e63221efe0f30383a9b0c5eb683bf2e23d118664631ce992d81ec4b6127ec0a760f8600000000000000000000000000000000"

data_bytes = Base.decode16!(raw_data , case: :mixed)

[owner, rate, pub_key, bls_key] = TypeDecoder.decode_raw(data_bytes, [:address, {:uint, 256}, {:bytes, 64},  {:bytes, 48}])

and get this error message, pub_key size is 64 bytes and bls_key size is 48 bytes.

** (FunctionClauseError) no function clause matching in ABI.TypeDecoder.decode_type/2    
    
    The following arguments were given to ABI.TypeDecoder.decode_type/2:
    
        # 1
        {:bytes, 48}
    
        # 2
        <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 48, 185, 105, 103, 142, 242, 207, 69, 139, 73, 184, 197, 104,
          217, 94, 99, 34, 30, 254, ...>>

    Attempted function clauses (showing 7 out of 7):

        defp decode_type(-{:uint, size_in_bits}-, data)
        defp decode_type(-{:int, size_in_bits}-, data)
        defp decode_type({:bytes, size}, data) when size > 0 and -size <= 32-
        defp decode_type(-{:array, type, size}-, data)
        defp decode_type(-{:tuple, types}-, data)
        defp decode_type(-:address-, data)
        defp decode_type(-:bool-, data)

    (ex_abi 0.7.0) lib/abi/type_decoder.ex:243: ABI.TypeDecoder.decode_type/2
    (ex_abi 0.7.0) lib/abi/type_decoder.ex:190: anonymous fn/3 in ABI.TypeDecoder.do_decode_raw/2
    (elixir 1.14.5) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ex_abi 0.7.0) lib/abi/type_decoder.ex:185: ABI.TypeDecoder.do_decode_raw/2
    (ex_abi 0.7.0) lib/abi/type_decoder.ex:179: ABI.TypeDecoder.decode_raw/2

Problem when parsing tuples and bool

Hello, I've got a contract with the following view:

    function getVoteInfo() public view returns (bool, address, string memory, string memory, string memory, string memory, uint) {
        bool isActive = bool(block.timestamp < finishTime);
        return (isActive, voteFrom, question, choice_a, choice_b, choice_c, finishTime);
    }

I am trying to call this function using something like

{:ok, is_active, vote_from, question, a, b, c, finish_on} = ExW3.Contract.call(:Voter, :getVoteInfo)

When the isActive is false it works fine. However, when it is true I get the following exception:

    ** (EXIT) an exception was raised:
        ** (CaseClauseError) no case clause matching: 256
            (ex_abi 0.5.8) lib/abi/type_decoder.ex:292: ABI.TypeDecoder.decode_type/2
            (ex_abi 0.5.8) lib/abi/type_decoder.ex:336: anonymous fn/3 in ABI.TypeDecoder.decode_type/3
            (elixir 1.12.1) lib/enum.ex:2356: Enum."-reduce/3-lists^foldl/2-0-"/3
            (ex_abi 0.5.8) lib/abi/type_decoder.ex:331: ABI.TypeDecoder.decode_type/3
            (ex_abi 0.5.8) lib/abi/type_decoder.ex:188: anonymous fn/3 in ABI.TypeDecoder.do_decode_raw/2
            (elixir 1.12.1) lib/enum.ex:2356: Enum."-reduce/3-lists^foldl/2-0-"/3
            (ex_abi 0.5.8) lib/abi/type_decoder.ex:185: ABI.TypeDecoder.do_decode_raw/2
            (ex_abi 0.5.8) lib/abi/type_decoder.ex:179: ABI.TypeDecoder.decode_raw/2
            (exw3 0.6.1) lib/exw3/abi.ex:52: ExW3.Abi.decode_output/3
            (exw3 0.6.1) lib/exw3/contract.ex:214: ExW3.Contract.eth_call_helper/4
            (exw3 0.6.1) lib/exw3/contract.ex:480: ExW3.Contract.handle_call/3
            (stdlib 3.15) gen_server.erl:721: :gen_server.try_handle_call/4
            (stdlib 3.15) gen_server.erl:750: :gen_server.handle_msg/6
            (stdlib 3.15) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

I checked the ABI.TypeDecoder.decode_type method and I see the following source:

  defp decode_type(:bool, data) do
    {encoded_value, rest} = decode_uint(data, 8)

    value =
      case encoded_value do
        1 -> true
        0 -> false
      end

    {value, rest}
  end

However it seems that for some reason the value it gets decodes to 256 and not 1 or 0. After more debugging I took a peek at the defp decode_type({:tuple, types}, data, full_data) function and added some inspects:

So, the decode_type({:tuple}) function receives the following data/full_data when the bool is true:

Data: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 159, 214, 229, 26,
  173, ...>>
Full data: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 159, 214, 229, 26,
  173, ...>>

Notice that there are 31 zeros before the last 1 so if this is decoded it will be 1.

However, the decode_type(:bool function receives the following data:

Decode bool data: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 159, 214, 229, 26,
  173, 136, ...>>

If you now cound the zeros before the one you'll cound 30 zeros so if this is to be decoded it will be decoded to 10 = 2! Then for some reason I can't understand this is converted to 256!! Any idea why the data is garbled?

Notice everything's working fine I I call the same method from ethers.js.

Error processing empty data field in event

Hello, First off want to thank the authors and maintainers so bringing this project to life.

logs = ExW3.get_logs(
        %{
          fromBlock: "0x" <> Integer.to_string(fromBlock, 16),
          toBlock: "0x" <> Integer.to_string(toBlock, 16),
          # [A, B] topics means A and B so lets send [[A, B]] which means OR
          topics: [["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
                         "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0"]]
        }
      )

abi = File.read!(Path.join(Application.app_dir(:myapp, "priv"), "static/721abi.json"))
       |> Jason.decode!()
       |> ABI.parse_specification(include_events?: true)

events =
      logs
      |> Enum.map(fn log ->
        [topic1, topic2, topic3, topic4] =
          case length(log["topics"]) do
            4 -> log["topics"]
            3 -> log["topics"] ++ [nil]
            2 -> log["topics"] ++ [nil, nil]
            _ -> raise "Invalid topics"
          end
       abi
        |> ABI.Event.find_and_decode(
          convert_hex_to_binary(topic1),
          convert_hex_to_binary(topic2),
          convert_hex_to_binary(topic3),
          convert_hex_to_binary(topic4),
          log["data"]
        )

The above code fetches logs for a block range and tries to extract event from it. But it fails on log when its data is 0x.

** (MatchError) no match of right hand side value: "0x"
    (ex_abi 0.5.13) lib/abi/type_decoder.ex:371: ABI.TypeDecoder.decode_uint/2
    (ex_abi 0.5.13) lib/abi/type_decoder.ex:190: anonymous fn/3 in ABI.TypeDecoder.do_decode_raw/2
    (elixir 1.14.0) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ex_abi 0.5.13) lib/abi/type_decoder.ex:185: ABI.TypeDecoder.do_decode_raw/2
    (ex_abi 0.5.13) lib/abi/type_decoder.ex:179: ABI.TypeDecoder.decode_raw/2
    (ex_abi 0.5.13) lib/abi/event.ex:84: ABI.Event.find_and_decode/6
    (Myapp 0.1.0) lib/myapp/chain/events.ex:60: anonymous fn/1 in myapp.Chain.Events.get_logs/3
    (elixir 1.14.0) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
    (elixir 1.14.0) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
    iex:2: (file)

Try for block range 15714764, 15714774 on mainnet and you can reproduce it. When i converted data 0x to integer the function succeeds but it returns get the wad / tokenId wrong in the event. Can the library handle topics and data in hex itself? Curious to get your thoughts as well.

Just found this https://github.com/wmitsuda/topic0/blob/main/docs/gotchas.md

So %ABI.FunctionSignature{} is querying by methodId. Can we figure out a way to add args to that calculation and query ? That way we can use the right ABI to decode the log.

Error decoding struct

iex(17)> ABI.TypeDecoder.decode(r, s, :output)
    ** (MatchError) no match of right hand side value: "0000000000000000000000001429d3e6d95c7a4ad947523784f6290409b0886b000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000183307f698000000000000000000000000000000000000000000000000000000183307f69800000000000000000000000000000000000000000000000000000000000000042697066733a2f2f6261666b726569686c346369367833767470796b32736a3674746b7674643669706b756664756d756e68646e3233676a7332656f616470336b7375000000000000000000000000000000000000000000000000000000000000"
    (ex_abi 0.5.16) lib/abi/type_decoder.ex:351: ABI.TypeDecoder.decode_type/3
    (ex_abi 0.5.16) lib/abi/type_decoder.ex:188: anonymous fn/3 in ABI.TypeDecoder.do_decode_raw/2
    (elixir 1.14.2) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ex_abi 0.5.16) lib/abi/type_decoder.ex:185: ABI.TypeDecoder.do_decode_raw/2
    (ex_abi 0.5.16) lib/abi/type_decoder.ex:179: ABI.TypeDecoder.decode_raw/2

The example is of a struct with four fields of type address, bytes, uint256 and uint256:

struct Contribution {
    address owner;
    bytes detailsUri;
    uint256 dateOfSubmission;
    uint256 dateOfEngagement;
}

mapping(uint256 => Contribution) public contributions;

When calling constructions, it errors decoding the output. The response string is correctly built. As an example, using the following output:

"0000000000000000000000001429d3e6d95c7a4ad947523784f6290409b0886b000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000183307f698000000000000000000000000000000000000000000000000000000183307f69800000000000000000000000000000000000000000000000000000000000000042697066733a2f2f6261666b726569686c346369367833767470796b32736a3674746b7674643669706b756664756d756e68646e3233676a7332656f616470336b7375000000000000000000000000000000000000000000000000000000000000"

I get the error above when decoding through the lib, while decoding through an online decoder like this one I get the right values.

From what I could gather, it's due to a bad offset. In type_decoder.ex:

  defp decode_type(:bytes, data, full_data) do
    {offset, rest} = decode_uint(data, 256)
    <<_padding::binary-size(offset), rest_data::binary>> = full_data
    {byte_size, dynamic_length_data} = decode_uint(rest_data, 256)
    decode_bytes(dynamic_length_data, byte_size, :right, rest)
  end

From a quick look, the value of offset does not match the actual offset, causing the match with full_data to fail - although I wasn't able to look deep into the reasons for it.

Error when decoding array of uint256

Hi, I'm a new at elixir development so maybe I'm missing something in my bug description but I'm currently using ExWeb3 that ends up depending on this package. I'm using version 0.5.11 of this package.

While attempting to call a contract method that returns uint256[] I get the following error only if the array is not empty:

[error] GenServer ContractManager terminating
** (MatchError) no match of right hand side value: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2>>
    (ex_abi 0.5.11) lib/abi/type_decoder.ex:371: ABI.TypeDecoder.decode_uint/2
    (ex_abi 0.5.11) lib/abi/type_decoder.ex:271: anonymous fn/3 in ABI.TypeDecoder.decode_type/2
    (elixir 1.13.4) lib/enum.ex:2396: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ex_abi 0.5.11) lib/abi/type_decoder.ex:266: ABI.TypeDecoder.decode_type/2
    (ex_abi 0.5.11) lib/abi/type_decoder.ex:311: ABI.TypeDecoder.decode_type/3
    (ex_abi 0.5.11) lib/abi/type_decoder.ex:333: anonymous fn/3 in ABI.TypeDecoder.decode_type/3
    (elixir 1.13.4) lib/enum.ex:2396: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ex_abi 0.5.11) lib/abi/type_decoder.ex:331: ABI.TypeDecoder.decode_type/3
    (ex_abi 0.5.11) lib/abi/type_decoder.ex:188: anonymous fn/3 in ABI.TypeDecoder.do_decode_raw/2
    (elixir 1.13.4) lib/enum.ex:2396: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ex_abi 0.5.11) lib/abi/type_decoder.ex:185: ABI.TypeDecoder.do_decode_raw/2
    (ex_abi 0.5.11) lib/abi/type_decoder.ex:179: ABI.TypeDecoder.decode_raw/2
    (exw3 0.6.1) lib/exw3/abi.ex:52: ExW3.Abi.decode_output/3
    (exw3 0.6.1) lib/exw3/contract.ex:214: ExW3.Contract.eth_call_helper/4
    (exw3 0.6.1) lib/exw3/contract.ex:480: ExW3.Contract.handle_call/3
    (stdlib 3.17.1) gen_server.erl:721: :gen_server.try_handle_call/4
    (stdlib 3.17.1) gen_server.erl:750: :gen_server.handle_msg/6
    (stdlib 3.17.1) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

This is the raw JSON-RPC response from the ethereum node

{"jsonrpc":"2.0","id":5,"result":"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"}

this is issue is comming when i compile the blockscout code in windows OS

@vbaranov

==> libsecp256k1
could not compile dependency :libsecp256k1, "mix compile" failed. Errors may have been logged above. You can recompile this dependency with "mix deps.compile libsecp256k1", update it with "mix deps.update libsecp256k1" or clean it with "mix deps.clean libsecp256k1"
** (ErlangError) Erlang error: :enoent
(elixir 1.13.4) lib/system.ex:1044: System.cmd("make", [], [into: %IO.Stream{device: :standard_io, line_or_bytes: :line, raw: false}])
e:/Phoniex/blockscout-master/deps/libsecp256k1/mix.exs:3: Mix.Tasks.Compile.MakeBindings.run/1
(mix 1.13.4) lib/mix/task.ex:397: anonymous fn/3 in Mix.Task.run_task/3
(mix 1.13.4) lib/mix/tasks/compile.all.ex:92: Mix.Tasks.Compile.All.run_compiler/2
(mix 1.13.4) lib/mix/tasks/compile.all.ex:72: Mix.Tasks.Compile.All.compile/4
(mix 1.13.4) lib/mix/tasks/compile.all.ex:59: Mix.Tasks.Compile.All.with_logger_app/2
(mix 1.13.4) lib/mix/tasks/compile.all.ex:36: Mix.Tasks.Compile.All.run/1
(mix 1.13.4) lib/mix/task.ex:397: anonymous fn/3 in Mix.Task.run_task/3

ABI spec parsing from contract abi

In this snapshot https://storage.googleapis.com/circleci-docker-artifacts/data-elixir-omg-tester-plasma-deployer-dev-95a6531-MIN_EXIT_PERIOD-20-PLASMA_CONTRACTS_SHA-67e6aae8d22fa3991c7d8bc43df9d4b6cf058350-VAULT-false.tar.gz

is a file called PaymentStandardExitRouter.json.

If we load this file by:

path
|> File.read!()
|> Jason.decode!()
|> Map.fetch!("abi")
|> ABI.parse_specification(include_events?: false)

we can observe that the specification for "standardExits" function is wrong:

%ABI.FunctionSelector{
      function: "standardExits",
      input_names: ["exit_ids"],
      inputs_indexed: nil,
      method_id: <<121, 75, 85, 147>>,
      returns: [
        array:
          {:tuple,
           [
             :bool,
             {:uint, 256},
             {:bytes, 32},
             :address,
             {:uint, 256},
             {:uint, 256},
             {:uint, 256}
           ]}
      ],
      type: :function,
      types: [array: {:uint, 168}]
    }

the bug is that returned types should be: ```
types: [array: {:tuple, [:bool, {:uint, 256}, {:bytes, 32}, :address, {:uint, 256}, {:uint, 256}]}]



{:ex_abi, "~> 0.5.1"} and {:ex_abi, "~> 0.5.2"} 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.