Learn how to consolidate USDC from multiple chains into a unified Gateway balance on Arc using CCTP Fast Transfer and developer-controlled wallets.

Stablecoins are deployed and settled independently on each chain. Multichain applications must coordinate liquidity and reconcile balances across networks. For teams building financial infrastructure, this directly impacts capital efficiency, UX latency, and operational risk.
On Arc (currently in testnet), Circle’s CCTP and Gateway operate as native interoperability primitives. CCTP provides canonical USDC transfer across supported chains, while Gateway consolidates those crosschain flows into a unified USDC balance.
Combined with Arc’s sub-second finality and predictable fees, these primitives allow developers to route liquidity into a single high-speed settlement environment. The result is a unified USDC architecture that eliminates fragmented balances and reduces crosschain overhead by design.
In this blog post, we’ll show how to build this flow end-to-end, bridging USDC into Arc, then depositing into a unified USDC balance on Gateway, which would enable near-instant crosschain payouts on supported chains.
Instant Crosschain Payouts from a Unified USDC Balance
Consider a financial application that accepts USDC deposits from users across multiple networks and needs to pay out recipients on demand. As volume grows, liquidity becomes fragmented. Funds arrive on different chains, treasury balances must be maintained per network, and payout infrastructure needs to be pre-funded to avoid delays. Capital sits idle in multiple places, and operations teams are forced to constantly rebalance.
For products where payout speed matters, like exchanges crediting users, marketplaces releasing seller funds, remittance apps sending stablecoins globally, or fintech platforms settling merchant balances, settlement latency and gas volatility directly affect user experience and margins.
By routing inbound USDC through CCTP Transfer into the organization’s Circle wallet on Arc, liquidity is consolidated into a single unified EVM address. Depositing from Arc into Gateway creates a unified USDC balance within a high-speed, predictable settlement environment. Because deposits on Arc finalize in sub-second time with stable, low fees, this becomes the most cost-efficient path to coordinating liquidity, and one of the fastest paths for USDC arriving from chains with slower finality.
Instead of maintaining fragmented treasuries and absorbing crosschain overhead, the application operates from a single unified balance on Arc. Funds settle quickly into one unified balance, and payouts can be executed instantly across supported networks, without pre-funding multiple wallets or waiting on slower deposit confirmations.
Implementing the Consolidation Pattern
The following implementation demonstrates how to consolidate crosschain USDC into a unified Gateway balance on Arc using CCTP Fast Transfer and Gateway.
Architecture Overview
The consolidation flow consists of two phases:
- Consolidation to Arc: Bridge USDC from multiple supported chains to a single Circle wallet on Arc using CCTP Fast Transfer. This consolidates liquidity into one unified EVM address.
- Gateway Deposit: Deposit the consolidated USDC balance on Arc into Gateway, creating a unified USDC balance that can be accessed for payouts across supported networks.
This pattern is particularly effective for organizations using Circle’s Developer-Controlled Wallets. A single adapter manages custody across chains, eliminating the need to handle multiple private keys or coordinate separate wallet providers.
The script below provides a minimal, reproducible reference implementation of this flow. It demonstrates how to route USDC to Arc and deposit into Gateway using developer-controlled wallets.
In production environments, these steps would typically be integrated into your existing treasury systems and signing stack (such as MPC or custody providers), with monitoring, retry logic, and payout orchestration layered on top.
The script will demonstrate a consolidation of USDC across Avalanche Fuji, Base Sepolia and Ethereum Sepolia to Arc Testnet via CCTP using Bridge Kit, before depositing into a unified Gateway balance.
Testnet wallet creation
With the developer-controlled wallets SDK, you can create a set of wallets with a unified EVM address. This setup simulates the treasury-controlled environment described above, where an application operates across multiple networks but coordinates liquidity through a single address. This will be the DEPOSITOR_ADDRESS used throughout the script.
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const client = initiateDeveloperControlledWalletsClient({
apiKey: "<YOUR_API_KEY>",
entitySecret: "<YOUR_ENTITY_SECRET>",
});
// Create a wallet set
const walletSetResponse = await client.createWalletSet({
name: "Gateway Source Wallets",
});
// Create wallets on supported chains
const walletsResponse = await client.createWallets({
blockchains: ["ARC-TESTNET", "AVAX-FUJI", "BASE-SEPOLIA", "ETH-SEPOLIA"],
count: 1,
walletSetId: walletSetResponse.data?.walletSet?.id ?? "",
metadata: [{ refId: "source-depositor" }],
});
Refer to the Access USDC Crosschain quickstart for step-by-step instructions and a more detailed explanation on how unified EVM addressing works.
You can fund the new wallets from the public faucet or the console faucet from the developer console.
Bridge USDC onto Arc
This section sets up Bridge Kit to consolidate USDC from multiple chains onto Arc Testnet. Bridge Kit abstracts CCTP's complexity into a single kit.bridge() call that handles the full flow: token approval, burning on source chain, fetching Circle's attestation, and minting on destination. It also provides a .on() method for the developer to register event listeners to track each step of the process.
// Initialize Bridge Kit and track all bridge events
const kit = new BridgeKit();
kit.on("*", (payload) => console.log(payload.method, payload.values));
// Set up Circle Wallets adapter for transaction signing
const adapter = createCircleWalletsAdapter({
apiKey: CIRCLE_API_KEY!,
entitySecret: CIRCLE_ENTITY_SECRET!,
});
// Define source chains and amounts to bridge
const sources = [
{ chain: BridgeChain.Ethereum_Sepolia, amount: "USDC_ON_ETH" },
{ chain: BridgeChain.Base_Sepolia, amount: "USDC_ON_BASE" },
{ chain: BridgeChain.Avalanche_Fuji, amount: "USDC_ON_AVAX" },
];
console.log("\n=== Bridging funds to Arc Testnet ===");
// Bridge from each source chain to Arc Testnet
for (const source of sources) {
await kit.bridge({
from: { adapter, chain: source.chain, address: DEPOSITOR_ADDRESS! },
to: { adapter, chain: BridgeChain.Arc_Testnet, address: DEPOSITOR_ADDRESS! },
amount: source.amount,
});
}
console.log("All funds bridged to Arc Testnet");
Deposit into unified USDC balance
This section demonstrates how to deposit the consolidated USDC on Arc into the Gateway Wallet, creating a unified balance. Gateway deposits require two onchain transactions: first, approving the Gateway contract to spend your USDC, then calling the deposit function.
Both transactions use the developer-controlled wallets SDK to execute contract calls on Arc Testnet.
// Initialize Developer Controlled Wallets client for contract execution
const client = initiateDeveloperControlledWalletsClient({
apiKey: CIRCLE_API_KEY!,
entitySecret: CIRCLE_ENTITY_SECRET!,
});
// Step 1: Approve Gateway Wallet to spend USDC
console.log(`Approving ${totalAmount} USDC on Arc Testnet...`);
const approve = await client.createContractExecutionTransaction({
walletAddress: DEPOSITOR_ADDRESS!,
blockchain: "ARC-TESTNET",
contractAddress: ARC_USDC, // USDC token contract
abiFunctionSignature: "approve(address,uint256)",
abiParameters: [GATEWAY_WALLET, amountInSubunits], // Approve Gateway to spend amount
fee: { type: "level", config: { feeLevel: "MEDIUM" } },
});
await waitForTxCompletion(client, approve.data!.id!, "USDC approve");
// Step 2: Deposit USDC into Gateway Wallet
console.log(`Depositing ${totalAmount} USDC to Gateway Wallet`);
const deposit = await client.createContractExecutionTransaction({
walletAddress: DEPOSITOR_ADDRESS!,
blockchain: "ARC-TESTNET",
contractAddress: GATEWAY_WALLET, // Gateway Wallet contract
abiFunctionSignature: "deposit(address,uint256)",
abiParameters: [ARC_USDC, amountInSubunits], // Token address and amount
fee: { type: "level", config: { feeLevel: "MEDIUM" } },
});
await waitForTxCompletion(client, deposit.data!.id!, "Gateway deposit");Full script for reference
Below is the complete script. To run it on your local machine, you'll need Node.js installed with the required dependencies, a .env file configured with your Circle API credentials, and funded testnet wallets. The script uses 2 USDC from each source chain for demonstration. You can adjust the amounts in the sources array to match your wallet balances.
/**
* Setup:
* 1. npm install @circle-fin/bridge-kit @circle-fin/adapter-circle-wallets @circle-fin/developer-controlled-wallets tsx typescript
* 2. Create .env: CIRCLE_API_KEY=... CIRCLE_ENTITY_SECRET=... DEPOSITOR_ADDRESS=0x...
* 3. Run: npx tsx --env-file=.env consolidate.ts
*/
import { BridgeKit, BridgeChain } from "@circle-fin/bridge-kit";
import { createCircleWalletsAdapter } from "@circle-fin/adapter-circle-wallets";
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const CIRCLE_API_KEY = process.env.CIRCLE_API_KEY;
const CIRCLE_ENTITY_SECRET = process.env.CIRCLE_ENTITY_SECRET;
const DEPOSITOR_ADDRESS = process.env.DEPOSITOR_ADDRESS;
if (!CIRCLE_API_KEY || !CIRCLE_ENTITY_SECRET || !DEPOSITOR_ADDRESS) {
console.error(
"Missing required environment variables: CIRCLE_API_KEY, CIRCLE_ENTITY_SECRET, DEPOSITOR_ADDRESS",
);
process.exit(1);
}
const GATEWAY_WALLET = "0x0077777d7EBA4688BDeF3E311b846F25870A19B9";
const ARC_USDC = "0x3600000000000000000000000000000000000000";
// Helper function to poll until transaction reaches terminal state
async function waitForTxCompletion(
client: ReturnType<typeof initiateDeveloperControlledWalletsClient>,
txId: string,
label: string,
) {
process.stdout.write(`Waiting for ${label} (txId=${txId})\n`);
while (true) {
const { data } = await client.getTransaction({ id: txId });
process.stdout.write(".");
if (data?.transaction?.state === "COMPLETE") {
process.stdout.write("\n");
console.log(`${label} final state: ${data.transaction.state}`);
break;
}
await new Promise((r) => setTimeout(r, 3000));
}
}
async function main() {
const kit = new BridgeKit();
kit.on("*", (payload) => console.log(payload.method, payload.values));
const adapter = createCircleWalletsAdapter({
apiKey: CIRCLE_API_KEY!,
entitySecret: CIRCLE_ENTITY_SECRET!,
});
// Step 1: Bridge to Arc
const sources = [
{ chain: BridgeChain.Ethereum_Sepolia, amount: "2" },
{ chain: BridgeChain.Base_Sepolia, amount: "2" },
{ chain: BridgeChain.Avalanche_Fuji, amount: "2" },
];
console.log("\n=== Bridging funds to Arc Testnet ===");
for (const source of sources) {
await kit.bridge({
from: { adapter, chain: source.chain, address: DEPOSITOR_ADDRESS! },
to: {
adapter,
chain: BridgeChain.Arc_Testnet,
address: DEPOSITOR_ADDRESS!,
},
amount: source.amount,
});
}
console.log("All funds bridged to Arc Testnet");
// Step 2: Deposit to Gateway
const totalAmount = sources.reduce(
(sum, source) => sum + parseFloat(source.amount),
0,
);
const amountInSubunits = Math.floor(totalAmount * 1e6).toString(); // Convert to USDC subunits (6 decimals)
console.log("\n=== Depositing to Gateway Wallet ===");
const client = initiateDeveloperControlledWalletsClient({
apiKey: CIRCLE_API_KEY!,
entitySecret: CIRCLE_ENTITY_SECRET!,
});
// Approve Gateway
console.log(`Approving ${totalAmount} USDC on Arc Testnet...`);
const approve = await client.createContractExecutionTransaction({
walletAddress: DEPOSITOR_ADDRESS!,
blockchain: "ARC-TESTNET",
contractAddress: ARC_USDC,
abiFunctionSignature: "approve(address,uint256)",
abiParameters: [GATEWAY_WALLET, amountInSubunits],
fee: { type: "level", config: { feeLevel: "MEDIUM" } },
});
// Wait for approval
await waitForTxCompletion(client, approve.data!.id!, "USDC approve");
// Deposit to Gateway
console.log(`Depositing ${totalAmount} USDC to Gateway Wallet`);
const deposit = await client.createContractExecutionTransaction({
walletAddress: DEPOSITOR_ADDRESS!,
blockchain: "ARC-TESTNET",
contractAddress: GATEWAY_WALLET,
abiFunctionSignature: "deposit(address,uint256)",
abiParameters: [ARC_USDC, amountInSubunits],
fee: { type: "level", config: { feeLevel: "MEDIUM" } },
});
// Wait for deposit
await waitForTxCompletion(client, deposit.data!.id!, "Gateway deposit");
}
main().catch((error) => {
console.error("\nError:", error);
process.exit(1);
});
Crosschain gain
Arc is not designed to compete with other chains. It is built to coordinate liquidity across them. By routing USDC into Arc, consolidating it into a unified balance, and executing payouts outward, developers can abstract away crosschain fragmentation and reduce treasury overhead. The result is a move from multi-chain balance reconciliation to deterministic, high-speed settlement. In doing so, Arc transforms crosschain stablecoin complexity into programmable liquidity infrastructure.
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 refer to the Circle Developer terms of service.
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.
USDC is issued by regulated affiliates of Circle. See Circle’s list of regulatory authorizations.
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.




