Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developers.circle.com/llms.txt

Use this file to discover all available pages before exploring further.

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:
  • Installed Node.js v18 or later and npm.
  • An EOA (externally owned account) wallet private key for signing transactions and payment authorizations.
  • Obtained testnet USDC from the Circle Faucet.
  • Testnet ETH (or native gas token) for the one-time deposit transaction.
Nanopayments require an EOA wallet. Smart contract account (SCA) wallets are not supported because Gateway verifies payment signatures offchain using ecrecover, which is incompatible with EIP-1271 contract signatures.

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 @circle-fin/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 "@circle-fin/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}`);
}
getBalances() calls the Get Token Balances API endpoint. The deposit itself is an onchain transaction and does not use the Gateway API. After the deposit confirms, your Gateway balance can be used for gasless payments to any supported seller. See the discussion at Fast deposits about increasing deposit speeds.

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);
Under the hood, pay() negotiates the 402 flow and submits the payment through the Settle x402 Payment API endpoint.
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 using the Get Token Balances API endpoint:
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. The withdraw() method calls the Create Transfer Attestation API endpoint:
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. The supports() method queries the Get Supported x402 Payment Kinds API endpoint:
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.