A walkthrough of a reference implementation of Circle Nanopayments from the buyer and seller side. Read to learn how to build AI agent payment systems on Arc.

Supporting agentic payments requires the ability to transact at massive scale, with thousands of sub-cent payments per minute for compute, storage, API calls, and other metered services. Traditional payment rails impose minimum fees and pricing thresholds that make it impractical to charge below a certain amount, as transaction costs quickly exceed the value being transferred. Meanwhile, blockchain networks lack predictable fee models, and gas costs can fluctuate in ways that make sub-cent transactions, such as $0.001 per request, economically unviable.
This is the problem that Nanopayments powered by Circle Gateway is designed to address.
Nanopayments enable near gas-free USDC payments of any size, down to $0.000001, with verification in less than a second, making high-frequency value transfer practical. Rather than requiring each individual payment to be submitted and settled onchain as its own transaction, buyers sign EIP 3009 payment authorizations and exchange them with the seller through a virtual ledger powered by Circle Gateway. Signatures are verified instantly offchain and settled in batches onchain, avoiding the need for a separate onchain gas charge for each individual payment while preserving secure and verifiable transfers.
This blog walks through a reference implementation of Circle Nanopayments from the buyer and seller side. You'll see how the core components work together: from paywalling API endpoints with x402 to handling batched payment authorizations to withdrawing earnings crosschain.. By the end, you'll understand how to integrate nanopayments into your own agent systems.
Overview of the architecture
This reference implementation uses:
- Next.js for serving paywalled APIs and the seller dashboard
- x402 protocol for HTTP 402 based payment negotiation
- Circle Gateway for offchain balance management, signature verification, and batched settlement
- Arc Testnet for onchain settlement, with USDC as the payment currency
- Supabase for persisting payments, balances, and withdrawals with real time updates
- LangChain agent as the autonomous buyer executing payments
The seller side runs a Next.js app with paywalled routes wrapped in withGateway() middleware. The buyer side runs an autonomous agent that signs payment messages locally and makes thousands of API calls without broadcasting transactions. Circle Gateway handles verification instantly and settles batches onchain later.
What you'll build
By running this demo, you'll have:
- A Next.js seller app with paywalled API endpoints using x402 ($0.0003 to $0.03 per call)
- An AI payment agent that autonomously purchases resources with near-gas free payment.
- A seller dashboard showing real-time payments and Circle Gateway balance
- Crosschain withdrawal support so sellers can move earnings anywhere
Get started
Clone and run the reference implementation: github.com/circlefin/arc-nanopayments
git clone https://github.com/circlefin/arc-nanopayments
cd arc-nanopayments && npm install
npm run generate-wallets # Creates seller and buyer wallets
npm run dev # Start the Next.js app
npm run agent # Start the payment agent
Open http://localhost:3000/dashboard and watch nanopayments happen in real time.
The core workflow
Here's how nanopayments work in practice, from paywalling an endpoint to settling payments to withdrawing earnings.
1. Paywall an endpoint with x402
The payment paywalling layer lives in lib/x402.ts. The withGateway() wrapper checks for payment signatures, verifies them locally, and queues them for batch settlement.
Here's how you paywall a route. This example is from app/api/premium/quote/route.ts:
import { withGateway } from "@/lib/x402";
const handler = async (_req: NextRequest) => {
return NextResponse.json({
quote: "The best way to predict the future is to invent it. — Alan Kay",
category: "technology",
timestamp: new Date().toISOString(),
});
};
export const GET = withGateway(handler, "$0.001", "/api/premium/quote");
That's it. One line wraps your handler. Unpaid requests get 402 Payment Required. Paid requests pass through after Circle Gateway verifies the signature.
That separation matters. Payment verification happens offchain (instant, no gas). Settlement happens onchain in batches (amortized cost across thousands of payments).
2. Make near gas-free payments from the buyer side
The buyer agent (agent.mts) uses @circle-fin/x402-batching/client SDK to sign payment messages locally instead of broadcasting transactions. Each signed message authorizes a USDC transfer allowing the agent to make thousands of API calls at near gas-free USDC payments.
The core of the agent is the startPaymentLoop() function. This function creates an interval that makes one payment per second to different API endpoints, tracks in-flight requests, enforces spending limits, and logs every transaction to the dashboard. Here's the actual payment loop from the codebase:
function startPaymentLoop() {
balanceInterval = setInterval(checkAndRedeposit, 30_000);
paymentInterval = setInterval(() => {
if (paused) return;
const ep = endpoints[index % endpoints.length];
index++;
inFlight++;
const start = Date.now();
gateway
.pay(ep.url, { method: ep.method, body: ep.body })
.then((result) => {
inFlight--;
const ms = Date.now() - start;
const amount = parseFloat(result.formattedAmount);
totalSpent += amount;
const limitInfo = spendingLimit !== null
? ` [spent: ${totalSpent.toFixed(6)}/${spendingLimit.toFixed(6)} USDC]`
: "";
console.log(
`#${index} ${ep.method} ${ep.url.split("/").pop()} -> ${result.formattedAmount} USDC (${ms}ms) [in-flight: ${inFlight}]${limitInfo}`,
);
if (spendingLimit !== null && totalSpent >= spendingLimit) {
handleLimitReached();
}
})
.catch((err) => {
inFlight--;
const ms = Date.now() - start;
console.error(
`#${index} ${ep.url.split("/").pop()} FAILED (${ms}ms): ${err.message} [in-flight: ${inFlight}]`,
);
});
}, 1000);
}
The key insight here is that: gateway.pay() signs a payment authorization locally without broadcasting a transaction. Circle Gateway verifies the signature instantly and queues it for batch settlement. One onchain transaction can settle thousands of these signed payments, making the gas cost per payment approach zero.
3. Check Circle Gateway balance in real time
Sellers need to know how much they've earned. The dashboard queries Circle Gateway's balance API to show their unified balance without querying the blockchain directly.
From app/api/gateway/balance/route.ts:
export async function GET() {
const address = process.env.SELLER_ADDRESS;
if (!address) {
return NextResponse.json(
{ error: "SELLER_ADDRESS not configured" },
{ status: 500 },
);
}
const sellerAddress = address as `0x${string}`;
try {
const [gatewayResponse, walletBalance] = await Promise.all([
fetch(GATEWAY_API, {
method: "POST",
headers: { "Content-Type": "application/json" },
cache: "no-store",
body: JSON.stringify({
token: "USDC",
sources: [{ domain: ARC_TESTNET_DOMAIN, depositor: sellerAddress }],
}),
}),
getWalletUsdcBalance(sellerAddress),
]);
if (!gatewayResponse.ok) {
const text = await gatewayResponse.text();
console.error("Gateway API error:", gatewayResponse.status, text);
return NextResponse.json({
wallet: { balance: walletBalance },
gateway: {
total: "0",
available: "0",
withdrawing: "0",
withdrawable: "0",
},
});
}
const data = await gatewayResponse.json();
const bal = data.balances?.find(
(b: { domain: number }) => b.domain === ARC_TESTNET_DOMAIN,
);
// Gateway API may return balance as a decimal string or atomic units
const raw = bal?.balance ?? "0";
const withdrawingRaw = bal?.withdrawing ?? "0";
const withdrawableRaw = bal?.withdrawable ?? "0";
const parse = (v: string) =>
v.includes(".") ? v : formatUnits(BigInt(v), 6);
const available = parse(raw);
const withdrawing = parse(withdrawingRaw);
const withdrawable = parse(withdrawableRaw);
const total = (
parseFloat(available) + parseFloat(withdrawing)
).toFixed(6);
return NextResponse.json({
wallet: { balance: walletBalance },
gateway: { total, available, withdrawing, withdrawable },
});
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error("Balance fetch error:", message);
return NextResponse.json({
wallet: { balance: "0" },
gateway: {
total: "0",
available: "0",
withdrawing: "0",
withdrawable: "0",
},
});
}
}
This runs client-side in the dashboard, updating every few seconds so sellers see earnings accumulate in real time. It fetches both the Gateway balance (offchain USDC available for withdrawal) and the wallet balance (onchain USDC), handling errors gracefully and parsing atomic units correctly.
4. Withdraw to any supported chain
Once sellers have accumulated earnings, they can withdraw from their Circle Gateway balance to their wallet on Arc, or cross-chain to Base, Ethereum, Arbitrum, Avalanche, or any other supported chain. Circle Gateway handles the complexity—sellers just call withdraw() and get native USDC wherever they need it.
From app/api/gateway/withdraw/route.ts:
// Create gateway client with seller's private key
const gateway = new GatewayClient({
chain: "arcTestnet",
privateKey: process.env.SELLER_PRIVATE_KEY as `0x${string}`,
});
// Execute withdrawal (same-chain or cross-chain)
const result = await gateway.withdraw(amount, {
chain: destinationChain as SupportedChainName,
recipient: destinationAddress
? (destinationAddress as `0x${string}`)
: undefined,
});
// Record transaction hash to database
await supabase
.from("withdrawals")
.update({ status: "confirmed", tx_hash: result.mintTxHash })
.eq("id", withdrawal.id);
// Return withdrawal details to dashboard
return NextResponse.json({
txHash: result.mintTxHash,
amount: result.formattedAmount,
sourceChain: result.sourceChain,
destinationChain: result.destinationChain,
recipient: result.recipient,
status: "confirmed",
});
Supported chains include Arc Testnet, Base Sepolia, Ethereum Sepolia, Arbitrum Sepolia, Optimism Sepolia, Avalanche Fuji, and Polygon Amoy—Circle Gateway handles the routing automatically.
Why this matters on Arc
This model works if the settlement layer is predictable.
On Arc, nanopayment flows can be priced, settled, and withdrawn in stablecoins. There is no need to introduce volatility or separate gas tokens.
The stack becomes straightforward:
- Nanopayments powered by Circle Gateway gives you the payment verification and batching protocol.
- Arc provides a stablecoin-native settlement environment
- Circle Gateway provides the unified balance and cross-chain infrastructure
For developers building in the agentic economy, that combination is practical. You can charge sub-cent amounts, avoid the need for a separate onchain gas charge for each payment, and let sellers withdraw earnings to supported chains they need.
What you can build from this
- Pay-per-compute marketplaces where agents spin up GPU instances and pay per millisecond
- Pay-per-API-call services charging $0.001 per request without gas destroying margins
- Agent-to-agent commerce platforms where autonomous agents discover, negotiate, and transact
- Micropayment subscription models with usage-based pricing at fractions of a cent
- Cross-chain agent treasuries that automatically rebalance earnings across multiple networks
From demo to production
This starter demonstrates the core flow. Production systems need additional infrastructure:
- Reliability: Add retry logic for transient failures, timeout handling, Circle Gateway availability monitoring, and fallback paths if Gateway is unreachable.
- Security: Implement rate limiting per buyer, fraud detection for unusual payment patterns, signature expiry enforcement, and seller authorization allowlists.
- Observability: Build payment analytics dashboards, per-agent cost tracking, settlement monitoring, and balance depletion alerts so you catch issues before they become problems.
Build something with it
This repo is open source and designed to be forked. Use it as a starting point for your nanopayment infrastructure, extend the seller dashboard, add new premium endpoints, or integrate it into your existing agent platform.
GitHub: github.com/circlefin/arc-nanopayments
Circle Technology Services, LLC (“CTS”) is a software provider and does not provide regulated financial or advisory services. You are solely responsible for services you provide to users, including obtaining any necessary licenses or approvals and otherwise complying with applicable laws. For additional details, please click here to see the Circle Developer terms of service.
USDC is issued by regulated affiliates of Circle. See Circle’s list of regulatory authorizations.
Circle Wallets are provided by Circle Technology Services, LLC (“CTS”). CTS is a software provider and does not provide regulated financial or advisory services. You are solely responsible for services you provide to users, including obtaining any necessary licenses or approvals and otherwise complying with applicable laws. For additional details, refer to the Circle Developer Terms of Service.
Arc testnet is offered by Circle Technology Services, LLC (“CTS”). CTS is a software provider and does not provide regulated financial or advisory services. You are solely responsible for services you provide to users, including obtaining any necessary licenses or approvals and otherwise complying with applicable laws.
Arc has not been reviewed or approved by the New York State Department of Financial Services.
The product features described in these materials are for informational purposes only. All product features may be modified, delayed, or cancelled without prior notice, at any time and at the sole discretion of Circle Technology Services, LLC. Nothing herein constitutes a commitment, warranty, guarantee or investment advice.
AI features and agent activity on Arc may produce errors or unexpected behavior; you are solely responsible for reviewing, testing, and approving any AI-generated prompts, code, or transactions, and Circle makes no warranties as to their accuracy, completeness, or fitness.




