Use App Kit to deposit into a Unified Balance and spend from it. In this
quickstart, you’ll write scripts that deposit from Base Sepolia and Solana
Devnet, check your balance, and spend on Arc Testnet.
These are examples only. You can use any of the
supported blockchains and fund the
Unified Balance from as many sources as you need. The scripts use built-in
public RPC URLs, which may be rate-limited or unreliable. For a more stable
connection, you can
configure a custom RPC.
Prerequisites
Before you begin, ensure that you’ve:
- Installed Node.js v22+.
- Created an EVM wallet using a wallet provider such as
MetaMask and added the
Base Sepolia
and
Arc Testnet
networks.
- Created a Solana wallet (for example, Phantom or
Solflare) on Devnet.
- Funded your wallets with testnet tokens:
- Get testnet USDC from the Circle Faucet on
Base Sepolia and Solana Devnet.
- Get testnet ETH on Base Sepolia from a
public faucet (needed for
deposit and spend transactions on Base Sepolia).
- Get SOL for Solana Devnet from the
Solana Faucet.
- Fund the recipient wallet on Arc Testnet if needed (USDC on Arc can cover
gas for the destination credit when you spend on Arc).
- Obtained an Arc Testnet address that will receive USDC when you spend on Arc
Testnet.
Step 1. Set up your project
1.1. Create the project and install dependencies
Create a new directory and install App Kit and its dependencies:
mkdir unified-balance-multichain
cd unified-balance-multichain
npm init -y
npm pkg set type=module
npm install @circle-fin/app-kit @circle-fin/adapter-viem-v2 @circle-fin/adapter-solana viem @solana/web3.js
npm install --save-dev typescript tsx @types/node
Only need a Unified Balance and want a lighter install than the full App Kit?
Install the standalone package instead: @circle-fin/unified-balance-kit
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 a .env file in the project directory:
EVM_PRIVATE_KEY=0xYOUR_EVM_PRIVATE_KEY
SOLANA_PRIVATE_KEY=YOUR_SOLANA_PRIVATE_KEY
EVM_RECIPIENT_ADDRESS=0xYOUR_RECIPIENT_ADDRESS
- Replace
0xYOUR_EVM_PRIVATE_KEY with the private key for the wallet that
holds USDC on Base Sepolia.
- Replace
YOUR_SOLANA_PRIVATE_KEY with the base58 private key for the wallet
that holds USDC on Solana Devnet.
- Replace
0xYOUR_RECIPIENT_ADDRESS with the address that should receive USDC
on Arc Testnet when you spend.
Edit .env files in your IDE or editor so credentials are not leaked to your
shell history.
Step 2. Deposit into a Unified Balance
In this step, you’ll deposit from Base Sepolia and Solana Devnet using two small
scripts. Each script handles one source blockchain only.
2.1. Create the deposit scripts
You can combine both deposits in a single script if you prefer. One main
function can create both adapters and call kit.unifiedBalance.deposit once per
blockchain (await each call in sequence). This example uses two files to keep
each deposit easy to run and verify on its own.
Create Base Sepolia deposit script
Create deposit-base.ts. This script deposits 2.00 USDC from your Base Sepolia
wallet into your Unified Balance:import { AppKit } from "@circle-fin/app-kit";
import { inspect } from "node:util";
import { createViemAdapterFromPrivateKey } from "@circle-fin/adapter-viem-v2";
const DEPOSIT_AMOUNT = "2.00";
const kit = new AppKit();
const adapter = createViemAdapterFromPrivateKey({
privateKey: process.env.EVM_PRIVATE_KEY as `0x${string}`,
});
async function main() {
const result = await kit.unifiedBalance.deposit({
from: { adapter, chain: "Base_Sepolia" },
amount: DEPOSIT_AMOUNT,
token: "USDC",
});
console.log("Result:", inspect(result, false, null, true));
}
void main();
Create Solana Devnet deposit script
Create deposit-solana.ts. This script deposits 1.00 USDC from your Solana
Devnet wallet into your Unified Balance:import { AppKit } from "@circle-fin/app-kit";
import { inspect } from "node:util";
import { createSolanaAdapterFromPrivateKey } from "@circle-fin/adapter-solana";
const DEPOSIT_AMOUNT = "1.00";
const kit = new AppKit();
const adapter = createSolanaAdapterFromPrivateKey({
privateKey: process.env.SOLANA_PRIVATE_KEY as string,
});
async function main() {
const result = await kit.unifiedBalance.deposit({
from: { adapter, chain: "Solana_Devnet" },
amount: DEPOSIT_AMOUNT,
token: "USDC",
});
console.log("Result:", inspect(result, false, null, true));
}
void main();
2.2. Run the deposit scripts
Run the Base Sepolia deposit script
In your terminal, run:npx tsx --env-file=.env deposit-base.ts
You’ll see output like:Result:
{
amount: '2.00',
token: 'USDC',
chain: 'Base_Sepolia',
txHash: '0x...',
explorerUrl: 'https://sepolia.basescan.org/tx/0x...',
...
}
Run the Solana Devnet deposit script
In your terminal, run:npx tsx --env-file=.env deposit-solana.ts
You’ll see output like:Result:
{
amount: '1.00',
token: 'USDC',
chain: 'Solana_Devnet',
txHash: '2k41...',
explorerUrl: 'https://solscan.io/tx/2k41...?cluster=devnet',
...
}
2.3. Verify the deposits
Open the explorerUrl from each deposit result and confirm the onchain
transactions on Base Sepolia and Solana Devnet. When both deposits are
finalized, continue to the next step.
Step 3. Check your Unified Balance
In this step, you query your Unified Balance across the Base Sepolia and Solana
Devnet depositors and print the confirmed and pending amounts.
3.1. Create the balance check script
Create a check-balance.ts file:
import { AppKit } from "@circle-fin/app-kit";
import { inspect } from "node:util";
import { createViemAdapterFromPrivateKey } from "@circle-fin/adapter-viem-v2";
import { createSolanaAdapterFromPrivateKey } from "@circle-fin/adapter-solana";
const kit = new AppKit();
const evmAdapter = createViemAdapterFromPrivateKey({
privateKey: process.env.EVM_PRIVATE_KEY as `0x${string}`,
});
const solanaAdapter = createSolanaAdapterFromPrivateKey({
privateKey: process.env.SOLANA_PRIVATE_KEY as string,
});
async function main() {
const balances = await kit.unifiedBalance.getBalances({
// Both wallets that deposited, one adapter per source
sources: [{ adapter: evmAdapter }, { adapter: solanaAdapter }],
networkType: "testnet",
includePending: true,
});
console.log("Result:", inspect(balances, false, null, true));
}
void main();
3.2. Run the balance check script
In your terminal, run:
npx tsx --env-file=.env check-balance.ts
You’ll see output like:
Result:
{
token: 'USDC',
totalConfirmedBalance: '3.00',
totalPendingBalance: '0.00',
breakdown: [
{
depositor: '0x...',
totalConfirmed: '2.00',
totalPending: '0.00',
breakdown: [{ chain: 'Base_Sepolia', confirmedBalance: '2.00', ... }]
},
{
depositor: '...',
totalConfirmed: '1.00',
totalPending: '0.00',
breakdown: [{ chain: 'Solana_Devnet', confirmedBalance: '1.00', ... }]
}
]
}
After a deposit, funds can appear in totalPendingBalance before they are
reflected in totalConfirmedBalance. Wait until the confirmed balance is
sufficient before you spend.
Step 4. Spend from the combined balance
In this step, you spend USDC on Arc Testnet from your Unified Balance.
4.1. Create the spend script
Create a spend.ts file. This script spends 2.50 USDC on Arc Testnet for the
recipient.
App Kit chooses
how much USDC to use from each blockchain.
import { AppKit } from "@circle-fin/app-kit";
import { inspect } from "node:util";
import { createViemAdapterFromPrivateKey } from "@circle-fin/adapter-viem-v2";
import { createSolanaAdapterFromPrivateKey } from "@circle-fin/adapter-solana";
const SPEND_AMOUNT = "2.50";
const kit = new AppKit();
const evmAdapter = createViemAdapterFromPrivateKey({
privateKey: process.env.EVM_PRIVATE_KEY as `0x${string}`,
});
const solanaAdapter = createSolanaAdapterFromPrivateKey({
privateKey: process.env.SOLANA_PRIVATE_KEY as string,
});
async function main() {
const recipientAddress = process.env.EVM_RECIPIENT_ADDRESS as string;
console.log(
`Spending ${SPEND_AMOUNT} USDC on Arc_Testnet for ${recipientAddress}...\n`,
);
const result = await kit.unifiedBalance.spend({
amount: SPEND_AMOUNT,
token: "USDC",
from: [{ adapter: evmAdapter }, { adapter: solanaAdapter }],
to: {
adapter: evmAdapter,
chain: "Arc_Testnet",
recipientAddress,
},
});
console.log("Result:", inspect(result, false, null, true));
}
void main();
4.2. Run the spend script
In your terminal, run:
npx tsx --env-file=.env spend.ts
When the script completes, you should see output similar to:
Spending 2.50 USDC on Arc_Testnet for 0x...
Result:
{ recipientAddress: '0x...', destinationChain: 'Arc Testnet', txHash: '0x...', ... }
4.3. Verify the spend
Use the explorerUrl from the spend result to confirm that USDC arrived at the
recipient address on Arc Testnet. The received amount can be less than the
requested spend after fees. For more on fees, see
How Unified Balance fees work.