Skip to content

[Proposal] Escrow Scheme for x402 using Base Commerce Payments Protocol #1011

@A1igator

Description

@A1igator

[Proposal] Escrow Scheme for x402 using Base Commerce Payments Protocol

Based on Agentokratia's escrow proposal (#834) and its reference implementation: x402-escrow (MIT).

Summary

We at x402r.org propose an escrow scheme for x402 v2 that leverages the Base Commerce Payments Protocol to enable refundable payments and conditional fund handling. The client signs once to authorize a maximum amount, and the facilitator settles through the commerce-payments operator — routing funds into escrow or directly to the receiver.

Built on audited on-chain escrow contracts deployed on Base and other EVM chains, this scheme adds the auth/capture pattern as a building block for x402.

Problem

The exact scheme works well for immediate-delivery payments, but creates friction for:

  • High-value transactions — No recourse if service fails after payment
  • Variable pricing — Usage-based billing (LLM tokens, compute time, API calls)
  • Long-running tasks — Work that takes hours or days to complete

Solution

The escrow scheme supports two settlement paths through the commerce-payments operator:

Method Function Behavior
authorize authorize() Funds held in escrow. Can be captured, refunded, or voided.
charge charge() Funds sent directly to receiver. Refundable post-settlement.

Both methods share identical function signatures and use the same operator, fee system, and token collector infrastructure.

Lifecycle

Authorize (default):

SIGN → AUTHORIZE → RESOURCE DELIVERED
  1. Client signs an ERC-3009 receiveWithAuthorization for the maximum amount
  2. Facilitator calls authorize() on the operator — funds locked in escrow
  3. Server returns the resource (HTTP 200)

Post-settlement, the commerce-payments contracts enable capture, refund, void, or reclaim.

Charge:

SIGN → CHARGE → RESOURCE DELIVERED
  1. Client signs an ERC-3009 authorization (same as above)
  2. Facilitator calls charge() on the operator — funds go directly to receiver
  3. Server returns the resource (HTTP 200)

Post-settlement, the operator can refund within refundExpiry if needed. Unlike the authorize path, the payer cannot reclaim() — funds are already with the receiver.

Specification

The escrow scheme uses standard x402 v2 structures with additional fields in extra:

PaymentRequirements

{
  "x402Version": 2,
  "accepts": [{
    "scheme": "escrow",
    "network": "eip155:8453",
    "amount": "1000000",
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "payTo": "0xReceiverAddress",
    "maxTimeoutSeconds": 60,
    "extra": {
      "name": "USDC",
      "version": "2",
      "escrowAddress": "0xEscrowAddress",
      "operatorAddress": "0xOperatorAddress",
      "tokenCollector": "0xCollectorAddress",
      "settlementMethod": "authorize",
      "minFeeBps": 0,
      "maxFeeBps": 1000,
      "feeReceiver": "0xOperatorAddress"
    }
  }]
}

PaymentPayload

{
  "x402Version": 2,
  "resource": {
    "url": "https://api.example.com/resource",
    "method": "GET"
  },
  "accepted": {
    "scheme": "escrow",
    "network": "eip155:8453",
    "amount": "1000000",
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "payTo": "0xReceiverAddress",
    "maxTimeoutSeconds": 60,
    "extra": {
      "name": "USDC",
      "version": "2",
      "escrowAddress": "0xEscrowAddress",
      "operatorAddress": "0xOperatorAddress",
      "tokenCollector": "0xCollectorAddress",
      "settlementMethod": "authorize",
      "minFeeBps": 0,
      "maxFeeBps": 1000,
      "feeReceiver": "0xOperatorAddress"
    }
  },
  "payload": {
    "authorization": {
      "from": "0xPayerAddress",
      "to": "0xCollectorAddress",
      "value": "1000000",
      "validAfter": "0",
      "validBefore": "1740672154",
      "nonce": "0xf374...3480"
    },
    "signature": "0x2d6a...571c",
    "paymentInfo": {
      "operator": "0xOperatorAddress",
      "receiver": "0xReceiverAddress",
      "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "maxAmount": "1000000",
      "preApprovalExpiry": 1740672154,
      "authorizationExpiry": 4294967295,
      "refundExpiry": 281474976710655,
      "minFeeBps": 0,
      "maxFeeBps": 1000,
      "feeReceiver": "0xOperatorAddress",
      "salt": "0x0000...0001"
    }
  }
}

extra Fields

Required:

Field Type Description
name string EIP-712 domain name for the token (e.g., "USDC")
version string EIP-712 domain version (e.g., "2")
escrowAddress address AuthCaptureEscrow contract address
operatorAddress address Operator address
tokenCollector address Token collector contract address

Optional:

Field Type Default Description
settlementMethod "authorize" | "charge" "authorize" Settlement path
minFeeBps uint16 0 Minimum fee in basis points
maxFeeBps uint16 0 Maximum fee in basis points
feeReceiver address address(0) Fee recipient
preApprovalExpirySeconds uint48 ERC-3009 signature validity
authorizationExpirySeconds uint48 Capture deadline
refundExpirySeconds uint48 Refund request deadline

Relationship to exact

Aspect exact escrow
Settlement Immediate transfer Via escrow contract (authorize) or direct (charge)
Refundable No Yes (both paths)
Fee system None Commerce-payments managed (min/max bps)
Gas payer Facilitator Facilitator
Signature ERC-3009 / Permit2 ERC-3009
On-chain contracts Token only Token + Escrow + Operator + Collector

The charge settlement method gives escrow a direct-settlement path (like exact) while retaining post-settlement refund capability through the commerce-payments infrastructure.

Relationship to Other Escrow Proposals

Proposal Focus Relationship
#834 (Agentokratia) Session-based payments with facilitator tracking This proposal generalizes the auth/capture pattern; session tracking is one operator implementation
#839 (cart.fun) Pre-funded usage-based payments Uses their own escrow contracts; this proposal uses the audited Base Commerce Payments contracts instead
#864 (x402r - us) Refundable payments with dispute resolution Built on Base Commerce Payments; custom operator contracts implemented
#946 Channel scheme with Merkle proof settlement Alternative settlement approach; could potentially use escrow scheme for fund locking
#1247 (trust-escrow) Task delivery escrow for agents Task delivery verification and dispute resolution could be built as an operator on top of this scheme

Safety

  • Cannot overchargeamount capped by client-signed maxAmount
  • Client can reclaim — Funds recoverable after authorizationExpiry if operator disappears
  • Fee bounds enforced on-chainminFeeBps/maxFeeBps are client-signed
  • Replay prevention — Nonces derived from keccak256(chainId, escrowAddress, paymentInfoHash)
  • Expiry ordering enforced — Contract requires preApprovalExpiry <= authorizationExpiry <= refundExpiry

Status

Spec PR: #1425

Reference implementation available at x402r-scheme (TypeScript, EVM).

References

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions