# `X402.EIP712`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/eip712.ex#L1)

Generic [EIP-712](https://eips.ethereum.org/EIPS/eip-712) hashing primitives.

Shared by the x402 signing modules: `X402.EIP3009` hashes
`TransferWithAuthorization` structs with these primitives,
`X402.Permit2` hashes Permit2 `PermitWitnessTransferFrom` structs, and
`X402.Extensions.EIP2612GasSponsoring` hashes EIP-2612 `Permit` structs.
The module covers the common x402 domain shape
(`name`/`version`/`chainId`/`verifyingContract`, the version optional
for version-less domains such as Permit2's) and the generic
`hash_struct/2` / `digest/2` combination

    digest = keccak256(0x19 0x01 || domainSeparator || structHash)

together with the ABI word encoders the struct hashes are built from.

Cryptographic hashing requires the optional `ex_keccak` dependency and
returns `{:error, :missing_dependency}` when it is unavailable; the library
itself compiles without it.

# `domain`

```elixir
@type domain() :: %{
  optional(:version) =&gt; String.t(),
  name: String.t(),
  chain_id: non_neg_integer(),
  verifying_contract: String.t()
}
```

An EIP-712 domain map with `:name`, `:version`, `:chain_id`, and
`:verifying_contract` keys.

Functions accepting a domain also take the wire-style keys
(`"name"`, `"version"`, `"chainId"`, `"verifyingContract"`). Domains
whose contracts declare no version (for example the canonical Permit2
contract) omit the version key entirely; `domain_separator/1` then hashes
the three-field `EIP712Domain` type.

# `domain_error`

```elixir
@type domain_error() ::
  :invalid_requirements | {:missing_extra, String.t()} | :unsupported_network
```

# `encode_error`

```elixir
@type encode_error() ::
  :missing_dependency
  | :invalid_address
  | :invalid_amount
  | :invalid_bytes32
  | :invalid_word
  | {:missing_field, String.t()}
```

# `chain_id_from_caip2`
*since 0.6.0* 

```elixir
@spec chain_id_from_caip2(term()) ::
  {:ok, non_neg_integer()} | {:error, :unsupported_network}
```

Extracts the chain id from an `eip155:<chainId>` CAIP-2 network identifier.

## Examples

    iex> X402.EIP712.chain_id_from_caip2("eip155:84532")
    {:ok, 84532}

    iex> X402.EIP712.chain_id_from_caip2("solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp")
    {:error, :unsupported_network}

# `digest`
*since 0.6.0* 

```elixir
@spec digest(map(), &lt;&lt;_::256&gt;&gt;) :: {:ok, &lt;&lt;_::256&gt;&gt;} | {:error, encode_error()}
```

Computes the final EIP-712 digest for a domain and a struct hash.

Returns `keccak256(0x19 0x01 || domainSeparator || structHash)` as a
32-byte binary — the value a signer signs.

# `domain`
*since 0.6.0* 

```elixir
@spec domain(map()) :: {:ok, domain()} | {:error, domain_error()}
```

Derives the EIP-712 domain from v2 payment requirements.

Per the exact-EVM scheme specification, `extra.name` and `extra.version`
are required, the chain id comes from the CAIP-2 `network`, and the
verifying contract is the `asset` address. Unlike `X402.EIP3009.domain/1`,
the `extra.assetTransferMethod` is not restricted, so the same derivation
serves EIP-3009, Permit2, and extension signing.

## Examples

    iex> X402.EIP712.domain(%{
    ...>   "network" => "eip155:84532",
    ...>   "asset" => "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    ...>   "extra" => %{"name" => "USDC", "version" => "2"}
    ...> })
    {:ok,
     %{
       name: "USDC",
       version: "2",
       chain_id: 84532,
       verifying_contract: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
     }}

    iex> X402.EIP712.domain(%{"network" => "eip155:84532", "asset" => "0xasset", "extra" => %{}})
    {:error, {:missing_extra, "name"}}

# `domain_separator`
*since 0.6.0* 

```elixir
@spec domain_separator(map()) :: {:ok, &lt;&lt;_::256&gt;&gt;} | {:error, encode_error()}
```

Computes the EIP-712 domain separator for a domain map.

Returns `keccak256(typeHash || keccak256(name) || keccak256(version) ||
chainId || verifyingContract)` as a 32-byte binary. When the domain has
no version (absent or `nil` — for example the canonical Permit2
contract), the version-less three-field `EIP712Domain` type is hashed
instead, per EIP-712's rule that absent fields are dropped from the
domain type.

# `encode_address`
*since 0.6.0* 

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

ABI-encodes a `0x`-prefixed EVM address into a 32-byte word.

## Examples

    iex> {:ok, word} = X402.EIP712.encode_address("0x1111111111111111111111111111111111111111")
    iex> byte_size(word)
    32

    iex> X402.EIP712.encode_address("0x123")
    {:error, :invalid_address}

# `encode_bytes32`
*since 0.6.0* 

```elixir
@spec encode_bytes32(term()) :: {:ok, &lt;&lt;_::256&gt;&gt;} | {:error, :invalid_bytes32}
```

Decodes a `0x`-prefixed hex string into a 32-byte binary.

## Examples

    iex> {:ok, bytes} = X402.EIP712.encode_bytes32("0x" <> String.duplicate("ab", 32))
    iex> byte_size(bytes)
    32

    iex> X402.EIP712.encode_bytes32("0xdead")
    {:error, :invalid_bytes32}

# `encode_dynamic_bytes`
*since 0.6.0* 

```elixir
@spec encode_dynamic_bytes(binary()) :: binary()
```

ABI-encodes a dynamic `bytes` value: a 32-byte length word followed by the
bytes right-padded to a 32-byte boundary.

Used to build calldata for functions taking `bytes` arguments
(`isValidSignature`, the `bytes`-signature `transferWithAuthorization`
variant, `aggregate3`).

## Examples

    iex> X402.EIP712.encode_dynamic_bytes(<<0xAB>>)
    <<1::unsigned-big-integer-size(256), 0xAB, 0::unsigned-big-integer-size(248)>>

# `encode_uint256`
*since 0.6.0* 

```elixir
@spec encode_uint256(term()) :: {:ok, &lt;&lt;_::256&gt;&gt;} | {:error, :invalid_amount}
```

ABI-encodes a non-negative integer (or decimal string) into a 32-byte word.

## Examples

    iex> X402.EIP712.encode_uint256(1)
    {:ok, <<1::unsigned-big-integer-size(256)>>}

    iex> X402.EIP712.encode_uint256("not a number")
    {:error, :invalid_amount}

# `hash_struct`
*since 0.6.0* 

```elixir
@spec hash_struct(String.t(), [&lt;&lt;_::256&gt;&gt;]) ::
  {:ok, &lt;&lt;_::256&gt;&gt;} | {:error, :missing_dependency | :invalid_word}
```

Computes an EIP-712 struct hash from a type string and encoded words.

`type` is the full encoded type (for example
`"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"`)
and `words` the ABI-encoded 32-byte field values, in declaration order —
see `encode_address/1`, `encode_uint256/1`, and `encode_bytes32/1`.

---

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