Transfer SPL Token (USDC)
Send any SPL token (e.g. USDC) from one Solana wallet to another. Automatically resolves Associated Token Accounts (ATAs) — creates the recipient ATA if needed.
Components used:
GET_SOLANA_ACCOUNT_BALANCE— check SPL token balanceBUILD_SOLANA_TRANSFER_INSTRUCTION— build SPL transfer instruction (resolves ATAs automatically)BUILD_SOLANA_TRANSACTION— construct the unsigned transactionSIGN_WITH_KEY_SHARE— MPC sign (Ed25519)BUILD_SOLANA_SIGNATURE_PAIR_ITEM— wrap the signatureSIGN_SOLANA_TRANSACTION— finalize the signed transactionBROADCAST_SOLANA_TRANSACTION— submit to the network
Code​
import { WorkspaceClient, ComponentModule } from 'caller-sdk';
const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });
const RPC_URL = 'https://api.mainnet-beta.solana.com';
const KEY_ID = 'your-key-id';
const DERIV = [2147483692, 2147484149, 2147483648, 0]; // m/44'/501'/0'/0'
const FROM = 'FROMpubkey...'; // base58 sender address
const TO = 'TOPubkey...'; // base58 recipient address
// USDC on Solana mainnet
const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
async function transferUsdc(rawAmount: string) {
// 1. Check USDC balance (returned as hex string)
const { value } = await workspace
.call(ComponentModule.GET_SOLANA_ACCOUNT_BALANCE, {
jsonRpcUrl: RPC_URL,
account: FROM,
tokenMint: USDC_MINT,
})
.promise();
console.log(`USDC balance: ${BigInt(value)} raw units`);
if (BigInt(value) < BigInt(rawAmount)) throw new Error('Insufficient USDC balance');
// 2. Build SPL transfer instruction
// Automatically resolves ATAs; creates recipient ATA if missing
const { instructions } = await workspace
.call(ComponentModule.BUILD_SOLANA_TRANSFER_INSTRUCTION, {
jsonRpcUrl: RPC_URL,
from: FROM,
to: TO,
amount: rawAmount,
tokenMint: USDC_MINT,
})
.promise();
// 3. Build unsigned transaction (no direct lamport transfer — only instructions)
const { unsignedTransaction, serializedHash } = await workspace
.call(ComponentModule.BUILD_SOLANA_TRANSACTION, {
jsonRpcUrl: RPC_URL,
feePayer: FROM,
from: null,
to: null,
lamports: '0',
instructions,
nonceAccount: null,
})
.promise();
// 4. MPC sign
const { signature } = await workspace
.call(ComponentModule.SIGN_WITH_KEY_SHARE, {
keyId: KEY_ID,
derivationPath: DERIV,
messageHash: serializedHash,
})
.promise();
// 5. Wrap + sign + broadcast
const { item } = await workspace
.call(ComponentModule.BUILD_SOLANA_SIGNATURE_PAIR_ITEM, {
publicKey: FROM,
signature,
})
.promise();
const { signedTransaction } = await workspace
.call(ComponentModule.SIGN_SOLANA_TRANSACTION, {
unsignedTransaction,
signatures: [item],
})
.promise();
const { transactionSignature } = await workspace
.call(ComponentModule.BROADCAST_SOLANA_TRANSACTION, {
jsonRpcUrl: RPC_URL,
signedTransaction,
})
.promise();
console.log('Transaction:', transactionSignature);
return transactionSignature;
}
// Send 5 USDC (USDC has 6 decimals: 5 * 1_000_000 = 5_000_000)
const sig = await transferUsdc('5000000');
Workflow canvas layout​
GET_SOLANA_ACCOUNT_BALANCE (tokenMint = USDC)
│ value (guard check)
â–¼
BUILD_SOLANA_TRANSFER_INSTRUCTION (tokenMint = USDC)
│ instructions[]
â–¼
BUILD_SOLANA_TRANSACTION
│ serializedHash │ unsignedTransaction
▼ │
SIGN_WITH_KEY_SHARE │
│ signature │
▼ │
BUILD_SOLANA_SIGNATURE_PAIR_ITEM │
│ item │
└──────────┬──────────────┘
â–¼
SIGN_SOLANA_TRANSACTION
│ signedTransaction
â–¼
BROADCAST_SOLANA_TRANSACTION
│ transactionSignature
â–¼
[Confirmed]
ATA creation
If the recipient does not yet have a USDC ATA, BUILD_SOLANA_TRANSFER_INSTRUCTION automatically includes an createAssociatedTokenAccount instruction. The sender pays the ATA rent (~0.002 SOL).