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

Permit2 `PermitWitnessTransferFrom` building, EIP-712 signing, and proxy
settlement calldata for the x402 `exact` (Permit2 transfer method) and
`upto` schemes on EVM networks.

Implements the client half of both Permit2-based payment flows: building a
Permit2 authorization from v2 payment requirements, computing its EIP-712
digest against the canonical Permit2 domain, and signing it through the
`X402.Signer` behaviour to produce the scheme `payload` map

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

carried inside a v2 `PaymentPayload` (see `X402.Client.build_payment/3`
for the full envelope). It also ABI-encodes the `settle` calls the
facilitator broadcasts to the x402 proxy contracts
(`exact_settle_calldata/2`, `upto_settle_calldata/3`), shared by
`X402.Verify.EVM`'s simulation and `X402.Facilitator.Engine`'s settlement.

## The authorizations

In both flows the client signs a `PermitWitnessTransferFrom` message for
the canonical [Permit2](https://github.com/Uniswap/permit2) contract
(`0x000000000022D473030F116dDEE9F6B43aC78BA3`) where
`permitted.token` / `permitted.amount` are the requirements' `asset` and
`amount`, and the `spender` is an x402 proxy contract deployed at the
same address on every supported EVM chain via `CREATE2`. The proxy is the
only party that can consume the permit, and it enforces the witness:

* **exact** (`extra.assetTransferMethod` `"permit2"`) — the spender is
  the `x402ExactPermit2Proxy`
  (`0x402085c248EeA27D92E8b30b2C58ed07f9E20001`) and the witness
  `Witness(address to,uint256 validAfter)` binds the requirements'
  `payTo`. The permitted amount is the exact amount settled.
* **upto** — the spender is the `x402UptoPermit2Proxy`
  (`0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002`) and the witness
  `Witness(address to,address facilitator,uint256 validAfter)` binds
  `payTo` and the facilitator announced in `extra.facilitatorAddress`, so
  no other party can settle. The permitted amount is a **maximum**; the
  facilitator settles the actual usage up to that ceiling.

The witness shape selects the typed-data variant: an authorization whose
witness carries a `facilitator` field hashes as the upto type, otherwise
as the exact type.

The EIP-712 domain is the canonical Permit2 domain — name `"Permit2"`,
the chain id from the CAIP-2 `network`, the Permit2 contract as the
verifying contract, and **no version field**.

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.

# `exact_settle_calldata`
*since 0.9.0* 

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

Builds the `x402ExactPermit2Proxy.settle` calldata for an exact
authorization and its raw (inner) signature bytes.

Encodes `settle(PermitTransferFrom permit, address owner, Witness witness,
bytes signature)` (selector `0x13cd3b53`) where `permit` is
`((permitted.token, permitted.amount), nonce, deadline)`, `owner` is the
authorization's `from`, and the witness is `(witness.to,
witness.validAfter)`. The proxy forwards the call to Permit2's
`permitWitnessTransferFrom` with `permitted.amount` as the requested
amount. Shared by `X402.Verify.EVM`'s `eth_call` simulation and
`X402.Facilitator.Engine`'s settlement transaction.

## Examples

    iex> asset = "0x2222222222222222222222222222222222222222"
    iex> authorization = %{
    ...>   "from" => "0x1111111111111111111111111111111111111111",
    ...>   "permitted" => %{"token" => asset, "amount" => "10000"},
    ...>   "spender" => "0x402085c248EeA27D92E8b30b2C58ed07f9E20001",
    ...>   "nonce" => "7",
    ...>   "deadline" => "99999999999",
    ...>   "witness" => %{"to" => "0x3333333333333333333333333333333333333333", "validAfter" => "0"}
    ...> }
    iex> {:ok, calldata} = X402.Permit2.exact_settle_calldata(authorization, <<1::520>>)
    iex> {binary_part(calldata, 0, 4), byte_size(calldata)}
    {<<0x13, 0xCD, 0x3B, 0x53>>, 4 + 8 * 32 + 32 + 96}

    iex> X402.Permit2.exact_settle_calldata(%{}, <<1::520>>)
    {:error, {:missing_field, "permitted"}}

# `upto_settle_calldata`
*since 0.9.0* 

```elixir
@spec upto_settle_calldata(map(), String.t() | non_neg_integer(), binary()) ::
  {:ok, binary()} | {:error, encode_error() | :invalid_signature}
```

Builds the `x402UptoPermit2Proxy.settle` calldata for an upto
authorization, the amount to settle, and the raw (inner) signature bytes.

Encodes `settle(PermitTransferFrom permit, uint256 amount, address owner,
Witness witness, bytes signature)` (selector `0xff11e7b4`) where the
witness is `(witness.to, witness.facilitator, witness.validAfter)` and
`amount` is the actual settlement amount — at most `permitted.amount`,
which the proxy enforces (`AmountExceedsPermitted`). The proxy also
requires `msg.sender` to equal `witness.facilitator`
(`UnauthorizedFacilitator`).

## Examples

    iex> asset = "0x2222222222222222222222222222222222222222"
    iex> authorization = %{
    ...>   "from" => "0x1111111111111111111111111111111111111111",
    ...>   "permitted" => %{"token" => asset, "amount" => "10000"},
    ...>   "spender" => "0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002",
    ...>   "nonce" => "7",
    ...>   "deadline" => "99999999999",
    ...>   "witness" => %{
    ...>     "to" => "0x3333333333333333333333333333333333333333",
    ...>     "facilitator" => "0x4444444444444444444444444444444444444444",
    ...>     "validAfter" => "0"
    ...>   }
    ...> }
    iex> {:ok, calldata} = X402.Permit2.upto_settle_calldata(authorization, "9000", <<1::520>>)
    iex> {binary_part(calldata, 0, 4), byte_size(calldata)}
    {<<0xFF, 0x11, 0xE7, 0xB4>>, 4 + 10 * 32 + 32 + 96}

    iex> X402.Permit2.upto_settle_calldata(%{}, "1", <<1::520>>)
    {:error, {:missing_field, "permitted"}}

# `authorization`

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

A Permit2 `PermitWitnessTransferFrom` authorization in wire shape.

String keys `"from"`, `"permitted"` (`%{"token", "amount"}`),
`"spender"`, `"nonce"`, `"deadline"`, and `"witness"`
(`%{"to", "validAfter"}` for exact, `%{"to", "facilitator", "validAfter"}`
for upto).

# `authorization_error`

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

# `domain_error`

```elixir
@type domain_error() :: :invalid_requirements | :unsupported_network
```

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

# `build_exact_authorization`
*since 0.9.0* 

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

Builds an exact `PermitWitnessTransferFrom` authorization in wire shape.

`permitted` carries the requirements' `asset` and exact `amount`; the
`spender` is the `x402ExactPermit2Proxy`; the `nonce` is a fresh random
uint256; the `deadline` is now plus `maxTimeoutSeconds`; and the witness
binds `payTo` (`to`) with `validAfter` `"0"` (immediately valid),
mirroring the reference SDKs.

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

# `build_upto_authorization`
*since 0.6.0* 

```elixir
@spec build_upto_authorization(map(), String.t()) ::
  {:ok, authorization()} | {:error, authorization_error()}
```

Builds an upto `PermitWitnessTransferFrom` authorization in wire shape.

`permitted` carries the requirements' `asset` and maximum `amount`; the
`spender` is the `x402UptoPermit2Proxy`; the `nonce` is a fresh random
uint256; the `deadline` is now plus `maxTimeoutSeconds`; and the witness
binds `payTo` (`to`), `extra.facilitatorAddress` (`facilitator`), and
`validAfter` `"0"` (immediately valid), mirroring the reference SDKs.

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

# `digest`
*since 0.9.0* 

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

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

Returns `keccak256(0x19 0x01 || domainSeparator || structHash)` as a
32-byte binary — the value the client signs and the value to recover
the payer address from (`X402.EIP3009.recover_signer/2`). The witness
shape selects the type string: a witness with a `facilitator` field
hashes as the upto `Witness(address to,address facilitator,uint256
validAfter)`, otherwise as the exact `Witness(address to,uint256
validAfter)`. `domain` and `authorization` accept both the internal
snake-case atom keys and the wire-style string keys; the authorization's
`from` is not part of the signed struct (Permit2 recovers the owner from
the signature).

# `domain`
*since 0.9.0* 

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

Derives the canonical Permit2 EIP-712 domain from v2 payment requirements.

The domain is `name: "Permit2"`, the chain id from the CAIP-2 `network`,
and the canonical Permit2 contract as the verifying contract. Permit2
declares no domain version, so the returned map carries no `:version`
key and `X402.EIP712.domain_separator/1` hashes the three-field
`EIP712Domain` type. The same domain serves the exact and upto flows.

## Examples

    iex> X402.Permit2.domain(%{"network" => "eip155:84532"})
    {:ok,
     %{
       name: "Permit2",
       chain_id: 84532,
       verifying_contract: "0x000000000022D473030F116dDEE9F6B43aC78BA3"
     }}

    iex> X402.Permit2.domain(%{"network" => "solana:mainnet"})
    {:error, :unsupported_network}

# `exact_proxy_address`
*since 0.9.0* 

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

Returns the `x402ExactPermit2Proxy` contract address — the spender of
`exact` payments using the Permit2 transfer method.

Deployed at the same address on every supported EVM chain via `CREATE2`.

## Examples

    iex> X402.Permit2.exact_proxy_address()
    "0x402085c248EeA27D92E8b30b2C58ed07f9E20001"

# `facilitator_address`
*since 0.6.0* 

```elixir
@spec facilitator_address(map()) ::
  {:ok, String.t()} | {:error, {:missing_extra, String.t()}}
```

Fetches the facilitator address from the requirements' `extra`.

The upto scheme requires `extra.facilitatorAddress` — the facilitator
announces it via `GET /supported` (`X402.Facilitator.supported/1`) and
the resource server forwards it in each upto requirements entry; the
client binds it into the signed witness.

## Examples

    iex> X402.Permit2.facilitator_address(%{
    ...>   "extra" => %{"facilitatorAddress" => "0x2222222222222222222222222222222222222222"}
    ...> })
    {:ok, "0x2222222222222222222222222222222222222222"}

    iex> X402.Permit2.facilitator_address(%{"extra" => %{}})
    {:error, {:missing_extra, "facilitatorAddress"}}

# `permit2_address`
*since 0.6.0* 

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

Returns the canonical Permit2 contract address.

## Examples

    iex> X402.Permit2.permit2_address()
    "0x000000000022D473030F116dDEE9F6B43aC78BA3"

# `random_nonce`
*since 0.6.0* 

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

Returns a fresh random uint256 nonce as a decimal string.

Permit2 uses unordered nonces; the reference SDKs draw 32 random bytes
per authorization, making collisions negligible.

## Examples

    iex> nonce = X402.Permit2.random_nonce()
    iex> String.match?(nonce, ~r/^[0-9]+$/)
    true

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

The witness shape selects the exact or upto typed data (see the module
documentation). Returns the `0x`-prefixed 65-byte `r || s || v`
signature.

# `sign_exact`
*since 0.9.0* 

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

Signs the `exact` Permit2 scheme payload for the given requirements.

Derives the canonical Permit2 domain from the requirements' `network`,
builds a fresh authorization from the signer's address (`from`), the
requirements' `asset`/`amount` (`permitted`), `payTo` (the witness), and
`maxTimeoutSeconds` (the `deadline`), computes the EIP-712 digest, and
signs it through `signer`.

## Examples

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

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

# `sign_upto`
*since 0.6.0* 

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

Signs the `upto` Permit2 scheme payload for the given requirements.

Derives the canonical Permit2 domain from the requirements' `network`,
builds a fresh authorization from the signer's address (`from`), the
requirements' `asset`/`amount` (`permitted`), `payTo` and
`extra.facilitatorAddress` (the witness), and `maxTimeoutSeconds` (the
`deadline`), computes the EIP-712 digest, and signs it through `signer`.

Requirements without `extra.facilitatorAddress` return
`{:error, {:missing_extra, "facilitatorAddress"}}` — the facilitator
address is required so the witness can bind settlement to it.

## Examples

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

    {:ok, %{"signature" => _, "permit2Authorization" => _}} =
      X402.Permit2.sign_upto(
        %{
          "scheme" => "upto",
          "network" => "eip155:84532",
          "amount" => "5000000",
          "asset" => "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
          "payTo" => "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
          "maxTimeoutSeconds" => 300,
          "extra" => %{
            "name" => "USDC",
            "version" => "2",
            "facilitatorAddress" => "0x2222222222222222222222222222222222222222"
          }
        },
        signer
      )

# `upto_digest`
*since 0.6.0* 

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

Computes the EIP-712 digest of a `PermitWitnessTransferFrom` — same as
`digest/2`.

# `upto_domain`
*since 0.6.0* 

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

Derives the canonical Permit2 EIP-712 domain — same as `domain/1`.

## Examples

    iex> X402.Permit2.upto_domain(%{"network" => "eip155:84532"})
    {:ok,
     %{
       name: "Permit2",
       chain_id: 84532,
       verifying_contract: "0x000000000022D473030F116dDEE9F6B43aC78BA3"
     }}

    iex> X402.Permit2.upto_domain(%{"network" => "solana:mainnet"})
    {:error, :unsupported_network}

# `upto_proxy_address`
*since 0.6.0* 

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

Returns the `x402UptoPermit2Proxy` contract address — the upto spender.

Deployed at the same address on every supported EVM chain via `CREATE2`.

## Examples

    iex> X402.Permit2.upto_proxy_address()
    "0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002"

---

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