Native USDC Developer Guide
USDC enables the seamless transfer of digital dollars on the Codex network using a smart contract. This guide will walk you through building a simple React app that allows users to connect their wallet and send USDC transactions over the Codex Testnet.
Prerequisites
Before you begin, ensure you have the following:
Development Environment
- Node.js (16+) installed (comes with npm).
- MetaMask browser extension installed.
Fund Your Wallet
You’ll need two types of tokens to test USDC transfers:
-
Native gas token (ETH on Codex Testnet) to cover transaction fees
- Send Sepolia Testnet ETH to
0xcf4df2bdb14c8fdb25fdaccec10ce5c4baedb3de
- You’ll receive Codex Testnet ETH in return
- Send Sepolia Testnet ETH to
-
USDC tokens for testing
- Use the CCTP V2 Sample App to get testnet USDC from other chains
Project Setup
Step 1: Create a new project
Run the following commands to set up your project:
mkdir usdc-transfer-appcd usdc-transfer-appnpm init -y
Step 2: Install dependencies
Run the following command to install required libraries:
npm install react@latest react-dom@latest @types/react@latest @types/react-dom@latest @vitejs/plugin-react@latest typescript@latest vite@latest viem@latest
Step 3: Ensure package.json is configured
After installing dependencies, open your package.json file, remove
"type" : "commonjs"
and verify that it looks similar to this:
{ "name": "usdc-transfer-app", "version": "1.0.0", "main": "index.js", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "keywords": [], "author": "", "license": "ISC", "description": "", "dependencies": { "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "@vitejs/plugin-react": "^4.4.1", "react": "^19.1.0", "react-dom": "^19.1.0", "typescript": "^5.8.3", "viem": "^2.28.0", "vite": "^6.3.2" }}
Step 4: If your package.json does not have the scripts section
Manually add the following inside package.json:
"scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview"}
This ensures that you can run the project using:
npm run dev
Configure Blockchain Clients
Setting up public and wallet clients
Create a src/clients.ts file and add:
import { http, createPublicClient, createWalletClient, custom, defineChain} from "viem";
declare global { interface Window { ethereum: any; }}
// Custom Codex chain definition with Thirdweb RPCconst codexTestnet = defineChain({ id: 812242, name: "Codex Testnet", nativeCurrency: { decimals: 18, name: "Codex", symbol: "CDX" }, rpcUrls: { default: { http: ["https://812242.rpc.thirdweb.com"] } }, blockExplorers: { default: { name: "Codex Explorer", url: "https://explorer.codex-stg.xyz/" } }, testnet: true});
export const publicClient = createPublicClient({ chain: codexTestnet, transport: http()});
export const walletClient = createWalletClient({ chain: codexTestnet, transport: custom(window.ethereum)});
Define USDC Contract Details
Add this to src/constants.ts:
export const USDC_CONTRACT_ADDRESS = "0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f";
export const USDC_ABI = [ { constant: false, inputs: [ { name: "_to", type: "address" }, { name: "_value", type: "uint256" } ], name: "transfer", outputs: [{ name: "", type: "bool" }], type: "function" }, { constant: true, inputs: [{ name: "_owner", type: "address" }], name: "balanceOf", outputs: [{ name: "balance", type: "uint256" }], type: "function" }];
Implement Wallet Connection & USDC Transfer
Create src/App.tsx
import React, { useState } from "react";import { publicClient, walletClient } from "./clients";import { USDC_CONTRACT_ADDRESS, USDC_ABI } from "./constants";import { type Address, type Hash, type TransactionReceipt } from "viem";
function USDCApp() { const [account, setAccount] = useState<Address>(); const [balance, setBalance] = useState<string>(); const [hash, setHash] = useState<Hash>(); const [receipt, setReceipt] = useState<TransactionReceipt>(); const [recipient, setRecipient] = useState<string>(""); const [amount, setAmount] = useState<string>(""); const [isTransferring, setIsTransferring] = useState(false);
// Fetch USDC balance const fetchBalance = async (address: Address) => { const balance = (await publicClient.readContract({ address: USDC_CONTRACT_ADDRESS, abi: USDC_ABI, functionName: "balanceOf", args: [address] })) as bigint; setBalance((Number(balance) / 10 ** 6).toFixed(2)); };
// Connect Wallet const connect = async () => { const [address] = await walletClient.requestAddresses(); setAccount(address); fetchBalance(address); };
// Transfer USDC const transferUSDC = async (e: React.FormEvent) => { e.preventDefault(); if (!account || !recipient || !amount) return;
try { setIsTransferring(true); const amountInWei = BigInt(Math.floor(Number(amount) * 10 ** 6));
const { request } = await publicClient.simulateContract({ account, address: USDC_CONTRACT_ADDRESS, abi: USDC_ABI, functionName: "transfer", args: [recipient as Address, amountInWei] });
const hash = await walletClient.writeContract(request); setHash(hash);
const receipt = await publicClient.waitForTransactionReceipt({ hash }); setReceipt(receipt);
// Refresh balance after transfer await fetchBalance(account);
// Clear form setRecipient(""); setAmount(""); } catch (error) { console.error("Transfer failed:", error); } finally { setIsTransferring(false); } };
return ( <div> <h1>USDC Transfer Sample App</h1> {account ? ( <> <p> <strong>Connected Wallet:</strong> {account} </p> <p> <strong>USDC Balance:</strong>{" "} {balance ? `${balance} USDC` : "Fetching..."} </p>
<form onSubmit={transferUSDC} style={{ marginTop: "20px" }}> <div style={{ marginBottom: "10px" }}> <label> Recipient Address: <input type="text" value={recipient} onChange={(e) => setRecipient(e.target.value)} placeholder="0x..." style={{ marginLeft: "10px", width: "300px" }} /> </label> </div> <div style={{ marginBottom: "10px" }}> <label> Amount (USDC): <input type="number" value={amount} onChange={(e) => setAmount(e.target.value)} placeholder="0.00" step="0.01" min="0" style={{ marginLeft: "10px" }} /> </label> </div> <button type="submit" disabled={isTransferring}> {isTransferring ? "Transferring..." : "Transfer USDC"} </button> </form>
{hash && ( <div style={{ marginTop: "20px" }}> <p> <strong>Transaction Hash:</strong> {hash} </p> </div> )}
{receipt && ( <div style={{ marginTop: "10px" }}> <p> <strong>Transaction Status:</strong>{" "} {receipt.status === "success" ? "Success" : "Failed"} </p> </div> )} </> ) : ( <button onClick={connect}>Connect Wallet</button> )} </div> );}
export default USDCApp;
Configure Your Entry Point
Create src/main.tsx
import React from "react";import ReactDOM from "react-dom/client";import USDCApp from "./App";
ReactDOM.createRoot(document.getElementById("root")!).render( <React.StrictMode> <USDCApp /> </React.StrictMode>);
Create Your HTML File
Create index.html in your root directory
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>USDC Transfer Sample App</title></head><body> <div id="root"></div> <script type="module" src="/src/main.tsx"></script></body></html>
Start Your Application
Run the following command:
npm run dev
Then open http://localhost:5173 in your browser.