Circle Internet Financial
Circle Internet Financial Logo

Dec 17, 2025

December 19, 2025

A Practical Guide to Building With Circle Gateway

what you’ll learn

Circle Gateway marks a new era in crosschain liquidity infrastructure. Learn how you can unify liquidity across multiple chains using Circle’s infrastructure.

A Practical Guide to Building With Circle Gateway

Crosschain liquidity management has long been one of the biggest challenges in onchain finance. As developers and institutions expand across multiple ecosystems, managing capital efficiency, settlement risk, and treasury visibility becomes increasingly complex.

Circle Gateway changes that paradigm.

This guide is designed for product and engineering teams who are exploring how to unify liquidity across multiple chains using Circle’s infrastructure. It provides a practical framework for integrating Gateway into your existing systems. You’ll learn when to use it, how it simplifies liquidity operations, and how it unlocks better crosschain user experiences.

After reading this, you’ll have the foundational knowledge to confidently start building with Circle Gateway.

What is Circle Gateway?

While legacy liquidity systems require developers to pre-fund balances on each blockchain, Circle Gateway introduces a unified USDC liquidity layer that can mint and burn on any supported network on demand.

Gateway consolidates your treasury into one single balance — no more parked liquidity or idle capital spread across chains. With a single contract call, you can instantly mint USDC on Arbitrum, Arc, Base, OP Mainnet, Polygon PoS, and more, while maintaining a single balance for reconciliation and accounting.

Its core characteristics include:

  1. Capital efficiency & chain abstraction: Instead of needing to separately hold USDC on every chain you support, you maintain a single unified balance.
  2. Instant crosschain availability: After deposit and finality, transfers via Gateway can execute in < 500 ms removing bridging delays.
  3. Simplicity of integration: A single integration, one set of contracts and API, works across all supported chains.
  4. Non-custodial & safe: Users retain full control of their assets; moving funds requires explicit user authorization, and there's a fallback withdrawal route if off-chain services are down.

When should you build with Circle Gateway?

Building with Gateway requires rethinking how your system handles liquidity, settlement, and capital deployment. It’s best suited for environments where on-chain fragmentation slows operations or ties up capital.

Consider these key scenarios:

Fragmented crosschain liquidity

Use Gateway when your system must transfer USDC across multiple networks without maintaining prefunded balances.

Example: Crosschain funding and settlements that require just-in-time liquidity delivery.

Capital inefficiency in treasury ops

Use Gateway when managing balances across many chains creates idle capital or reconciliation overhead.

Example: Fintech and PSP platforms centralizing liquidity and minting on demand.

Disconnected payment flows

Use Gateway when multichain payment routing, reconciliation, or settlement leads to operational friction.

Example: Payment infrastructure unifying deposits and settlements across supported chains.

Fragmented user liquidity

Use Gateway when your users need access to USDC across ecosystems but your backend lacks unified liquidity control.

Example: Wallets or embedded finance platforms that serve users on multiple networks.

Gateway Integration Foundations

At its core, Circle Gateway consists of three foundational components:

Gateway Wallet Contracts: Non-custodial smart contracts deployed on multiple chains where users deposit USDC.

Gateway Minter Contracts: Smart contracts that receive attestations from the Gateway system and mint USDC on the destination chain.

Gateway System: An offchain system operated by Circle that provides APIs for initiating transfers, observes onchain events, and manages attestations.

Depositing USDC to Gateway 

import { initiateDeveloperControlledWalletsClient } from '@circle-fin/developer-controlled-wallets'

// Gateway contract addresses on Arc Testnet
const GATEWAY_WALLET_ADDRESS = '0x0077777d7EBA4688BDeF3E311b846F25870A19B9'
const USDC_TOKEN_ADDRESS = '0x3600000000000000000000000000000000000000'

async function depositToGateway(walletId: string, amount: string) {
  // Initialize Circle client
  const client = initiateDeveloperControlledWalletsClient({
    apiKey: process.env.CIRCLE_API_KEY!,
    entitySecret: process.env.CIRCLE_ENTITY_SECRET!,
  })

  // Convert USDC to smallest unit (6 decimals)
  const amountInSmallestUnit = (parseFloat(amount) * 1_000_000).toString()

  // Step 1: Approve Gateway contract to spend USDC
  const approvalTx = await client.createContractExecutionTransaction({
    walletId,
    contractAddress: USDC_TOKEN_ADDRESS,
    abiFunctionSignature: 'approve(address,uint256)',
    abiParameters: [GATEWAY_WALLET_ADDRESS, amountInSmallestUnit],
    fee: { type: 'level', config: { feeLevel: 'MEDIUM' } },
  })

  console.log('Approval transaction:', approvalTx.data?.id)

  // Wait for approval to finalize (5 seconds is typical for testnets)
  await new Promise(resolve => setTimeout(resolve, 5000))

  // Step 2: Deposit USDC into Gateway
  const depositTx = await client.createContractExecutionTransaction({
    walletId,
    contractAddress: GATEWAY_WALLET_ADDRESS,
    abiFunctionSignature: 'deposit(address,uint256)',
    abiParameters: [USDC_TOKEN_ADDRESS, amountInSmallestUnit],
    fee: { type: 'level', config: { feeLevel: 'MEDIUM' } },
  })

  console.log('Deposit transaction:', depositTx.data?.id)
  
  return {
    approvalTxId: approvalTx.data?.id,
    depositTxId: depositTx.data?.id,
  }
}

Constructing a Burn Intent for Gateway Transfers

import { randomBytes } from 'crypto'

// Gateway contract addresses (testnet)
const GATEWAY_WALLET_ADDRESS = '0x0077777d7EBA4688BDeF3E311b846F25870A19B9'
const GATEWAY_MINTER_ADDRESS = '0x0022222ABE238Cc2C7Bb1f21003F0a260052475B'

// USDC addresses per chain
const USDC_ADDRESSES: Readonly<Record<number, string>> = {
  26: '0x3600000000000000000000000000000000000000', // Arc Testnet
  1: '0x5425890298aed601595a70AB815c96711a31Bc65',  // Avalanche Fuji
  6: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',  // Base Sepolia
  0: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',  // Ethereum Sepolia
} 

// Maximum uint256 value for no expiration
const MAX_UINT256 = ((BigInt(1) << BigInt(256)) - BigInt(1)).toString()

function createBurnIntent(
  walletAddress: string,
  sourceDomain: number,
  destinationDomain: number,
  amount: string,
  recipient?: string
) {
  // Convert USDC to atomic units (6 decimals)
  const valueInSmallestUnit = (parseFloat(amount) * 1_000_000).toString()
  
  // Get USDC token addresses for source and destination chains
  const sourceTokenAddress = USDC_ADDRESSES[sourceDomain]
  const destinationTokenAddress = USDC_ADDRESSES[destinationDomain]
  
  return {
    maxBlockHeight: MAX_UINT256,  // No expiration
    maxFee: '20000',               // 0.02 USDC to cover Gateway fees
    spec: {
      version: 1,
      sourceDomain,                // e.g., 26 for Arc Testnet
      destinationDomain,           // e.g., 6 for Base Sepolia
      sourceContract: GATEWAY_WALLET_ADDRESS,
      destinationContract: GATEWAY_MINTER_ADDRESS,
      sourceToken: sourceTokenAddress,
      destinationToken: destinationTokenAddress,
      sourceDepositor: walletAddress,
      destinationRecipient: recipient || walletAddress,
      sourceSigner: walletAddress,
      destinationCaller: '0x0000000000000000000000000000000000000000', // Anyone can mint
      value: valueInSmallestUnit,
      salt: '0x' + randomBytes(32).toString('hex'), // Unique salt prevents replay
      hookData: '0x',              // No hook data
    },
  }
}

Requesting an Attestation from Gateway API

const GATEWAY_API_URL = 'https://gateway-api-testnet.circle.com/v1'

async function requestAttestation(burnIntent: any, signature: string) {
  // Submit signed burn intent to Gateway API
  const response = await fetch(`${GATEWAY_API_URL}/transfer`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify([
      {
        burnIntent: burnIntent,  // Your EIP-712 formatted burn intent
        signature: signature,    // EIP-712 signature from your wallet
      },
    ]),
  })

  if (!response.ok) {
    const errorBody = await response.json()
    throw new Error(errorBody.message || 'Gateway API request failed')
  }

  // Parse the attestation response
  const result = await response.json()
  
  return {
    attestation: result.attestation,  // The attestation bytes
    signature: result.signature,      // Gateway's signature
  }
}

Minting USDC on Destination Chain

import { initiateDeveloperControlledWalletsClient } from '@circle-fin/developer-controlled-wallets'
import { randomBytes } from 'crypto'

// Gateway Minter contract address (same on all chains)
const GATEWAY_MINTER_ADDRESS = '0x0022222ABE238Cc2C7Bb1f21003F0a260052475B'

async function mintOnDestinationChain(
  destinationWalletId: string,
  attestation: string,
  attestationSignature: string
) {
  // Initialize Circle client
  const client = initiateDeveloperControlledWalletsClient({
    apiKey: process.env.CIRCLE_API_KEY!,
    entitySecret: process.env.CIRCLE_ENTITY_SECRET!,
  })

  // Call gatewayMint on the destination chain
  const mintResponse = await client.createContractExecutionTransaction({
    walletId: destinationWalletId,           // Wallet on DESTINATION chain
    contractAddress: GATEWAY_MINTER_ADDRESS, // Gateway Minter contract
    abiFunctionSignature: 'gatewayMint(bytes,bytes)',
    abiParameters: [
      attestation,                           // Attestation from Gateway API
      attestationSignature,                  // Gateway's signature
    ],
    fee: {
      type: 'level',
      config: {
        feeLevel: 'MEDIUM',
      },
    },
    idempotencyKey: randomBytes(16).toString('hex'), // Prevents duplicate txs
  })

  if (!mintResponse.data?.id) {
    throw new Error('Failed to create mint transaction')
  }

  console.log('Mint transaction ID:', mintResponse.data.id)
  
  return {
    transactionId: mintResponse.data.id,
  }
}

Conclusion

Circle Gateway marks a new era in multichain liquidity infrastructure. By consolidating fragmented balances into a unified liquidity layer, it empowers builders to move capital more efficiently, manage treasury operations intelligently, and deliver faster, more consistent user experiences across networks.

Whether you’re building crosschain fintech rails, market infrastructure, or embedded stablecoin apps, Gateway is the foundation for programmable liquidity in onchain — giving you the speed of crypto with the reliability of modern finance.

One balance. One API. One USDC experience everywhere.

Check out our quickstart guide here.

Related posts

How to Build Real-Time Stablecoin FX in Your App with StableFX

How to Build Real-Time Stablecoin FX in Your App with StableFX

December 15, 2025
Time is Money with Circle Gateway

Time is Money with Circle Gateway

December 5, 2025
Calling Smart Contracts with Circle Wallets

Calling Smart Contracts with Circle Wallets

December 1, 2025
Blog
A Practical Guide to Building With Circle Gateway
a-practical-guide-to-building-with-circle-gateway
December 19, 2025
Circle Gateway marks a new era in crosschain liquidity infrastructure. Learn how you can unify liquidity across multiple chains using Circle’s infrastructure.
Developer
No items found.