Go to file
Ayrat Badykov 26621a9b2e
release 0.7.2 (#167)
2024-04-08 09:57:35 +03:00
.github update ex_keccak to 0.7.5 (#166) 2024-04-08 09:51:17 +03:00
lib release 0.7.1 (#165) 2024-03-12 14:42:29 +02:00
priv ABI parsing tuple type 2020-05-06 14:44:42 +03:00
src Added enum type (#135) 2023-09-01 10:25:50 +03:00
test Fix parsing when function ABI is missing the `outputs` field (#163) 2024-03-12 14:26:11 +02:00
.formatter.exs format code 2018-06-22 11:15:20 +03:00
.gitignore Create a formal lexer+parser for typespecs and function signatures 2018-01-15 19:11:42 -08:00
.tool-versions update jason to 1.4.0 (#107) 2022-09-15 10:00:22 +03:00
CHANGELOG.md release 0.7.2 (#167) 2024-04-08 09:57:35 +03:00
LICENSE Initial commit 2018-06-22 11:08:34 +03:00
README.md release 0.7.2 (#167) 2024-04-08 09:57:35 +03:00
mix.exs release 0.7.2 (#167) 2024-04-08 09:57:35 +03:00
mix.lock update ex_keccak to 0.7.5 (#166) 2024-04-08 09:51:17 +03:00

README.md

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 to your list of dependencies in mix.exs:

def deps do
  [
    {:ex_abi, "~> 0.7.2"}
  ]
end

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

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