Packages
abi
1.0.0-bravo6
1.3.0
1.2.0
1.1.0
1.0.0-bravo6
1.0.0-bravo4
1.0.0-bravo3
1.0.0-bravo2
1.0.0-bravo1
1.0.0-alpha9
1.0.0-alpha8
1.0.0-alpha7
1.0.0-alpha6
1.0.0-alpha5
1.0.0-alpha4
1.0.0-alpha3
1.0.0-alpha2
1.0.0-alpha1
0.1.21
0.1.21-alpha2
0.1.21-alpha1
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Ethereum's ABI Interface
Current section
Files
Jump to
Current section
Files
lib/abi/type_decoder.ex
defmodule ABI.TypeDecoder do
@moduledoc """
`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.
"""
@type decode_options :: %{
optional(:decode_structs) => boolean()
}
@doc """
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"]
"""
def decode(encoded_data, function_selector, opts \\ []) do
if is_nil(function_selector.function) do
decode_raw(encoded_data, function_selector.types, opts)
else
[res] = decode_raw(encoded_data, [%{type: {:tuple, function_selector.types}}], opts)
Tuple.to_list(res)
end
end
@doc """
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}]
"""
def decode_raw(encoded_data, types, opts \\ []) do
do_decode(types, encoded_data, [], opts)
end
@spec do_decode([ABI.FunctionSelector.type()], binary(), [any()], decode_options()) :: [any()]
defp do_decode([], bin, _, _opts) when byte_size(bin) > 0,
do: raise("Found extra binary data: #{inspect(bin)}")
defp do_decode([], _, acc, _opts), do: Enum.reverse(acc)
defp do_decode([type | remaining_types], data, acc, opts) do
{decoded, remaining_data} = decode_type(type.type, data, opts)
do_decode(remaining_types, remaining_data, [decoded | acc], opts)
end
@spec decode_type(ABI.FunctionSelector.type(), binary(), decode_options()) :: {any(), binary()}
defp decode_type({:uint, size_in_bits}, data, _opts) do
decode_uint(data, size_in_bits)
end
defp decode_type({:int, size_in_bits}, data, _opts) do
decode_int(data, size_in_bits)
end
defp decode_type(:address, data, _opts), do: decode_bytes(data, 20, :left)
defp decode_type(:bool, data, _opts) do
{encoded_value, rest} = decode_uint(data, 8)
value =
case encoded_value do
1 -> true
0 -> false
end
{value, rest}
end
defp decode_type(:string, data, _opts) do
{string_size_in_bytes, rest} = decode_uint(data, 256)
{raw_bytes, rest} = decode_bytes(rest, string_size_in_bytes, :right)
{nul_terminate_string(raw_bytes), rest}
end
defp decode_type(:bytes, data, _opts) do
{byte_size, rest} = decode_uint(data, 256)
decode_bytes(rest, byte_size, :right)
end
defp decode_type({:bytes, 0}, data, _opts), do: {<<>>, data}
defp decode_type({:bytes, size}, data, _opts) when size > 0 and size <= 32 do
decode_bytes(data, size, :right)
end
defp decode_type({:array, type}, data, opts) do
{element_count, rest} = decode_uint(data, 256)
decode_type({:array, type, element_count}, rest, opts)
end
defp decode_type({:array, _type, 0}, data, _opts), do: {[], data}
defp decode_type({:array, type, element_count}, data, opts) do
repeated_type = Enum.map(1..element_count, fn _ -> %{type: type} end)
{tuple, rest} = decode_type({:tuple, repeated_type}, data, opts)
{tuple |> Tuple.to_list(), rest}
end
defp decode_type({:tuple, types}, starting_data, opts) do
decode_structs = Keyword.get(opts, :decode_structs, false)
# First pass, decode static types
{elements, rest} =
Enum.reduce(types, {[], starting_data}, fn %{type: type}, {elements, data} ->
if ABI.FunctionSelector.is_dynamic?(type) do
{tail_position, rest} = decode_type({:uint, 256}, data, opts)
{[{:dynamic, type, tail_position} | elements], rest}
else
{el, rest} = decode_type(type, data, opts)
{[el | elements], rest}
end
end)
# Second pass, decode dynamic types
{elements, rest} =
Enum.reduce(elements |> Enum.reverse(), {[], rest}, fn el, {elements, data} ->
case el do
{:dynamic, type, _tail_position} ->
{el, rest} = decode_type(type, data, opts)
{[el | elements], rest}
_ ->
{[el | elements], data}
end
end)
{tuple_value(types, Enum.reverse(elements), decode_structs), rest}
end
defp decode_type(els, _, _) do
raise "Unsupported decoding type: #{inspect(els)}"
end
def tuple_value(types, elements, decode_structs) do
if decode_structs and Enum.all?(types, fn type -> type[:name] != nil and type[:name] != <<>> end) do
Enum.zip(types, elements)
|> Enum.map(fn {type, element} ->
{String.to_atom(Macro.underscore(type[:name])), element}
end)
|> Enum.into(%{})
else
List.to_tuple(elements)
end
end
@spec decode_uint(binary(), integer()) :: {integer(), binary()}
defp decode_uint(data, size_in_bits) do
# TODO: Create `left_pad` repo, err, add to `ABI.Math`
total_bit_size = size_in_bits + ABI.Math.mod(256 - size_in_bits, 256)
<<value::integer-size(total_bit_size), rest::binary>> = data
{value, rest}
end
@spec decode_int(binary(), integer()) :: {integer(), binary()}
defp decode_int(data, size_in_bits) do
total_bit_size = size_in_bits + ABI.Math.mod(256 - size_in_bits, 256)
<<value::integer-signed-big-size(total_bit_size), rest::binary>> = data
{value, rest}
end
@spec decode_bytes(binary(), integer(), atom()) :: {binary(), binary()}
def decode_bytes(data, size_in_bytes, padding_direction) do
# TODO: Create `unright_pad` repo, err, add to `ABI.Math`
total_size_in_bytes = size_in_bytes + ABI.Math.mod(32 - ABI.Math.mod(size_in_bytes, 32), 32)
padding_size_in_bytes = total_size_in_bytes - size_in_bytes
case padding_direction do
:left ->
<<_padding::binary-size(padding_size_in_bytes), value::binary-size(size_in_bytes),
rest::binary>> = data
{value, rest}
:right ->
<<value::binary-size(size_in_bytes), _padding::binary-size(padding_size_in_bytes),
rest::binary>> = data
{value, rest}
end
end
defp nul_terminate_string(raw_string) do
raw_string = :erlang.iolist_to_binary(raw_string)
[pre_nul_part | _] = :binary.split(raw_string, <<0>>)
pre_nul_part
end
end