Skip to main content
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:
npx tsc --init
Then, update the tsconfig.json file:
cat <<'EOF' > tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "types": ["node"]
  }
}
EOF

1.4. Configure environment variables

Create a .env file in the project root and add your wallet private key:
.env
PRIVATE_KEY=0x...
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:
pay.ts
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:
pay.ts
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:
  1. Sends the initial request to the URL.
  2. Receives the 402 Payment Required response with payment details.
  3. Signs an EIP-3009 authorization offchain (zero gas).
  4. Retries the request with the PAYMENT-SIGNATURE header.
pay.ts
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:
pay.ts
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:
npm run pay
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:
pay.ts
const result = await client.withdraw("5");
console.log(`Withdrew ${result.formattedAmount} USDC`);
console.log(`Tx: ${result.mintTxHash}`);
To withdraw to a different blockchain:
pay.ts
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.