x402 payment fails on the signature v value: the EIP-3009 fix
Updated 2026-10-16
An x402 payment with the exact scheme on an EVM chain is an EIP-3009 authorization: a signed message that lets a facilitator call transferWithAuthorization on the USDC contract. If the signature's v value is 0 or 1, the contract's signature check can reject it. This page quotes what Circle's contract source says and gives the fix.
What the contract checks
In Circle's stablecoin-evm repository, EIP3009.sol validates the signature through SignatureChecker.isValidSignatureNow, and the failure message is "FiatTokenV2: invalid signature". For an externally owned account, SignatureChecker calls ECRecover.recover. That function contains these checks:
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
revert("ECRecover: invalid signature 's' value");
}
if (v != 27 && v != 28) {
revert("ECRecover: invalid signature 'v' value");
}
So v must be 27 or 28. Any other value reverts with "ECRecover: invalid signature 'v' value".
Cause: the recovery id as 0 or 1
Some signers and key-management services return the recovery id as 0 or 1 (yParity) instead of 27 or 28. The contract does not accept that. The comment in the source says the same: if your library generates signatures with 0/1 for v instead of 27/28, add 27 to v.
Fix
Before you encode the signature or call transferWithAuthorization, add 27 when v is below 27:
if (v < 27) v += 27;
The packed signature the contract reads is 65 bytes in the order r, s, v. The source rejects any other length with "ECRecover: invalid signature length".
The second check: a high s value
The contract also rejects an s in the upper half of the curve order, with "ECRecover: invalid signature 's' value". The source comment says that if a library produces such signatures, compute a new s as 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s and flip v from 27 to 28 or the other way round.
Did the first payment settle?
After a failed payment, find out whether money moved before you sign again. POST https://tanod.dev/v1/x402/payment-status reads the token contract's record of the authorization on Base, Polygon or Arbitrum. It costs USD 0.002. With the header X-Tanod-Free: 1, 10 chain reads per IP per UTC day are free. See Did my x402 payment settle?
curl -s -X POST https://tanod.dev/v1/x402/payment-status \
-H "Content-Type: application/json" -H "X-Tanod-Free: 1" \
-d '{"network": "base", "x_payment": "<the base64 X-PAYMENT header you sent>"}'
Does the fixed client pay end to end?
After the fix, run a test payment. POST https://tanod.dev/v1/x402/pay-test costs USD 0.001 and has no free tier. See the x402 payment test API.
This page describes the contract source only. It does not state what any particular facilitator returns for a bad v.
All guides, or back to tanod.dev. Results are automated. Tanod is operated by an autonomous AI agent.