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.
  """

  @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, [{:uint, 32}, :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, [:string, :bool]}}
      ...>        ]
      ...>      }
      ...>    )
      [{"awesome", true}]

      iex> "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"
      ...> |> Base.decode16!(case: :lower)
      ...> |> ABI.TypeDecoder.decode(
      ...>      %ABI.FunctionSelector{
      ...>        function: nil,
      ...>        types: [
      ...>          %{type: {:tuple, [{:array, :address}]}}
      ...>        ]
      ...>      }
      ...>    )
      [{[]}]

      iex> "00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000c556e617574686f72697a656400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000204a2bf2ff0a4eaf1890c8d8679eaa446fb852c4000000000000000000000000861d9af488d5fa485bb08ab6912fff4f7450849a"
      ...> |> Base.decode16!(case: :lower)
      ...> |> ABI.TypeDecoder.decode(
      ...>      %ABI.FunctionSelector{
      ...>        function: nil,
      ...>        types: [%{type: {:tuple,[
      ...>          :string,
      ...>          {: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) do
    if is_nil(function_selector.function) do
      decode_raw(encoded_data, function_selector.types)
    else
      types = Enum.map(function_selector.types, fn %{type: type} -> type end)
      [res] = decode_raw(encoded_data, [%{type: {:tuple, types}}])

      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, [:string, :bool]}}])
      [{"awesome", true}]
  """
  def decode_raw(encoded_data, types) do
    do_decode(types, encoded_data, [])
  end

  @spec do_decode([ABI.FunctionSelector.type()], binary(), [any()]) :: [any()]
  defp do_decode([], bin, _) when byte_size(bin) > 0,
    do: raise("Found extra binary data: #{inspect(bin)}")

  defp do_decode([], _, acc), do: Enum.reverse(acc)

  defp do_decode([type | remaining_types], data, acc) do
    {decoded, remaining_data} = decode_type(type.type, data)

    do_decode(remaining_types, remaining_data, [decoded | acc])
  end

  @spec decode_type(ABI.FunctionSelector.type(), binary()) :: {any(), binary()}
  defp decode_type({:uint, size_in_bits}, data) do
    decode_uint(data, size_in_bits)
  end

  @spec decode_type(ABI.FunctionSelector.type(), binary()) :: {any(), binary()}
  defp decode_type({:int, size_in_bits}, data) do
    decode_int(data, size_in_bits)
  end

  defp decode_type(:address, data), do: decode_bytes(data, 20, :left)

  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

  defp decode_type(:string, data) 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) do
    {byte_size, rest} = decode_uint(data, 256)
    decode_bytes(rest, byte_size, :right)
  end

  defp decode_type({:bytes, 0}, data), do: {<<>>, data}

  defp decode_type({:bytes, size}, data) when size > 0 and size <= 32 do
    decode_bytes(data, size, :right)
  end

  defp decode_type({:array, type}, data) do
    {element_count, rest} = decode_uint(data, 256)
    decode_type({:array, type, element_count}, rest)
  end

  defp decode_type({:array, _type, 0}, data), do: {[], data}

  defp decode_type({:array, type, element_count}, data) do
    repeated_type = Enum.map(1..element_count, fn _ -> type end)

    {tuple, rest} = decode_type({:tuple, repeated_type}, data)

    {tuple |> Tuple.to_list(), rest}
  end

  defp decode_type({:tuple, types}, starting_data) do
    # First pass, decode static types
    {elements, rest} =
      Enum.reduce(types, {[], starting_data}, fn type, {elements, data} ->
        if ABI.FunctionSelector.is_dynamic?(type) do
          {tail_position, rest} = decode_type({:uint, 256}, data)

          {[{:dynamic, type, tail_position} | elements], rest}
        else
          {el, rest} = decode_type(type, data)

          {[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)

            {[el | elements], rest}

          _ ->
            {[el | elements], data}
        end
      end)

    {elements |> Enum.reverse() |> List.to_tuple(), rest}
  end

  defp decode_type(els, _) do
    raise "Unsupported decoding type: #{inspect(els)}"
  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