defmodule Web3.ABI do
@moduledoc """
ABI is a module for encoding and decoding Ethereum Contract.
Web3.ABI inspired by
- [ABX](https://github.com/Kabie/abx)
- [ExABI](https://github.com/poanetwork/ex_abi)
"""
require Logger
@doc false
def to_hex(bytes) when is_binary(bytes), do: "0x" <> Base.encode16(bytes, case: :lower)
@doc false
def unhex("0x" <> hex), do: Base.decode16!(hex, case: :mixed)
basic_types =
[:address, :bool, :string, :bytes]
|> Enum.map(&{to_string(&1), &1})
int_types =
for i <- 1..32 do
{"int#{i * 8}", {:int, i * 8}}
end
uint_types =
for i <- 1..32 do
{"uint#{i * 8}", {:uint, i * 8}}
end
bytes_types =
for i <- 1..32 do
{"bytes#{i}", {:bytes, i}}
end
all_types = basic_types ++ int_types ++ uint_types ++ bytes_types
# all_type_definitions =
# all_types
# |> Keyword.values()
# |> Enum.reduce(fn type, acc ->
# quote do
# unquote(type) | unquote(acc)
# end
# end)
# @type types() :: unquote(all_type_definitions)
def parse_type(%{type: "tuple", components: inner_types}) do
{:tuple, inner_types |> Enum.map(&parse_type/1)}
end
for {name, type} <- all_types do
def parse_type(%{type: unquote(name)}) do
unquote(type)
end
end
def parse_type(%{type: type_name} = type_def) do
case Regex.run(~r/(.*)\[(\d*)\]/, type_name) do
[_, inner_type, ""] ->
{:array, parse_type(%{type_def | type: inner_type})}
[_, inner_type, n] ->
{:array, parse_type(%{type_def | type: inner_type}), String.to_integer(n)}
_ ->
Logger.warn("Unsupported type name: #{type_name}")
String.to_atom(type_name)
end
end
for {name, type} <- all_types do
def type_name(unquote(type)) do
unquote(name)
end
end
def type_name({:array, inner_type}) do
type_name(inner_type) <> "[]"
end
def type_name({:array, inner_type, n}) do
"#{type_name(inner_type)}[#{n}]"
end
def type_name({:tuple, inner_types}) do
"(#{inner_types |> Enum.map(&type_name/1) |> Enum.join(",")})"
end
def type_name(type) do
Logger.warn("Unsupported type: #{inspect(type)}")
to_string(type)
end
end