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
| Wallet | Pays? | What to do |
|---|---|---|
| An ordinary address (MetaMask, Ledger, a raw key) | Yes | Nothing special |
| An MPC wallet that returns a standard ECDSA signature | Yes | Nothing special, it looks like an ordinary address |
| A deployed contract wallet implementing ERC-1271 | Yes | Use the adapter below |
| A contract wallet that is not yet deployed (ERC-6492) | No | Deploy it first |
A contract account without isValidSignature | No | Not 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
| Path | Contract wallets |
|---|---|
Public facilitator, either through @xdcai/x402-seller or raw HTTP | Yes |
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
/verifyor/settledirectly. - Set
maxAmount,allowedAssets,allowedPayToand 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 AI | This documentation, seller onboarding, the dashboard, credentials storage, support |
| You, the wallet integrator | The 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.