In this quickstart, you will deposit USDC into a Gateway Wallet, pay for an
x402-protected resource without gas fees, and check your balance. By the end,
you’ll have a working client that can make gasless payments to any
x402-compatible API that supports Circle Gateway.
Prerequisites
Before you begin, ensure you have:
- Node.js v18 or later and npm installed.
- An EVM wallet private key (for signing transactions and payment
authorizations).
- Testnet USDC from the Circle Faucet.
- Testnet ETH (or native gas token) for the one-time deposit transaction.
Step 1: Set up your project
1.1. Create a new project
Create a new directory and initialize a Node.js project:
mkdir nanopayments-buyer
cd nanopayments-buyer
npm init -y
npm pkg set type=module
npm pkg set scripts.pay="tsx --env-file=.env pay.ts"
1.2. Install dependencies
npm install @circlefin/x402-batching viem tsx typescript
npm install --save-dev @types/node
1.3. Initialize TypeScript
This step is optional. It helps prevent missing types in your IDE or editor.
Create a tsconfig.json file:
Then, update the tsconfig.json file:
cat <<'EOF' > tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"types": ["node"]
}
}
EOF
Create a .env file in the project root and add your wallet private key:
Replace the value with your EVM wallet private key.
This is a sensitive credential. Do not commit it to version control or share it
publicly.
Step 2: Initialize the client
Create a new file pay.ts and initialize the GatewayClient with your chain
and private key:
import { GatewayClient } from "@circlefin/x402-batching/client";
const client = new GatewayClient({
chain: "arcTestnet",
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
});
The chain parameter determines which blockchain the client connects to for
deposits and withdrawals. See the
SDK reference for all supported chain
names.
Step 3: Deposit USDC into Gateway
Before you can make gasless payments, deposit USDC from your wallet into the
Gateway Wallet contract. This is a one-time onchain transaction:
const balances = await client.getBalances();
console.log(`Gateway balance: ${balances.gateway.formattedAvailable} USDC`);
// 1 USDC = 1_000_000 base units (6 decimals)
if (balances.gateway.available < 1_000_000n) {
console.log("Depositing 1 USDC...");
const deposit = await client.deposit("1");
console.log(`Deposit tx: ${deposit.depositTxHash}`);
}
After the deposit confirms, your Gateway balance can be used for gasless
payments to any supported seller.
Step 4: Pay for a resource
Add the payment logic to pay.ts. Call client.pay() with the URL of an
x402-protected resource. The client handles the full payment flow automatically:
- Sends the initial request to the URL.
- Receives the
402 Payment Required response with payment details.
- Signs an EIP-3009 authorization offchain (zero gas).
- Retries the request with the
PAYMENT-SIGNATURE header.
const url = "http://localhost:3000/premium-data";
const { data, status } = await client.pay(url);
console.log(`Status: ${status}`);
console.log("Response:", data);
Don’t have a seller URL to test with? Set up a local test API in two minutes
using the seller quickstart.
Step 5: Check your balance
Add balance checking after the payment:
const updated = await client.getBalances();
console.log(`Wallet USDC: ${updated.wallet.formatted}`);
console.log(`Gateway available: ${updated.gateway.formattedAvailable}`);
Step 6: Run the script
Run the complete script:
You should see the deposit transaction (if needed), the response from the paid
resource, and your updated balance.
Step 7: Withdraw funds (optional)
You can withdraw USDC from Gateway back to your wallet at any time. Same-chain
withdrawals are instant:
const result = await client.withdraw("5");
console.log(`Withdrew ${result.formattedAmount} USDC`);
console.log(`Tx: ${result.mintTxHash}`);
To withdraw to a different blockchain:
const crossChain = await client.withdraw("5", { chain: "baseSepolia" });
console.log(`Withdrew to ${crossChain.destinationChain}`);
Crosschain withdrawals require native gas tokens on the destination blockchain
to cover the minting transaction.
Check support before paying
Before attempting a payment, you can verify that the target URL supports Gateway
batching:
const support = await client.supports(url);
if (!support.supported) {
console.error("This URL does not support Gateway payments");
} else {
const { data } = await client.pay(url);
}
This is useful when building clients that interact with APIs where Gateway
support is not guaranteed.