---
title: "EIP-3009 transferWithAuthorization for x402 | Settlens"
description: "How EIP-3009 transferWithAuthorization and the EIP-712 domain power x402 exact EVM payments, field by field, and what can be verified offline versus on chain."
url: https://settlens.com/learn/eip-3009
last_updated: 2026-10-13
published: 2026-10-13
---

# EIP-3009 and EIP-712 in x402 payments

> **In short:** By default, x402's exact scheme on EVM chains has the payer sign an EIP-3009 transferWithAuthorization with EIP-712: from, to, value, a validity window and a random nonce, bound to one token and chain. The facilitator submits it and cannot change amount or recipient; balance and nonce use need chain state.

On EVM chains, the x402 exact scheme uses EIP-3009 authorizations by default: the payer signs an EIP-712 message off chain and the facilitator submits it. This guide explains every signed field, how the x402 exact scheme fills them in, and what a signature does and does not prove.

[EIP-3009](https://eips.ethereum.org/EIPS/eip-3009), "Transfer With Authorization", is a draft ERC created in 2020. It adds functions to an ERC-20 token so a holder can authorize a transfer by signing a typed message, and anyone holding that signature can submit it on chain. The signer never sends a transaction or pays gas. The signature itself follows [EIP-712](https://eips.ethereum.org/EIPS/eip-712), the standard for hashing and signing typed structured data.

In x402, EIP-3009 is the default [asset transfer method](https://settlens.com/learn/glossary#asset-transfer-method) of the [`exact` scheme on EVM chains](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_evm.md), recommended for tokens that implement `transferWithAuthorization` natively, with USDC as the specification's example. Tokens without it use [Permit2](https://settlens.com/learn/glossary#permit2), and smart accounts can use ERC-7710 delegation; both have a different payload and are out of scope here.

## What transferWithAuthorization does

The token contract exposes this function. It checks the authorization and the signature, marks the [nonce](https://settlens.com/learn/glossary#nonce) as used and moves `value` from `from` to `to`:

**EIP-3009 interface (excerpt)**

```text
function transferWithAuthorization(
    address from,
    address to,
    uint256 value,
    uint256 validAfter,
    uint256 validBefore,
    bytes32 nonce,
    uint8 v,
    bytes32 r,
    bytes32 s
) external;

function authorizationState(address authorizer, bytes32 nonce)
    external view returns (bool);

event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
```

The EIP also defines `receiveWithAuthorization`, which additionally requires the caller to be the payee, and an optional `cancelAuthorization` that lets the payer cancel an authorization before it is used.

## The authorization fields

These six values are what the payer signs. In an x402 payload they travel as strings in `payload.authorization`, next to the 65-byte `signature`.

_TransferWithAuthorization fields and what the EIP-3009 reference implementation enforces_

| Field | Type | Meaning | Enforced on chain |
| --- | --- | --- | --- |
| `from` | address | The payer, who signs. | The recovered signer must equal from. |
| `to` | address | The payee. | Funds go here and nowhere else. |
| `value` | uint256 | Amount in the token's atomic units. | Exactly this amount is transferred. |
| `validAfter` | uint256 | Unix time after which the authorization is valid. | `now > validAfter` |
| `validBefore` | uint256 | Unix time before which it is valid. | `now < validBefore` |
| `nonce` | bytes32 | A random 32-byte value chosen by the payer. | Must be unused for this payer, then marked used. |

The nonces are random rather than sequential, one of the two differences the EIP lists against [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) permits; the other is that tokens move directly, without the ERC-20 allowance step. With sequential nonces, pending authorizations fail when they are submitted out of order; random nonces let a payer create many authorizations in parallel without that risk. Anyone can ask `authorizationState(from, nonce)` whether a nonce has been used.

## The EIP-712 domain

EIP-712 signs a hash of typed data inside a _domain_, so a signature made for one contract on one chain cannot be replayed elsewhere. EIP-3009 uses four domain fields:

- `name`: the human-readable name of the signing domain, here the token's EIP-712 name.
- `version`: the current major version of the domain. Signatures from different versions are not compatible.
- `chainId`: the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain id.
- `verifyingContract`: the contract that verifies the signature, here the token itself.

The digest the payer signs combines the [domain separator](https://settlens.com/learn/glossary#domain-separator) with the hash of the message:

**EIP-712 digest for TransferWithAuthorization**

```text
domainSeparator = keccak256(abi.encode(
    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
    keccak256(name), keccak256(version), chainId, verifyingContract))

structHash = keccak256(abi.encode(
    TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
    from, to, value, validAfter, validBefore, nonce))

digest = keccak256(0x1901 ‖ domainSeparator ‖ structHash)

TRANSFER_WITH_AUTHORIZATION_TYPEHASH =
  keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
  = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267
```

Wallets produce this signature with `eth_signTypedData_v4`, given the domain, the type definitions and the message.

## How the x402 exact EVM scheme uses it

An x402 payment requirement carries everything needed to rebuild the domain, and the payload carries the message. The [facilitator](https://settlens.com/learn/glossary#facilitator), which submits the transaction and pays the gas, cannot change the amount or the destination: both are inside the signed message.

_Diagram: Diagram: the EIP-712 domain fields name, version, chainId and verifyingContract come from the requirement's extra.name, extra.version, eip155 network and asset; the message fields from, to, value, validAfter, validBefore and nonce come from the payload, with to equal to payTo and value equal to amount; both are hashed into the EIP-712 digest, ecrecover returns the signer, and the payment is valid when the signer equals from._

_Where each signed value comes from in an x402 exact EVM payment._

_x402 requirement fields and their role in the EIP-3009 signature_

| x402 field | Role in the signature |
| --- | --- |
| `extra.name` | Signed as the domain `name`. Required for EIP-3009: a wrong name produces a signature the token rejects. |
| `extra.version` | Signed as the domain `version`. Required for EIP-3009, for the same reason. |
| `network` | Its reference is the domain `chainId`: `eip155:84532` signs chain 84532. Binds the signature to one chain. |
| `asset` | Signed as the domain `verifyingContract`. Binds the signature to one token contract. |
| `payTo` | Must equal the message's `to`, or the payment goes elsewhere. |
| `amount` | Must equal the message's `value`. The core specification lists `invalid_exact_evm_payload_authorization_value_mismatch` for this case (see the [x402 error codes](https://settlens.com/learn/x402-error-codes)). |
| `maxTimeoutSeconds` | Not signed. It is the maximum time allowed for payment completion; keep the authorization window short as well. |

According to the [exact EVM specification](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_evm.md), a facilitator verifying an EIP-3009 payment checks that the signature recovers to `authorization.from`, that the payer has enough balance, that the amount and validity window meet the requirement, that the token and network match, and then simulates `transferWithAuthorization`. Settlement calls that function with the signature and the authorization. The [core specification](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md) adds that EIP-3009 contracts prevent nonce reuse on chain, which is what stops a replayed authorization.

## Worked example: the specification's own payment

The `PAYMENT-SIGNATURE` example in the x402 V2 HTTP transport specification is a real signature. Settlens' verifier, run when this page was built, rebuilds the domain from the example requirement and recovers the payer:

_Values computed at build time from the specification example_

| Value | Result |
| --- | --- |
| Domain | `name "USDC", version "2", chainId 84532, verifyingContract 0x036CbD53842c5426634e7929541eC2318f3dCF7e` |
| Domain separator | `0x71f17a3b2ff373b803d70a5a07c046c1a2bc8e89c09ef722fcb047abe94c9818` |
| Struct hash | `0x49ad644555c7bdf3ea4fc022a4940ae5589b230b1dde0065c5504fa19bbe39a4` |
| Digest | `0xf256992871671abcb27ff92885a7afa46218724e5fc0bac35d050115aa1d22e6` |
| Recovered signer | `0x857b06519e91e3a54538791bdbb0e22373e36b66`, equal to `authorization.from` |

The authorization was valid for 65 seconds on February 27, 2025, between `validAfter` 1740672089 and `validBefore` 1740672154. Checked at a time inside that window, for example with the MCP tool `verify_payment_signature` and its `now` argument, every property holds:

**verify_payment_signature result with now = 1740672100**

```json
{
  "scheme": "exact",
  "recovered": "0x857b06519e91e3a54538791bdbb0e22373e36b66",
  "signatureValid": true,
  "recipientBound": true,
  "amountBound": true,
  "fresh": true,
  "verified": true,
  "reasons": [],
  "checkedAt": 1740672100
}
```

Checked with the CLI at a time on October 13, 2026 (`--now`; without it the CLI uses its clock), long after the window closed, the same file is rejected for that reason alone:

**settlens verify-payment (window expired)**

```text
$ settlens verify-payment payment.json --now 2026-10-13T10:00:00Z
REJECTED exact EVM payment authorization
Recovered signer: 0x857b06519e91e3a54538791bdbb0e22373e36b66
Signature valid:  true
Recipient bound:  true
Amount bound:     true
Within window:    false (at 1791885600)
Reasons:
  - authorization is outside its validAfter/validBefore window (EIP-3009 requires validAfter < now < validBefore).
```

Change only the network in the requirement to Base mainnet (`eip155:8453`) and the rebuilt domain has a different `chainId`. The digest changes, so the signature recovers a different, meaningless address. This is the cross-chain replay protection EIP-712 provides:

**settlens verify-payment (same signature, other chain)**

```text
$ settlens verify-payment payment-base-mainnet.json --now 2026-10-13T10:00:00Z
REJECTED exact EVM payment authorization
Recovered signer: 0x46e5af7a18131f1bc0947cf44151ee84b58c92a9
Signature valid:  false
Recipient bound:  true
Amount bound:     true
Within window:    false (at 1791885600)
Reasons:
  - Recovered signer 0x46e5af7a18131f1bc0947cf44151ee84b58c92a9 does not match authorization.from 0x857b06519E91e3A54538791bDbb0E22373e36b66.
  - authorization is outside its validAfter/validBefore window (EIP-3009 requires validAfter < now < validBefore).
```

## What can be verified offline, and what needs the chain

A signature check proves that the payer agreed to specific terms. It does not prove the payment will settle. The split matters when you decide what an offline verification, such as Settlens' `verify-payment`, can tell you.

_Properties of an EIP-3009 payment authorization by where they can be checked_

| Property | Offline, no RPC | Needs chain state |
| --- | --- | --- |
| Signature recovers to authorization.from | Yes | No |
| to equals the advertised payTo | Yes | No |
| value equals the advertised amount | Yes | No |
| Bound to the advertised token and chain | Yes, through the domain | No |
| Inside validAfter and validBefore | Yes, against a clock | No |
| extra.name and extra.version are the token's real domain | No | Yes |
| Payer balance covers value | No | Yes |
| Nonce not used yet (authorizationState) | No | Yes |
| Token implements transferWithAuthorization | No | Yes |
| Transfer settled | No | Yes |

Settlens' `verify-payment` covers the first five rows and stops there by design: it needs no RPC, wallet or key, and it never checks balances, nonce use or settlement. Read more on the [Verify product page](https://settlens.com/product/verify) or in the [CLI reference](https://settlens.com/docs/cli). To check one payment without installing anything, use the [EIP-3009 signature verifier](https://settlens.com/tools/eip-3009-verifier).

## Security notes for implementers

- **Reject the zero address.** `ecrecover` returns the zero address for a malformed signature; EIP-3009 requires rejecting it, otherwise funds of the zero address could be "authorized".
- **Prefer canonical signatures.** [EIP-2](https://eips.ethereum.org/EIPS/eip-2) made transaction signatures with an `s` value above `secp256k1n/2` invalid, but the `ecrecover` precompile still accepts them, so each signature has a malleable twin. Settlens rejects high-s signatures.
- **Use short windows and random nonces.** Anyone who obtains a signed authorization can submit it until `validBefore`, even if the resource was never delivered. Generate each nonce randomly.
- **Check the domain hints.** An offline check compares the signature with the domain the server advertised. If `extra.name` or `extra.version` are wrong, the signature verifies offline and still fails on chain.
- **Contracts should receive, not transfer.** EIP-3009 warns that a contract wrapping `transferWithAuthorization` can be front-run from the mempool, and recommends `receiveWithAuthorization`, which checks that the caller is the payee.

> **Checklist**
>
> These points, and the server and facilitator behaviors around them, are part of the [x402 security checklist](https://settlens.com/learn/x402-security-checklist).

## Frequently asked questions

**How is EIP-3009 different from an EIP-2612 permit?**

An EIP-2612 permit approves an allowance with a sequential nonce, and a second transaction spends it with transferFrom. EIP-3009 moves the tokens directly in one call and uses random 32-byte nonces, so several authorizations can be pending at once.

**Can the facilitator change the amount or the recipient?**

No. to and value are part of the signed message. Changing either changes the digest, so the signature no longer recovers to the payer and the token rejects the transfer.

**Does a valid signature mean the payment will settle?**

No. Settlement also needs enough balance, an unused nonce, a token that implements EIP-3009 with the advertised domain, and a successful transaction. Those depend on chain state.

**Why does x402 put the token name and version in extra?**

They are part of the EIP-712 domain. Without them the client cannot build the digest to sign, and a verifier cannot rebuild it to check the signature without calling the chain.

## Sources

1. [ERC-3009: Transfer With Authorization](https://eips.ethereum.org/EIPS/eip-3009) — Ethereum Improvement Proposals
2. [EIP-712: Typed structured data hashing and signing](https://eips.ethereum.org/EIPS/eip-712) — Ethereum Improvement Proposals
3. [EIP-155: Simple replay attack protection](https://eips.ethereum.org/EIPS/eip-155) — Ethereum Improvement Proposals
4. [EIP-2: Homestead Hard-fork Changes](https://eips.ethereum.org/EIPS/eip-2) — Ethereum Improvement Proposals
5. [ERC-2612: Permit Extension for EIP-20 Signed Approvals](https://eips.ethereum.org/EIPS/eip-2612) — Ethereum Improvement Proposals
6. [x402 scheme: exact on EVM](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_evm.md) — x402 Foundation
7. [x402 protocol specification, version 2](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md) — x402 Foundation
