Skip to main content
x402r builds on the canonical Commerce Payments Protocol (no fork). Three contracts from this stack form the base layer:
  • AuthCaptureEscrow: singleton escrow that holds funds and gates lifecycle actions on the captureAuthorizer (committed on-chain as PaymentInfo.operator).
  • ERC3009PaymentCollector: collects funds via signed ERC-3009 receiveWithAuthorization.
  • Permit2PaymentCollector: collects funds via Uniswap Permit2 permitTransferFrom.
All three sit at universal CREATE2 addresses (same address on every supported chain).

AuthCaptureEscrow

Core escrow contract for holding ERC-20 tokens during the payment lifecycle.

Payment State Machine

Key methods

authorize()

Pulls funds into escrow via the token collector. Only the captureAuthorizer (typically a facilitator EOA, or a smart contract acting as captureAuthorizer) can call it.
tokenCollector is ERC3009PaymentCollector or Permit2PaymentCollector depending on assetTransferMethod in the scheme extra. collectorData carries the raw ERC-3009 signature or the ABI-encoded Permit2 signature.

charge()

Single-shot atomic settlement: pulls funds and transfers directly to the receiver, no escrow hold.

capture()

Releases escrowed funds to the receiver, minus fees.

void()

Returns all escrowed funds to the payer. Full-only: void() empties the authorization in one transaction.

reclaim()

Payer-only: gated by onlySender(paymentInfo.payer). The payer can pull funds back out of escrow after captureDeadline if the captureAuthorizer never captured. No third party (including the operator or arbiter) can call reclaim on the payer’s behalf.

refund()

Returns funds to the payer after capture, sourced via a token collector (typically pulled from the merchant’s balance).

Access control

Lifecycle actions (authorize, charge, capture, void, refund) check msg.sender against PaymentInfo.operator (the captureAuthorizer). Anyone can call reclaim after captureDeadline. The escrow has no global “operator whitelist.” Access is per-payment, governed by the signed PaymentInfo.

Security features

  • Replay prevention: each payment has a unique nonce derived from (chainId, escrowAddress, paymentInfoHash), consumed on-chain at settlement
  • Fee bounds enforcement: the client signs minFeeBps / maxFeeBps / feeReceiver in PaymentInfo; the escrow rejects out-of-bounds captures/charges
  • Expiry ordering: contract enforces preApprovalExpiry <= authorizationExpiry <= refundExpiry
  • Reentrancy protection on all state-changing entry points

ERC3009PaymentCollector

Collects ERC-20 tokens into escrow using the client’s off-chain ERC-3009 signature. The payer never submits a transaction.

How it works

The escrow calls the token collector during authorize() or charge(), passing the client’s signature as collectorData. The collector executes receiveWithAuthorization (ERC-3009) to pull tokens from the payer.

Features

  • ERC-3009 receiveWithAuthorization(): gasless token transfers via signed messages
  • EIP-6492 support: handles smart wallet clients with deployment bytecode in signatures (via ERC6492SignatureHandler)
  • Nonce-based replay protection: each authorization can run only once; the nonce is the payer-agnostic PaymentInfo hash
  • Deadline-based expiry: validBefore (typically now + maxTimeoutSeconds) blocks stale authorizations

ERC-3009 signature

The client signs an EIP-712 typed data message with primary type ReceiveWithAuthorization:
The auth-capture scheme uses receiveWithAuthorization (not transferWithAuthorization). The token collector is the to address, which then routes tokens into the escrow.

Permit2PaymentCollector

Collects ERC-20 tokens through Uniswap Permit2 permitTransferFrom. The operator selects this collector when assetTransferMethod === "permit2" in the scheme extra. Any ERC-20 the payer has approved Permit2 for becomes spendable through this collector. The client signs a Permit2 PermitTransferFrom; the deterministic nonce binds the merchant address, removing the need for a separate witness struct. See the auth-capture wire format for the full Permit2 wire format.