Skip to content

Add ERC: Signed Service Payment Quotes - #1990

Open
SergeevDmitry wants to merge 6 commits into
ethereum:masterfrom
SergeevDmitry:erc-signed-service-payment-quotes
Open

SergeevDmitry wants to merge 6 commits into
ethereum:masterfrom
SergeevDmitry:erc-signed-service-payment-quotes

Conversation

@SergeevDmitry

Copy link
Copy Markdown

Summary

This PR introduces a new ERC for provider-signed EVM payment quotes tied to a specific priced request.

The ERC defines a common format for:

  • EIP-712 signed quotes;
  • optional payer binding;
  • settlement chain, asset, recipient, and amount;
  • request commitments through versioned request schemes;
  • issuer verification for EOAs and ERC-1271 accounts;
  • quote validity and basic settlement conformance.

Request canonicalization and transport-specific behavior are intentionally left outside the core ERC so protocols can define those pieces independently.

Discussion: https://ethereum-magicians.org/t/signed-service-payment-quotes-candidate-erc/29577

Validation

  • eipw passes locally with the ERC repository configuration.
  • The proposal includes canonical EIP-712 test vectors.

@eip-review-bot

eip-review-bot commented Sep 3, 2026 •

Copy link
Copy Markdown
Collaborator

File ERCS/erc-8409.md

Requires 1 more review from Editors: @g11tech, @jochem-brouwer, @samwilsn, @xinbenlv

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

The commit 6ab130f (as a parent of 066cbc9) contains errors.
Please inspect the Run Summary for details.

Comment thread ERCS/erc-XXXX.md Outdated
Comment thread ERCS/erc-XXXX.md Outdated
@github-actions github-actions Bot removed the w-ci label Sep 4, 2026
@babyblueviper1

Copy link
Copy Markdown

Checked the EIP-3009 side directly before adding anything — its own natspec calls validBefore "the time before which this is valid," and every real implementation (Circle's FiatTokenV2 included) enforces that at call time against block.timestamp, confirming the execution-time reading cedricbrown found the hard way is exactly what 3009 does, not an edge case.

The shape of this bug is worth naming explicitly, because it will recur exactly here: "transport-independent" means the same field gets interpreted by whichever settlement mechanism happens to carry it, and two mechanisms can each be a perfectly valid reading of the spec's own text while enforcing at different moments. That's not a wording gap, it's an underspecified protected property — validUntil looks like one thing (a timestamp) under a weak read of the schema, but the actual guarantee it gives a payer (exposure window) depends on an enforcement point the schema doesn't carry. Declaring it execution-time-bound, as cedricbrown proposed, is the right fix specifically because it's the reading every 3009-based rail already implements by reflex — making it explicit doesn't change behavior for the common case, it just stops a different-but-conformant rail from silently picking the other one.

The requestHash/nonce gap is the same shape one layer down: "conformant" and "replay-safe" look identical from inside this ERC's own boundary, and only diverge once you look at the request-scheme layer it deliberately doesn't own. Worth at least a normative MUST that a request scheme define single-use semantics for quoteId+requestHash together, even without specifying how — leaving it fully silent means two conformant implementations can disagree on double-charge safety and neither is wrong per this spec.

- Distinguish submission cutoffs from enforced execution deadlines
- Require validity checks before authorization and new submission
- Define binding requirements for submission events and deadline disclosure
- Clarify invocation identity and consumption state across retries and reissues
- Distinguish duplicate-payment prevention from service deduplication
- Add 15 behavioral test cases for expiry and single-use guarantees
- Preserve the EIP-712 schema and existing cryptographic test vectors
@babyblueviper1

Copy link
Copy Markdown

Checked the pushed commit against the actual spec text, not just the summary. This lands the exact fix from the earlier back-and-forth, precisely:

"Neither quoteId nor the pair (quoteId, requestHash) is sufficient by itself to identify shared consumption state across reissues, because a reissued quote has a new quoteId."

That's the collision we traced together — pairing a required-to-change quoteId with requestHash as a consumption key breaks by construction on reissue. And the fix generalizes past my own narrower framing rather than just patching the specific case:

"A request nonce used for this purpose MUST remain stable across retries and reissues for the same invocation and MUST differ for a new intentional invocation."

That's the right shape — stable-across-retry, distinct-across-intent — and it's the same property our own action_binding_nonce fix landed on this same week for the identical collision class (a timestamp-only differentiator letting two real identical-looking calls collide). Good to see it stated as a general requirement on the request scheme rather than baked into this ERC's own object.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants