# `Onchain.ABI`
[🔗](https://github.com/ZenHive/onchain-stack/blob/onchain-v0.16.0/packages/onchain/lib/onchain/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 `Onchain.ABI.describe/0..2` for progressive API discovery:

    Onchain.ABI.describe()                    # Level 1: all annotated modules
    Onchain.ABI.describe(:abi)                # Level 2: functions in this module
    Onchain.ABI.describe(:abi, :encode)       # Level 3: full contract for encode/2

A static `api_manifest.json` covering every public function is emitted by
`mix onchain.manifest`. Downstream consumers may diff that manifest across
onchain version bumps as a contract-stability check.

Hex-string callers can use `encode_hex_call/2`, `decode_hex_call/3`,
`decode_hex_error/3`, and `decode_response/3` (also `decode_types/3`). These
conveniences share the binary codecs and return tagged error tuples.

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `decode_hex_error!` | 3 | Decode custom-error revert data. Raises on error. | `hex_revert_data: value`, `error_definitions: value`, `opts: value` |
| `decode_hex_error` | 3 | Decode Solidity 0.8.4+ custom-error revert data against a list of candidate error signatures. | `hex_revert_data: value`, `error_definitions: value`, `opts: value` |
| `decode_hex_call!` | 3 | Decode selector-prefixed calldata. Raises on error. | `signature_or_selector: value`, `hex_calldata: value`, `opts: value` |
| `decode_hex_call` | 3 | Decode selector-prefixed calldata to function args. | `signature_or_selector: value`, `hex_calldata: value`, `opts: value` |
| `decode_types!` | 3 | Decode arbitrary ABI-encoded hex data. Alias of decode_response!/3. | `type_signature: value`, `hex_data: value`, `opts: value` |
| `decode_types` | 3 | Decode arbitrary ABI-encoded hex data. Alias of decode_response/3. | `type_signature: value`, `hex_data: value`, `opts: value` |
| `decode_response!` | 3 | Decode hex-encoded ABI response data to Elixir values. Raises on error. | `type_signature: value`, `hex_data: value`, `opts: value` |
| `decode_response` | 3 | Decode hex-encoded ABI response data to Elixir values. | `type_signature: value`, `hex_data: value`, `opts: value` |
| `encode_hex_call!` | 2 | Encode a function call to 0x-prefixed hex calldata. Raises on error. | `signature: value`, `params: value` |
| `encode_hex_call` | 2 | Encode a function call to 0x-prefixed hex calldata. | `signature: value`, `params: value` |
| `parse_specification` | 1 | Parses an ABI specification document into a list of Onchain.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() | Onchain.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 `Onchain.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> Onchain.ABI.decode("baz(uint,address)", "00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001" |> Base.decode16!(case: :lower))
    [50, <<1::160>>]

    iex> Onchain.ABI.decode("(address[])", "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" |> Base.decode16!(case: :lower))
    [[]]

    iex> Onchain.ABI.decode("(uint256)", "000000000000000000000000000000000000000000000000000000000000000a" |> Base.decode16!(case: :lower))
    [10]

    iex> Onchain.ABI.decode("(string)", "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b457468657220546f6b656e000000000000000000000000000000000000000000" |> Base.decode16!(case: :lower))
    ["Ether Token"]

    iex> Onchain.ABI.decode("((uint256,uint256),string)", "000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000b457468657220546f6b656e000000000000000000000000000000000000000000" |> Base.decode16!(case: :lower))
    [{0x11, 0x22}, "Ether Token"]

    iex> Onchain.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!
    ...> |> Onchain.ABI.parse_specification
    ...> |> Enum.find(&(&1.function == "bark")) # bark(address,bool)
    ...> |> Onchain.ABI.decode("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001" |> Base.decode16!(case: :lower))
    [<<1::160>>, true]

    iex> Onchain.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() | Onchain.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 = Onchain.ABI.encode("transfer(address,uint256)", [<<1::160>>, 100])
    iex> Onchain.ABI.decode_call("transfer(address,uint256)", calldata)
    {:ok, [<<1::160>>, 100]}

    iex> Onchain.ABI.decode_call("deposit()", <<0xd0, 0xe3, 0x0d, 0xb0>>)
    {:ok, []}

    iex> Onchain.ABI.decode_call("transfer(address,uint256)", <<0xde, 0xad, 0xbe, 0xef>>)
    {:error, :selector_mismatch}

    iex> Onchain.ABI.decode_call("transfer(address,uint256)", <<0xa9, 0x05>>)
    {:error, :calldata_too_short}

    iex> Onchain.ABI.decode_call(%Onchain.ABI.FunctionSelector{function: nil, types: []}, <<0::32>>)
    {:error, :no_function_name}

# `decode_error`

```elixir
@spec decode_error(binary(), [binary() | Onchain.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 = Onchain.ABI.encode("InsufficientBalance(uint256,uint256)", [10, 100])
    iex> Onchain.ABI.decode_error(revert_data, ["InsufficientBalance(uint256,uint256)"])
    {:ok, %{error: "InsufficientBalance", args: [10, 100]}}

    iex> revert_data = Onchain.ABI.encode("Unauthorized(address)", [<<1::160>>])
    iex> Onchain.ABI.decode_error(revert_data, [
    ...>   "InsufficientBalance(uint256,uint256)",
    ...>   "Unauthorized(address)"
    ...> ])
    {:ok, %{error: "Unauthorized", args: [<<1::160>>]}}

    iex> revert_data = Onchain.ABI.encode("Error(string)", ["insufficient balance"])
    iex> Onchain.ABI.decode_error(revert_data, [])
    {:ok, %{error: "Error", args: ["insufficient balance"]}}

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

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

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

# `decode_event`

```elixir
@spec decode_event(
  binary() | Onchain.ABI.FunctionSelector.t(),
  binary(),
  [binary()],
  keyword()
) ::
  {:ok, String.t() | nil, map()} | {:error, Onchain.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> Onchain.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> Onchain.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> Onchain.ABI.decode_event(
    ...>   %Onchain.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]
    }}

# `decode_hex_call`

```elixir
@spec decode_hex_call(
  String.t() | Onchain.ABI.FunctionSelector.t(),
  String.t(),
  keyword()
) ::
  {:ok, list() | map()} | {:error, {:decode_error, term()}}
```

Decode selector-prefixed calldata to function args.

## Parameters

  * `signature_or_selector` - Function signature like "transfer(address,uint256)" OR an `Onchain.ABI.FunctionSelector` struct. The 4-byte selector of the signature must match the first 4 bytes of calldata. (value)
  * `hex_calldata` - 0x-prefixed hex string of selector-prefixed ABI-encoded calldata (value)
  * `opts` - Forwarded to `Onchain.ABI.decode_call/3`. Pass `decode_structs: true` for a named-field map instead of a positional list. Pass `strict: true` to reject non-canonical payloads (`{:error, {:decode_error, {:strict_violation, detail}}}`). (default: `[]`, value)

## Returns

List of args (or map when `decode_structs: true`). Error reasons: `:calldata_too_short`, `:selector_mismatch`, `:no_function_name`, `{:invalid_hex, _}`, `{:strict_violation, detail}`, or upstream exception message string. (`{:ok, list | map} | {:error, {:decode_error, reason}}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Forwarded to `Onchain.ABI.decode_call/3`. Pass `decode_structs: true` for a named-field map instead of a positional list. Pass `strict: true` to reject non-canonical payloads (`{:error, {:decode_error, {:strict_violation, detail}}}`).",
      kind: :value
    },
    signature_or_selector: %{
      description: "Function signature like \"transfer(address,uint256)\" OR an `Onchain.ABI.FunctionSelector` struct. The 4-byte selector of the signature must match the first 4 bytes of calldata.",
      kind: :value
    },
    hex_calldata: %{
      description: "0x-prefixed hex string of selector-prefixed ABI-encoded calldata",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, list | map} | {:error, {:decode_error, reason}}",
    description: "List of args (or map when `decode_structs: true`). Error reasons: `:calldata_too_short`, `:selector_mismatch`, `:no_function_name`, `{:invalid_hex, _}`, `{:strict_violation, detail}`, or upstream exception message string."
  }
}
```

# `decode_hex_call!`

```elixir
@spec decode_hex_call!(
  String.t() | Onchain.ABI.FunctionSelector.t(),
  String.t(),
  keyword()
) ::
  list() | map()
```

Decode selector-prefixed calldata. Raises on error.

## Parameters

  * `signature_or_selector` - Function signature string or `Onchain.ABI.FunctionSelector` struct (value)
  * `hex_calldata` - 0x-prefixed hex string of selector-prefixed calldata (value)
  * `opts` - Forwarded to `Onchain.ABI.decode_call/3` (e.g. `decode_structs: true`, `strict: true`) (default: `[]`, value)

## Returns

List of decoded args (or map when `decode_structs: true`) (`list | map`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Forwarded to `Onchain.ABI.decode_call/3` (e.g. `decode_structs: true`, `strict: true`)",
      kind: :value
    },
    signature_or_selector: %{
      description: "Function signature string or `Onchain.ABI.FunctionSelector` struct",
      kind: :value
    },
    hex_calldata: %{
      description: "0x-prefixed hex string of selector-prefixed calldata",
      kind: :value
    }
  },
  returns: %{
    type: "list | map",
    description: "List of decoded args (or map when `decode_structs: true`)"
  }
}
```

# `decode_hex_error`

```elixir
@spec decode_hex_error(
  String.t(),
  [String.t() | Onchain.ABI.FunctionSelector.t()],
  keyword()
) ::
  {:ok, %{error: String.t() | nil, args: list() | map()}}
  | {:error, {:decode_error, term()}}
```

Decode Solidity 0.8.4+ custom-error revert data against a list of candidate error signatures.

## Parameters

  * `hex_revert_data` - 0x-prefixed hex string of revert data (4-byte error selector + ABI-encoded args) (value)
  * `error_definitions` - List of candidate error signatures like ["InsufficientBalance(uint256,uint256)", "Unauthorized()"] (or hieroglyph FunctionSelector structs). The first one whose 4-byte selector matches the prefix of `hex_revert_data` decodes the args. (value)
  * `opts` - Forwarded to `Onchain.ABI.decode_error/3`. Pass `strict: true` to reject non-canonical payloads (`{:error, {:decode_error, {:strict_violation, detail}}}`). (default: `[]`, value)

## Returns

Map with the matched error name (or `nil`) and decoded args. Error reasons: `:calldata_too_short`, `:no_match`, `{:invalid_hex, _}`, `{:strict_violation, detail}`, or upstream exception message string. (`{:ok, %{error: name, args: list}} | {:error, {:decode_error, reason}}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Forwarded to `Onchain.ABI.decode_error/3`. Pass `strict: true` to reject non-canonical payloads (`{:error, {:decode_error, {:strict_violation, detail}}}`).",
      kind: :value
    },
    error_definitions: %{
      description: "List of candidate error signatures like [\"InsufficientBalance(uint256,uint256)\", \"Unauthorized()\"] (or hieroglyph FunctionSelector structs). The first one whose 4-byte selector matches the prefix of `hex_revert_data` decodes the args.",
      kind: :value
    },
    hex_revert_data: %{
      description: "0x-prefixed hex string of revert data (4-byte error selector + ABI-encoded args)",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, %{error: name, args: list}} | {:error, {:decode_error, reason}}",
    description: "Map with the matched error name (or `nil`) and decoded args. Error reasons: `:calldata_too_short`, `:no_match`, `{:invalid_hex, _}`, `{:strict_violation, detail}`, or upstream exception message string."
  }
}
```

# `decode_hex_error!`

```elixir
@spec decode_hex_error!(
  String.t(),
  [String.t() | Onchain.ABI.FunctionSelector.t()],
  keyword()
) :: %{
  error: String.t() | nil,
  args: list() | map()
}
```

Decode custom-error revert data. Raises on error.

## Parameters

  * `hex_revert_data` - 0x-prefixed hex string of revert data (value)
  * `error_definitions` - List of candidate error signatures or FunctionSelector structs (value)
  * `opts` - Forwarded to `Onchain.ABI.decode_error/3` (e.g. `strict: true`) (default: `[]`, value)

## Returns

Map with the matched error name and decoded args (`%{error: name, args: list}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Forwarded to `Onchain.ABI.decode_error/3` (e.g. `strict: true`)",
      kind: :value
    },
    error_definitions: %{
      description: "List of candidate error signatures or FunctionSelector structs",
      kind: :value
    },
    hex_revert_data: %{
      description: "0x-prefixed hex string of revert data",
      kind: :value
    }
  },
  returns: %{
    type: "%{error: name, args: list}",
    description: "Map with the matched error name and decoded args"
  }
}
```

# `decode_response`

```elixir
@spec decode_response(String.t(), String.t(), keyword()) ::
  {:ok, list() | map()} | {:error, {:decode_error, term()}}
```

Decode hex-encoded ABI response data to Elixir values.

## Parameters

  * `type_signature` - Tuple type signature wrapped in parentheses, e.g. "(uint256)" or "(uint256,bool)". Bare comma-separated types like "uint256,bool" are NOT accepted and raise an unhelpful upstream error. (value)
  * `hex_data` - 0x-prefixed hex string of ABI-encoded data (value)
  * `opts` - Forwarded to `Onchain.ABI.decode/3`. Pass `strict: true` to reject non-canonical padding, trailing bytes, and over-long dynamic length prefixes (`{:error, {:decode_error, {:strict_violation, detail}}}`). Pass `decode_structs: true` for a named-field map instead of a positional list. (default: `[]`, value)

## Returns

List of decoded values (or map when `decode_structs: true`). `reason` may be `{:strict_violation, detail}` when `strict: true`. (`{:ok, list | map} | {:error, {:decode_error, reason}}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Forwarded to `Onchain.ABI.decode/3`. Pass `strict: true` to reject non-canonical padding, trailing bytes, and over-long dynamic length prefixes (`{:error, {:decode_error, {:strict_violation, detail}}}`). Pass `decode_structs: true` for a named-field map instead of a positional list.",
      kind: :value
    },
    type_signature: %{
      description: "Tuple type signature wrapped in parentheses, e.g. \"(uint256)\" or \"(uint256,bool)\". Bare comma-separated types like \"uint256,bool\" are NOT accepted and raise an unhelpful upstream error.",
      kind: :value
    },
    hex_data: %{
      description: "0x-prefixed hex string of ABI-encoded data",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, list | map} | {:error, {:decode_error, reason}}",
    description: "List of decoded values (or map when `decode_structs: true`). `reason` may be `{:strict_violation, detail}` when `strict: true`."
  }
}
```

# `decode_response!`

```elixir
@spec decode_response!(String.t(), String.t(), keyword()) :: list() | map()
```

Decode hex-encoded ABI response data to Elixir values. Raises on error.

## Parameters

  * `type_signature` - Tuple type signature wrapped in parentheses, e.g. "(uint256)" or "(uint256,bool)". Bare comma-separated types are NOT accepted. (value)
  * `hex_data` - 0x-prefixed hex string of ABI-encoded data (value)
  * `opts` - Forwarded to `Onchain.ABI.decode/3` (e.g. `strict: true`) (default: `[]`, value)

## Returns

List of decoded values (or map when `decode_structs: true`) (`union`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Forwarded to `Onchain.ABI.decode/3` (e.g. `strict: true`)",
      kind: :value
    },
    type_signature: %{
      description: "Tuple type signature wrapped in parentheses, e.g. \"(uint256)\" or \"(uint256,bool)\". Bare comma-separated types are NOT accepted.",
      kind: :value
    },
    hex_data: %{
      description: "0x-prefixed hex string of ABI-encoded data",
      kind: :value
    }
  },
  returns: %{
    type: :union,
    description: "List of decoded values (or map when `decode_structs: true`)"
  }
}
```

# `decode_types`

```elixir
@spec decode_types(String.t(), String.t(), keyword()) ::
  {:ok, list() | map()} | {:error, {:decode_error, term()}}
```

Decode arbitrary ABI-encoded hex data. Alias of decode_response/3.

## Parameters

  * `type_signature` - Tuple type signature wrapped in parentheses, e.g. "(uint256)" or "(uint256,bool)". Bare comma-separated types are NOT accepted. (value)
  * `hex_data` - 0x-prefixed hex string of ABI-encoded data (value)
  * `opts` - Forwarded to decode_response/3 (e.g. `strict: true`) (default: `[]`, value)

## Returns

List of decoded values. Identical to decode_response/3 — use this name when the input isn't an RPC response (mempool calldata, custom ABI payloads). (`{:ok, list | map} | {:error, {:decode_error, reason}}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Forwarded to decode_response/3 (e.g. `strict: true`)",
      kind: :value
    },
    type_signature: %{
      description: "Tuple type signature wrapped in parentheses, e.g. \"(uint256)\" or \"(uint256,bool)\". Bare comma-separated types are NOT accepted.",
      kind: :value
    },
    hex_data: %{
      description: "0x-prefixed hex string of ABI-encoded data",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, list | map} | {:error, {:decode_error, reason}}",
    description: "List of decoded values. Identical to decode_response/3 — use this name when the input isn't an RPC response (mempool calldata, custom ABI payloads)."
  }
}
```

# `decode_types!`

```elixir
@spec decode_types!(String.t(), String.t(), keyword()) :: list() | map()
```

Decode arbitrary ABI-encoded hex data. Alias of decode_response!/3.

## Parameters

  * `type_signature` - Tuple type signature wrapped in parentheses, e.g. "(uint256)" or "(uint256,bool)". Bare comma-separated types are NOT accepted. (value)
  * `hex_data` - 0x-prefixed hex string of ABI-encoded data (value)
  * `opts` - Forwarded to decode_response!/3 (e.g. `strict: true`) (default: `[]`, value)

## Returns

List of decoded values (or map when `decode_structs: true`) (`union`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Forwarded to decode_response!/3 (e.g. `strict: true`)",
      kind: :value
    },
    type_signature: %{
      description: "Tuple type signature wrapped in parentheses, e.g. \"(uint256)\" or \"(uint256,bool)\". Bare comma-separated types are NOT accepted.",
      kind: :value
    },
    hex_data: %{
      description: "0x-prefixed hex string of ABI-encoded data",
      kind: :value
    }
  },
  returns: %{
    type: :union,
    description: "List of decoded values (or map when `decode_structs: true`)"
  }
}
```

# `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() | Onchain.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 `Onchain.ABI.FunctionSelector` structs returned from `parse_specification/1`.

## Examples

    iex> Onchain.ABI.encode("(uint256)", [{10}])
    ...> |> Base.encode16(case: :lower)
    "000000000000000000000000000000000000000000000000000000000000000a"

    iex> Onchain.ABI.encode("baz(uint,address)", [50, <<1::160>>])
    ...> |> Base.encode16(case: :lower)
    "a291add600000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001"

    iex> Onchain.ABI.encode("price(string)", ["BAT"])
    ...> |> Base.encode16(case: :lower)
    "fe2c6198000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034241540000000000000000000000000000000000000000000000000000000000"

    iex> Onchain.ABI.encode("baz(uint8)", [9999])
    ** (RuntimeError) Data overflow encoding uint, data `9999` cannot fit in 8 bits

    iex> Onchain.ABI.encode("(uint,address)", [{50, <<1::160>>}])
    ...> |> Base.encode16(case: :lower)
    "00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000001"

    iex> Onchain.ABI.encode("(string)", [{"Ether Token"}])
    ...> |> Base.encode16(case: :lower)
    "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b457468657220546f6b656e000000000000000000000000000000000000000000"

    iex> Onchain.ABI.encode("((uint256,uint256),string)", [{{0x11, 0x22}, "Ether Token"}])
    ...> |> Base.encode16(case: :lower)
    "000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000b457468657220546f6b656e000000000000000000000000000000000000000000"

    iex> Onchain.ABI.encode("((uint256,(uint256,uint256)),string)", [{{0x11, {0x22, 0x33}}, "Ether Token"}])
    ...> |> Base.encode16(case: :lower)
    "0000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b457468657220546f6b656e000000000000000000000000000000000000000000"

    iex> Onchain.ABI.encode("(string)", [{String.duplicate("1234567890", 10)}])
    ...> |> Base.encode16(case: :lower)
    "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000643132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393000000000000000000000000000000000000000000000000000000000"

    iex> File.read!("priv/dog.abi.json")
    ...> |> Jason.decode!
    ...> |> Onchain.ABI.parse_specification
    ...> |> Enum.find(&(&1.function == "bark")) # bark(address,bool)
    ...> |> Onchain.ABI.encode([<<1::160>>, true])
    ...> |> Base.encode16(case: :lower)
    "b85d0bd200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001"

# `encode_call`

```elixir
@spec encode_call(binary() | Onchain.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
`Onchain.ABI.FunctionSelector`, plus the argument list, and receive the full
transaction calldata blob.

## Examples

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

    iex> Onchain.ABI.encode_call(%Onchain.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(Onchain.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 `%Onchain.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] =
    ...>   Onchain.ABI.parse_specification([%{
    ...>     "type" => "constructor",
    ...>     "stateMutability" => "nonpayable",
    ...>     "inputs" => [%{"name" => "supply", "type" => "uint256"}]
    ...>   }])
    iex> Onchain.ABI.encode_constructor(selector, [1000])
    ...> |> Base.encode16(case: :lower)
    "00000000000000000000000000000000000000000000000000000000000003e8"

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

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

# `encode_error`

```elixir
@spec encode_error(binary() | Onchain.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
`Onchain.ABI.FunctionSelector`, plus the argument list, and receive the full revert data blob.

## Examples

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

    iex> Onchain.ABI.encode_error(%Onchain.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() | Onchain.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> Onchain.ABI.encode_event_topics(
    ...>   "Transfer(address indexed from,address indexed to,uint256 amount)",
    ...>   [~h[0xb2b7c1795f19fbc28fda77a95e59edbb8b3709c8], :any]
    ...> )
    [
      ~h[0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef],
      ~h[0x000000000000000000000000b2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
      :any
    ]

# `encode_hex_call`

```elixir
@spec encode_hex_call(String.t(), list()) ::
  {:ok, String.t()} | {:error, {:encode_error, term()}}
```

Encode a function call to 0x-prefixed hex calldata.

## Parameters

  * `signature` - Function signature, e.g. "balanceOf(address)" (value)
  * `params` - List of parameter values matching the signature (value)

## Returns

0x-prefixed hex-encoded calldata (`{:ok, hex_string} | {:error, {:encode_error, reason}}`)

```elixir
# descripex:contract
%{
  params: %{
    signature: %{
      description: "Function signature, e.g. \"balanceOf(address)\"",
      kind: :value
    },
    params: %{
      description: "List of parameter values matching the signature",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, hex_string} | {:error, {:encode_error, reason}}",
    description: "0x-prefixed hex-encoded calldata",
    example: "0x70a08231..."
  }
}
```

# `encode_hex_call!`

```elixir
@spec encode_hex_call!(String.t(), list()) :: String.t()
```

Encode a function call to 0x-prefixed hex calldata. Raises on error.

## Parameters

  * `signature` - Function signature, e.g. "balanceOf(address)" (value)
  * `params` - List of parameter values matching the signature (value)

## Returns

0x-prefixed hex-encoded calldata (`string`)

```elixir
# descripex:contract
%{
  params: %{
    signature: %{
      description: "Function signature, e.g. \"balanceOf(address)\"",
      kind: :value
    },
    params: %{
      description: "List of parameter values matching the signature",
      kind: :value
    }
  },
  returns: %{type: :string, description: "0x-prefixed hex-encoded calldata"}
}
```

# `encode_packed`

```elixir
@spec encode_packed(binary() | Onchain.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> Onchain.ABI.encode_packed("foo(int16,bytes1,uint16,string)", [-1, <<0x42>>, 3, "Hello, world!"])
    ...> |> Base.encode16(case: :lower)
    "ffff42000348656c6c6f2c20776f726c6421"

    iex> Onchain.ABI.encode_packed("leaf(address,uint256)", [<<1::160>>, 100])
    ...> |> Base.encode16(case: :lower)
    "00000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000064"

    iex> Onchain.ABI.encode_packed("foo(uint8[])", [[1, 2, 3]])
    ...> |> Base.encode16(case: :lower)
    "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003"

# `event_signature`

```elixir
@spec event_signature(binary() | Onchain.ABI.FunctionSelector.t()) :: binary()
```

Returns the signature for an event.

## Examples

    iex> Onchain.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(Onchain.ABI.FunctionSelector.t()) :: String.t()
```

Renders an `Onchain.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
`Onchain.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> Onchain.ABI.parse_specification([%{"type" => "function", "name" => "transfer", "inputs" => [%{"name" => "to", "type" => "address"}, %{"name" => "amount", "type" => "uint256"}]}])
    ...> |> hd()
    ...> |> Onchain.ABI.format_abi_item()
    "transfer(address,uint256)"

    iex> Onchain.ABI.format_abi_item(%Onchain.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> Onchain.ABI.format_abi_item(%Onchain.ABI.FunctionSelector{function: nil, function_type: :constructor, types: [%{type: {:uint, 8}}]})
    "(uint8)"

# `get_abi_item`

```elixir
@spec get_abi_item(
  [Onchain.ABI.FunctionSelector.t()],
  String.t(),
  [Onchain.ABI.FunctionSelector.type()] | nil
) ::
  {:ok, Onchain.ABI.FunctionSelector.t()}
  | {:error, :not_found}
  | {:error, {:ambiguous, [Onchain.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 =
    ...>   Onchain.ABI.parse_specification([
    ...>     %{"type" => "function", "name" => "transfer", "inputs" => [
    ...>       %{"type" => "address"},
    ...>       %{"type" => "uint256"}
    ...>     ]}
    ...>   ])
    iex> {:ok, %Onchain.ABI.FunctionSelector{function: "transfer"}} = Onchain.ABI.get_abi_item(abi, "transfer", nil)

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

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

# `method_id`

```elixir
@spec method_id(binary() | Onchain.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> Onchain.ABI.method_id("transfer(address,uint256)") |> Base.encode16(case: :lower)
    "a9059cbb"

    iex> Onchain.ABI.method_id("deposit()") |> Base.encode16(case: :lower)
    "d0e30db0"

    iex> Onchain.ABI.method_id(%Onchain.ABI.FunctionSelector{function: "deposit", types: []}) |> Base.encode16(case: :lower)
    "d0e30db0"

    iex> Onchain.ABI.method_id(%Onchain.ABI.FunctionSelector{function: nil, types: [%{type: {:uint, 256}}]})
    ""

# `parse_specification`

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

Parses the given ABI specification document into an array of `Onchain.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!
    ...> |> Onchain.ABI.parse_specification
    [%Onchain.ABI.FunctionSelector{function: "bark", function_type: :function, state_mutability: :nonpayable, returns: [], types: [%{name: "at", type: :address}, %{name: "loudly", type: :bool}]},
     %Onchain.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"
    ...> }]
    ...> |> Onchain.ABI.parse_specification
    [
      %Onchain.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"
    ...> }]
    ...> |> Onchain.ABI.parse_specification
    [%Onchain.ABI.FunctionSelector{function: nil, function_type: :constructor, state_mutability: :nonpayable, types: [%{name: "_numProposals", type: {:uint, 8}}], returns: nil}]

    iex> Onchain.ABI.decode("(string)", "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000643132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393000000000000000000000000000000000000000000000000000000000" |> Base.decode16!(case: :lower))
    [String.duplicate("1234567890", 10)]

    iex> [%{
    ...>   "payable" => false,
    ...>   "stateMutability" => "nonpayable",
    ...>   "type" => "fallback"
    ...> }]
    ...> |> Onchain.ABI.parse_specification
    [%Onchain.ABI.FunctionSelector{function: nil, function_type: :fallback, state_mutability: :nonpayable, returns: nil, types: []}]

---

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