The following examples are demonstrations of integrations and should not be
used in production. In production, store sensitive information such as API keys
and private keys in a secrets manager or vault.
This guide covers native USDC on Polygon PoS. Native USDC behaves like any
other ERC-20 token: you can read balances, approve spenders, and transfer
tokens directly onchain.
For cross-chain USDC transfers using Circle’s CCTP, see
USDC Gateway Integration.
| Approach | Description | When to use |
|---|
| Native USDC | Standard ERC-20 contract directly on Polygon PoS | When you only need payments within Polygon |
| Gateway USDC | Circle’s cross-chain system that moves USDC between blockchains (CCTP) | When your users need to send or receive USDC across chains |
Example: Read Balance and Transfer
The following example uses viem to check the USDC balance
and send 1 USDC on Polygon.
// pnpm add viem
import { createPublicClient, createWalletClient, http, parseUnits } from "viem";
import { polygon } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
// Native USDC contract on Polygon PoS (not bridged USDC.e)
const USDC = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359";
const erc20 = [
{ type: "function", name: "decimals", stateMutability: "view", inputs: [], outputs: [{ type: "uint8" }] },
{ type: "function", name: "balanceOf", stateMutability: "view", inputs: [{ type: "address" }], outputs: [{ type: "uint256" }] },
{ type: "function", name: "transfer", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [{ type: "bool" }] },
];
const account = privateKeyToAccount(process.env.PRIV_KEY as `0x${string}`);
const rpc = http(process.env.POLYGON_RPC_URL);
const pub = createPublicClient({ chain: polygon, transport: rpc });
const wallet = createWalletClient({ chain: polygon, transport: rpc, account });
async function main() {
const me = account.address;
const decimals = await pub.readContract({ address: USDC, abi: erc20, functionName: "decimals" });
const bal = await pub.readContract({ address: USDC, abi: erc20, functionName: "balanceOf", args: [me] });
console.log("Balance:", Number(bal) / 10 ** Number(decimals), "USDC");
// Send 1 USDC
const amount = parseUnits("1", Number(decimals));
const hash = await wallet.writeContract({ address: USDC, abi: erc20, functionName: "transfer", args: ["0xRecipient...", amount] });
console.log("tx:", hash);
}
main();