Verify an EIP-3009 payment signature

Paste an x402 payment requirement and the PAYMENT-SIGNATURE that answers it. Settlens rebuilds the EIP-712 domain, recovers the signer and checks that the authorization pays the advertised recipient the advertised amount inside its time window.

Free and offline: no RPC, no wallet, no payment key.

In short

The Settlens EIP-3009 verifier is a free browser tool for x402 exact EVM payments: paste the payment requirement and the PAYMENT-SIGNATURE payload, and it rebuilds the EIP-712 domain, recovers the signer and checks the recipient, amount and validAfter/validBefore window. Offline: no RPC, no wallet.

Verify a payment signature

The sample pays the example request of the x402 checker. It was signed with a public test key, never a real wallet, for a 60-second window; the sample sets the check time inside it.

The accepts entry the payment was made for: scheme, network, amount, asset, payTo, maxTimeoutSeconds and extra with name and version.

The header value (Base64), the decoded PaymentPayload, or an object with signature and authorization.

ISO 8601 or Unix seconds. Empty: the current time, so a captured payment is usually reported as outside its window.

Where to find the values

The requirement is one entry of accepts in the resource's PAYMENT-REQUIRED header. The payload is the PAYMENT-SIGNATURE header the client sent with its retry. Check the requirement the server advertised, not the copy in the payload's accepted field.

What happens to what you paste

Your browser sends it to the Settlens MCP endpoint, which runs the offline verify_payment_signature tool in memory and keeps no copy. A signed authorization can be submitted on chain by anyone who holds it until validBefore, so prefer expired captures. Pilot participants can run the same check locally with settlens verify-payment. Details in the privacy notice.

The verdict, the recovered signer and each binding and time check appear here.

How the signature is checked

Everything needed is in the requirement and the payload: no RPC call, no block explorer and no private key.

Where each EIP-712 domain field comes from
Domain fieldTaken fromIn the sample
namerequirement.extra.nameUSDC
versionrequirement.extra.version2
chainIdThe number in eip155:<id>84532
verifyingContractrequirement.asset0x036CbD53842c5426634e7929541eC2318f3dCF7e
  1. Recover the signer

    The EIP-712 digest of the TransferWithAuthorization message is computed and the signer recovered from the 65-byte signature. A high-s signature is refused (see EIP-2). The signer must equal authorization.from.

  2. Check the binding

    authorization.to must equal payTo and authorization.value must equal amount. The asset is bound through the domain: a signature for another token does not recover to the payer.

  3. Check the time window

    The check time must be strictly after validAfter and strictly before validBefore, as the EIP-3009 reference implementation requires.

JSONThe sample PAYMENT-SIGNATURE, decoded
{
  "x402Version": 2,
  "resource": {
    "url": "https://api.example.com/premium-data",
    "description": "Access to premium market data",
    "mimeType": "application/json"
  },
  "accepted": {
    "scheme": "exact",
    "network": "eip155:84532",
    "amount": "10000",
    "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    "payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
    "maxTimeoutSeconds": 60,
    "extra": {
      "name": "USDC",
      "version": "2"
    }
  },
  "payload": {
    "signature": "0x891a819dc49005e71effabe634613e1369899c3560b2c8528f6683e3ec8720fa28978b59ff09405aa6ca9a89712b7cb02d171443a755027d8d2a66dab29be3b61b",
    "authorization": {
      "from": "0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a",
      "to": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
      "value": "10000",
      "validAfter": "1791885570",
      "validBefore": "1791885630",
      "nonce": "0xaf4e5ec1828f3f4bde1bd708e9cfa238f4336b2f95736111a4b591ea3c916d07"
    }
  }
}

payload.authorization holds the signed TransferWithAuthorization fields and payload.signature the 65-byte r || s || v signature over them. accepted is the client's copy of the requirement it chose; the verifier checks against the requirement you paste.

The nonce is 32 random bytes. The token contract records it when the authorization is used, which is how replays fail on chain.

What a rejection looks like

Each check is reported on its own, so the reasons say what to fix: an expired window, a changed amount or the wrong recipient.

verify_payment_signaturethe sample, four inputs
  • verified

    The sample, checked at 2026-10-13T10:00:00Z

    Recovered 0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a: every check passed.

  • rejected

    The same payment one minute after validBefore

    authorization is outside its validAfter/validBefore window (EIP-3009 requires validAfter < now < validBefore).

  • rejected

    The value raised to 25000 after signing

    Recovered signer 0x1b20ee3930a9b2d8fd2f70548b3d84decd3d332d does not match authorization.from 0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a. authorization.value does not match requirement.amount.

  • rejected

    Checked against a requirement with another payTo

    authorization.to does not match requirement.payTo.

Real answers computed with the Settlens server code when this page was built.

Raising the value after signing breaks two checks at once: the amount no longer matches the requirement, and the signature no longer recovers to the payer because the signed message changed.

The same verification runs in the Verify stage of a Settlens assessment, which also checks Solana signers and facilitators.

What the verifier does not check

  • No chain state. No balances, so a verified authorization can still fail to settle for lack of funds.
  • No replay detection. It does not check whether the nonce was already used on chain (authorizationState).
  • No settlement. A verified signature does not prove a transfer happened.
  • Not the token contract. The domain name and version come from the requirement; Settlens does not confirm they match the contract deployed at asset.
  • EOA signatures only. Only 65-byte ECDSA signatures are recovered. Smart contract wallet signatures (EIP-1271) are rejected.
  • EIP-3009 only. Permit2 payloads of the exact EVM scheme are not verified, and the requirement's scheme is not checked to be exact.
  • EVM only here. Solana transactions are checked by the verify_solana_transaction MCP tool.

Use it from your agent or a terminal

The verifier is a page in front of a public MCP tool. An auditor or an agent can call it directly.

Shellcurl, with the sample
curl -s https://settlens.com/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"verify_payment_signature","arguments":{"requirement":{"scheme":"exact","network":"eip155:84532","amount":"10000","asset":"0x036CbD53842c5426634e7929541eC2318f3dCF7e","payTo":"0x209693Bc6afc0C5328bA36FaF03C514EF312287C","maxTimeoutSeconds":60,"extra":{"name":"USDC","version":"2"}},"payload":{"signature":"0x891a819dc49005e71effabe634613e1369899c3560b2c8528f6683e3ec8720fa28978b59ff09405aa6ca9a89712b7cb02d171443a755027d8d2a66dab29be3b61b","authorization":{"from":"0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a","to":"0x209693Bc6afc0C5328bA36FaF03C514EF312287C","value":"10000","validAfter":"1791885570","validBefore":"1791885630","nonce":"0xaf4e5ec1828f3f4bde1bd708e9cfa238f4336b2f95736111a4b591ea3c916d07"}},"now":"2026-10-13T10:00:00Z"}}}'
ShellClaude Code
claude mcp add --transport http settlens https://settlens.com/mcp

Every argument and output field is in the verify_payment_signature reference. Pilot participants run the same check offline with settlens verify-payment, documented in the CLI reference.

Check the request first

Before a payment is signed, the x402 checker decides whether the request should be paid at all.

Questions about the EIP-3009 verifier

Does the verifier need my private key or an RPC node?

No. It recovers the signer from the signature and the signed fields alone. It never asks for a key, never connects to a chain and never pays or settles anything.

Why is the signature valid but the payment rejected?

The signature only proves who signed. The payment is verified when the authorization also pays the requirement's payTo the exact amount and the check time is inside the validAfter to validBefore window. Without a check time, the current time is used, so an old capture is outside its window.

Does a verified result mean the payment settled?

No. It means the authorization is correctly signed and bound to the requirement at the check time. Balances, nonce reuse and settlement are decided on chain.

Are smart contract wallets and Permit2 supported?

No. Only 65-byte ECDSA signatures from externally owned accounts and EIP-3009 TransferWithAuthorization payloads are verified. EIP-1271 signatures and Permit2 payloads are rejected or out of scope.

Sources

  1. EIP-3009: Transfer With AuthorizationEthereum Improvement Proposals · eips.ethereum.org
  2. EIP-712: Typed structured data hashing and signingEthereum Improvement Proposals · eips.ethereum.org
  3. EIP-2: Homestead hard-fork changes (low-s signatures)Ethereum Improvement Proposals · eips.ethereum.org
  4. x402 exact scheme on EVMx402 Foundation · github.com
  5. x402 V2 HTTP transportx402 Foundation · github.com
  6. CAIP-2: Blockchain ID SpecificationChain Agnostic Standards Alliance · github.com

Verify payments as part of an assessment

A pilot checks one paid endpoint or facilitator end to end, including how its facilitator handles a forged signature, with evidence your partners can verify.