Skip to content

Support for alternative x402 payment schemes (escrow / commerce) #75

@A1igator

Description

@A1igator

Summary

x402r adds escrow-backed refunds to x402 payments. The Ampersend SDK currently hardcodes scheme: "exact" at every layer — type definitions, adapters, wallets, and the hosted API schema. This makes it impossible for agents to pay for resources protected by alternative x402 schemes like "commerce" (escrow-based payments with refund eligibility).

We'd like to integrate x402r commerce-protected resources with Ampersend agents and are documenting the specific blockers below.

Blockers

1. Scheme dispatch is hardcoded to "exact"

Four places enforce the exact scheme:

Location File Issue
TreasurerSchemeClientV1/V2 x402/http/adapter.ts readonly scheme = "exact" — x402Client can't find a handler for other schemes
AccountWallet.createPayment() x402/wallets/account/wallet.ts Throws WalletError if requirements.scheme !== "exact"
SmartAccountWallet.createPayment() x402/wallets/smart-account/wallet.ts Same throw on non-exact
PaymentRequirements schema ampersend/types.ts Schema.Literal("exact") with annotation "starting with exact only for MVP" — the hosted treasurer rejects non-exact requirements before they reach the wallet

Suggested fix: Allow wrapWithAmpersend() to accept a Map<string, SchemeClient> (or similar registry) so callers can register additional scheme handlers. The x402 x402Client already has scheme dispatch built in — the wallet layer just needs to stop short-circuiting it. The API schema would also need to be relaxed for the hosted treasurer.

2. No ReceiveWithAuthorization signing path

The commerce/escrow scheme uses ERC-3009 ReceiveWithAuthorization instead of TransferWithAuthorization. This is required because the escrow contract must be msg.sender when calling receiveWithAuthorization() on the token contract — transferWithAuthorization allows anyone to submit, which breaks the escrow security model.

Currently the SDK only defines TransferWithAuthorization EIP-712 types and signing. There are zero references to ReceiveWithAuthorization in the codebase.

Suggested fix: Add ReceiveWithAuthorization as an alternate EIP-712 signing path, selected by the scheme handler. The struct fields are identical to TransferWithAuthorization — only the primaryType string differs.

3. v2 adapter silently coerces scheme

x402/http/v2-adapter.ts contains:

scheme: v2Req.scheme as "exact",

This casts any scheme value to "exact" at the type level rather than failing or passing it through. A "commerce" requirement would be silently mistyped.

Suggested fix: Remove the cast — pass scheme through as-is and let the scheme dispatch handle validation.

4. Python SDK has the same pattern

The Python SDK (smart_account/exact.py, cosigned.py) has equivalent hardcoding — ValueError on non-exact schemes. Same fixes would apply there.

What integration looks like

With these changes, an Ampersend EOA agent paying for a commerce-protected resource would look like:

import { wrapWithAmpersend } from "@ampersend_ai/ampersend-sdk";
import { commerceSchemeClient } from "@x402r/evm/commerce/client";

const client = wrapWithAmpersend(httpClient, {
  wallet: accountWallet,
  schemes: new Map([
    // exact scheme registered by default
    ["commerce", commerceSchemeClient],
  ]),
});

// Agent fetches commerce-protected resource — 402 → escrow payment → response
const response = await client.get("https://api.example.com/protected");

No changes are required on the x402r side. The extra field passthrough has been confirmed working — commerce-specific keys (operatorAddress, escrowAddress, tokenCollector, fee bounds) don't collide with Ampersend's extra usage.

Validated assumptions

  • Treasurer interface is scheme-agnostic (approves based on amount, not scheme) — only the API schema blocks non-exact
  • extra field is preserved throughout the pipeline with no stripping or collisions
  • MCP/A2A transports delegate to the wallet layer — no transport changes needed
  • x402 v2 protocol is natively supported

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions