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

ABI encoding/decoding for Ethereum contract calls.

Wraps the `hieroglyph` ABI library (transitively pulled in by cartouche) with `0x`-prefixed hex string
handling and consistent error tuples. Consumers work with hex strings from
RPC; this module bridges the gap.

## Type Signatures

`decode_response/2` (and its alias `decode_types/2`) expects **tuple type
syntax** for return values — the type list MUST be wrapped in parentheses,
e.g. `"(uint256)"` or `"(uint256,bool)"`. Bare comma-separated types
(`"uint256,bool"`) raise an unhelpful upstream error and are not accepted.
This is the standard pattern for decoding `eth_call` responses — NOT
function signatures with names.

Use `decode_response/2` when the input is an `eth_call` reply; use
`decode_types/2` when the input is arbitrary ABI-encoded bytes
(mempool calldata, custom payloads). They behave identically.

Decode functions accept an optional trailing keyword forwarded unchanged to
hieroglyph (`ABI.decode/3`, `ABI.decode_call/3`, `ABI.decode_error/3`).
`strict: true` rejects non-canonical payloads; `decode_structs: true` returns
a named-field map. The default (no opts / `strict: false`) stays permissive.

## Error Format

- Encode errors: `{:error, {:encode_error, reason}}`
- Decode errors: `{:error, {:decode_error, reason}}`

Where `reason` is either:
- A string from the upstream exception message
- A tuple like `{:invalid_hex, hex_data}` preserving the original hex error
- `{:strict_violation, detail}` when `strict: true` rejected a non-canonical
  payload (non-zero high padding on bool/uint/int, trailing bytes after the
  declared payload, or a string/bytes length prefix that exceeds the available
  data). Bang variants raise.

## Functions

| Function | Purpose |
|----------|---------|
| `encode_call/2` | Function signature + params → hex calldata |
| `encode_call!/2` | Same, raises on error |
| `decode_response/3` | Type signature + hex data → decoded values (optional decode opts) |
| `decode_response!/3` | Same, raises on error |
| `decode_types/3` | Alias of `decode_response/3` for non-RPC callers |
| `decode_types!/3` | Alias of `decode_response!/3`, raises on error |
| `decode_call/3` | Selector-prefixed calldata → decoded function args (forwards opts) |
| `decode_call!/3` | Same, raises on error |
| `decode_error/3` | Solidity 0.8.4+ custom-error revert data → `%{error, args}` (optional decode opts) |
| `decode_error!/3` | Same, raises on error |

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `decode_error!` | 3 | Decode custom-error revert data. Raises on error. | `hex_revert_data: value`, `error_definitions: value`, `opts: value` |
| `decode_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_call!` | 3 | Decode selector-prefixed calldata. Raises on error. | `signature_or_selector: value`, `hex_calldata: value`, `opts: value` |
| `decode_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_call!` | 2 | Encode a function call to 0x-prefixed hex calldata. Raises on error. | `signature: value`, `params: value` |
| `encode_call` | 2 | Encode a function call to 0x-prefixed hex calldata. | `signature: value`, `params: value` |

# `decode_call`

```elixir
@spec decode_call(String.t() | 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 a hieroglyph 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 hieroglyph's `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 hieroglyph's `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 a hieroglyph 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_call!`

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

Decode selector-prefixed calldata. Raises on error.

## Parameters

  * `signature_or_selector` - Function signature string or hieroglyph FunctionSelector struct (value)
  * `hex_calldata` - 0x-prefixed hex string of selector-prefixed calldata (value)
  * `opts` - Forwarded to hieroglyph's `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 hieroglyph's `ABI.decode_call/3` (e.g. `decode_structs: true`, `strict: true`)",
      kind: :value
    },
    signature_or_selector: %{
      description: "Function signature string or hieroglyph 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_error`

```elixir
@spec decode_error(String.t(), [String.t() | 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 hieroglyph's `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 hieroglyph's `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_error!`

```elixir
@spec decode_error!(String.t(), [String.t() | 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 hieroglyph's `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 hieroglyph's `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 hieroglyph's `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 hieroglyph's `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 hieroglyph's `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 hieroglyph's `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`)"
  }
}
```

# `encode_call`

```elixir
@spec encode_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_call!`

```elixir
@spec encode_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"}
}
```

---

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