Encodes and decodes Solidity ABI data: calldata for a function call, return data, and event logs, as the Solidity ABI specification defines them.
iex> calldata = EEVM.ABI.encode_call("transfer(address,uint256)", [0xB0B, 250])
iex> byte_size(calldata)
68
iex> EEVM.ABI.decode(["bool"], <<1::256>>)
{:ok, [true]}Types are Solidity type strings, such as "uint256", "address[]", or
"(uint256,bytes)[2]" for a fixed-size array of tuples. Signatures may
carry parameter names and spaces, as in Solidity source, and uint and
int mean uint256 and int256; selectors and event topics are always
computed from the canonical form, so "transfer(address to, uint amount)"
encodes the same calldata as "transfer(address,uint256)".
Values
| ABI type | Elixir value |
|---|---|
uint<N>, int<N> | integer |
address | integer, as eevm represents addresses; encoding also takes anything EEVM.address/1 takes |
bool | true or false |
bytes<N> | binary of exactly N bytes |
bytes, string | binary |
T[], T[k] | list |
(T1,T2,...) | tuple |
fixed, ufixed, and function types are not supported.
Errors
Following eevm's error rule, encoding raises ArgumentError for an
unknown type or a value that doesn't fit its type, since both are mistakes
in the caller's code. Decoding returns {:error, :invalid_abi_data} for
data that doesn't match its types, since return data and logs come from
contracts. Like Solidity's own decoder, decoding is strict: an integer,
address, bool, or bytes<N> word with bits outside its type, or an
offset or length past the end of the data, is an error rather than being
masked or truncated. Bytes after the encoded values are ignored.
Decoding work is capped at 16 times the size of the data, which is far
more than any canonical encoding needs. Crafted offsets that point many
values at the same bytes, which would make decoding take time quadratic
in the input size, fail with {:error, :invalid_abi_data} instead.
Summary
Types
An event log, as in result.logs: topics are integers.
An Elixir value for an ABI type; see the table in the module docs.
Functions
Decodes ABI-encoded data, such as a call's return_data, as values of
types. Returns {:ok, values}, one value per type.
Decodes an event log, such as an entry of result.logs, as the event
signature, which marks indexed parameters with indexed as Solidity
does. Returns {:ok, values} with every parameter's value in declaration
order.
ABI-encodes values as the arguments of a function with parameter
types, without a selector: the encoding of the tuple of the values.
Builds calldata for a call to the function signature with args: its
4-byte selector followed by the ABI-encoded arguments. Pass the result to
EEVM.call/4 or as a transaction's data.
Types
@type log() :: %{ :topics => [non_neg_integer()], :data => binary(), optional(atom()) => term() }
An event log, as in result.logs: topics are integers.
An Elixir value for an ABI type; see the table in the module docs.
Functions
Decodes ABI-encoded data, such as a call's return_data, as values of
types. Returns {:ok, values}, one value per type.
Returns {:error, :invalid_abi_data} when data doesn't hold valid
values of types, and raises ArgumentError for an unknown type.
Examples
iex> EEVM.ABI.decode(["uint256"], <<1000::256>>)
{:ok, [1000]}
iex> data = EEVM.ABI.encode(["int8", "string", "uint16[]"], [-1, "eevm", [1, 2]])
iex> EEVM.ABI.decode(["int8", "string", "uint16[]"], data)
{:ok, [-1, "eevm", [1, 2]]}
iex> EEVM.ABI.decode(["uint8"], <<256::256>>)
{:error, :invalid_abi_data}
@spec decode_log(String.t(), log()) :: {:ok, [value()]} | {:error, :event_mismatch | :invalid_abi_data}
Decodes an event log, such as an entry of result.logs, as the event
signature, which marks indexed parameters with indexed as Solidity
does. Returns {:ok, values} with every parameter's value in declaration
order.
An indexed uint, int, address, bool, or bytes<N> parameter
decodes to its value. Any other indexed parameter (bytes, string,
arrays, tuples) decodes to its topic's 32 bytes, the Keccak-256 hash of
its encoding, since the log doesn't hold the value itself.
Returns {:error, :event_mismatch} when the log's first topic isn't the
event's topic or its topic count doesn't match the indexed parameters (so
you can try a log against several events), and
{:error, :invalid_abi_data} when the log is that event but its data or
topics don't decode. Anonymous events, which have no event topic, aren't
supported.
Example
iex> log = %{
...> address: 0x7070,
...> topics: [EEVM.event_topic("Transfer(address,address,uint256)"), 0xA11CE, 0xB0B],
...> data: <<250::256>>
...> }
iex> EEVM.ABI.decode_log("Transfer(address indexed from, address indexed to, uint256 value)", log)
{:ok, [0xA11CE, 0xB0B, 250]}
iex> EEVM.ABI.decode_log("Approval(address indexed,address indexed,uint256)", log)
{:error, :event_mismatch}
ABI-encodes values as the arguments of a function with parameter
types, without a selector: the encoding of the tuple of the values.
Raises ArgumentError for an unknown type, a wrong number of values, or a
value that doesn't fit its type.
Examples
iex> EEVM.ABI.encode(["uint32", "bool"], [69, true]) |> EEVM.hex()
"0x" <> String.pad_leading("45", 64, "0") <> String.pad_leading("1", 64, "0")
iex> EEVM.ABI.encode(["string"], ["hi"]) |> byte_size()
96
Builds calldata for a call to the function signature with args: its
4-byte selector followed by the ABI-encoded arguments. Pass the result to
EEVM.call/4 or as a transaction's data.
Raises ArgumentError for a malformed signature, a wrong number of
arguments, or an argument that doesn't fit its type.
Examples
iex> calldata = EEVM.ABI.encode_call("balanceOf(address)", [0xA11CE])
iex> EEVM.hex(calldata)
"0x70a08231" <> String.duplicate("0", 59) <> "a11ce"
iex> EEVM.ABI.encode_call("transfer(address to, uint amount)", [0xB0B, 1]) ==
...> EEVM.ABI.encode_call("transfer(address,uint256)", [0xB0B, 1])
true