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

EIP-3009 `TransferWithAuthorization` building and EIP-712 signing.

Implements the client half of the x402 `exact` scheme on EVM networks with
the default `eip3009` asset transfer method: building an authorization from
v2 payment requirements, computing its EIP-712 digest, and signing it
through the `X402.Signer` behaviour to produce the scheme `payload` map

    %{"signature" => "0x...", "authorization" => %{...}}

carried inside a v2 `PaymentPayload` (see `X402.Client.build_payment/3` for
the full envelope).

The EIP-712 domain is derived from the payment requirements as specified by
the exact-EVM scheme: `name`/`version` from `extra`, the chain id from the
CAIP-2 `network`, and the verifying contract from `asset`.

Cryptographic operations require the optional `ex_secp256k1` and
`ex_keccak` dependencies and return `{:error, :missing_dependency}` when
they are unavailable; the library itself compiles without them.

# `transfer_calldata`
*since 0.6.0* 

```elixir
@spec transfer_calldata(map(), binary(), :eoa | :erc1271 | :erc6492_counterfactual) ::
  {:ok, binary()} | {:error, encode_error() | :invalid_signature}
```

Builds the `transferWithAuthorization` calldata for an authorization and
its raw signature bytes.

The overload is selected by the VERIFIED signature type, never by byte
length: only `:eoa` signatures take the `(v, r, s)` variant (selector
`0xe3ee160e`, with `v` normalized to `27`/`28` as EIP-3009 contracts
expect); contract signatures (`:erc1271`, `:erc6492_counterfactual`) —
which can also be exactly 65 bytes — always take the dynamic-`bytes`
variant (selector `0xcf092995`), whose token-side SignatureChecker
routes by account code. Shared by `X402.Verify.EVM`'s `eth_call` simulation and
`X402.Facilitator.Engine`'s settlement transaction — the calldata a
facilitator signs is always built here, from the verified authorization
fields, and nowhere else.

## Examples

    iex> authorization = %{
    ...>   "from" => "0x1111111111111111111111111111111111111111",
    ...>   "to" => "0x2222222222222222222222222222222222222222",
    ...>   "value" => "10000",
    ...>   "validAfter" => "0",
    ...>   "validBefore" => "99999999999",
    ...>   "nonce" => "0x" <> String.duplicate("ab", 32)
    ...> }
    iex> {:ok, calldata} = X402.EIP3009.transfer_calldata(authorization, <<1::520>>, :eoa)
    iex> {binary_part(calldata, 0, 4), byte_size(calldata)}
    {<<0xE3, 0xEE, 0x16, 0x0E>>, 4 + 9 * 32}
    iex> {:ok, contract} = X402.EIP3009.transfer_calldata(authorization, <<1::520>>, :erc1271)
    iex> binary_part(contract, 0, 4)
    <<0xCF, 0x09, 0x29, 0x95>>

    iex> X402.EIP3009.transfer_calldata(%{}, <<1::520>>, :eoa)
    {:error, {:missing_field, "from"}}

# `authorization`

```elixir
@type authorization() :: %{optional(String.t()) =&gt; String.t()}
```

A `TransferWithAuthorization` authorization in wire shape: string keys
`"from"`, `"to"`, `"value"`, `"validAfter"`, `"validBefore"`, `"nonce"`.

# `domain`

```elixir
@type domain() :: X402.EIP712.domain()
```

An EIP-712 domain map — see `t:X402.EIP712.domain/0`.

# `domain_error`

```elixir
@type domain_error() ::
  X402.EIP712.domain_error() | {:unsupported_transfer_method, term()}
```

# `encode_error`

```elixir
@type encode_error() :: X402.EIP712.encode_error()
```

# `payload`

```elixir
@type payload() :: %{optional(String.t()) =&gt; String.t() | authorization()}
```

The scheme `payload` map for a signed EIP-3009 payment.

# `build_authorization`
*since 0.6.0* 

```elixir
@spec build_authorization(map(), String.t(), keyword()) ::
  {:ok, authorization()} | {:error, :invalid_requirements}
```

Builds a `TransferWithAuthorization` authorization map in wire shape.

`value` and `to` come from the requirements' `amount` and `payTo`;
`validAfter` is `now - valid_after_buffer`, `validBefore` is
`now + maxTimeoutSeconds`, and `nonce` is a fresh random 32-byte value.

Field values are validated when the digest is computed, not here.

## Options

* `:valid_after_buffer` (`t:non_neg_integer/0`) - Seconds subtracted from the current time for the authorization's
  `validAfter`, tolerating clock skew between payer, facilitator, and
  chain. The default value is `60`.

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

See `X402.EIP712.chain_id_from_caip2/1`.

## Examples

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

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

# `derive_address`
*since 0.6.0* 

```elixir
@spec derive_address(binary()) ::
  {:ok, String.t()} | {:error, :missing_dependency | :invalid_private_key}
```

Derives the lowercase EVM address for a 32-byte secp256k1 private key.

# `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. Requirements selecting a
non-default `extra.assetTransferMethod` are rejected.

## Examples

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

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

# `eip712_digest`
*since 0.6.0* 

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

Computes the EIP-712 digest of a `TransferWithAuthorization`.

Returns `keccak256(0x19 0x01 || domainSeparator || structHash)` as a
32-byte binary. `domain` and `authorization` accept both the internal
snake-case atom keys and the wire-style string keys.

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

See `X402.EIP712.encode_address/1`.

## Examples

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

    iex> X402.EIP3009.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.

See `X402.EIP712.encode_bytes32/1`.

## Examples

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

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

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

See `X402.EIP712.encode_uint256/1`.

## Examples

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

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

# `public_key_to_address`
*since 0.6.0* 

```elixir
@spec public_key_to_address(binary()) ::
  {:ok, String.t()} | {:error, :missing_dependency | :invalid_public_key}
```

Converts a 65-byte uncompressed (or 64-byte) secp256k1 public key to a
lowercase `0x`-prefixed EVM address.

# `random_nonce`
*since 0.6.0* 

```elixir
@spec random_nonce() :: String.t()
```

Returns a fresh random 32-byte nonce as a `0x`-prefixed hex string.

## Examples

    iex> nonce = X402.EIP3009.random_nonce()
    iex> String.match?(nonce, ~r/^0x[0-9a-f]{64}$/)
    true

# `receive_authorization_digest`
*since 0.9.0* 

```elixir
@spec receive_authorization_digest(map(), map()) ::
  {:ok, binary()} | {:error, encode_error()}
```

Computes the `ReceiveWithAuthorization` digest used by token collectors.

Uses the same six fields as `eip712_digest/2`, but a distinct type hash.
A receive authorization cannot be substituted for a transfer authorization.

## Examples

    iex> X402.EIP3009.receive_authorization_digest(%{}, %{})
    {:error, {:missing_field, "from"}}

# `recover_signer`
*since 0.6.0* 

```elixir
@spec recover_signer(binary(), binary()) ::
  {:ok, String.t()}
  | {:error, :missing_dependency | :invalid_signature | term()}
```

Recovers the signer address from an EIP-712 digest and a 65-byte signature.

Accepts the `0x`-prefixed hex signature produced by `sign_authorization/3`
or the raw 65-byte binary. Useful for verifying a signed payment locally.

# `sign`
*since 0.6.0* 

```elixir
@spec sign(map(), X402.Signer.t(), keyword()) ::
  {:ok, payload()} | {:error, domain_error() | encode_error() | term()}
```

Signs the `exact`/`eip3009` scheme payload for the given requirements.

Derives the EIP-712 domain from the requirements, builds a fresh
authorization from the signer's address (`from`), `payTo` (`to`), and
`amount` (`value`) with a random 32-byte nonce, computes the EIP-712
digest, and signs it through `signer`.

## Options

* `:valid_after_buffer` (`t:non_neg_integer/0`) - Seconds subtracted from the current time for the authorization's
  `validAfter`, tolerating clock skew between payer, facilitator, and
  chain. The default value is `60`.

## Examples

    {:ok, signer} = X402.Signer.LocalKey.new(private_key)

    {:ok, %{"signature" => _, "authorization" => _}} =
      X402.EIP3009.sign(
        %{
          "scheme" => "exact",
          "network" => "eip155:84532",
          "amount" => "10000",
          "asset" => "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
          "payTo" => "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
          "maxTimeoutSeconds" => 60,
          "extra" => %{"name" => "USDC", "version" => "2"}
        },
        signer
      )

# `sign_authorization`
*since 0.6.0* 

```elixir
@spec sign_authorization(X402.Signer.t(), map(), map()) ::
  {:ok, String.t()} | {:error, encode_error() | term()}
```

Signs an authorization's EIP-712 digest with a signer.

Returns the `0x`-prefixed 65-byte `r || s || v` signature.

---

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