This proposal defines an EIP-712 format for service providers to sign EVM payment quotes for a specific request.
The goal is to keep the provider’s quoted terms separate from the payer’s authorization and the settlement mechanism itself.
A quote commits to the issuer, an optional payer, the settlement chain, asset, recipient, amount, request, quote ID, and validity window.
The format is transport-independent. HTTP, x402, MCP, A2A, and other protocols can define their own request schemes and transport bindings while using the same signed quote structure.
I’d especially appreciate feedback on:
-
whether this is the right standardization boundary compared with protocol-specific signed offers;
-
how request schemes should be extended and shared between implementations;
-
EOA and ERC-1271 verification semantics;
-
settlement, expiry, and replay behavior.
Links
1 Like
the expiry semantics are what’d bite me here. wired a signed-quote flow onto x402 in the spring and lost a weekend to manual refunds, because i read validUntil as submit-by and the rail didn’t. the exact scheme settles through EIP-3009 transferWithAuthorization, and 3009 does require(now < validBefore) inside the call. execution time, against block time. so a payment posted three seconds before the window shuts reverts when the block lands late. then the client retries. then you’re reconciling by hand.
your spec says validUntil limits creation or submission and explicitly doesn’t require an already-submitted transaction to be included before it. a paragraph later, a settlement mechanism enforcing validity at execution time MUST reject after validUntil. both are true at once and that’s the problem. same signed quote, two different expiry moments depending on which rail carries it, and nothing in ServicePaymentQuote tells the payer which one they’re in. validAfter and validUntil are lifted from 3009 where they mean execution time, so anyone already shipping 3009 implements that reading by reflex.
i’d just declare validUntil an execution-time bound and let issuers size their own skew. that’s the only version where a payer can work out their exposure from the quote alone. an explicit enforcement-point field would also do it, i just don’t trust a field that half the integrations will hardcode and never read.
bearer quotes have the same shape of hole. quoteId doesn’t imply one-time use, cross-quote consumption is out of scope, so single-use rests entirely on the client nonce inside requestHash. which lives in the request scheme. the one layer this ERC deliberately doesn’t define. two implementations can both conform and one of them double-charges.