RathRath Finance
Advanced Features

Nano payments and x402

Use Circle Gateway Nanopayments and x402 to pay Tachyon transaction relay costs in USDC with gasless, batched payment authorization.

Overview

Submit blockchain transactions through Tachyon and pay the relay cost in USDC using x402 and Circle Gateway Nanopayments.

What Is x402?

x402 is an open payment protocol built around the HTTP 402 Payment Required status code. When a client calls a paid API, the server returns payment requirements instead of requiring an account, subscription, or checkout page. The client signs a payment authorization and retries the same request with the payment proof attached. This allows applications and AI agents to pay for APIs programmatically. Learn more about x402.

What Is Circle Gateway?

Circle Gateway provides a unified USDC balance that can be used across its supported blockchain networks. A user deposits USDC into the Gateway Wallet contract, after which applications can query the available balance and authorize payments through the Gateway SDK. In this integration, GatewayClient manages the deposit, balance, payment authorization, and settlement details. Learn more about Circle Gateway.

What Are Nanopayments?

Circle Gateway Nanopayments are small USDC payments authorized with offchain signatures and settled onchain in batches. Instead of broadcasting a separate transaction and paying gas for every API request, buyers sign individual payment authorizations while Circle combines many payments into fewer settlement transactions. This makes high-frequency and sub-cent payments practical. Learn more about Nanopayments.

How Tachyon Uses Them

Tachyon exposes an x402-protected transaction relay endpoint. The client pays the requested relay cost from its Circle Gateway USDC balance, and Tachyon pays the destination-chain gas and submits the blockchain transaction. Learn more about the Tachyon relay API.

After a one-time USDC deposit into Gateway, the buyer signs each payment authorization offchain. Circle batches settlement, while Tachyon executes the requested blockchain transaction.

Learn more about the Circle buyer flow

Circle describes Nanopayments as supporting gas-free USDC payments down to $0.000001. The initial Gateway deposit is still an onchain transaction. The individual x402 payment authorizations after that deposit do not require a separate gas payment from the buyer.

How It Works

The integration combines three systems:

  1. Tachyon receives and relays the blockchain transaction.
  2. x402 negotiates payment through the HTTP 402 Payment Required flow.
  3. Circle Gateway Nanopayments signs the USDC authorization and settles payments in batches.

The request flow is:

  1. Deposit USDC into the Circle Gateway Wallet contract.
  2. Call Tachyon's x402-protected /api/submit-tx endpoint.
  3. Tachyon returns payment requirements when payment is needed.
  4. GatewayClient.pay() signs the payment authorization offchain.
  5. The client retries the request with the payment signature.
  6. Tachyon accepts the payment and submits the requested transaction.
  7. Circle Gateway settles the accumulated payment authorizations in batches.

The Circle payment authorization is gasless for the buyer, but the destination blockchain transaction still consumes gas. Tachyon pays for that execution and charges the corresponding cost through the x402 payment.

Prerequisites

  • Node.js 18+
  • An EVM private key
  • USDC and native gas token on Base for the initial Gateway deposit
  • Access to the Tachyon x402 transaction endpoint

GATEWAY_PRIVATE_KEY controls the wallet and its Gateway balance. Keep it in a server-side environment variable or secure key-management system. Never expose it in browser code or commit it to source control.

Step-by-Step Implementation

Install Dependencies

npm install @circle-fin/x402-batching viem

Initialize Circle Gateway

import {
  GatewayClient,
  SupportedChainName,
} from "@circle-fin/x402-batching/client";
import { encodeFunctionData, parseAbi } from "viem";

const client = new GatewayClient({
  chain: "base" as SupportedChainName,
  privateKey: process.env.GATEWAY_PRIVATE_KEY as `0x${string}`,
});

The configured chain is used for Gateway deposits, balance queries, and payments.

Fund the Gateway Balance

The Gateway balance must contain enough USDC to pay for the Tachyon request. Check the balance before depositing so the script does not deposit on every run:

const balances = await client.getBalances();
console.log(
  `Gateway available: ${balances.gateway.formattedAvailable} USDC`
);

const minimumBalance = BigInt(100_000); // 0.1 USDC, 6 decimals

if (balances.gateway.available < minimumBalance) {
  const deposit = await client.deposit("0.1");
  console.log(
    `Deposited ${deposit.formattedAmount} USDC: ${deposit.depositTxHash}`
  );
}

The deposit is an onchain transaction. Wait for it to confirm before relying on the updated Gateway balance.

Build the Tachyon Transaction

const callData = encodeFunctionData({
  abi: parseAbi(["function sayHello(string message)"]),
  functionName: "sayHello",
  args: ["Hello from Tachyon!"],
});

const tx = {
  chainId: 8453,
  to: "0xA7A833e6641D7901F30EaD6f27d4Ee2C9bb670a7",
  callData,
  value: "0",
  gasLimit: "100000",
  label: process.env.TX_LABEL ?? "circle-gateway-nanopayment",
};

This example calls sayHello("Hello from Tachyon!") on Base.

Pay and Submit

Call the Tachyon endpoint through GatewayClient.pay():

const baseUrl =
  process.env.TACHYON_BASE_URL ?? "https://tachyon.rath.fi";
const url = new URL("/api/submit-tx", baseUrl).toString();

const result = await client.pay<SubmitTxResponse>(url, {
  method: "POST",
  body: tx,
});

pay() handles the x402 negotiation, signs the Circle Gateway payment authorization, and returns the Tachyon API response.

Read the Result

if (!result.data.success) {
  throw new Error(
    result.data.error?.message ?? "Tachyon submission failed"
  );
}

console.log(`Tachyon tx id: ${result.data.data?.txId}`);
console.log(
  `Estimated execution cost: $${result.data.data?.estimatedCostUSD}`
);
console.log(`Paid: ${result.formattedAmount} USDC`);
console.log(`Gateway settlement: ${result.transaction}`);

The Tachyon txId identifies the relay task. The Gateway fields describe the USDC payment authorization and settlement.

Complete Example

import {
  GatewayClient,
  SupportedChainName,
} from "@circle-fin/x402-batching/client";
import { encodeFunctionData, parseAbi } from "viem";

type SubmitTxResponse = {
  success: boolean;
  data?: {
    txId: string;
    estimatedCostUSD?: number;
    x402Payment?: {
      amount: string;
      amountUsd: number;
      network: string;
      transaction: string;
    };
  };
  error?: {
    message: string;
  };
};

function readEnv(name: string): string {
  const value = process.env[name];
  if (!value) {
    throw new Error(`Missing environment variable: ${name}`);
  }
  return value;
}

async function main() {
  const baseUrl =
    process.env.TACHYON_BASE_URL ?? "https://tachyon.rath.fi";
  const url = new URL("/api/submit-tx", baseUrl).toString();

  const client = new GatewayClient({
    chain: "base" as SupportedChainName,
    privateKey: readEnv("GATEWAY_PRIVATE_KEY") as `0x${string}`,
  });

  let balances = await client.getBalances();
  console.log(
    `Gateway available: ${balances.gateway.formattedAvailable} USDC`
  );

  const minimumBalance = BigInt(100_000); // 0.1 USDC, 6 decimals

  if (balances.gateway.available < minimumBalance) {
    const deposit = await client.deposit("0.1");
    console.log(
      `Deposited ${deposit.formattedAmount} USDC: ${deposit.depositTxHash}`
    );

    balances = await client.getBalances();
    console.log(
      `Updated Gateway balance: ${balances.gateway.formattedAvailable} USDC`
    );
  }

  const callData = encodeFunctionData({
    abi: parseAbi(["function sayHello(string message)"]),
    functionName: "sayHello",
    args: ["Hello from Tachyon!"],
  });

  const tx = {
    chainId: 8453,
    to: "0xA7A833e6641D7901F30EaD6f27d4Ee2C9bb670a7",
    callData,
    value: "0",
    gasLimit: "100000",
    label: process.env.TX_LABEL ?? "circle-gateway-nanopayment",
  };

  const result = await client.pay<SubmitTxResponse>(url, {
    method: "POST",
    body: tx,
  });

  if (!result.data.success) {
    throw new Error(
      result.data.error?.message ?? "Tachyon submission failed"
    );
  }

  console.log(`Tachyon tx id: ${result.data.data?.txId}`);
  console.log(
    `Estimated execution cost: $${result.data.data?.estimatedCostUSD}`
  );
  console.log(`Paid: ${result.formattedAmount} USDC`);
  console.log(`Gateway settlement: ${result.transaction}`);
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});

Why Use Tachyon with Circle Gateway Nanopayments?

USDC Payments for Transaction Relaying

Applications and AI agents can pay Tachyon transaction relay costs in USDC instead of maintaining a separate native-token payment flow for each API request.

Gasless Per-Request Authorization

After the initial deposit, each buyer payment is authorized with an offchain signature. There is no separate onchain payment transaction for every Tachyon request.

Batched Settlement

Circle Gateway aggregates signed authorizations and settles net positions in batches. This reduces the per-payment settlement overhead and supports high-frequency usage.

Agentic and Usage-Based Payments

The x402 flow is suitable for AI agents, machine-to-machine services, pay-per-request APIs, and other automated systems that need programmatic payments without subscriptions or manual checkout.

Key Considerations

  • The Gateway deposit requires USDC and native gas on the selected chain.
  • Maintain enough available Gateway balance for the expected Tachyon cost.
  • Validate the Tachyon response before treating a request as submitted.
  • Circle Gateway settlement may happen after Tachyon accepts the signed payment authorization.
  • Confirm current Circle-supported networks before selecting a production chain.

On this page