Swap ETH for USDC (Uniswap)
Get a live quote from Uniswap, build the swap transaction, sign it with an MPC key, broadcast, and confirm the new USDC balance.
Components used:
GET_UNISWAP_SWAP_QUOTE— fetch best output amount across Uniswap versionsBUILD_EVM_UNISWAP_SWAP_CALLDATA— encode the swap transactionSIGN_WITH_KEY_SHARE— MPC sign the transaction hashBROADCAST_EVM_TRANSACTION— submit to the networkWAIT_FOR_EVM_TRANSACTION— wait for on-chain confirmationGET_EVM_ACCOUNT_BALANCE— verify output balance
Code​
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 KEY_ID = 'your-key-id';
const DERIV = [2147483692, 2147483708, 2147483648, 0, 0]; // m/44'/60'/0'/0/0
const WALLET_ADDR = '0xYourAddress';
// Token addresses (Ethereum mainnet)
const WETH_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
const USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
const SLIPPAGE_BPS = 50; // 0.5% slippage tolerance
async function swapEthForUsdc(amountInWei: string) {
// 1. Get a live swap quote
const { amountOut } = await workspace
.call(ComponentModule.GET_UNISWAP_SWAP_QUOTE, {
jsonRpcUrl: RPC_URL,
tokenIn: WETH_ADDRESS,
tokenOut: USDC_ADDRESS,
amountIn: amountInWei,
})
.promise();
console.log(`Quote: ${amountInWei} wei → ${amountOut} USDC (raw)`);
// 2. Apply slippage: minimum output = quote * (1 - slippage)
const amountOutMinimum = (BigInt(amountOut) * BigInt(10000 - SLIPPAGE_BPS) / 10000n).toString();
// 3. Build and sign the swap transaction
// BUILD_EVM_UNISWAP_SWAP_CALLDATA returns a ready-to-sign unsignedTransaction
const { unsignedTransaction } = await workspace
.call(ComponentModule.BUILD_EVM_UNISWAP_SWAP_CALLDATA, {
jsonRpcUrl: RPC_URL,
chainId: 1,
tokenIn: WETH_ADDRESS,
tokenOut: USDC_ADDRESS,
recipient: WALLET_ADDR,
amountIn: Number(amountInWei),
amountOutMinimum: Number(amountOutMinimum),
})
.promise();
// 4. MPC sign the transaction
// Parse the serializedHash from the unsignedTransaction for signing
const txObj = JSON.parse(unsignedTransaction);
const { signature } = await workspace
.call(ComponentModule.SIGN_WITH_KEY_SHARE, {
keyId: KEY_ID,
derivationPath: DERIV,
messageHash: txObj.serializedHash,
})
.promise();
// 5. Assemble + broadcast
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();
console.log('Swap tx:', transactionHash);
// 6. Wait for confirmation
await workspace
.call(ComponentModule.WAIT_FOR_EVM_TRANSACTION, {
jsonRpcUrl: RPC_URL,
transactionHash,
})
.promise();
// 7. Verify new USDC balance
const { balance } = await workspace
.call(ComponentModule.GET_EVM_ACCOUNT_BALANCE, {
jsonRpcUrl: RPC_URL,
account: WALLET_ADDR,
tokenAddress: USDC_ADDRESS,
})
.promise();
console.log('New USDC balance:', balance);
return { transactionHash, usdcBalance: balance };
}
// Swap 0.1 ETH for USDC
await swapEthForUsdc('100000000000000000');
Workflow canvas layout​
GET_UNISWAP_SWAP_QUOTE
│ amountOut
â–¼
(compute amountOutMinimum)
│
â–¼
BUILD_EVM_UNISWAP_SWAP_CALLDATA
│ unsignedTransaction (includes serializedHash)
│
├──── serializedHash ──▶ SIGN_WITH_KEY_SHARE
│ │ signature
│ │
└────── unsignedTransaction ──▶ SIGN_EVM_TRANSACTION
│ signedTransaction
â–¼
BROADCAST_EVM_TRANSACTION
│ transactionHash
â–¼
WAIT_FOR_EVM_TRANSACTION
│
â–¼
GET_EVM_ACCOUNT_BALANCE
Always set slippage protection
amountOutMinimum: 0 exposes the swap to sandwich attacks. Use 0.5–1% slippage based on the live quote from GET_UNISWAP_SWAP_QUOTE.