with one click
conflux-x402-payment-protocol
// Learn how to implement standard and gasless (EIP-3009) x402 payment flows on Conflux eSpace.
// Learn how to implement standard and gasless (EIP-3009) x402 payment flows on Conflux eSpace.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | Conflux x402 Payment Protocol |
| description | Learn how to implement standard and gasless (EIP-3009) x402 payment flows on Conflux eSpace. |
This skill guides you through implementing an x402-compliant payment flow on the Conflux eSpace network (EVM compatible). It covers both standard native token payments and advanced gasless payments using EIP-3009 and a Relayer.
@x402/express, @x402/core, @x402/evm, ethers, express.The client pays gas and transfers CFX directly to the server's address.
ExactEvmScheme (Native)sendTransaction.The client signs an authorization, and the server (Relayer) submits it on-chain, paying the gas.
GaslessScheme (Tunnel Mode)receiveWithAuthorization.Separate the Relayer (Server) and Payer (Client) wallets for proper testing.
# Server (Relayer) - Pays Gas
SERVER_PRIVATE_KEY=0x...
# Client (Payer) - Signs Authorization
CLIENT_PRIVATE_KEY=0x...
Implementing a gasless flow requires bypassing some standard x402 library validations to "tunnel" the full signature payload to the server.
payment-signature header.receiveWithAuthorization tx to contract -> Returns Resource.Standard schemes expect a simple Transaction Hash. For gasless, we need to send the full signature (v, r, s, nonce). We use a custom scheme object to pass the payload through without strict validation.
const gaslessScheme = {
scheme: "exact", // Hijack 'exact' or use custom name
get name() { return "exact"; },
deserialize(token) { return token; }, // Pass-through raw token
registerMoneyParser(parser) { this.moneyParsers.push(parser); return this; },
parsePrice(amount, network) { /* ... implementation ... */ },
enhancePaymentRequirements(req) { return req; }
};
The facilitator acts as the Relayer. In verify, it decodes the signature. In settle, it submits the transaction using the Server's Wallet.
class RelayerFacilitatorClient {
constructor() {
// Use SERVER_PRIVATE_KEY
this.wallet = new ethers.Wallet(process.env.SERVER_PRIVATE_KEY, provider);
}
async verify(payload, requirements) {
// Decode payload.proof (Tunnel Mode)
// Verify signature matches 'from' address
return { isValid: true, ... };
}
async settle(payload, requirements) {
// Call contract.receiveWithAuthorization(...)
// Server pays GAS here
}
}
The client must bundle the signature into a Base64 JSON string and put it in the proof field.
CRITICAL: The client must also echo back the exact accepted requirements object, otherwise the server middleware will reject the request.
const tokenPayload = {
x402Version: 2,
accepted: paymentInfo.accepts[0], // MUST ECHO EXACT REQUIREMENTS
proof: base64SignatureData // Tunnel signature here
};
[!IMPORTANT] Dynamic Relayer Address: The
payToaddress in the payment requirements MUST match the Relayer's wallet address (msg.sender). If they mismatch, thereceiveWithAuthorizationcontract call will revert. Always setpayTodynamically from the Relayer's wallet.
[!TIP] Async Initialization: Registering custom schemes often requires the server to be fully initialized before handling requests. Wrap your server setup in an
asyncfunction andawait resourceServer.initialize().
[!WARNING] Matching Logic: The
@x402/corelibrary performs a deep equality check on the requirements. If your client modifies the requirements list (e.g., dropping fields likeextraormaxTimeoutSeconds), the match will fail. Always use the object provided by the server.
For simple native token payments, you can use the standard ExactEvmScheme with a custom money parser.
// Money Parser for CFX
evmScheme.registerMoneyParser((amount, network) => {
if (network === "eip155:71") {
return {
amount: ethers.utils.parseEther(amount.toString()).toString(),
asset: "0x0000000000000000000000000000000000000000", // Native
extra: { symbol: "CFX", decimals: 18 }
};
}
return null;
});
To reduce latency (~15s -> ~2s), considering the following strategies:
Don't wait for the blockchain confirmation (tx.wait()) before responding to the client.
To handle high throughput, you must manage the Relayer's nonce manually.
nonce once, then increment locally for each subsequent transaction.Public endpoints like evmtestnet.confluxrpc.com are rate-limited.
1 Confirmation (Default): await tx.wait(1). Takes ~15s. Good balance for most apps.
Optimistic (0 Confirmation): Immediate response. Best for UX.
Safe (50+ Confirmations): For high-value transfers. Takes ~1 minute.
Finalized (400+ Confirmations): For exchanges/bridges. Takes ~6 minutes.
examples/server.js: Complete Gasless Server implementation.
examples/client.js: Complete Gasless Client implementation.
examples/contracts/: Smart contracts (MockUSDC) and deployment scripts.