ABI (hieroglyph v1.5.0)

Copy Markdown View Source

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/2

A 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

FunctionArityDescriptionParam Kinds
parse_specification1Parses an ABI specification document into a list of ABI.FunctionSelector structs.doc: value
get_abi_item3Finds an ABI fragment by name with optional input-type disambiguation for overloads.abi: value, name: value, arg_types: value
format_abi_item1Renders a FunctionSelector as its canonical Solidity signature string.function_selector: value
event_signature1Returns the 32-byte topic hash for an event signature.function_signature: value
encode_event_topics2Builds an eth_getLogs topic filter list from an event signature and indexed values.function_signature: value, indexed_values: value
decode_event4Decodes an event from raw log data and indexed topics.function_signature: value, data: value, topics: value, opts: value
encode_packed2Encodes values using Solidity's non-standard packed mode (abi.encodePacked).signature_or_selector: value, values: value
decode_error3Decodes selector-prefixed revert data against known custom-error definitions, plus the Solidity built-in Error(string)/Panic(uint256) errors.revert_data: value, error_definitions: value, opts: value
encode_error3Encodes args into selector-prefixed revert data for a named custom error.signature_or_selector: value, data: value, opts: value
decode_call3Decodes selector-prefixed calldata (4-byte method ID + ABI-encoded args) and verifies the selector matches.signature_or_selector: value, calldata: value, opts: value
decode3Decodes the given data based on the function or tuple signature.function_signature: value, data: value, opts: value
method_id1Returns the 4-byte function selector (method ID) for a function signature.signature: value
encode_constructor2Encodes constructor arguments for contract deployment (no selector prefix).selector: value, data: value
encode_call3Encodes args into selector-prefixed calldata for a named function.signature_or_selector: value, data: value, opts: value
encode2Encodes 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 args into selector-prefixed calldata for a named function.

Encodes constructor arguments for contract deployment.

Encodes args into selector-prefixed revert data for a Solidity 0.8.4+ custom error.

Builds an eth_getLogs topic filter list for indexed event arguments.

Encodes a list of values using Solidity's non-standard packed mode.

Returns the signature for an event.

Renders an ABI.FunctionSelector as its canonical Solidity signature string.

Finds an ABI fragment by name in a parsed specification list.

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

__descripex_modules__()

@spec __descripex_modules__() :: [module()]

Return the list of modules registered with this library.

decode(function_signature, data, opts \\ [])

@spec decode(binary() | ABI.FunctionSelector.t(), binary(), keyword()) ::
  [any()] | map() | {:error, {:strict_violation, term()}}

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 — when true, 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/3 calls String.to_existing_atom/1 and raises ArgumentError when an atom has not been interned. See the README "Pre-interning atoms for decode_structs: true" section for the one-liner migration.
  • :strict — when true, rejects non-canonical bool/uint/int padding, trailing bytes after the declared payload, and string/bytes length prefixes that exceed the available data. Strict failures return {:error, {:strict_violation, detail}}.

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}

decode_call(signature_or_selector, calldata, opts \\ [])

@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; decoded is the same shape decode/3 returns
  • {: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 has function: nil, so there's no selector to verify against; use decode/3 with the payload directly
  • {:error, {:strict_violation, detail}}strict: true rejected a non-canonical payload

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}

decode_error(revert_data, error_definitions, opts \\ [])

@spec decode_error(binary(), [binary() | ABI.FunctionSelector.t()], keyword()) ::
  {: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.

Built-in errors

Every Solidity revert path emits one of two compiler-defined errors, so they are recognized implicitly — no caller needs to register them, and they resolve even when error_definitions is []:

  • Error(string) — selector 0x08c379a0. The standard require/revert reason string. Decodes to %{error: "Error", args: [reason]}.

  • Panic(uint256) — selector 0x4e487b71. The 0.8.x assert/arithmetic panic. Decodes to %{error: "Panic", args: [code]}, where code is the integer panic code. The standard codes are:

    CodeMeaning
    0x01assert evaluated to false
    0x11arithmetic overflow or underflow
    0x12division or modulo by zero
    0x32array access out of bounds

A user-supplied definition whose selector collides with a built-in still wins: error_definitions is consulted first, and the built-ins are only a fallback.

Mirrors viem's decodeErrorResult, which resolves Error/Panic implicitly.

Returns:

  • {:ok, %{error: name, args: decoded}} — the first definition matching the 4-byte selector, or a built-in error when none does. name is the error's function name; decoded matches decode/3's shape (a list of args)
  • {:error, :calldata_too_short} — fewer than 4 bytes provided
  • {:error, :no_match} — no definition or built-in selector matched
  • {:error, {:strict_violation, detail}}strict: true rejected a non-canonical payload

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> revert_data = ABI.encode("Error(string)", ["insufficient balance"])
iex> ABI.decode_error(revert_data, [])
{:ok, %{error: "Error", args: ["insufficient balance"]}}

iex> revert_data = ABI.encode("Panic(uint256)", [0x11])
iex> ABI.decode_error(revert_data, [])
{:ok, %{error: "Panic", args: [17]}}

iex> ABI.decode_error(<<0xde, 0xad, 0xbe, 0xef>>, ["NotFound()"])
{:error, :no_match}

iex> ABI.decode_error(<<0xa9, 0x05>>, ["NotFound()"])
{:error, :calldata_too_short}

decode_event(function_signature, data, topics, opts \\ [])

@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 match keccak256(canonical_signature)
  • {:error, {:topics_length_mismatch, %{got: _, expected: _}}} — topic count did not match the indexed-parameter count (plus the implicit topics[0] slot when check_event_signature: true)
  • {:error, {:malformed_data, message}} — non-indexed payload failed to decode (truncated, wrong types, or otherwise inconsistent with function_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]
}}

describe()

@spec describe() :: [map()]

Return a Level 1 overview of all modules in this library.

describe(mod_or_short)

@spec describe(module() | atom()) :: [map()]

Return Level 2 function list for a module (by full atom or short name).

describe(mod_or_short, func_name)

@spec describe(module() | atom(), atom()) :: map() | nil

Return Level 3 function detail (or nil if not found).

encode(function_signature, data)

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

encode_call(signature_or_selector, data, opts \\ [])

@spec encode_call(binary() | ABI.FunctionSelector.t(), [any()], keyword()) :: binary()

Encodes args into selector-prefixed calldata for a named function.

This is the encode-side counterpart to decode_call/3: pass a signature or ABI.FunctionSelector, plus the argument list, and receive the full transaction calldata blob.

Examples

iex> ABI.encode_call("transfer(address,uint256)", [<<1::160>>, 100])
...> |> Base.encode16(case: :lower)
"a9059cbb00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000064"

iex> ABI.encode_call(%ABI.FunctionSelector{function: nil, types: []}, [])
** (ArgumentError) encode_call/3 requires a function name; use encode/2 for payload-only data

encode_constructor(selector, data)

@spec encode_constructor(ABI.FunctionSelector.t(), [any()]) :: binary()

Encodes constructor arguments for contract deployment.

Constructors have no selector — deploy calldata is contract_bytecode <> encoded_args, so this returns the ABI-encoded args with no 4-byte prefix (unlike encode_call/3). The caller concatenates the bytecode. Mirrors viem's encodeDeployData, scoped to just the args blob.

Only accepts a %ABI.FunctionSelector{function_type: :constructor} — pass the :constructor entry from parse_specification/1. A non-constructor selector raises ArgumentError. Round-trips through decode/3 against the constructor's parsed types.

Examples

iex> [selector] =
...>   ABI.parse_specification([%{
...>     "type" => "constructor",
...>     "stateMutability" => "nonpayable",
...>     "inputs" => [%{"name" => "supply", "type" => "uint256"}]
...>   }])
iex> ABI.encode_constructor(selector, [1000])
...> |> Base.encode16(case: :lower)
"00000000000000000000000000000000000000000000000000000000000003e8"

iex> selector = %ABI.FunctionSelector{function_type: :constructor, types: []}
iex> ABI.encode_constructor(selector, [])
""

iex> selector = %ABI.FunctionSelector{function: "transfer", function_type: :function, types: []}
iex> ABI.encode_constructor(selector, [])
** (ArgumentError) encode_constructor/2 requires a constructor selector (function_type: :constructor)

encode_error(signature_or_selector, data, opts \\ [])

@spec encode_error(binary() | ABI.FunctionSelector.t(), [any()], keyword()) ::
  binary()

Encodes args into selector-prefixed revert data for a Solidity 0.8.4+ custom error.

This is the encode-side counterpart to decode_error/2: pass an error signature or ABI.FunctionSelector, plus the argument list, and receive the full revert data blob.

Examples

iex> ABI.encode_error("InsufficientBalance(uint256,uint256)", [10, 100])
...> |> Base.encode16(case: :lower)
"cf479181000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000064"

iex> ABI.encode_error(%ABI.FunctionSelector{function: nil, types: []}, [])
** (ArgumentError) encode_error/3 requires a function name; use encode/2 for payload-only data

encode_event_topics(function_signature, indexed_values)

@spec encode_event_topics(binary() | ABI.FunctionSelector.t(), [any() | :any]) :: [
  binary() | :any
]

Builds an eth_getLogs topic filter list for indexed event arguments.

Pass indexed argument values in event order. Use :any for an unfiltered indexed slot. Indexed value types encode as 32-byte topics; indexed reference types encode as keccak256 hashes of their in-place event encoding.

Examples

iex> ABI.encode_event_topics(
...>   "Transfer(address indexed from,address indexed to,uint256 amount)",
...>   [~h[0xb2b7c1795f19fbc28fda77a95e59edbb8b3709c8], :any]
...> )
[
  ~h[0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef],
  ~h[0x000000000000000000000000b2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
  :any
]

encode_packed(signature_or_selector, values)

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

event_signature(function_signature)

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

format_abi_item(function_selector)

@spec format_abi_item(ABI.FunctionSelector.t()) :: String.t()

Renders an ABI.FunctionSelector as its canonical Solidity signature string.

This is the general-purpose formatter behind method_id/1 and event_signature/1 (both hash this exact string): it reuses ABI.FunctionSelector.encode/1 — the shared sig-builder — so the formatted output can never drift from what gets hashed. First consumers are the api_manifest.json CI artifact and error/log messages that would otherwise inspect raw structs.

Tuple types expand to parenthesized member lists ((uint256,address)) and arrays render with [] / [N] suffixes. Anonymous fragments (constructor, fallback) carry function: nil and format without a leading name. Mirrors viem's formatAbiItem and round-trips with FunctionSelector.decode/1 — the produced string re-parses to the same types.

Examples

iex> ABI.parse_specification([%{"type" => "function", "name" => "transfer", "inputs" => [%{"name" => "to", "type" => "address"}, %{"name" => "amount", "type" => "uint256"}]}])
...> |> hd()
...> |> ABI.format_abi_item()
"transfer(address,uint256)"

iex> ABI.format_abi_item(%ABI.FunctionSelector{
...>   function: "swap",
...>   types: [
...>     %{type: {:tuple, [%{type: :address}, %{type: {:uint, 256}}]}},
...>     %{type: {:array, {:uint, 256}}},
...>     %{type: {:array, :address, 3}}
...>   ]
...> })
"swap((address,uint256),uint256[],address[3])"

iex> ABI.format_abi_item(%ABI.FunctionSelector{function: nil, function_type: :constructor, types: [%{type: {:uint, 8}}]})
"(uint8)"

get_abi_item(abi, name, arg_types)

@spec get_abi_item(
  [ABI.FunctionSelector.t()],
  String.t(),
  [ABI.FunctionSelector.type()] | nil
) ::
  {:ok, ABI.FunctionSelector.t()}
  | {:error, :not_found}
  | {:error, {:ambiguous, [ABI.FunctionSelector.t()]}}

Finds an ABI fragment by name in a parsed specification list.

Operates on the output of parse_specification/1. When several fragments share a name (Solidity overloads, or a function and event with the same label), pass arg_types — a list of internal type atoms matching each input's type field (e.g. [:address, {:uint, 256}]) — to select the intended overload. Mirrors viem's getAbiItem.

Examples

iex> abi =
...>   ABI.parse_specification([
...>     %{"type" => "function", "name" => "transfer", "inputs" => [
...>       %{"type" => "address"},
...>       %{"type" => "uint256"}
...>     ]}
...>   ])
iex> {:ok, %ABI.FunctionSelector{function: "transfer"}} = ABI.get_abi_item(abi, "transfer", nil)

iex> abi =
...>   ABI.parse_specification([
...>     %{"type" => "function", "name" => "pick", "inputs" => [%{"type" => "uint256"}]},
...>     %{"type" => "function", "name" => "pick", "inputs" => [
...>       %{"type" => "uint256"},
...>       %{"type" => "address"}
...>     ]}
...>   ])
iex> {:error, {:ambiguous, _}} = ABI.get_abi_item(abi, "pick", nil)
iex> {:ok, %ABI.FunctionSelector{types: [%{type: {:uint, 256}}, %{type: :address}]}} =
...>   ABI.get_abi_item(abi, "pick", [{:uint, 256}, :address])

iex> abi = ABI.parse_specification([%{"type" => "function", "name" => "only", "inputs" => []}])
iex> ABI.get_abi_item(abi, "missing", nil)
{:error, :not_found}

method_id(signature)

@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}}]})
""

parse_specification(doc)

@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: []}]