Skip to main content
Circle Gateway allows you to establish a unified USDC balance consisting of USDC stored on multiple source chains. Once established, you can transfer this balance instantly to any destination chain. This guide demonstrates how to establish a unified USDC balance by depositing USDC into the Gateway Wallet contract. You can perform this action on multiple chains to establish a chain-abstracted balance. Select a tab below for EVM or Solana-specific instructions.

Prerequisites

Before you begin, ensure that you’ve:
  • Installed Node.js v22+
  • Prepared an EVM testnet wallet with the private key available
  • Funded your testnet wallet with USDC and native tokens
  • Created a TypeScript project and have viem installed
  • You’ve set up a .env file with the following variables:
    .env
    EVM_PRIVATE_KEY={YOUR_PRIVATE_KEY}
    

Steps

Follow these steps to establish a unified USDC balance on Arc Testnet. You can adapt this example for any supported EVM chain.

Step 1. Approve the Gateway Wallet to transfer USDC from your address

Create a new file called deposit.ts in the root of your project, and add the following code to it. This code calls the approve() method on the USDC contract to allow the Gateway Wallet contract to transfer USDC from your wallet.
deposit.ts
import {
  createPublicClient,
  createWalletClient,
  getContract,
  http,
  erc20Abi,
  formatUnits,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { arcTestnet } from "viem/chains";

/* Constants */
const GATEWAY_WALLET_ADDRESS = "0x0077777d7EBA4688BDeF3E311b846F25870A19B9";
const USDC_ADDRESS = "0x3600000000000000000000000000000000000000";
const DEPOSIT_AMOUNT = 10_000_000n; // 10 USDC (6 decimals)

const gatewayWalletAbi = [
  {
    type: "function",
    name: "deposit",
    inputs: [
      { name: "token", type: "address" },
      { name: "value", type: "uint256" },
    ],
    outputs: [],
    stateMutability: "nonpayable",
  },
] as const;

if (!process.env.EVM_PRIVATE_KEY) throw new Error("EVM_PRIVATE_KEY not set");
const account = privateKeyToAccount(
  process.env.EVM_PRIVATE_KEY as `0x${string}`,
);

// Set up clients
const publicClient = createPublicClient({
  chain: arcTestnet,
  transport: http(),
});

const walletClient = createWalletClient({
  account,
  chain: arcTestnet,
  transport: http(),
});

// Get contract instances
const usdc = getContract({
  address: USDC_ADDRESS,
  abi: erc20Abi,
  client: walletClient,
});

const gatewayWallet = getContract({
  address: GATEWAY_WALLET_ADDRESS,
  abi: gatewayWalletAbi,
  client: walletClient,
});

// Approve Gateway Wallet to spend USDC
console.log(`Approving ${formatUnits(DEPOSIT_AMOUNT, 6)} USDC...`);

const approvalTx = await usdc.write.approve(
  [gatewayWallet.address, DEPOSIT_AMOUNT],
  { account },
);
await publicClient.waitForTransactionReceipt({ hash: approvalTx });
console.log(`Approved: ${approvalTx}`);

Step 2. Call the deposit method on the Gateway Wallet contract

Add the following code to the deposit.ts file to call the deposit() method on the Gateway Wallet contract. Note that you must use the deposit() method and not the standard transfer on the USDC contract.
Warning: Directly transferring USDC to the Gateway Wallet contract with a standard ERC-20 transfer will result in loss of that USDC. You must use one of the deposit methods on the wallet contract to get a unified USDC balance.
deposit.ts
console.log(
  `Depositing ${formatUnits(DEPOSIT_AMOUNT, 6)} USDC to Gateway Wallet...`,
);

const depositTx = await gatewayWallet.write.deposit(
  [usdc.address, DEPOSIT_AMOUNT],
  { account },
);
await publicClient.waitForTransactionReceipt({ hash: depositTx });
console.log(`Deposit tx: ${depositTx}`);

Step 3. Run the script

Run the script with the following command:
npx tsx --env-file=.env deposit.ts

Step 4. Wait for the required number of block confirmations

Wait for the required number of block confirmations. Once the deposit transaction is final, your unified balance reflects the amount from Arc Testnet. If you have balances on other chains, the total balance is the sum of all the USDC from deposit transactions across all supported chains that have reached finality.