# `X402.Extensions.EIP2612GasSponsoring`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/extensions/eip2612_gas_sponsoring.ex#L1)

Builds, signs, and validates the `eip2612GasSponsoring` extension.

The extension enables a gasless approval flow for tokens that implement
[EIP-2612](https://eips.ethereum.org/EIPS/eip-2612): instead of paying gas
for an `approve` transaction, the client signs an off-chain EIP-2612
`Permit` authorizing the canonical
[Permit2](https://github.com/Uniswap/permit2) contract as spender, and the
facilitator submits it on-chain (paying the gas) via
`x402Permit2Proxy.settleWithPermit`.

Server side, `build_extension/0` declares support under
`extensions.eip2612GasSponsoring` in a `PAYMENT-REQUIRED` response, and
`extract_info/1` / `validate_info/1` check the client-populated data echoed
back in a `PaymentPayload`.

Client side, `sign_permit/3` signs the `Permit` typed data for a selected
payment requirements entry through the `X402.Signer` behaviour, and
`put_info/2` attaches the resulting info to a payload's extensions.
`enricher/2` packages both for `X402.Client.build_payment/3`:

    {:ok, payload} =
      X402.Client.build_payment(payment_required, signer,
        extensions: [
          X402.Extensions.EIP2612GasSponsoring.enricher(signer, nonce: "0")
        ]
      )

The library has no chain access, so the owner's current EIP-2612 `:nonce`
(read from the token contract's `nonces(owner)`) must be supplied by the
caller.

See the
[eip2612GasSponsoring extension spec](https://github.com/x402-foundation/x402/blob/main/specs/extensions/eip2612_gas_sponsoring.md).

# `info`

```elixir
@type info() :: %{optional(binary()) =&gt; binary()}
```

Client-populated extension info in wire shape: string keys `"from"`,
`"asset"`, `"spender"`, `"amount"`, `"nonce"`, `"deadline"`,
`"signature"`, and `"version"`.

# `info_error`

```elixir
@type info_error() ::
  :extension_missing
  | {:missing_info_field, String.t()}
  | {:invalid_info_field, String.t()}
```

# `sign_error`

```elixir
@type sign_error() ::
  :invalid_nonce
  | :invalid_deadline
  | :invalid_amount
  | :invalid_requirements
  | X402.EIP712.domain_error()
  | X402.EIP712.encode_error()
  | term()
```

# `t`

```elixir
@type t() :: %{required(binary()) =&gt; map()}
```

A built `extensions.eip2612GasSponsoring` declaration (`info` + `schema`).

# `build_extension`
*since 0.6.0* 

```elixir
@spec build_extension() :: t()
```

Builds the server-side extension declaration (`info` + `schema`).

Resource servers advertise support by placing the declaration under
`extensions.eip2612GasSponsoring` in a `PAYMENT-REQUIRED` response; the
client populates the actual permit data.

## Examples

    iex> ext = X402.Extensions.EIP2612GasSponsoring.build_extension()
    iex> ext["info"]["version"]
    "1"
    iex> ext["schema"]["required"]
    ["from", "asset", "spender", "amount", "nonce", "deadline", "signature", "version"]

# `enricher`
*since 0.6.0* 

```elixir
@spec enricher(
  X402.Signer.t(),
  keyword()
) :: (map(), map() | nil -&gt; {:ok, map()} | {:error, sign_error()})
```

Returns an enricher for `X402.Client.build_payment/3`'s `:extensions`.

The enricher signs an EIP-2612 permit for the payload's accepted
requirements and attaches it via `put_info/2` — but only when the server
advertised `eip2612GasSponsoring` in the `PaymentRequired` extensions;
otherwise the payload passes through unchanged.

Takes the same options as `sign_permit/3` (`:nonce` is required).

# `extract_info`
*since 0.6.0* 

```elixir
@spec extract_info(map()) :: {:ok, info()} | {:error, info_error()}
```

Extracts the client-populated info from a `PaymentPayload` map.

Returns the info when the extension is present and every required field
is populated. Field formats are not checked here — see `validate_info/1`.

## Examples

    iex> X402.Extensions.EIP2612GasSponsoring.extract_info(%{"payload" => %{}})
    {:error, :extension_missing}

# `key`
*since 0.6.0* 

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

Returns the extension key, `"eip2612GasSponsoring"`.

## Examples

    iex> X402.Extensions.EIP2612GasSponsoring.key()
    "eip2612GasSponsoring"

# `permit2_address`
*since 0.6.0* 

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

Returns the canonical Permit2 contract address — the default spender.

## Examples

    iex> X402.Extensions.EIP2612GasSponsoring.permit2_address()
    "0x000000000022D473030F116dDEE9F6B43aC78BA3"

# `permit_digest`
*since 0.6.0* 

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

Computes the EIP-712 digest of an EIP-2612 `Permit` message.

`domain` is the token's EIP-712 domain (see `X402.EIP712.domain/1`) and
`permit` a map with `"owner"`, `"spender"`, `"value"`, `"nonce"`, and
`"deadline"` keys (snake-case atom keys are also accepted). Useful for
verifying a signed permit with `X402.EIP3009.recover_signer/2`.

# `put_info`
*since 0.6.0* 

```elixir
@spec put_info(map(), info()) :: map()
```

Attaches client-populated info to a payload's extensions.

Places the info under `extensions.eip2612GasSponsoring.info`, following
the append-only rule: when the payload already echoes the server's
declaration, server-declared info fields are preserved (they win over
client values) and the declared `schema` is kept; the client's fields are
added alongside them.

## Examples

    iex> info = %{"from" => "0x1111111111111111111111111111111111111111"}
    iex> payload = X402.Extensions.EIP2612GasSponsoring.put_info(%{"payload" => %{}}, info)
    iex> payload["extensions"]["eip2612GasSponsoring"]["info"]["from"]
    "0x1111111111111111111111111111111111111111"

# `schema`
*since 0.6.0* 

```elixir
@spec schema() :: map()
```

Returns the JSON Schema (Draft 2020-12) for the client-populated info.

# `sign_permit`
*since 0.6.0* 

```elixir
@spec sign_permit(map(), X402.Signer.t(), keyword()) ::
  {:ok, info()} | {:error, sign_error()}
```

Signs an EIP-2612 `Permit` for a payment requirements entry.

The EIP-712 domain is the token's: `extra.name` / `extra.version`, the
chain id from the CAIP-2 `network`, and the `asset` contract as verifying
contract. The permit authorizes `:spender` (canonical Permit2 by default)
to spend `:amount` (the requirements' `amount` by default) of the owner's
tokens until `:deadline`.

Returns the client-populated extension info in wire shape, ready for
`put_info/2`.

## Options

* `:nonce` - Required. The owner's current EIP-2612 nonce on the token contract (the value of
  `nonces(owner)`), as an integer or decimal string. The library has no
  chain access, so the caller must read it.

* `:deadline` - Unix timestamp at which the permit signature expires. Defaults to the
  current time plus the requirements' `maxTimeoutSeconds`.

* `:amount` - Approval amount in atomic units. Defaults to the requirements'
  `amount` — `x402Permit2Proxy.settleWithPermit` enforces that the
  permit value matches the Permit2 permitted amount exactly.

* `:spender` (`t:String.t/0`) - The approved spender. Defaults to the canonical Permit2 contract.

## Examples

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

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

# `validate_info`
*since 0.6.0* 

```elixir
@spec validate_info(term()) :: :ok | {:error, info_error()}
```

Validates the format of client-populated info.

Checks that addresses match `^0x[a-fA-F0-9]{40}$`, that `amount`,
`nonce`, and `deadline` are decimal strings, that `signature` is a
`0x`-prefixed hex string, and that `version` is a dotted numeric version.

## Examples

    iex> X402.Extensions.EIP2612GasSponsoring.validate_info(%{
    ...>   "from" => "0x857b06519E91e3A54538791bDbb0E22373e36b66",
    ...>   "asset" => "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    ...>   "spender" => "0x000000000022D473030F116dDEE9F6B43aC78BA3",
    ...>   "amount" => "10000",
    ...>   "nonce" => "0",
    ...>   "deadline" => "1740672154",
    ...>   "signature" => "0xabcdef",
    ...>   "version" => "1"
    ...> })
    :ok

    iex> X402.Extensions.EIP2612GasSponsoring.validate_info(%{"from" => "0x123"})
    {:error, {:invalid_info_field, "from"}}

---

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