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

Ethereum address validation, checksumming, and comparison.

Curated 7-function API wrapping `Signet.Hex` and `Signet.Util` with flexible
input handling (hex strings or 20-byte binaries) and normalized error tuples.

## Error Format

All failable functions return `{:error, {:invalid_address, input}}` where `input`
is the original value that failed validation. Bang variants raise `Signet.Hex.HexError`.

## Functions

| Function | Purpose |
|----------|---------|
| `validate/1` | Any input → 20-byte binary |
| `valid?/1` | Check if input is a valid address |
| `checksum/1` | EIP-55 checksummed hex string |
| `checksum!/1` | EIP-55 checksummed hex string (raises) |
| `normalize/1` | Lowercase 0x-prefixed hex string |
| `equal?/2` | Compare two addresses (any format) |
| `zero?/1` | Check if address is the zero address |

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `zero?` | 1 | Check if an address is the zero address (0x0000...0000). | `input: value` |
| `equal?` | 2 | Compare two addresses for equality, regardless of format. | `a: value`, `b: value` |
| `normalize` | 1 | Return a lowercase 0x-prefixed hex string for an address. | `input: value` |
| `checksum!` | 1 | Return the EIP-55 checksummed hex string. Raises on invalid input. | `input: value` |
| `checksum` | 1 | Return the EIP-55 checksummed hex string for an address. | `input: value` |
| `valid?` | 1 | Check whether the input is a valid Ethereum address. | `input: value` |
| `validate` | 1 | Validate and normalize an address to a 20-byte binary. | `input: value` |

# `checksum`

```elixir
@spec checksum(term()) :: {:ok, String.t()} | {:error, {:invalid_address, term()}}
```

Return the EIP-55 checksummed hex string for an address.

## Parameters

  * `input` - Hex string (with or without 0x) or 20-byte binary (value)

## Returns

EIP-55 checksummed address (`{:ok, String.t()} | {:error, {:invalid_address, input}}`)

```elixir
# descripex:contract
%{
  params: %{
    input: %{
      description: "Hex string (with or without 0x) or 20-byte binary",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, String.t()} | {:error, {:invalid_address, input}}",
    description: "EIP-55 checksummed address",
    example: "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
  }
}
```

# `checksum!`

```elixir
@spec checksum!(term()) :: String.t()
```

Return the EIP-55 checksummed hex string. Raises on invalid input.

## Parameters

  * `input` - Hex string (with or without 0x) or 20-byte binary (value)

## Returns

EIP-55 checksummed address (`string`)

```elixir
# descripex:contract
%{
  params: %{
    input: %{
      description: "Hex string (with or without 0x) or 20-byte binary",
      kind: :value
    }
  },
  returns: %{
    type: :string,
    description: "EIP-55 checksummed address",
    example: "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
  }
}
```

# `equal?`

```elixir
@spec equal?(term(), term()) :: boolean()
```

Compare two addresses for equality, regardless of format.

## Parameters

  * `a` - First address (hex string or 20-byte binary) (value)
  * `b` - Second address (hex string or 20-byte binary) (value)

## Returns

true if both resolve to the same 20-byte address (`boolean`)

```elixir
# descripex:contract
%{
  params: %{
    b: %{
      description: "Second address (hex string or 20-byte binary)",
      kind: :value
    },
    a: %{
      description: "First address (hex string or 20-byte binary)",
      kind: :value
    }
  },
  returns: %{
    type: :boolean,
    description: "true if both resolve to the same 20-byte address"
  }
}
```

# `normalize`

```elixir
@spec normalize(term()) :: {:ok, String.t()} | {:error, {:invalid_address, term()}}
```

Return a lowercase 0x-prefixed hex string for an address.

## Parameters

  * `input` - Hex string (with or without 0x) or 20-byte binary (value)

## Returns

Lowercase hex address (`{:ok, String.t()} | {:error, {:invalid_address, input}}`)

```elixir
# descripex:contract
%{
  params: %{
    input: %{
      description: "Hex string (with or without 0x) or 20-byte binary",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, String.t()} | {:error, {:invalid_address, input}}",
    description: "Lowercase hex address",
    example: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"
  }
}
```

# `valid?`

```elixir
@spec valid?(term()) :: boolean()
```

Check whether the input is a valid Ethereum address.

## Parameters

  * `input` - Value to check (value)

## Returns

true if valid 20-byte address (`boolean`)

```elixir
# descripex:contract
%{
  params: %{input: %{description: "Value to check", kind: :value}},
  returns: %{type: :boolean, description: "true if valid 20-byte address"}
}
```

# `validate`

```elixir
@spec validate(term()) :: {:ok, &lt;&lt;_::160&gt;&gt;} | {:error, {:invalid_address, term()}}
```

Validate and normalize an address to a 20-byte binary.

## Parameters

  * `input` - Hex string (with or without 0x) or 20-byte binary (value)

## Returns

Validated 20-byte binary address (`{:ok, <<20 bytes>>} | {:error, {:invalid_address, input}}`)

```elixir
# descripex:contract
%{
  params: %{
    input: %{
      description: "Hex string (with or without 0x) or 20-byte binary",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, <<20 bytes>>} | {:error, {:invalid_address, input}}",
    description: "Validated 20-byte binary address"
  }
}
```

# `zero?`

```elixir
@spec zero?(term()) :: boolean()
```

Check if an address is the zero address (0x0000...0000).

## Parameters

  * `input` - Hex string or 20-byte binary (value)

## Returns

true if the zero address (`boolean`)

```elixir
# descripex:contract
%{
  params: %{input: %{description: "Hex string or 20-byte binary", kind: :value}},
  returns: %{type: :boolean, description: "true if the zero address"}
}
```

---

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