transfer_erc20
Send ERC20 tokens to a specified address using a private key on Ethereum and EVM-compatible networks. Specify token contract, recipient, and amount to execute the transfer.
Instructions
Transfer ERC20 tokens to another address
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| amount | Yes | The amount of tokens to send (in token units, e.g., '10' for 10 tokens) | |
| network | No | Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet. | |
| privateKey | Yes | Private key of the sending account (this is used for signing and is never stored) | |
| toAddress | Yes | The recipient address | |
| tokenAddress | Yes | The address of the ERC20 token contract |
Implementation Reference
- src/core/tools.ts:1056-1083 (handler)The MCP tool handler for 'transfer_erc20' that retrieves the configured wallet's private key, calls the transferERC20 service helper, and returns the transaction details including txHash and token info.async ({ tokenAddress, to, amount, network = "ethereum" }) => { try { const privateKey = getConfiguredPrivateKey(); const senderAddress = getWalletAddressFromKey(); const result = await services.transferERC20(tokenAddress as Address, to as Address, amount, privateKey, network); return { content: [{ type: "text", text: JSON.stringify({ network, tokenAddress, from: senderAddress, to, amount: result.amount.formatted, symbol: result.token.symbol, decimals: result.token.decimals, txHash: result.txHash, message: "Transaction sent. Use get_transaction_receipt to check confirmation." }, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error transferring ERC20 tokens: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }
- src/core/tools.ts:1042-1047 (schema)Zod input schema defining parameters for the transfer_erc20 tool: tokenAddress (string), to (string), amount (string), network (optional string).inputSchema: { tokenAddress: z.string().describe("The ERC20 token contract address"), to: z.string().describe("Recipient address or ENS name"), amount: z.string().describe("Amount to send (in token units, accounting for decimals)"), network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") },
- src/core/tools.ts:1038-1055 (registration)Registration of the 'transfer_erc20' tool with the MCP server, including description, input schema, and annotations indicating it's a destructive write operation.server.registerTool( "transfer_erc20", { description: "Transfer ERC20 tokens to an address. Uses the configured wallet.", inputSchema: { tokenAddress: z.string().describe("The ERC20 token contract address"), to: z.string().describe("Recipient address or ENS name"), amount: z.string().describe("Amount to send (in token units, accounting for decimals)"), network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") }, annotations: { title: "Transfer ERC20 Tokens", readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true } },
- Core helper function implementing ERC20 transfer logic: resolves ENS names, fetches token decimals and symbol, parses amount using parseUnits, and executes the transfer transaction via viem's walletClient.writeContract.export async function transferERC20( tokenAddressOrEns: string, toAddressOrEns: string, amount: string, privateKey: string | `0x${string}`, network: string = 'ethereum' ): Promise<{ txHash: Hash; amount: { raw: bigint; formatted: string; }; token: { symbol: string; decimals: number; }; }> { // Resolve ENS names to addresses if needed const tokenAddress = await resolveAddress(tokenAddressOrEns, network) as Address; const toAddress = await resolveAddress(toAddressOrEns, network) as Address; // Ensure the private key has 0x prefix const formattedKey = typeof privateKey === 'string' && !privateKey.startsWith('0x') ? `0x${privateKey}` as `0x${string}` : privateKey as `0x${string}`; // Get token details const publicClient = getPublicClient(network); const contract = getContract({ address: tokenAddress, abi: erc20TransferAbi, client: publicClient, }); // Get token decimals and symbol const decimals = await contract.read.decimals(); const symbol = await contract.read.symbol(); // Parse the amount with the correct number of decimals const rawAmount = parseUnits(amount, decimals); // Create wallet client for sending the transaction const walletClient = getWalletClient(formattedKey, network); // Send the transaction const hash = await walletClient.writeContract({ address: tokenAddress, abi: erc20TransferAbi, functionName: 'transfer', args: [toAddress, rawAmount], account: walletClient.account!, chain: walletClient.chain }); return { txHash: hash, amount: { raw: rawAmount, formatted: amount }, token: { symbol, decimals } }; }