USDC

Quickstart: Set up and transfer USDC on XDC

Learn how to check balances and transfer USDC on the XDC Apothem testnet using Viem and Node.js.

This guide shows you how to transfer USDC on the XDC Apothem testnet. You'll write a simple script that checks your balance and sends a test transfer.

Before you begin, make sure you have:

  • Installed Node.js v18+.
  • Prepared an XDC Apothem testnet wallet funded with:
    • Testnet USDC for the transfer.
    • Testnet XDC for gas fees.
  • A private key and a recipient address stored in a .env file.
  1. Initialize a new Node.js project and install dependencies:

    Shell
    npm init -y
    npm pkg set type=module
    npm install viem dotenv
    
  2. In the project root, create a .env file with your private key and recipient address:

    Shell
    PRIVATE_KEY=<YOUR_PRIVATE_KEY>
    RECIPIENT_ADDRESS=0x<RECIPIENT_ADDRESS>
    
  3. Create an index.js file. You'll add code step by step in the following sections.

In index.js, import required modules and define constants for the XDC Apothem testnet and the USDC contract:

JavaScript
import "dotenv/config";
import {
  createPublicClient,
  createWalletClient,
  http,
  formatUnits,
  parseUnits,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { xdcTestnet } from "viem/chains";

const USDC_ADDRESS = "0xb5AB69F7bBada22B28e79C8FFAECe55eF1c771D4";
const USDC_DECIMALS = 6;
const USDC_ABI = [
  {
    name: "balanceOf",
    type: "function",
    stateMutability: "view",
    inputs: [{ name: "account", type: "address" }],
    outputs: [{ name: "", type: "uint256" }],
  },
  {
    name: "transfer",
    type: "function",
    stateMutability: "nonpayable",
    inputs: [
      { name: "to", type: "address" },
      { name: "amount", type: "uint256" },
    ],
    outputs: [{ name: "", type: "bool" }],
  },
];

Next, load your private key and recipient address from .env:

JavaScript
const PRIVATE_KEY_RAW = process.env.PRIVATE_KEY;
const RECIPIENT = process.env.RECIPIENT_ADDRESS;

if (!PRIVATE_KEY_RAW || !RECIPIENT) {
  console.error("Error: Missing PRIVATE_KEY or RECIPIENT_ADDRESS in .env");
  process.exit(1);
}

if (!/^0x[a-fA-F0-9]{40}$/.test(RECIPIENT)) {
  console.error("Error: Recipient address is invalid");
  process.exit(1);
}

const PRIVATE_KEY = PRIVATE_KEY_RAW.startsWith("0x")
  ? PRIVATE_KEY_RAW
  : `0x${PRIVATE_KEY_RAW}`;

Create a public client for reading blockchain data and a wallet client for sending transactions:

JavaScript
const account = privateKeyToAccount(PRIVATE_KEY);

const publicClient = createPublicClient({
  chain: xdcTestnet,
  transport: http(),
});

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

Now, write the main logic to check your balance and send a transfer:

JavaScript
(async () => {
  try {
    // Check balance
    const balance = await publicClient.readContract({
      address: USDC_ADDRESS,
      abi: USDC_ABI,
      functionName: "balanceOf",
      args: [account.address],
    });

    const balanceFormatted = Number(formatUnits(balance, USDC_DECIMALS));
    const amount = 10; // send 10 USDC

    console.log("Sender:", account.address);
    console.log("Recipient:", RECIPIENT);
    console.log("Balance:", balanceFormatted);

    if (amount > balanceFormatted) {
      console.error("Error: Insufficient balance");
      process.exit(1);
    }

    const amountInDecimals = parseUnits(amount.toString(), USDC_DECIMALS);

    // Transfer
    const hash = await walletClient.writeContract({
      address: USDC_ADDRESS,
      abi: USDC_ABI,
      functionName: "transfer",
      args: [RECIPIENT, amountInDecimals],
    });

    console.log("Transfer successful!");
    console.log("Tx hash:", hash);
    console.log("Explorer:", `https://testnet.xdcscan.com/tx/${hash}`);
  } catch (err) {
    console.error("Transfer failed:", err.message || err);
    process.exit(1);
  }
})();

After running the script:

Shell
node index.js

You'll see output similar to the following:

Text
Sender: 0x1A2b...7890
Recipient: 0x9F8f...1234
Balance: 250.0
Transfer successful!
Tx hash: 0xabc123...def456
Explorer: https://testnet.xdcscan.com/tx/0xabc123...def456

To verify the transfer, copy the transaction hash URL from the Explorer: line and open it in your browser. This will take you to the XDC Apothem testnet explorer, where you can view full transaction details.

In this quickstart, you learned how to check balances and transfer USDC on the XDC Apothem testnet using Viem and Node.js. Here are the key points to remember:

  • Testnet only. XDC testnet USDC has no real value.
  • Gas fees. You need a small amount of testnet XDC for gas.
  • Security. Keep your private key in .env. Never commit secrets.
  • Minimal ABI. The script only uses balanceOf and transfer for simplicity.
Did this page help you?
© 2023-2025 Circle Technology Services, LLC. All rights reserved.