# `ABI`
[🔗](https://github.com/ZenHive/onchain-stack/blob/hieroglyph-v1.8.1/packages/hieroglyph/lib/abi.ex#L1)

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
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `parse_specification` | 1 | Parses an ABI specification document into a list of ABI.FunctionSelector structs. | `doc: value` |
| `get_abi_item` | 3 | Finds an ABI fragment by name with optional input-type disambiguation for overloads. | `abi: value`, `name: value`, `arg_types: value` |
| `format_abi_item` | 1 | Renders a FunctionSelector as its canonical Solidity signature string. | `function_selector: value` |
| `event_signature` | 1 | Returns the 32-byte topic hash for an event signature. | `function_signature: value` |
| `encode_event_topics` | 2 | Builds an eth_getLogs topic filter list from an event signature and indexed values. | `function_signature: value`, `indexed_values: 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` | 3 | Decodes 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_error` | 3 | Encodes args into selector-prefixed revert data for a named custom error. | `signature_or_selector: value`, `data: value`, `opts: 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_constructor` | 2 | Encodes constructor arguments for contract deployment (no selector prefix). | `selector: value`, `data: value` |
| `encode_call` | 3 | Encodes args into selector-prefixed calldata for a named function. | `signature_or_selector: value`, `data: value`, `opts: value` |
| `encode` | 2 | Encodes the given data into the function signature or tuple signature. | `function_signature: value`, `data: value` |

# `__descripex_modules__`

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

Return the list of modules registered with this library.

# `decode`

```elixir
@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`

```elixir
@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 {: .info}
>
> 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`

```elixir
@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](https://soliditylang.org/blog/2021/04/21/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:

  | Code   | Meaning                                                     |
  |--------|-------------------------------------------------------------|
  | `0x01` | `assert` evaluated to `false`                               |
  | `0x11` | arithmetic overflow or underflow                            |
  | `0x12` | division or modulo by zero                                  |
  | `0x32` | array 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 {: .info}
>
> 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`

```elixir
@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`

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

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

# `describe`

```elixir
@spec describe(module() | atom() | String.t()) :: [map()]
```

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

# `describe`

```elixir
@spec describe(module() | atom() | String.t(), atom()) :: map() | nil
```

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

# `encode`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@spec encode_packed(binary() | ABI.FunctionSelector.t(), [any()]) :: binary()
```

Encodes a list of values using Solidity's
[non-standard packed mode](https://docs.soliditylang.org/en/stable/abi-spec.html#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 {: .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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@spec parse_specification([map()]) :: [ABI.FunctionSelector.t()]
```

Parses the given ABI specification document into an array of `ABI.FunctionSelector`s.

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`](https://hex.pm/packages/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: []}]

---

*Consult [api-reference.md](api-reference.md) for complete listing*
