Documentation for ABI, the function interface language for Solidity. Generally, the ABI describes how to take binary Ethereum and transform it to or from types that Solidity understands.
Agent Discovery
Use ABI.describe/0..2 for progressive API discovery:
ABI.describe() # Level 1: all annotated modules
ABI.describe(:abi) # Level 2: functions in this module
ABI.describe(:abi, :encode) # Level 3: full contract for encode/2A static api_manifest.json covering every public function is emitted by
mix descripex.manifest --app hieroglyph (a dedicated mix hieroglyph.manifest wrapper ships in 1.2.0 alongside Phase 3 of the agent
economy work — see CHANGELOG). Downstream consumers may diff that manifest
across hieroglyph version bumps as a contract-stability check.
API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
parse_specification | 1 | Parses an ABI specification document into a list of ABI.FunctionSelector structs. | doc: value |
event_signature | 1 | Returns the 32-byte topic hash for an event signature. | function_signature: value |
decode_event | 4 | Decodes an event from raw log data and indexed topics. | function_signature: value, data: value, topics: value, opts: value |
encode_packed | 2 | Encodes values using Solidity's non-standard packed mode (abi.encodePacked). | signature_or_selector: value, values: value |
decode_error | 2 | Decodes selector-prefixed revert data against a list of known custom-error definitions. | revert_data: value, error_definitions: value |
decode_call | 3 | Decodes selector-prefixed calldata (4-byte method ID + ABI-encoded args) and verifies the selector matches. | signature_or_selector: value, calldata: value, opts: value |
decode | 3 | Decodes the given data based on the function or tuple signature. | function_signature: value, data: value, opts: value |
method_id | 1 | Returns the 4-byte function selector (method ID) for a function signature. | signature: value |
encode | 2 | Encodes the given data into the function signature or tuple signature. | function_signature: value, data: value |
Summary
Functions
Return the list of modules registered with this library.
Decodes the given data based on the function or tuple signature.
Decodes selector-prefixed calldata (4-byte method ID followed by ABI-encoded args) and verifies the prefix matches the expected selector.
Decodes selector-prefixed revert data against a list of known custom-error definitions.
Decodes an event, including indexed and non-indexed data.
Return a Level 1 overview of all modules in this library.
Return Level 2 function list for a module (by full atom or short name).
Return Level 3 function detail (or nil if not found).
Encodes the given data into the function signature or tuple signature.
Encodes a list of values using Solidity's non-standard packed mode.
Returns the signature for an event.
Returns the 4-byte function selector (method ID) for a function signature.
Parses the given ABI specification document into an array of ABI.FunctionSelectors.
Functions
@spec __descripex_modules__() :: [module()]
Return the list of modules registered with this library.
Decodes the given data based on the function or tuple signature.
In place of a signature, you can also pass one of the ABI.FunctionSelector structs returned from parse_specification/1.
Options
:decode_structs— whentrue, returns a map keyed by snake_case atoms derived from each parameter's name (instead of the default list). Field-name atoms must already exist in the VM atom table —decode/3callsString.to_existing_atom/1and raisesArgumentErrorwhen an atom has not been interned. See the README "Pre-interning atoms fordecode_structs: true" section for the one-liner migration.
Examples
iex> ABI.decode("baz(uint,address)", "00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001" |> Base.decode16!(case: :lower))
[50, <<1::160>>]
iex> ABI.decode("(address[])", "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" |> Base.decode16!(case: :lower))
[[]]
iex> ABI.decode("(uint256)", "000000000000000000000000000000000000000000000000000000000000000a" |> Base.decode16!(case: :lower))
[10]
iex> ABI.decode("(string)", "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b457468657220546f6b656e000000000000000000000000000000000000000000" |> Base.decode16!(case: :lower))
["Ether Token"]
iex> ABI.decode("((uint256,uint256),string)", "000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000b457468657220546f6b656e000000000000000000000000000000000000000000" |> Base.decode16!(case: :lower))
[{0x11, 0x22}, "Ether Token"]
iex> ABI.decode("((uint256,(uint256,uint256)),string)", "0000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b457468657220546f6b656e000000000000000000000000000000000000000000" |> Base.decode16!(case: :lower))
[{0x11, {0x22, 0x33}}, "Ether Token"]
iex> File.read!("priv/dog.abi.json")
...> |> Jason.decode!
...> |> ABI.parse_specification
...> |> Enum.find(&(&1.function == "bark")) # bark(address,bool)
...> |> ABI.decode("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001" |> Base.decode16!(case: :lower))
[<<1::160>>, true]
iex> ABI.decode("(uint256 a,bool b)", "000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000001" |> Base.decode16!(case: :lower), decode_structs: true)
%{a: 10, b: true}
@spec decode_call(binary() | ABI.FunctionSelector.t(), binary(), keyword()) :: {:ok, [any()] | map()} | {:error, decode_call_error()}
Decodes selector-prefixed calldata (4-byte method ID followed by ABI-encoded args) and verifies the prefix matches the expected selector.
Symmetric counterpart to encode/2, which produces selector-prefixed
output. Use decode/2 for payload-only data (return values, or calldata
that has already been routed by selector).
Returns:
{:ok, decoded}— selector matched;decodedis the same shapedecode/3returns{:error, :calldata_too_short}— fewer than 4 bytes provided{:error, :selector_mismatch}— first 4 bytes don't match the expected selector{:error, :no_function_name}— the selector hasfunction: nil, so there's no selector to verify against; usedecode/3with the payload directly
Note
Only the selector check is wrapped in {:error, _}. When the selector
matches but the payload is malformed (truncated or wrongly-typed bytes),
the underlying decode/3 still raises — same contract as calling
decode/3 directly.
Examples
iex> calldata = ABI.encode("transfer(address,uint256)", [<<1::160>>, 100])
iex> ABI.decode_call("transfer(address,uint256)", calldata)
{:ok, [<<1::160>>, 100]}
iex> ABI.decode_call("deposit()", <<0xd0, 0xe3, 0x0d, 0xb0>>)
{:ok, []}
iex> ABI.decode_call("transfer(address,uint256)", <<0xde, 0xad, 0xbe, 0xef>>)
{:error, :selector_mismatch}
iex> ABI.decode_call("transfer(address,uint256)", <<0xa9, 0x05>>)
{:error, :calldata_too_short}
iex> ABI.decode_call(%ABI.FunctionSelector{function: nil, types: []}, <<0::32>>)
{:error, :no_function_name}
@spec decode_error(binary(), [binary() | ABI.FunctionSelector.t()]) :: {:ok, %{error: String.t() | nil, args: [any()] | map()}} | {:error, decode_error_error()}
Decodes selector-prefixed revert data against a list of known custom-error definitions.
Solidity 0.8.4 introduced
custom errors;
when a contract reverts with revert MyError(arg1, arg2), the revert data is
keccak256("MyError(type1,type2)")[0..3] ++ abi.encode(arg1, arg2) — exactly
the same shape as a call's selector-prefixed calldata.
This helper mirrors decode_call/3 for that shape: try each candidate error
signature against the revert's 4-byte prefix, decode the payload using
whichever matches first.
Returns:
{:ok, %{error: name, args: decoded}}— the first definition matching the 4-byte selector.nameis the error's function name;decodedmatchesdecode/3's shape (a list of args){:error, :calldata_too_short}— fewer than 4 bytes provided{:error, :no_match}— no definition's selector matched
Note
Only the selector match is wrapped in {:error, _}. When a selector
matches but the payload is malformed, the underlying decode/3 still
raises — same contract as decode_call/3.
Examples
iex> revert_data = ABI.encode("InsufficientBalance(uint256,uint256)", [10, 100])
iex> ABI.decode_error(revert_data, ["InsufficientBalance(uint256,uint256)"])
{:ok, %{error: "InsufficientBalance", args: [10, 100]}}
iex> revert_data = ABI.encode("Unauthorized(address)", [<<1::160>>])
iex> ABI.decode_error(revert_data, [
...> "InsufficientBalance(uint256,uint256)",
...> "Unauthorized(address)"
...> ])
{:ok, %{error: "Unauthorized", args: [<<1::160>>]}}
iex> ABI.decode_error(<<0xde, 0xad, 0xbe, 0xef>>, ["NotFound()"])
{:error, :no_match}
iex> ABI.decode_error(<<0xa9, 0x05>>, ["NotFound()"])
{:error, :calldata_too_short}
@spec decode_event( binary() | ABI.FunctionSelector.t(), binary(), [binary()], keyword() ) :: {:ok, String.t() | nil, map()} | {:error, ABI.Event.decode_error()}
Decodes an event, including indexed and non-indexed data.
Returns:
{:ok, event_name, args_map}on success{:error, {:event_signature_mismatch, %{expected: _, got: _}}}—topics[0]did not matchkeccak256(canonical_signature){:error, {:topics_length_mismatch, %{got: _, expected: _}}}— topic count did not match the indexed-parameter count (plus the implicittopics[0]slot whencheck_event_signature: true){:error, {:malformed_data, message}}— non-indexed payload failed to decode (truncated, wrong types, or otherwise inconsistent withfunction_selector.types)
Examples
iex> ABI.decode_event(
...> "Transfer(address indexed from, address indexed to, uint256 amount)",
...> ~h[0x00000000000000000000000000000000000000000000000000000004a817c800],
...> [
...> ~h[0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef],
...> ~h[0x000000000000000000000000b2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
...> ~h[0x0000000000000000000000007795126b3ae468f44c901287de98594198ce38ea]
...> ]
...> )
{:ok,
"Transfer", %{
"amount" => 20000000000,
"from" => ~h[0xb2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
"to" => ~h[0x7795126b3ae468f44c901287de98594198ce38ea]
}}
iex> ABI.decode_event(
...> "Transfer(address indexed from, address indexed to, uint256 amount)",
...> ~h[0x00000000000000000000000000000000000000000000000000000004a817c800],
...> [
...> ~h[0x000000000000000000000000b2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
...> ~h[0x0000000000000000000000007795126b3ae468f44c901287de98594198ce38ea]
...> ],
...> check_event_signature: false
...> )
{:ok,
"Transfer", %{
"amount" => 20000000000,
"from" => ~h[0xb2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
"to" => ~h[0x7795126b3ae468f44c901287de98594198ce38ea]
}}
iex> ABI.decode_event(
...> %ABI.FunctionSelector{
...> function: "Transfer",
...> types: [
...> %{type: :address, name: "from", indexed: true},
...> %{type: :address, name: "to", indexed: true},
...> %{type: {:uint, 256}, name: "amount"},
...> ]
...> },
...> ~h[0x00000000000000000000000000000000000000000000000000000004a817c800],
...> [
...> ~h[0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef],
...> ~h[0x000000000000000000000000b2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
...> ~h[0x0000000000000000000000007795126b3ae468f44c901287de98594198ce38ea]
...> ]
...> )
{:ok,
"Transfer", %{
"amount" => 20000000000,
"from" => ~h[0xb2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
"to" => ~h[0x7795126b3ae468f44c901287de98594198ce38ea]
}}
@spec describe() :: [map()]
Return a Level 1 overview of all modules in this library.
Return Level 2 function list for a module (by full atom or short name).
Return Level 3 function detail (or nil if not found).
@spec encode(binary() | ABI.FunctionSelector.t(), [any()]) :: binary()
Encodes the given data into the function signature or tuple signature.
In place of a signature, you can also pass one of the ABI.FunctionSelector structs returned from parse_specification/1.
Examples
iex> ABI.encode("(uint256)", [{10}])
...> |> Base.encode16(case: :lower)
"000000000000000000000000000000000000000000000000000000000000000a"
iex> ABI.encode("baz(uint,address)", [50, <<1::160>>])
...> |> Base.encode16(case: :lower)
"a291add600000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001"
iex> ABI.encode("price(string)", ["BAT"])
...> |> Base.encode16(case: :lower)
"fe2c6198000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034241540000000000000000000000000000000000000000000000000000000000"
iex> ABI.encode("baz(uint8)", [9999])
** (RuntimeError) Data overflow encoding uint, data `9999` cannot fit in 8 bits
iex> ABI.encode("(uint,address)", [{50, <<1::160>>}])
...> |> Base.encode16(case: :lower)
"00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001"
iex> ABI.encode("(string)", [{"Ether Token"}])
...> |> Base.encode16(case: :lower)
"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b457468657220546f6b656e000000000000000000000000000000000000000000"
iex> ABI.encode("((uint256,uint256),string)", [{{0x11, 0x22}, "Ether Token"}])
...> |> Base.encode16(case: :lower)
"000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000b457468657220546f6b656e000000000000000000000000000000000000000000"
iex> ABI.encode("((uint256,(uint256,uint256)),string)", [{{0x11, {0x22, 0x33}}, "Ether Token"}])
...> |> Base.encode16(case: :lower)
"0000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b457468657220546f6b656e000000000000000000000000000000000000000000"
iex> ABI.encode("(string)", [{String.duplicate("1234567890", 10)}])
...> |> Base.encode16(case: :lower)
"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000643132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393000000000000000000000000000000000000000000000000000000000"
iex> File.read!("priv/dog.abi.json")
...> |> Jason.decode!
...> |> ABI.parse_specification
...> |> Enum.find(&(&1.function == "bark")) # bark(address,bool)
...> |> ABI.encode([<<1::160>>, true])
...> |> Base.encode16(case: :lower)
"b85d0bd200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001"
@spec encode_packed(binary() | ABI.FunctionSelector.t(), [any()]) :: binary()
Encodes a list of values using Solidity's non-standard packed mode.
Used primarily for keccak256(abi.encodePacked(...)) Merkle leaves and
signature schemes; never used for actual function calls (the spec defines
no decoding function — encoding is ambiguous with multiple dynamic args).
Tuple/struct values and nested arrays raise ArgumentError — the spec
does not define their packed encoding.
Warning
If both a and b are dynamic types, abi.encodePacked(a, b) is
ambiguous: abi.encodePacked("a", "bc") == abi.encodePacked("ab", "c").
Do not feed multiple dynamic args into packed-mode signature schemes
without controlling for that collision.
Examples
iex> ABI.encode_packed("foo(int16,bytes1,uint16,string)", [-1, <<0x42>>, 3, "Hello, world!"])
...> |> Base.encode16(case: :lower)
"ffff42000348656c6c6f2c20776f726c6421"
iex> ABI.encode_packed("leaf(address,uint256)", [<<1::160>>, 100])
...> |> Base.encode16(case: :lower)
"00000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000064"
iex> ABI.encode_packed("foo(uint8[])", [[1, 2, 3]])
...> |> Base.encode16(case: :lower)
"000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003"
@spec event_signature(binary() | ABI.FunctionSelector.t()) :: binary()
Returns the signature for an event.
Examples
iex> ABI.event_signature("Transfer(address indexed from, address indexed to, uint256 amount)")
...> |> Base.encode16(case: :lower)
"ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
@spec method_id(binary() | ABI.FunctionSelector.t()) :: binary()
Returns the 4-byte function selector (method ID) for a function signature.
The selector is keccak256(canonical_signature) truncated to its first 4
bytes. Returns <<>> for selectors with no function name (anonymous /
raw-tuple selectors used for return-value decoding).
Examples
iex> ABI.method_id("transfer(address,uint256)") |> Base.encode16(case: :lower)
"a9059cbb"
iex> ABI.method_id("deposit()") |> Base.encode16(case: :lower)
"d0e30db0"
iex> ABI.method_id(%ABI.FunctionSelector{function: "deposit", types: []}) |> Base.encode16(case: :lower)
"d0e30db0"
iex> ABI.method_id(%ABI.FunctionSelector{function: nil, types: [%{type: {:uint, 256}}]})
""
@spec parse_specification([map()]) :: [ABI.FunctionSelector.t()]
Parses the given ABI specification document into an array of ABI.FunctionSelectors.
Every entry in the document is parsed — including constructor, fallback,
receive, error, and event entries — and returned with its function_type
field set accordingly. Filter by that field if you only want plain
function entries.
This function can be used in combination with a JSON parser, e.g. Jason, to parse ABI specification JSON files.
Examples
iex> File.read!("priv/dog.abi.json")
...> |> Jason.decode!
...> |> ABI.parse_specification
[%ABI.FunctionSelector{function: "bark", function_type: :function, state_mutability: :nonpayable, returns: [], types: [%{name: "at", type: :address}, %{name: "loudly", type: :bool}]},
%ABI.FunctionSelector{function: "rollover", function_type: :function, state_mutability: :nonpayable, returns: [%{name: "is_a_good_boy", type: :bool}], types: []}]
iex> [%{
...> "constant" => true,
...> "inputs" => [
...> %{"name" => "at", "type" => "address"},
...> %{"name" => "loudly", "type" => "bool"}
...> ],
...> "name" => "bark",
...> "outputs" => [],
...> "payable" => false,
...> "stateMutability" => "pure",
...> "type" => "function"
...> }]
...> |> ABI.parse_specification
[
%ABI.FunctionSelector{function: "bark", function_type: :function, state_mutability: :pure, returns: [], types: [
%{type: :address, name: "at"},
%{type: :bool, name: "loudly"}
]}
]
iex> [%{
...> "inputs" => [
...> %{"name" => "_numProposals", "type" => "uint8"}
...> ],
...> "payable" => false,
...> "stateMutability" => "nonpayable",
...> "type" => "constructor"
...> }]
...> |> ABI.parse_specification
[%ABI.FunctionSelector{function: nil, function_type: :constructor, state_mutability: :nonpayable, types: [%{name: "_numProposals", type: {:uint, 8}}], returns: nil}]
iex> ABI.decode("(string)", "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000643132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393000000000000000000000000000000000000000000000000000000000" |> Base.decode16!(case: :lower))
[String.duplicate("1234567890", 10)]
iex> [%{
...> "payable" => false,
...> "stateMutability" => "nonpayable",
...> "type" => "fallback"
...> }]
...> |> ABI.parse_specification
[%ABI.FunctionSelector{function: nil, function_type: :fallback, state_mutability: :nonpayable, returns: nil, types: []}]