ABI.TypeDecoder is responsible for decoding types to the format
expected by Solidity. We generally take a function selector and binary
data and decode that into the original arguments according to the
specification.
API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
decode_bytes | 3 | Read size_in_bytes of content from a 32-byte-aligned ABI word, skipping padding on the matching side. Used to extract address, uint/int, bytes<M>, and string payloads from their slots. | data: value, size_in_bytes: value, padding_direction: value |
tuple_value | 3 | Combine a list of ABI argument types with decoded element values, returning either a tuple or, when decode_structs is enabled and every type carries a non-empty :name, a map keyed by snake_case atom field names. | types: value, elements: value, decode_structs: value |
decode_raw | 3 | Decode an ABI-encoded payload directly against an explicit type list, without consulting a FunctionSelector. | encoded_data: value, types: value, opts: value |
decode | 3 | Decode an ABI-encoded payload into a list of values, using a FunctionSelector to drive type interpretation. | encoded_data: value, function_selector: value, opts: value |
Summary
Functions
Decodes the given data based on the function selector.
Reads size_in_bytes of content out of data, skipping the 32-byte-slot
padding on whichever side matches padding_direction (:left for
left-padded types like address and uint/int, :right for
right-padded types like bytes<M> and string). Returns {value, rest}.
Similar to ABI.TypeDecoder.decode/2 except accepts a list of types instead
of a function selector.
Combines a list of ABI argument types with a list of decoded element values
into either a tuple or (when decode_structs is true and every type carries
a non-empty :name) a map keyed by the existing snake_case atom for each
field name.
Functions
@spec decode(binary(), ABI.FunctionSelector.t(), keyword()) :: [any()]
Decodes the given data based on the function selector.
Note, we don't currently try to guess the function name?
Examples
iex> "00000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode(
...> %ABI.FunctionSelector{
...> function: "baz",
...> types: [
...> %{type: {:uint, 32}},
...> %{type: :bool}
...> ],
...> returns: :bool
...> }
...> )
[69, true]
iex> "000000000000000000000000000000000000000000000000000000000000000b68656c6c6f20776f726c64000000000000000000000000000000000000000000"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode(
...> %ABI.FunctionSelector{
...> function: nil,
...> types: [
...> %{type: :string}
...> ]
...> }
...> )
["hello world"]
iex> "00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000001"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode(
...> %ABI.FunctionSelector{
...> function: nil,
...> types: [
...> %{type: {:tuple, [%{type: {:uint, 32}, name: "a"}, %{type: :bool, name: "b"}]}}
...> ]
...> }
...> )
[{17, true}]
iex> "00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000001"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode(
...> %ABI.FunctionSelector{
...> function: nil,
...> types: [
...> %{type: {:tuple, [%{type: {:uint, 32}, name: "a"}, %{type: :bool, name: "b"}]}}
...> ]
...> },
...> decode_structs: true
...> )
[%{a: 17, b: true}]
iex> "00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000001"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode(
...> %ABI.FunctionSelector{
...> function: nil,
...> types: [
...> %{type: {:tuple, [%{type: {:uint, 32}}, %{type: :bool}]}}
...> ]
...> }
...> )
[{17, true}]
iex> "00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000001"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode(
...> %ABI.FunctionSelector{
...> function: nil,
...> types: [
...> %{type: {:array, {:uint, 32}, 2}}
...> ]
...> }
...> )
[[17, 1]]
iex> "000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000001"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode(
...> %ABI.FunctionSelector{
...> function: nil,
...> types: [
...> %{type: {:array, {:uint, 32}}}
...> ]
...> }
...> )
[[17, 1]]
iex> "0000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000011020000000000000000000000000000000000000000000000000000000000000"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode(
...> %ABI.FunctionSelector{
...> function: nil,
...> types: [
...> %{type: {:array, {:uint, 32}, 2}},
...> %{type: :bool},
...> %{type: {:bytes, 2}}
...> ]
...> }
...> )
[[17, 1], true, <<16, 32>>]
iex> "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000007617765736f6d6500000000000000000000000000000000000000000000000000"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode(
...> %ABI.FunctionSelector{
...> function: nil,
...> types: [
...> %{type: {:tuple, [%{type: :string}, %{type: :bool}]}}
...> ]
...> }
...> )
[{"awesome", true}]
iex> "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode(
...> %ABI.FunctionSelector{
...> function: nil,
...> types: [
...> %{type: {:tuple, [%{type: {:array, :address}}]}}
...> ]
...> }
...> )
[{[]}]
iex> "00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000c556e617574686f72697a656400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000204a2bf2ff0a4eaf1890c8d8679eaa446fb852c4000000000000000000000000861d9af488d5fa485bb08ab6912fff4f7450849a"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode(
...> %ABI.FunctionSelector{
...> function: nil,
...> types: [%{type: {:tuple,[
...> %{type: :string},
...> %{type: {:array, {:uint, 256}}}
...> ]}}]
...> }
...> )
[{
"Unauthorized",
[
184341788326688649239867304918349890235378717380,
765664983403968947098136133435535343021479462042,
]
}]
iex> "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034241540000000000000000000000000000000000000000000000000000000000"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode(
...> %ABI.FunctionSelector{
...> function: "price",
...> types: [
...> %{type: :string}
...> ],
...> returns: {:uint, 256}
...> }
...> )
["BAT"]
Reads size_in_bytes of content out of data, skipping the 32-byte-slot
padding on whichever side matches padding_direction (:left for
left-padded types like address and uint/int, :right for
right-padded types like bytes<M> and string). Returns {value, rest}.
@spec decode_raw(binary(), [ABI.FunctionSelector.argument_type()], keyword()) :: [ any() ]
Similar to ABI.TypeDecoder.decode/2 except accepts a list of types instead
of a function selector.
Examples
iex> "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000007617765736f6d6500000000000000000000000000000000000000000000000000"
...> |> Base.decode16!(case: :lower)
...> |> ABI.TypeDecoder.decode_raw([%{type: {:tuple, [%{type: :string}, %{type: :bool}]}}])
[{"awesome", true}]
@spec tuple_value([ABI.FunctionSelector.argument_type()], [any()], boolean()) :: map() | tuple()
Combines a list of ABI argument types with a list of decoded element values
into either a tuple or (when decode_structs is true and every type carries
a non-empty :name) a map keyed by the existing snake_case atom for each
field name.
Field-name atoms must already exist in the VM atom table — decode_structs: true calls String.to_existing_atom/1 on Macro.underscore(name) and
raises ArgumentError if the atom has not been interned. This bounds atom
creation to the set of field names the caller has explicitly referenced in
their code, closing a DoS surface for consumers that ingest ABIs from
arbitrary sources.
Used internally by decode_type({:tuple, types}, ...) to render the
second-pass result; exposed because event-log decoding in ABI.Event
reuses the same shape.