USDC provides the ability to transfer digital dollars over public blockchains using smart contracts. The USDC smart contract allows you to send, receive, and store digital dollars onchain with a wallet. This quickstart guide provides step-by-step instructions to build a React app that allows users to connect their wallet and send USDC transactions over Sonic Blaze Testnet.
Before you begin, make sure you have:
Node.js
from
Nodejs.org. npm
comes with Node.js
.You will need the following contract address for the USDC token on Sonic Blaze Testnet:
0xA4879Fed32Ecbef99399e5cbC247E533421C4eC6
Perform the following installation and setup steps:
npm
:mkdir usdc-transfer-app
cd usdc-transfer-app
npm init -y
npm install react@latest \
react-dom@latest \
@types/react@latest \
@types/react-dom@latest \
@vitejs/plugin-react@latest \
typescript@latest \
vite@latest \
viem@latest
package.json
file. It should look
like 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"
}
}
If your package.json
file does not contain the scripts section, copy and paste
it from the above code into your file. Test that you can run the project with
the command:
npm run dev
Perform the following steps to import code and set up helpers for the application.
src/client.ts
and copy the following code into it. This
code imports required libraries and sets up the network configuration.import { http, createPublicClient, createWalletClient, custom } from 'viem'
import { sonicBlazeTestnet } from 'viem/chains'
declare global {
interface Window {
ethereum: any
}
}
export const publicClient = createPublicClient({
chain: sonicBlazeTestnet,
transport: http(),
})
export const walletClient = createWalletClient({
chain: sonicBlazeTestnet,
transport: custom(window.ethereum),
})
src/constants.ts
. This file contains the contract address and Application
Binary Interface (ABI) for the USDC token on Sonic Blaze.export const USDC_CONTRACT_ADDRESS =
'0xA4879Fed32Ecbef99399e5cbC247E533421C4eC6'
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',
},
]
The following steps create the main application component and show the steps to connect a wallet and transfer USDC.
src/App.tsx
and copy the following code into it. This
code contains the main application component that allows users to connect
their wallet and transfer USDC.import React, { useEffect, useState } from 'react';
import { publicClient, walletClient } from './clients';
import { USDC_CONTRACT_ADDRESS, USDC_ABI } from './constants';
import { type Address, encodeFunctionData, type Hash, type TransactionReceipt, stringify } 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);
const addressInput = React.createRef<HTMLInputElement>();
const valueInput = React.createRef<HTMLInputElement>();
// 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;
src/main.tsx
and
copying the following code into it. This code sets up the React application
and renders the USDCApp
component.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>
);
index.html
in the root directory and copy the following
code into it. This code sets up the HTML structure for the application.<!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>
npm run dev
You can open http://localhost:5173 in your browser to view the application.