# `Onchain.ABI`
[🔗](https://github.com/ZenHive/onchain/blob/v0.5.4/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.

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

## Functions

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

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `decode_error!` | 2 | Decode custom-error revert data. Raises on error. | `hex_revert_data: value`, `error_definitions: value` |
| `decode_error` | 2 | Decode Solidity 0.8.4+ custom-error revert data against a list of candidate error signatures. | `hex_revert_data: value`, `error_definitions: 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!` | 2 | Decode arbitrary ABI-encoded hex data. Alias of decode_response!/2. | `type_signature: value`, `hex_data: value` |
| `decode_types` | 2 | Decode arbitrary ABI-encoded hex data. Alias of decode_response/2. | `type_signature: value`, `hex_data: value` |
| `decode_response!` | 2 | Decode hex-encoded ABI response data to Elixir values. Raises on error. | `type_signature: value`, `hex_data: value` |
| `decode_response` | 2 | Decode hex-encoded ABI response data to Elixir values. | `type_signature: value`, `hex_data: 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. (default: `[]`, value)

## Returns

List of args (or map when `decode_structs: true`). Error reasons: `:calldata_too_short`, `:selector_mismatch`, `:no_function_name`, `{:invalid_hex, _}`, 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.",
      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, _}`, 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`) (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`)",
      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()]) ::
  {:ok, %{error: String.t() | nil, args: list()}}
  | {: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)

## Returns

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

```elixir
# descripex:contract
%{
  params: %{
    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, _}`, or upstream exception message string."
  }
}
```

# `decode_error!`

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

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)

## Returns

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

```elixir
# descripex:contract
%{
  params: %{
    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()) ::
  {:ok, list()} | {: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)

## Returns

List of decoded values (`{:ok, list} | {:error, {:decode_error, reason}}`)

```elixir
# descripex:contract
%{
  params: %{
    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} | {:error, {:decode_error, reason}}",
    description: "List of decoded values"
  }
}
```

# `decode_response!`

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

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)

## Returns

List of decoded values (`list`)

```elixir
# descripex:contract
%{
  params: %{
    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: :list, description: "List of decoded values"}
}
```

# `decode_types`

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

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

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

## Returns

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

```elixir
# descripex:contract
%{
  params: %{
    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} | {:error, {:decode_error, reason}}",
    description: "List of decoded values. Identical to decode_response/2 — 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()) :: list()
```

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

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

## Returns

List of decoded values (`list`)

```elixir
# descripex:contract
%{
  params: %{
    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: :list, description: "List of decoded values"}
}
```

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