ABI.TypeEncoder (hieroglyph v1.4.0)

Copy Markdown View Source

ABI.TypeEncoder is responsible for encoding types to the format expected by Solidity. We generally take a function selector and an array of data and encode that array according to the specification.

API Functions

FunctionArityDescriptionParam Kinds
encode_packed2Encodes values using Solidity's non-standard packed mode (abi.encodePacked) — types <32 bytes concatenated tight, dynamic types in-place without length prefix, array elements padded to 32 bytes.data: value, types: value
encode_raw2Encode a list of values directly against an explicit type list, without prepending a method-id selector. Used for return values, event data, or pre-routed calldata payloads.data: value, types: value
encode2Encode a list of values into ABI calldata using the given FunctionSelector, prefixing the 4-byte selector when the function name is set.data: value, function_selector: value

Summary

Functions

Encodes the given data based on the function selector.

Encodes a list of values using Solidity's non-standard packed mode.

Simiar to ABI.TypeEncoder.encode/2 except we accept an array of types instead of a function selector. We also do not pre-pend the method id.

Functions

encode(data, function_selector)

@spec encode([any()], ABI.FunctionSelector.t()) :: binary()

Encodes the given data based on the function selector.

Examples

iex> [69, true]
...> |> ABI.TypeEncoder.encode(
...>      %ABI.FunctionSelector{
...>        function: "baz",
...>        types: [
...>          %{type: {:uint, 32}},
...>          %{type: :bool}
...>        ],
...>        returns: :bool
...>      }
...>    )
...> |> Base.encode16(case: :lower)
"cdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001"

iex> ["BAT"]
...> |> ABI.TypeEncoder.encode(
...>      %ABI.FunctionSelector{
...>        function: "price",
...>        types: [
...>          %{type: :string}
...>        ],
...>        returns: {:uint, 256}
...>      }
...>    )
...> |> Base.encode16(case: :lower)
"fe2c6198000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034241540000000000000000000000000000000000000000000000000000000000"


iex> [Base.decode16!("ffffffffffffffffffffffffffffffffffffffff", case: :lower)]
...> |> ABI.TypeEncoder.encode(
...>      %ABI.FunctionSelector{
...>        function: "price",
...>        types: [
...>          %{type: :address}
...>        ]
...>      }
...>    )
...> |> Base.encode16(case: :lower)
"aea91078000000000000000000000000ffffffffffffffffffffffffffffffffffffffff"

iex> [1]
...> |> ABI.TypeEncoder.encode(
...>      %ABI.FunctionSelector{
...>        function: "price",
...>        types: [
...>          %{type: :address}
...>        ]
...>      }
...>    )
...> |> Base.encode16(case: :lower)
"aea910780000000000000000000000000000000000000000000000000000000000000001"

iex> ["hello world"]
...> |> ABI.TypeEncoder.encode(
...>      %ABI.FunctionSelector{
...>        function: nil,
...>        types: [
...>          %{type: :string},
...>        ]
...>      }
...>    )
...> |> Base.encode16(case: :lower)
"000000000000000000000000000000000000000000000000000000000000000b68656c6c6f20776f726c64000000000000000000000000000000000000000000"

iex> [{{0x11, 0x22}, "hello world"}]
...> |> ABI.TypeEncoder.encode(
...>      %ABI.FunctionSelector{
...>        function: nil,
...>        types: [
...>          %{type: {:tuple, [
...>            %{type: {:tuple, [%{type: {:uint, 256}},%{type: {:uint, 256}}]}},
...>            %{type: :string},
...>          ]}}
...>        ]
...>      }
...>    )
...> |> Base.encode16(case: :lower)
"000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000b68656c6c6f20776f726c64000000000000000000000000000000000000000000"

iex> [{"awesome", true}]
...> |> ABI.TypeEncoder.encode(
...>      %ABI.FunctionSelector{
...>        function: nil,
...>        types: [
...>          %{type: {:tuple, [%{type: :string}, %{type: :bool}]}}
...>        ]
...>      }
...>    )
...> |> Base.encode16(case: :lower)
"000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000007617765736f6d6500000000000000000000000000000000000000000000000000"

iex> [{17, true, <<32, 64>>}]
...> |> ABI.TypeEncoder.encode(
...>      %ABI.FunctionSelector{
...>        function: nil,
...>        types: [
...>          %{type: {:tuple, [%{type: {:uint, 32}}, %{type: :bool}, %{type: {:bytes, 2}}]}}
...>        ]
...>      }
...>    )
...> |> Base.encode16(case: :lower)
"000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000012040000000000000000000000000000000000000000000000000000000000000"

iex> [[17, 1]]
...> |> ABI.TypeEncoder.encode(
...>      %ABI.FunctionSelector{
...>        function: "baz",
...>        types: [
...>          %{type: {:array, {:uint, 32}, 2}}
...>        ]
...>      }
...>    )
...> |> Base.encode16(case: :lower)
"3d0ec53300000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000001"

iex> [[17, 1], true]
...> |> ABI.TypeEncoder.encode(
...>      %ABI.FunctionSelector{
...>        function: nil,
...>        types: [
...>          %{type: {:array, {:uint, 32}, 2}},
...>          %{type: :bool}
...>        ]
...>      }
...>    )
...> |> Base.encode16(case: :lower)
"000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001"

iex> [[17, 1]]
...> |> ABI.TypeEncoder.encode(
...>      %ABI.FunctionSelector{
...>        function: nil,
...>        types: [
...>          %{type: {:array, {:uint, 32}}}
...>        ]
...>      }
...>    )
...> |> Base.encode16(case: :lower)
"000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000001"

iex> [
...>   <<1::160>>,
...>   <<2::160>>,
...>   <<3::256>>,
...>   {
...>     4,
...>     <<5::160>>,
...>     <<6>>,
...>     <<7::512>>,
...>     8
...>   },
...>   9,
...>   <<0xa::256>>,
...>   <<0xb::256>>
...> ]
...> |> ABI.TypeEncoder.encode(
...>   %ABI.FunctionSelector{
...>     function: "test",
...>     function_type: :function,
...>     state_mutability: :nonpayable,
...>     types: [
...>       %{name: "a", type: :address},
...>       %{name: "b", type: :address},
...>       %{name: "c", type: {:bytes, 32}},
...>       %{
...>         name: "d",
...>         type:
...>           {:tuple,
...>            [
...>              %{name: "e", type: {:uint, 96}},
...>              %{name: "f", type: :address},
...>              %{name: "g", type: :bytes},
...>              %{name: "h", type: :bytes},
...>              %{name: "i", type: {:uint, 256}}
...>            ]}
...>       },
...>       %{name: "j", type: {:uint, 8}},
...>       %{name: "k", type: {:bytes, 32}},
...>       %{name: "l", type: {:bytes, 32}}
...>     ],
...>     returns: [%{name: "", type: :bytes}]
...>   }
...> )
...> |> Base.encode16(case: :lower)
"19c9d90a00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000010600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007"

iex> [%{x: 42, flag: true}]
...> |> ABI.TypeEncoder.encode(
...>      %ABI.FunctionSelector{
...>        function: nil,
...>        types: [
...>          %{type: {:tuple, [
...>            %{name: "x", type: {:uint, 32}},
...>            %{name: "flag", type: :bool}
...>          ]}}
...>        ]
...>      }
...>    )
...> |> Base.encode16(case: :lower)
"000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000001"

iex> [-255]
...> |> ABI.TypeEncoder.encode(%ABI.FunctionSelector{function: nil, types: [%{type: {:int, 16}}]})
...> |> Base.encode16(case: :lower)
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01"

encode_packed(data, types)

@spec encode_packed([any()], [ABI.FunctionSelector.argument_type()]) :: binary()

Encodes a list of values using Solidity's non-standard packed mode.

Used primarily for keccak256(abi.encodePacked(...)) Merkle leaves and signature schemes; never used for actual function calls (the spec defines no decoding function — encoding is ambiguous with multiple dynamic args).

Tuple/struct values and nested arrays raise ArgumentError — Solidity's spec does not define their packed encoding.

Examples

iex> ABI.TypeEncoder.encode_packed(
...>   [-1, <<0x42>>, 3, "Hello, world!"],
...>   [
...>     %{type: {:int, 16}},
...>     %{type: {:bytes, 1}},
...>     %{type: {:uint, 16}},
...>     %{type: :string}
...>   ]
...> ) |> Base.encode16(case: :lower)
"ffff42000348656c6c6f2c20776f726c6421"

encode_raw(data, types)

@spec encode_raw([any()], [ABI.FunctionSelector.argument_type()]) :: binary()

Simiar to ABI.TypeEncoder.encode/2 except we accept an array of types instead of a function selector. We also do not pre-pend the method id.

Examples

iex> [{"awesome", true}]
...> |> ABI.TypeEncoder.encode_raw([%{type: {:tuple, [%{type: :string}, %{type: :bool}]}}])
...> |> Base.encode16(case: :lower)
"000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000007617765736f6d6500000000000000000000000000000000000000000000000000"