Skip to content

feat: add unified EVM charge payment method#213

Open
0xBreadguy wants to merge 2 commits intotempoxyz:mainfrom
0xBreadguy:add-evm-charge
Open

feat: add unified EVM charge payment method#213
0xBreadguy wants to merge 2 commits intotempoxyz:mainfrom
0xBreadguy:add-evm-charge

Conversation

@0xBreadguy
Copy link
Copy Markdown

@0xBreadguy 0xBreadguy commented Apr 1, 2026

Summary

Adds a unified evm payment method for the charge intent, replacing the previously proposed chain-specific methods (megaeth in #205, sei in #206). A single method works across all EVM-compatible chains — the only differentiator is chainId in methodDetails.

Follows direction from @gakonst in #205 and #206 to consolidate into a general EVM spec.

Credential Types

Four credential types, with a clear preference hierarchy:

  1. type="permit2" (RECOMMENDED) — Client signs off-chain EIP-712 Permit2 authorization, server submits on-chain. Preferred because:

    • Server naturally sponsors gas
    • Splits are atomic via permitBatchWitnessTransferFrom()
    • Cryptographic challenge binding via PaymentWitness witness data
    • No nonce management burden on client
  2. type="authorization" (opt-in for EIP-3009 tokens) — Client signs off-chain transferWithAuthorization message, server submits directly to the token contract. For tokens that natively implement EIP-3009 (USDC, EURC). Zero setup — no Permit2 approval needed. Challenge binding via nonce = challengeHash, enforced on-chain by the token contract. No splits.

  3. type="transaction" (compatible fallback) — Client signs a standard ERC-20 transfer transaction, server broadcasts. For chains without Permit2 or clients that prefer direct signing. Client pays gas. No splits.

  4. type="hash" (constrained client fallback) — Client broadcasts transaction themselves, presents tx hash. For custodial wallets / hardware signers that can't hand off unbroadcast transactions. Client pays gas. No splits. Weaker challenge binding.

Key Design Decisions

  • Permit2 batch transfers for splits: All splits execute atomically in a single permitBatchWitnessTransferFrom() call. No partial execution risk. Splits require type="permit2".
  • permitted is always an array: Even for single transfers (length 1). No polymorphic fields — cleaner for typed language implementations.
  • Challenge binding via witness / nonce: Permit2 uses PaymentWitness { bytes32 challengeHash } in EIP-712 witness data. EIP-3009 uses the same challengeHash as the on-chain nonce — enforced by the token contract itself.
  • Gas sponsorship is structural: Off-chain signature types (permit2, authorization) = server always pays gas. On-chain transaction types (transaction, hash) = client always pays. No feePayer field needed.
  • No chain-specific appendix: Chain IDs reference chainlist.org. Implementation details belong in chain documentation, not the protocol spec.
  • Confirmation depth is server policy: The spec requires a successful tx receipt (1 block inclusion) and leaves finality depth to server discretion.
  • Native token transfers out of scope: ERC-20 only.
  • Splits require type="permit2": Authorization, transaction and hash credentials cannot carry splits.

New file

  • specs/methods/evm/draft-evm-charge-00.md

AI Disclosure

This spec was drafted with AI assistance (Claude). All content has been reviewed for technical accuracy, RFC compliance, and alignment with STYLE.md / CONTRIBUTING.md.

@Kbhat1
Copy link
Copy Markdown

Kbhat1 commented Apr 2, 2026

This is great, thanks for opening the unified one. Much simpler than opening chain specific

@0xBreadguy
Copy link
Copy Markdown
Author

This is great, thanks for opening the unified one. Much simpler than opening chain specific

Thanks! If you see anything you want included I'm all ears for refining. Tried to give rationale for the stepdown approach while incorporating a few features from the SUI/Solana/Tempo specs that I thought were meaningful

@gakonst
Copy link
Copy Markdown
Contributor

gakonst commented Apr 2, 2026

Reviewing soon but directionally looks great

@kyscott18
Copy link
Copy Markdown

I think this should include an "authorization" credential that uses ERC 3009. It lets the clients skip the step of approving the permit2 contract for tokens that support it (USDC).

@Schwartz10
Copy link
Copy Markdown

Schwartz10 commented Apr 3, 2026

@0xBreadguy totally agree on this direction! Wondering - is "evm" the right abstraction here? For instance, ignoring implementation complexity for a moment, any given service provider may actually be vm agnostic and care more about currencies than care about receiving a payment over an "evm" machine. For instance, I'm happy to receive USDC on Tempo, Base, or Solana

Not sure the best way to do it, but seems like it might increase usefulness if we can make the abstraction even more general, and work across multiple vms. What do you think?

One approach - I wonder if we could "chain" challenges together, and create an n of m relationship between challenges, kind of like a multisig. I.e. server sends 3 challenges - tempo.charge, base.charge, solana.charge, and requires at least 1 challenge complete. Client can send a credential responding to any challenge, server verifies according to that specific challenge.

Benefit of this chaining approach is a server can put together more programmatic access to gated content / service by composing individual methods/intents. Server can specify you need to pay AND do action X on smart contract Y in order to receive service. When individual methods can be chained, i think flexibility increases?

@0xBreadguy
Copy link
Copy Markdown
Author

I think this should include an "authorization" credential that uses ERC 3009. It lets the clients skip the step of approving the permit2 contract for tokens that support it (USDC).

My counter would be that as a universal spec that adds no materially new flow for clients.

Permit2 captures the single approval flow, is universal to erc20 tokens and can handle the splits feature.

3009 is an eip-specific implementation of the transfer credential, effectively.

Not opposed to including it but would want to get consensus from contributors.

Can also add a note as a candidate for v01 if we do not believe it fits within the MVP.

@0xBreadguy
Copy link
Copy Markdown
Author

@0xBreadguy totally agree on this direction! Wondering - is "evm" the right abstraction here? For instance, ignoring implementation complexity for a moment, any given service provider may actually be vm agnostic and care more about currencies than care about receiving a payment over an "evm" machine. For instance, I'm happy to receive USDC on Tempo, Base, or Solana

Not sure the best way to do it, but seems like it might increase usefulness if we can make the abstraction even more general, and work across multiple vms. What do you think?

This is what the method layer actually handles, imo. Servers can just issue multiple headers w/ separate methods and the client just selects the one they can fulfill (iiuc):

WWW-Authenticate: Payment method="evm", ...
WWW-Authenticate: Payment method="solana", ...
WWW-Authenticate: Payment method="tempo", ...

Given that, I think evm is the right abstraction for methods (and now think solana should be converted to svm if I'm being honest).


One approach - I wonder if we could "chain" challenges together, and create an n of m relationship between challenges, kind of like a multisig. I.e. server sends 3 challenges - tempo.charge, base.charge, solana.charge, and requires at least 1 challenge complete. Client can send a credential responding to any challenge, server verifies according to that specific challenge.

Benefit of this chaining approach is a server can put together more programmatic access to gated content / service by composing individual methods/intents. Server can specify you need to pay AND do action X on smart contract Y in order to receive service. When individual methods can be chained, i think flexibility increases?

As for the challenge bit - super interesting but want to be cognizant of scope creep for what this spec is meant to do, and think that level of change would be more appropriate at the protocol/structural layer as an extension of sorts. I do think it's a useful consideration.

@nlok5923
Copy link
Copy Markdown

nlok5923 commented Apr 3, 2026

On gas sponsorship guarantees, Permit2 is recommended partly because "server naturally sponsors gas", but nothing in the spec requires this. What stops a server from advertising type="permit2" and then making the client pay gas anyway? Should there be an explicit field in the challenge for this ?

@gakonst
Copy link
Copy Markdown
Contributor

gakonst commented Apr 3, 2026

I think would be great to add opt in specialization if 3009 is supported!

@gakonst
Copy link
Copy Markdown
Contributor

gakonst commented Apr 3, 2026

And yes EVM is the right abstraction here! The union of methods is of separate concern!

crumb-trail added a commit to crumb-trail/mpp-specs that referenced this pull request Apr 3, 2026
…cation

Add type="authorization" as opt-in credential for tokens implementing
EIP-3009 (USDC, EURC, etc.). Zero setup — no Permit2 approval needed.
Client signs off-chain transferWithAuthorization, server submits.
Challenge binding via nonce = challengeHash, enforced on-chain by
the token contract (stronger than server-side witness verification).

Also add explicit Gas Sponsorship Model section clarifying that gas
payment is structurally determined by credential type:
- permit2/authorization: server always pays (off-chain sigs)
- transaction/hash: client always pays (on-chain txs)
No explicit feePayer field needed.

Credential hierarchy is now:
  permit2 (recommended, full-featured)
  > authorization (opt-in for 3009 tokens, zero setup)
  > transaction (universal fallback)
  > hash (constrained clients)

Addresses feedback from @gakonst (3009 support) and
@nlok5923 (gas sponsorship guarantees) on PR tempoxyz#213.
…cation

Add type="authorization" as opt-in credential for tokens implementing
EIP-3009 (USDC, EURC, etc.). Zero setup — no Permit2 approval needed.
Client signs off-chain transferWithAuthorization, server submits.
Challenge binding via nonce = challengeHash, enforced on-chain by
the token contract.

Also add explicit Gas Sponsorship Model section clarifying that gas
payment is structurally determined by credential type:
- permit2/authorization: server always pays (off-chain sigs)
- transaction/hash: client always pays (on-chain txs)

Addresses feedback from @gakonst and @nlok5923 on PR tempoxyz#213.
@0xBreadguy
Copy link
Copy Markdown
Author

@kyscott18 - Updated to include EIP-3009 as an opt-in, per Georgios rec.

@nlok5923 - updated to be more clear re:gas sponsorship, which is a structural thing for the offchain sigs so doesn't really need explicit fields. lmk if the new note is unclear on reasoning.

@ilikesymmetry
Copy link
Copy Markdown

3009 is an eip-specific implementation of the transfer credential, effectively.

Not opposed to including it but would want to get consensus from contributors.

@0xBreadguy I am also in favor of including 3009. Agree with you that Permit2 works for all tokens, but it also requires pre-approval that not all accounts have. 3009 enables a better onboarding experience for tokens that support it and USDC is an important use case today to consider. I don't think including another signature mechanism negatively impacts the devx. It just enables more user choice.

@ilikesymmetry
Copy link
Copy Markdown

Otherwise I am in favor of the spec and think it will serve all EVM chains well, great proposal!

@gakonst
Copy link
Copy Markdown
Contributor

gakonst commented Apr 4, 2026

Thx folks hopefully we can merge this through the coming week - bit slower weekend due to Easter thx for the understanding 🙌✌️

@0xBreadguy
Copy link
Copy Markdown
Author

Updated the initial description to include recently implemented suggestions.

Will let everyone think on it

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants