# `ABI`
[🔗](https://github.com/ZenHive/hieroglyph/blob/main/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` |
| `event_signature` | 1 | Returns the 32-byte topic hash for an event signature. | `function_signature: value` |
| `decode_event` | 4 | Decodes an event from raw log data and indexed topics. | `function_signature: value`, `data: value`, `topics: value`, `opts: value` |
| `encode_packed` | 2 | Encodes values using Solidity's non-standard packed mode (abi.encodePacked). | `signature_or_selector: value`, `values: value` |
| `decode_error` | 2 | Decodes selector-prefixed revert data against a list of known custom-error definitions. | `revert_data: value`, `error_definitions: value` |
| `decode_call` | 3 | Decodes selector-prefixed calldata (4-byte method ID + ABI-encoded args) and verifies the selector matches. | `signature_or_selector: value`, `calldata: value`, `opts: value` |
| `decode` | 3 | Decodes the given data based on the function or tuple signature. | `function_signature: value`, `data: value`, `opts: value` |
| `method_id` | 1 | Returns the 4-byte function selector (method ID) for a function signature. | `signature: value` |
| `encode` | 2 | Encodes the given data into the function signature or tuple signature. | `function_signature: value`, `data: value` |

# `__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()
```

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.

## 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

> #### 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()]) ::
  {: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.

Returns:

* `{:ok, %{error: name, args: decoded}}` — the first definition matching the
  4-byte selector. `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's selector matched

> #### 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> 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()) :: [map()]
```

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

# `describe`

```elixir
@spec describe(module() | atom(), 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_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"

# `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*
