Send USDC from a Dynamic embedded wallet on EVM chains using the JavaScript SDK.
USDC is an ERC-20 token, so a transfer is a transfer(to, value) call on the USDC contract. The JS SDK exposes a viem WalletClient for any wallet account via createWalletClientForWalletAccount — you read the account from getWalletAccounts (or useGetWalletAccounts in React), encode the call with viem’s erc20Abi, and you’re done.
Follow the JavaScript Quickstart for a vanilla setup, or the React Quickstart (JS SDK) for a Vite + React app with <DynamicProvider> and the EVM extension. The rest of this guide picks up from there.Install viem alongside the SDK packages:
Read the user’s EVM wallet account with getWalletAccounts (or useGetWalletAccounts in React), narrow it with isEvmWalletAccount, then call writeContract against the USDC contract using viem’s built-in erc20Abi.
JavaScript
React
import { getWalletAccounts, getActiveNetworkData } from "@dynamic-labs-sdk/client";import { isEvmWalletAccount } from "@dynamic-labs-sdk/evm";import { createWalletClientForWalletAccount, createPublicClientFromNetworkData,} from "@dynamic-labs-sdk/evm/viem";import { parseUnits, erc20Abi } from "viem";// Base mainnet USDC — swap for the address on your target chain.const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";async function sendUsdc(recipient, amount) { const accounts = getWalletAccounts(); const walletAccount = accounts.find(isEvmWalletAccount); if (!walletAccount) throw new Error("No EVM wallet account found"); const walletClient = await createWalletClientForWalletAccount({ walletAccount }); const hash = await walletClient.writeContract({ address: USDC_ADDRESS, abi: erc20Abi, functionName: "transfer", args: [recipient, parseUnits(amount, 6)], }); const { networkData } = await getActiveNetworkData({ walletAccount }); const publicClient = createPublicClientFromNetworkData({ networkData }); await publicClient.waitForTransactionReceipt({ hash }); return hash;}
Use useGetWalletAccounts to reactively get the wallet, then call createWalletClientForWalletAccount inside the click handler:
createWalletClientForWalletAccount signs on whatever network the wallet is currently on. To target a specific chain, switch the wallet first with switchActiveNetwork — useSwitchActiveNetwork in React.
The sender needs native token (ETH, and so on) to pay gas for the transfer. To pay it for them instead, use EVM gas sponsorship: sendSponsoredTransaction takes the same transfer call as encoded calldata.
Wrap the send call (sendUsdc in the JavaScript tab, handleSend in the React tab) in a try/catch and match on the message to tell the common failures apart:
try { await sendUsdc(recipient, amount);} catch (err: any) { if (err.message?.includes("insufficient funds")) { // Not enough native token for gas, or not enough USDC. } else if (err.message?.includes("User rejected")) { // User dismissed the signing prompt. } else { console.error(err); }}
To catch a failing transfer before the user signs, preview it with simulateEvmTransaction, which returns the expected asset changes plus a security verdict.