Use this flow to send USDC from a browser wallet. The wallet runs in the
browser, and the user signs transactions in the wallet extension, so connect the
wallet first and call kit.send() only after the wallet is available.This example uses the
Viem adapter to
send USDC on an EVM-compatible blockchain in an existing browser app. The sample
configuration uses Arc Testnet, but you can use any
supported EVM chain and token.Prerequisites
Before you begin, ensure that you have:Step 1. Set up the project
1.1. Create the project and install dependencies
Create a new directory, install the App Kit packages, and add local browser demo
tooling:# Set up your directory and initialize a Node.js project
mkdir app-kit-send-browser-wallet
cd app-kit-send-browser-wallet
npm init -y
npm pkg set type=module
# Install App Kit packages
npm install @circle-fin/app-kit @circle-fin/adapter-viem-v2 viem
# Install TypeScript and a local Vite dev server for the browser demo
npm install --save-dev typescript vite
This step is optional. It helps prevent missing types in your IDE or editor.
Create a tsconfig.json file:Then, update the tsconfig.json file:cat <<'EOF' > tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"types": ["node"]
}
}
EOF
Step 2. Connect a browser wallet
This step shows the core browser wallet integration flow: discover an
EIP-6963 provider, create an App
Kit adapter from the selected provider, and pass that adapter into an App Kit
SDK method.The snippets below keep wallet discovery, wallet connection, and adapter setup
in small helper functions for readability.2.1. Discover a browser wallet with EIP-6963
This pattern is standards-based. The example uses MetaMask as the selected
wallet, but the discovery flow works with any wallet that announces an
EIP-6963 provider.
import type { EIP1193Provider } from "viem";
type EIP6963ProviderInfo = {
uuid: string;
name: string;
icon: string;
rdns: string;
};
type EIP6963ProviderDetail = {
info: EIP6963ProviderInfo;
provider: EIP1193Provider;
};
declare global {
interface WindowEventMap {
"eip6963:announceProvider": CustomEvent<EIP6963ProviderDetail>;
}
}
async function discoverBrowserWallets(): Promise<EIP6963ProviderDetail[]> {
const providers = new Map<string, EIP6963ProviderDetail>();
const handleProviderAnnouncement = (
event: WindowEventMap["eip6963:announceProvider"],
) => {
providers.set(event.detail.info.uuid, event.detail);
};
window.addEventListener(
"eip6963:announceProvider",
handleProviderAnnouncement,
);
window.dispatchEvent(new Event("eip6963:requestProvider"));
await new Promise((resolve) => window.setTimeout(resolve, 250));
window.removeEventListener(
"eip6963:announceProvider",
handleProviderAnnouncement,
);
return [...providers.values()];
}
2.2. Connect the wallet and request account access
After you select a provider, request account access before attempting to bridge.
This should happen in a user-triggered action such as a Connect wallet button.async function connectWallet(provider: EIP1193Provider) {
await provider.request({
method: "eth_requestAccounts",
params: undefined, // Required by the provider type even though this method has no params.
});
const accounts = (await provider.request({
method: "eth_accounts",
params: undefined, // Required by the provider type even though this method has no params.
})) as string[];
return {
connectedAddress: accounts[0] ?? null,
};
}
Keep wallet connection and bridging as separate user actions. This avoids
overlapping wallet permission or chain-switch requests while a previous wallet
prompt is still pending.
2.3. Create a Viem adapter from the selected wallet provider
Use the discovered provider to request account access, then create the App Kit
adapter that signs bridge transactions in the browser:import { createViemAdapterFromProvider } from "@circle-fin/adapter-viem-v2";
async function connectBrowserWallet() {
const providers = await discoverBrowserWallets();
const selectedWallet =
providers.find(
({ info }) => info.rdns === "io.metamask" || info.name === "MetaMask",
) ?? providers[0];
if (!selectedWallet) {
throw new Error("No EIP-6963 browser wallet found");
}
const { connectedAddress } = await connectWallet(selectedWallet.provider);
const adapter = await createViemAdapterFromProvider({
provider: selectedWallet.provider,
});
return {
adapter,
connectedAddress,
walletName: selectedWallet.info.name,
};
}
If multiple EVM wallets are installed, explicitly choose the wallet you want to
use instead of relying on the first announced provider. The browser demo used to
validate this quickstart prefers MetaMask when it is available.
Step 3. Send USDC
3.1. Call kit.send()
This is the only App Kit-specific send call you need after the wallet is
connected:import { AppKit } from "@circle-fin/app-kit";
import type { SendParams } from "@circle-fin/app-kit";
const kit = new AppKit();
async function sendUSDCWithBrowserWallet() {
const { adapter, connectedAddress, walletName } =
await connectBrowserWallet();
const sendParams: SendParams = {
from: { adapter, chain: "Arc_Testnet" },
to: "RECIPIENT_ADDRESS",
amount: "1.00",
token: "USDC",
};
const estimate = await kit.estimateSend(sendParams);
const result = await kit.send(sendParams);
console.log(`Submitted send with ${walletName}`, {
connectedAddress,
estimate,
result,
});
return result;
}
Using another
token or
blockchain? Change the token and
chain values in kit.send() and ensure the connected wallet holds enough
funds to complete the transfer. 3.2. Verify the transaction
After kit.send() resolves, inspect the returned result. Use the transaction
explorer URL to verify the amount and recipient on the blockchain.The following is an example of how the result of a successful send might look in
the browser console. The values are used in this example only and are not a real
transaction:{
name: "transfer",
state: "success",
txHash: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
explorerUrl: "https://testnet.arcscan.app/tx/0x1234567890abcdef...",
}
Send USDC from a developer-controlled wallet using the Circle Wallets adapter.
The example uses Arc Testnet, but you can use any blockchain the Circle Wallets
adapter supports.Prerequisites
Before you begin, ensure that you have:Step 1. Set up the project
1.1. Create the project and install dependencies
Create a new directory and install the App Kit SDK with the Circle Wallets
adapter and supporting tools:# Set up your directory and initialize a Node.js project
mkdir app-kit-send-circle-wallets
cd app-kit-send-circle-wallets
npm init -y
npm pkg set type=module
# Set up module type and start command
npm pkg set scripts.start="tsx --env-file=.env index.ts"
# Install runtime dependencies
npm install @circle-fin/app-kit @circle-fin/adapter-circle-wallets tsx
# Install dev dependencies
npm install --save-dev typescript @types/node
This step is optional. It helps prevent missing types in your IDE or editor.
Create a tsconfig.json file:Then, update the tsconfig.json file:cat <<'EOF' > tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"types": ["node"]
}
}
EOF
1.3. Set environment variables
Create an .env file in the project directory:Add your credentials. Replace YOUR_API_KEY with your Circle Developer API key,
YOUR_ENTITY_SECRET with your entity secret. You can fetch the address from the
Circle Developer Console
or the
list wallets
endpoint:CIRCLE_API_KEY=YOUR_API_KEY
CIRCLE_ENTITY_SECRET=YOUR_ENTITY_SECRET
Edit .env files in your IDE or editor so credentials are not leaked to your
shell history.
Step 2. Send USDC
2.1. Create the script
Create an index.ts file in the project directory and add the following code.
This code sends 1.00 USDC from your Circle Wallets-controlled Arc Testnet wallet
to a recipient on Arc Testnet:Using another
token or
blockchain? Change the token and
chain values in kit.send() and ensure the source wallet has enough funds to
complete the transfer. import { AppKit } from "@circle-fin/app-kit";
import { createCircleWalletsAdapter } from "@circle-fin/adapter-circle-wallets";
import type { SendParams } from "@circle-fin/app-kit";
const kit = new AppKit();
const sourceWalletAddress = "YOUR_SOURCE_WALLET_ADDRESS";
const recipientAddress = "RECIPIENT_ADDRESS";
const adapter = createCircleWalletsAdapter({
apiKey: process.env.CIRCLE_API_KEY!,
entitySecret: process.env.CIRCLE_ENTITY_SECRET!,
});
const sendParams: SendParams = {
from: {
adapter,
chain: "Arc_Testnet",
address: sourceWalletAddress,
},
to: recipientAddress,
amount: "1.00",
token: "USDC",
};
const estimate = await kit.estimateSend(sendParams);
const result = await kit.send(sendParams);
console.dir({ estimate, result }, { depth: null, colors: true });
2.2. Run the script
Save the index.ts file and run the script in your terminal:2.3. Verify the transaction
After the script finishes, inspect the returned result in the terminal output.
Use the transaction explorer URL to verify the amount and recipient on the
blockchain.The following is an example of how the result of a successful send might look in
the terminal output. The values are used in this example only and are not a real
transaction:{
estimate: { gas: 406817n, fee: '8138073262135265', gasPrice: 20004260545n },
result: {
name: 'send',
state: 'success',
txHash: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
explorerUrl: 'https://testnet.arcscan.app/tx/0x1234567890abcdef123456789...'
}
}