Transfer ETH / USDC
A complete send flow: check the wallet's balance, build and sign the transaction with an MPC key share, broadcast it, and wait for confirmation.
Components used:
GET_EVM_ACCOUNT_BALANCE— fetch native or ERC-20 balanceBUILD_EVM_CALLDATA— encode ERC-20transfer()(USDC only)BUILD_EVM_TRANSACTION— construct unsigned tx with gas estimationSIGN_WITH_KEY_SHARE— MPC sign the tx hashSIGN_EVM_TRANSACTION— combine unsigned tx + signatureBROADCAST_EVM_TRANSACTION— submit to the networkWAIT_FOR_EVM_TRANSACTION— wait for on-chain confirmation
Transfer ETH
import { WorkspaceClient, ComponentModule } from 'caller-sdk';
const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });
const RPC_URL = 'https://rpc.ankr.com/eth';
const FROM_ADDR = '0xYourAddress';
const TO_ADDR = '0xRecipientAddress';
const KEY_ID = 'your-key-id';
const DERIV = [2147483692, 2147483708, 2147483648, 0, 0]; // m/44'/60'/0'/0/0
async function transferEth(amountWei: string) {
// 1. Check balance
const { balance } = await workspace
.call(ComponentModule.GET_EVM_ACCOUNT_BALANCE, {
jsonRpcUrl: RPC_URL,
account: FROM_ADDR,
tokenAddress: '0x0000000000000000000000000000000000000000',
})
.promise();
console.log(`Balance: ${balance} wei`);
if (BigInt(balance) < BigInt(amountWei)) throw new Error('Insufficient ETH balance');
// 2. Build unsigned transaction (no calldata for plain ETH transfer)
const { unsignedTransaction, serializedHash } = await workspace
.call(ComponentModule.BUILD_EVM_TRANSACTION, {
jsonRpcUrl: RPC_URL,
from: FROM_ADDR,
to: TO_ADDR,
value: amountWei,
})
.promise();
// 3. MPC sign the transaction hash
const { signature } = await workspace
.call(ComponentModule.SIGN_WITH_KEY_SHARE, {
keyId: KEY_ID,
derivationPath: DERIV,
messageHash: serializedHash,
})
.promise();
// 4. Assemble signed transaction
const { signedTransaction } = await workspace
.call(ComponentModule.SIGN_EVM_TRANSACTION, {
unsignedTransaction,
signature,
})
.promise();
// 5. Broadcast
const { transactionHash } = await workspace
.call(ComponentModule.BROADCAST_EVM_TRANSACTION, {
jsonRpcUrl: RPC_URL,
signedTransaction,
})
.promise();
console.log('Broadcast:', transactionHash);
// 6. Wait for confirmation
await workspace
.call(ComponentModule.WAIT_FOR_EVM_TRANSACTION, {
jsonRpcUrl: RPC_URL,
transactionHash,
})
.promise();
console.log('Confirmed!');
return transactionHash;
}
Transfer USDC (ERC-20)
USDC uses the standard ERC-20 transfer(address,uint256) function. Encode the calldata first, then proceed identically to the ETH flow.
// USDC contract on Ethereum mainnet
const USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
// 6 decimals: 1 USDC = 1_000_000
const USDC_AMOUNT = '5000000'; // 5 USDC
async function transferUsdc() {
// 1. Check USDC balance
const { balance } = await workspace
.call(ComponentModule.GET_EVM_ACCOUNT_BALANCE, {
jsonRpcUrl: RPC_URL,
account: FROM_ADDR,
tokenAddress: USDC_ADDRESS,
})
.promise();
console.log(`USDC balance: ${balance}`);
// 2. Encode ERC-20 transfer() calldata
const { calldata } = await workspace
.call(ComponentModule.BUILD_EVM_CALLDATA, {
functionName: 'transfer',
abi: [{
type: 'function',
name: 'transfer',
inputs: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
outputs: [{ name: '', type: 'bool' }],
stateMutability: 'nonpayable',
}],
args: [TO_ADDR, USDC_AMOUNT],
})
.promise();
// 3. Build transaction — call the USDC contract, value = 0
const { unsignedTransaction, serializedHash } = await workspace
.call(ComponentModule.BUILD_EVM_TRANSACTION, {
jsonRpcUrl: RPC_URL,
from: FROM_ADDR,
to: USDC_ADDRESS, // call the token contract
value: '0',
calldata,
})
.promise();
// 4. MPC sign
const { signature } = await workspace
.call(ComponentModule.SIGN_WITH_KEY_SHARE, {
keyId: KEY_ID,
derivationPath: DERIV,
messageHash: serializedHash,
})
.promise();
// 5. Assemble + broadcast + wait
const { signedTransaction } = await workspace
.call(ComponentModule.SIGN_EVM_TRANSACTION, { unsignedTransaction, signature })
.promise();
const { transactionHash } = await workspace
.call(ComponentModule.BROADCAST_EVM_TRANSACTION, {
jsonRpcUrl: RPC_URL,
signedTransaction,
})
.promise();
await workspace
.call(ComponentModule.WAIT_FOR_EVM_TRANSACTION, {
jsonRpcUrl: RPC_URL,
transactionHash,
})
.promise();
return transactionHash;
}
Workflow canvas layout
GET_EVM_ACCOUNT_BALANCE
│
▼ (balance check — guard condition)
BUILD_EVM_CALLDATA ←─ (USDC only; skip for ETH)
│ calldata
▼
BUILD_EVM_TRANSACTION
│ serializedHash │ unsignedTransaction
▼ │
SIGN_WITH_KEY_SHARE │
│ signature │
└──────────┬──────────────┘
▼
SIGN_EVM_TRANSACTION
│ signedTransaction
▼
BROADCAST_EVM_TRANSACTION
│ transactionHash
▼
WAIT_FOR_EVM_TRANSACTION