# `Onchain.ERC20`
[🔗](https://github.com/ZenHive/onchain/blob/v0.5.0/lib/onchain/erc20.ex#L1)

ERC-20 token operations.

Read operations are thin wrappers around `Onchain.Contract.call/5`.
Write operations delegate to `Onchain.Signer.send_transaction/3`.
Returns raw integer values for balances — consumers use
`Onchain.Decimal.to_decimal/2` with the result of `decimals/2` to normalize.

## Error Format

Errors pass through from underlying modules:

| Source | Error Shape |
|--------|-------------|
| `Onchain.Address.validate/1` | `{:error, {:invalid_address, input}}` |
| `Onchain.Contract.call/5` | `{:error, {:encode_error, ...}}`, `{:error, {:rpc_error, ...}}`, `{:error, {:decode_error, ...}}` |
| `Onchain.ABI.encode_call/2` | `{:error, {:encode_error, ...}}` |
| `Onchain.Signer.send_transaction/3` | `{:error, {:missing_option, ...}}`, `{:error, {:sign_error, ...}}`, etc. |

## Functions

| Function | Purpose |
|----------|---------|
| `balance_of/3` | Token balance for a holder (raw integer) |
| `balance_of!/3` | Same, raises on error |
| `allowance/4` | Approved spending amount (raw integer) |
| `allowance!/4` | Same, raises on error |
| `decimals/2` | Token decimal places |
| `decimals!/2` | Same, raises on error |
| `symbol/2` | Token ticker symbol |
| `symbol!/2` | Same, raises on error |
| `approve/4` | Approve spender to transfer tokens (returns tx hash) |
| `approve!/4` | Same, raises on error |
| `transfer/4` | Transfer tokens to recipient (returns tx hash) |
| `transfer!/4` | Same, raises on error |

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `transfer!` | 4 | Transfer tokens to a recipient. Raises on error. | `token: value`, `to: value`, `amount: value`, `opts: value` |
| `transfer` | 4 | Transfer tokens to a recipient. | `token: value`, `to: value`, `amount: value`, `opts: value` |
| `approve!` | 4 | Approve a spender to transfer tokens on your behalf. Raises on error. | `token: value`, `spender: value`, `amount: value`, `opts: value` |
| `approve` | 4 | Approve a spender to transfer tokens on your behalf. | `token: value`, `spender: value`, `amount: value`, `opts: value` |
| `symbol!` | 2 | Get the ticker symbol of a token. Raises on error. | `token: value`, `opts: value` |
| `symbol` | 2 | Get the ticker symbol of a token. | `token: value`, `opts: value` |
| `decimals!` | 2 | Get the number of decimal places for a token. Raises on error. | `token: value`, `opts: value` |
| `decimals` | 2 | Get the number of decimal places for a token. | `token: value`, `opts: value` |
| `allowance!` | 4 | Get the approved spending amount. Raises on error. | `token: value`, `owner: value`, `spender: value`, `opts: value` |
| `allowance` | 4 | Get the amount an owner has approved a spender to transfer. | `token: value`, `owner: value`, `spender: value`, `opts: value` |
| `balance_of!` | 3 | Get the token balance of an address. Raises on error. | `token: value`, `holder: value`, `opts: value` |
| `balance_of` | 3 | Get the token balance of an address. | `token: value`, `holder: value`, `opts: value` |

# `allowance`

```elixir
@spec allowance(
  String.t() | binary(),
  String.t() | binary(),
  String.t() | binary(),
  keyword()
) ::
  {:ok, non_neg_integer()} | {:error, term()}
```

Get the amount an owner has approved a spender to transfer.

## Parameters

  * `token` - ERC-20 token contract address (value)
  * `owner` - Token owner address (value)
  * `spender` - Approved spender address (value)
  * `opts` - Options: :rpc_url, :timeout, :block (default: `[]`, value)

## Returns

Approved spending amount as raw integer (`{:ok, non_neg_integer()} | {:error, term()}`)

```elixir
# descripex:contract
%{
  params: %{
    owner: %{description: "Token owner address", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :block",
      kind: :value
    },
    token: %{description: "ERC-20 token contract address", kind: :value},
    spender: %{description: "Approved spender address", kind: :value}
  },
  returns: %{
    type: "{:ok, non_neg_integer()} | {:error, term()}",
    description: "Approved spending amount as raw integer",
    example: "0"
  }
}
```

# `allowance!`

```elixir
@spec allowance!(
  String.t() | binary(),
  String.t() | binary(),
  String.t() | binary(),
  keyword()
) ::
  non_neg_integer()
```

Get the approved spending amount. Raises on error.

## Parameters

  * `token` - ERC-20 token contract address (value)
  * `owner` - Token owner address (value)
  * `spender` - Approved spender address (value)
  * `opts` - Options: :rpc_url, :timeout, :block (default: `[]`, value)

## Returns

Approved spending amount (`non_neg_integer`)

```elixir
# descripex:contract
%{
  params: %{
    owner: %{description: "Token owner address", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :block",
      kind: :value
    },
    token: %{description: "ERC-20 token contract address", kind: :value},
    spender: %{description: "Approved spender address", kind: :value}
  },
  returns: %{type: :non_neg_integer, description: "Approved spending amount"}
}
```

# `approve`

```elixir
@spec approve(
  String.t() | binary(),
  String.t() | binary(),
  non_neg_integer(),
  keyword()
) ::
  {:ok, String.t()} | {:error, term()}
```

Approve a spender to transfer tokens on your behalf.

## Parameters

  * `token` - ERC-20 token contract address (value)
  * `spender` - Address to approve for spending (value)
  * `amount` - Amount to approve (raw integer, not decimal-adjusted) (value)
  * `opts` - Required: :private_key, :nonce, :chain_id, :rpc_url. Optional: :gas_limit, :max_fee_per_gas, :max_priority_fee_per_gas (value)

## Returns

Transaction hash hex string (`{:ok, String.t()} | {:error, term()}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      description: "Required: :private_key, :nonce, :chain_id, :rpc_url. Optional: :gas_limit, :max_fee_per_gas, :max_priority_fee_per_gas",
      kind: :value
    },
    token: %{description: "ERC-20 token contract address", kind: :value},
    amount: %{
      description: "Amount to approve (raw integer, not decimal-adjusted)",
      kind: :value
    },
    spender: %{description: "Address to approve for spending", kind: :value}
  },
  returns: %{
    type: "{:ok, String.t()} | {:error, term()}",
    description: "Transaction hash hex string"
  }
}
```

# `approve!`

```elixir
@spec approve!(
  String.t() | binary(),
  String.t() | binary(),
  non_neg_integer(),
  keyword()
) :: String.t()
```

Approve a spender to transfer tokens on your behalf. Raises on error.

## Parameters

  * `token` - ERC-20 token contract address (value)
  * `spender` - Address to approve for spending (value)
  * `amount` - Amount to approve (raw integer, not decimal-adjusted) (value)
  * `opts` - Required: :private_key, :nonce, :chain_id, :rpc_url. Optional: :gas_limit, :max_fee_per_gas, :max_priority_fee_per_gas (value)

## Returns

Transaction hash hex string (`string`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      description: "Required: :private_key, :nonce, :chain_id, :rpc_url. Optional: :gas_limit, :max_fee_per_gas, :max_priority_fee_per_gas",
      kind: :value
    },
    token: %{description: "ERC-20 token contract address", kind: :value},
    amount: %{
      description: "Amount to approve (raw integer, not decimal-adjusted)",
      kind: :value
    },
    spender: %{description: "Address to approve for spending", kind: :value}
  },
  returns: %{type: :string, description: "Transaction hash hex string"}
}
```

# `balance_of`

```elixir
@spec balance_of(String.t() | binary(), String.t() | binary(), keyword()) ::
  {:ok, non_neg_integer()} | {:error, term()}
```

Get the token balance of an address.

## Parameters

  * `token` - ERC-20 token contract address (value)
  * `holder` - Address to check balance for (value)
  * `opts` - Options: :rpc_url, :timeout, :block (default: `[]`, value)

## Returns

Raw token balance (use decimals/2 + Onchain.Decimal.to_decimal/2 to normalize) (`{:ok, non_neg_integer()} | {:error, term()}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :block",
      kind: :value
    },
    token: %{description: "ERC-20 token contract address", kind: :value},
    holder: %{description: "Address to check balance for", kind: :value}
  },
  returns: %{
    type: "{:ok, non_neg_integer()} | {:error, term()}",
    description: "Raw token balance (use decimals/2 + Onchain.Decimal.to_decimal/2 to normalize)",
    example: "1000000"
  }
}
```

# `balance_of!`

```elixir
@spec balance_of!(String.t() | binary(), String.t() | binary(), keyword()) ::
  non_neg_integer()
```

Get the token balance of an address. Raises on error.

## Parameters

  * `token` - ERC-20 token contract address (value)
  * `holder` - Address to check balance for (value)
  * `opts` - Options: :rpc_url, :timeout, :block (default: `[]`, value)

## Returns

Raw token balance (`non_neg_integer`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :block",
      kind: :value
    },
    token: %{description: "ERC-20 token contract address", kind: :value},
    holder: %{description: "Address to check balance for", kind: :value}
  },
  returns: %{type: :non_neg_integer, description: "Raw token balance"}
}
```

# `decimals`

```elixir
@spec decimals(
  String.t() | binary(),
  keyword()
) :: {:ok, non_neg_integer()} | {:error, term()}
```

Get the number of decimal places for a token.

## Parameters

  * `token` - ERC-20 token contract address (value)
  * `opts` - Options: :rpc_url, :timeout, :block (default: `[]`, value)

## Returns

Token decimal places (e.g. 6 for USDC, 18 for DAI) (`{:ok, non_neg_integer()} | {:error, term()}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :block",
      kind: :value
    },
    token: %{description: "ERC-20 token contract address", kind: :value}
  },
  returns: %{
    type: "{:ok, non_neg_integer()} | {:error, term()}",
    description: "Token decimal places (e.g. 6 for USDC, 18 for DAI)",
    example: "6"
  }
}
```

# `decimals!`

```elixir
@spec decimals!(
  String.t() | binary(),
  keyword()
) :: non_neg_integer()
```

Get the number of decimal places for a token. Raises on error.

## Parameters

  * `token` - ERC-20 token contract address (value)
  * `opts` - Options: :rpc_url, :timeout, :block (default: `[]`, value)

## Returns

Token decimal places (`non_neg_integer`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :block",
      kind: :value
    },
    token: %{description: "ERC-20 token contract address", kind: :value}
  },
  returns: %{type: :non_neg_integer, description: "Token decimal places"}
}
```

# `symbol`

```elixir
@spec symbol(
  String.t() | binary(),
  keyword()
) :: {:ok, String.t()} | {:error, term()}
```

Get the ticker symbol of a token.

## Parameters

  * `token` - ERC-20 token contract address (value)
  * `opts` - Options: :rpc_url, :timeout, :block (default: `[]`, value)

## Returns

Token symbol string (`{:ok, String.t()} | {:error, term()}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :block",
      kind: :value
    },
    token: %{description: "ERC-20 token contract address", kind: :value}
  },
  returns: %{
    type: "{:ok, String.t()} | {:error, term()}",
    description: "Token symbol string",
    example: "\"USDC\""
  }
}
```

# `symbol!`

```elixir
@spec symbol!(
  String.t() | binary(),
  keyword()
) :: String.t()
```

Get the ticker symbol of a token. Raises on error.

## Parameters

  * `token` - ERC-20 token contract address (value)
  * `opts` - Options: :rpc_url, :timeout, :block (default: `[]`, value)

## Returns

Token symbol string (`string`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :block",
      kind: :value
    },
    token: %{description: "ERC-20 token contract address", kind: :value}
  },
  returns: %{type: :string, description: "Token symbol string"}
}
```

# `transfer`

```elixir
@spec transfer(
  String.t() | binary(),
  String.t() | binary(),
  non_neg_integer(),
  keyword()
) ::
  {:ok, String.t()} | {:error, term()}
```

Transfer tokens to a recipient.

## Parameters

  * `token` - ERC-20 token contract address (value)
  * `to` - Recipient address (value)
  * `amount` - Amount to transfer (raw integer, not decimal-adjusted) (value)
  * `opts` - Required: :private_key, :nonce, :chain_id, :rpc_url. Optional: :gas_limit, :max_fee_per_gas, :max_priority_fee_per_gas (value)

## Returns

Transaction hash hex string (`{:ok, String.t()} | {:error, term()}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      description: "Required: :private_key, :nonce, :chain_id, :rpc_url. Optional: :gas_limit, :max_fee_per_gas, :max_priority_fee_per_gas",
      kind: :value
    },
    token: %{description: "ERC-20 token contract address", kind: :value},
    to: %{description: "Recipient address", kind: :value},
    amount: %{
      description: "Amount to transfer (raw integer, not decimal-adjusted)",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, String.t()} | {:error, term()}",
    description: "Transaction hash hex string"
  }
}
```

# `transfer!`

```elixir
@spec transfer!(
  String.t() | binary(),
  String.t() | binary(),
  non_neg_integer(),
  keyword()
) ::
  String.t()
```

Transfer tokens to a recipient. Raises on error.

## Parameters

  * `token` - ERC-20 token contract address (value)
  * `to` - Recipient address (value)
  * `amount` - Amount to transfer (raw integer, not decimal-adjusted) (value)
  * `opts` - Required: :private_key, :nonce, :chain_id, :rpc_url. Optional: :gas_limit, :max_fee_per_gas, :max_priority_fee_per_gas (value)

## Returns

Transaction hash hex string (`string`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      description: "Required: :private_key, :nonce, :chain_id, :rpc_url. Optional: :gas_limit, :max_fee_per_gas, :max_priority_fee_per_gas",
      kind: :value
    },
    token: %{description: "ERC-20 token contract address", kind: :value},
    to: %{description: "Recipient address", kind: :value},
    amount: %{
      description: "Amount to transfer (raw integer, not decimal-adjusted)",
      kind: :value
    }
  },
  returns: %{type: :string, description: "Transaction hash hex string"}
}
```

---

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