Skip to main content
The CDP SDK provides convenient methods for transferring native tokens (ETH, SOL) and fungible tokens (ERC-20, SPL) across supported networks.

EVM Token Transfers

Basic ETH Transfer

Transfer ETH using the high-level transfer method:
import { CdpClient } from "@coinbase/cdp-sdk";

const cdp = new CdpClient();
const sender = await cdp.evm.getOrCreateAccount({ name: "Sender" });

const result = await sender.transfer({
  to: "0x1234567890123456789012345678901234567890",
  amount: 1000000000000000n, // 0.001 ETH in wei
  token: "eth",
  network: "base-sepolia"
});

console.log(`Transaction hash: ${result.transactionHash}`);

ERC-20 Token Transfer

Transfer ERC-20 tokens (like USDC, USDT, DAI):
import { parseUnits } from "viem";

// Transfer 10 USDC (6 decimals)
const result = await sender.transfer({
  to: "0x9F663335Cd6Ad02a37B633602E98866CF944124d",
  amount: 10000000n, // 10 USDC in smallest unit
  token: "usdc",
  network: "base-sepolia"
});

// Or use parseUnits helper
const amount = parseUnits("10", 6); // 6 decimals for USDC
const result = await sender.transfer({
  to: "0x9F663335Cd6Ad02a37B633602E98866CF944124d",
  amount: amount,
  token: "usdc",
  network: "base-sepolia"
});
Token Decimals Guide:
  • ETH, WETH: 18 decimals
  • USDC, USDT: 6 decimals
  • DAI: 18 decimals
Always verify the token’s decimals before transferring!

Transfer to Another Account

You can transfer directly to another CDP account:
const sender = await cdp.evm.getOrCreateAccount({ name: "Sender" });
const receiver = await cdp.evm.getOrCreateAccount({ name: "Receiver" });

const result = await sender.transfer({
  to: receiver, // Pass the account object directly
  amount: 10000n, // 0.01 USDC
  token: "usdc",
  network: "base-sepolia"
});

Smart Account Transfers

Smart accounts can transfer tokens via user operations, optionally with gas sponsorship:
const owner = await cdp.evm.createAccount();
const smartAccount = await cdp.evm.createSmartAccount({ owner });

// Transfer with optional paymaster
const result = await smartAccount.transfer({
  to: "0x1234...",
  amount: 10000000n, // 10 USDC
  token: "usdc",
  network: "base-sepolia",
  paymasterUrl: "https://paymaster.example.com" // Optional
});

console.log(`User operation hash: ${result.userOpHash}`);

// Wait for confirmation
const confirmed = await smartAccount.waitForUserOperation(
  result.userOpHash
);
console.log(`Transaction hash: ${confirmed.transactionHash}`);

Solana Token Transfers

SOL Transfer

Transfer native SOL tokens:
import { CdpClient } from "@coinbase/cdp-sdk";

const cdp = new CdpClient();
const sender = await cdp.solana.getOrCreateAccount({ 
  name: "SolanaSender" 
});

const signature = await sender.transfer({
  to: "9aE4FvpEqMj3K4cZKXXCHwDr9FQZyH2yZ8xF5Cz7T3v2",
  amount: 1000000n, // 0.001 SOL (in lamports)
  token: "sol",
  network: "solana-devnet"
});

console.log(`Transaction signature: ${signature}`);
Solana Units:
  • 1 SOL = 1,000,000,000 lamports (9 decimals)
  • Always specify amounts in lamports

SPL Token Transfer

Transfer SPL tokens (Solana’s equivalent to ERC-20):
// Transfer USDC on Solana (6 decimals)
const signature = await sender.transfer({
  to: "9aE4FvpEqMj3K4cZKXXCHwDr9FQZyH2yZ8xF5Cz7T3v2",
  amount: 1000000n, // 1 USDC
  token: "usdc",
  network: "solana-devnet"
});

Checking Token Balances

Before transferring, check token balances:

EVM Balances

const balances = await account.listTokenBalances({
  network: "base-sepolia"
});

for (const balance of balances.tokenBalances) {
  console.log(
    `${balance.symbol}: ${balance.amount} (${balance.decimals} decimals)`
  );
}

Complete Transfer Example

Here’s a complete example with faucet funding and confirmation:
import { CdpClient } from "@coinbase/cdp-sdk";
import { createPublicClient, http } from "viem";
import { baseSepolia } from "viem/chains";

const cdp = new CdpClient();
const sender = await cdp.evm.getOrCreateAccount({ name: "Sender" });
const receiver = await cdp.evm.getOrCreateAccount({ name: "Receiver" });

// Fund sender with testnet tokens
console.log("Requesting USDC and ETH from faucet...");
const [usdcFaucet, ethFaucet] = await Promise.all([
  cdp.evm.requestFaucet({
    address: sender.address,
    network: "base-sepolia",
    token: "usdc"
  }),
  cdp.evm.requestFaucet({
    address: sender.address,
    network: "base-sepolia",
    token: "eth"
  })
]);

// Wait for faucet transactions
const publicClient = createPublicClient({
  chain: baseSepolia,
  transport: http()
});

await Promise.all([
  publicClient.waitForTransactionReceipt({
    hash: usdcFaucet.transactionHash
  }),
  publicClient.waitForTransactionReceipt({
    hash: ethFaucet.transactionHash
  })
]);

console.log("Faucet funds received!");

// Transfer USDC
console.log("Sending 0.01 USDC...");
const result = await sender.transfer({
  to: receiver,
  amount: 10000n, // 0.01 USDC
  token: "usdc",
  network: "base-sepolia"
});

// Wait for confirmation
const receipt = await publicClient.waitForTransactionReceipt({
  hash: result.transactionHash
});

console.log(`Transfer confirmed! Status: ${receipt.status}`);
console.log(
  `Explorer: https://sepolia.basescan.org/tx/${receipt.transactionHash}`
);

Troubleshooting

Insufficient Balance

If the transfer fails due to insufficient balance:
// Check balance first
const balances = await account.listTokenBalances({ 
  network: "base-sepolia" 
});
console.log(balances);

// Request testnet tokens
await cdp.evm.requestFaucet({
  address: account.address,
  network: "base-sepolia",
  token: "usdc"
});

Gas Estimation Failed

Ensure the account has enough native tokens (ETH/SOL) for gas:
await cdp.evm.requestFaucet({
  address: account.address,
  network: "base-sepolia",
  token: "eth" // Always need ETH for gas
});

Transfer to Invalid Address

Always validate addresses before transferring:
import { isAddress } from "viem";

const recipient = "0x1234...";
if (!isAddress(recipient)) {
  throw new Error("Invalid Ethereum address");
}

Token Decimal Confusion

Double-check token decimals:
// WRONG: Sending 1 full USDC (will send 0.000001 USDC)
await account.transfer({
  amount: 1n,
  token: "usdc",
  // ...
});

// CORRECT: Sending 1 full USDC
await account.transfer({
  amount: 1000000n, // 6 decimals
  token: "usdc",
  // ...
});

Next Steps

Build docs developers (and LLMs) love