Skip to main content

Smart contract wallets

A contract account has no private key of its own. It cannot produce a signature that recovers to an address, so it proves one a different way: the token asks the account isValidSignature(bytes32,bytes) and the account answers. That is ERC-1271.

USDC on XDC checks payment signatures with OpenZeppelin's SignatureChecker, which tries ECDSA first and falls back to ERC-1271. One code path, both kinds of wallet. So a deployed contract wallet can sign an EIP-3009 authorization and have it settle, and the public facilitator does exactly that today.

Which wallets work​

WalletPays?What to do
An ordinary address (MetaMask, Ledger, a raw key)YesNothing special
An MPC wallet that returns a standard ECDSA signatureYesNothing special, it looks like an ordinary address
A deployed contract wallet implementing ERC-1271YesUse the adapter below
A contract wallet that is not yet deployed (ERC-6492)NoDeploy it first
A contract account without isValidSignatureNoNot supported

The middle two are easy to confuse. If your MPC system signs on behalf of a normal address and hands you 65 bytes, you have an ordinary wallet and none of this page applies. If your funds sit in a deployed contract and approval happens inside it, read on.

Check the capability first​

Do not hardcode relayer addresses or assume what is supported. Ask:

curl -s https://public-facilitator.xdcai.tech/supported
{
"kinds": [{
"x402Version": 2,
"scheme": "exact",
"network": "eip155:50",
"asset": "0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1",
"extra": { "assetTransferMethod": "eip3009", "decimals": 6, "erc1271": true, "name": "USDC", "version": "2" }
}],
"signers": { "eip155:50": ["0xaf28621e287e4EA0F14FA7e7ba365206FD6279DA", "0xb8810105569AcD20d46E3a5736e7E9f7C48672bC"] },
"signerRoles": { "0xaf28621e287e4EA0F14FA7e7ba365206FD6279DA": "facilitator-relayer" }
}

extra.erc1271 is the answer for contract wallets. If it is missing or false, contract wallets are off and only ordinary addresses pay. signers lists the relayer addresses that submit settlement transactions and pay the gas; read them from here rather than copying them into your code, because they change.

Use eip155:50 as the network. The older xdc string is x402 v1 only.

The signer adapter​

The buyer SDK takes a signer object. For a contract wallet, the address is the wallet contract and the signature is whatever your approval system produces.

npm install @xdcai/x402-buyer@1.0.0-rc.2
import { createXdcPaymentFetch, XDC_USDC } from "@xdcai/x402-buyer";

const signer = {
// The deployed smart-wallet contract address. This is the payer.
address: smartWalletAddress,

async signTypedData(typedData: unknown) {
const signature = await mpcWallet.requestSignature(typedData);

// Return the bytes exactly as produced. Do not re-encode, truncate,
// or split them into v, r and s.
return signature;
},
};

const paidFetch = createXdcPaymentFetch({
signer,
maxAmount: 10_000n, // atomic USDC units, so 0.01 USDC
allowedAssets: [XDC_USDC],
allowedPayTo: [trustedSellerAddress],
});

const response = await paidFetch(protectedUrl, { paymentIntentId: uniqueId });

The facilitator team maintains a fuller example at examples/erc1271-smart-wallet.ts.

:::info About the version 1.0.0-rc.2 is a documentation release. Its compiled output is byte for byte identical to 1.0.0-rc.1, so upgrading changes no behavior and staying on rc.1 does not block a contract wallet. Pin rc.2 because that is where the guidance lives, and because npm latest still points at rc.1, so a floating range gives you the older README. :::

Four rules that decide whether it works​

The payer is the contract. signer.address is the deployed wallet address, and it is the address that must hold the USDC. Never use an MPC owner, participant, share holder or any other signing identity as the payer. Those addresses hold nothing.

Preserve the signature bytes. A contract signature is variable length and often carries an envelope, for example ERC-7739. It is not 65 bytes and it has no v, r, s. Any code that splits, pads or re-encodes it will produce something the account rejects.

Deploy before you pay. The account has to exist on chain for isValidSignature to be callable. Counterfactual addresses and ERC-6492 wrappers are not supported.

The account must actually accept what it signed. Confirm your contract validates the returned signature over the same typed data. If the account and the approval system disagree, verification fails with nothing to see on chain.

Where this works today​

PathContract wallets
Public facilitator, either through @xdcai/x402-seller or raw HTTPYes
Gateway at api.xdcai.tech/x402/connect/...Yes

The gateway settles through a fee-splitting vault. It now verifies and settles a contract wallet's signature on chain through ERC-1271, the same way the token does, so any ERC-1271 smart wallet can pay a gateway service and the platform fee split is unchanged. An EOA and a contract wallet use the exact same payment flow.

Security​

  • A seller API key never belongs in buyer code. Buyers need no key at all.
  • Buyers call the seller's URL. Never call /verify or /settle directly.
  • Set maxAmount, allowedAssets, allowedPayTo and the network explicitly, every time.
  • Give each logical purchase its own paymentIntentId.
  • If an outcome is ambiguous, stop and reconcile. Do not retry a payment you cannot account for.
  • Never log or transmit private keys, MPC shares or approval credentials.
  • Require an explicit human approval before the first real-money call.

Who owns what​

Responsible for
Facilitator/supported, /verify, /settle, signature verification for both wallet kinds, settlement, the buyer and seller SDKs
XDC AIThis documentation, seller onboarding, the dashboard, credentials storage, support
You, the wallet integratorThe deployed contract, approval and signature production, funding, and confirming your account validates the signature it returns

Not supported​

Do not build against any of these. They are out of scope for this release:

  • Wallets that are not deployed yet, and ERC-6492 signatures.
  • Contract accounts that do not implement ERC-1271.
  • Buyers calling facilitator endpoints directly.
  • Custody or MPC infrastructure operated by XDC AI. You run your own wallet.