sendTransaction
Execute Ethereum transactions by specifying recipient, amount, and optional details like data or network. Simulate transactions in mock mode for testing without sending actual funds.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| chainId | No | Optional. The chain ID to use. If provided with a named network and they don't match, the RPC's chain ID will be used. | |
| data | No | Optional. The hex data to include in the transaction | |
| mockMode | No | Optional. If true, just simulates the transaction without sending it. Default is false. | |
| provider | No | Optional. Either a network name or custom RPC URL. Use getAllNetworks to see available networks and their details, or getNetwork to get info about a specific network. You can use any network name returned by these tools as a provider value. | |
| to | Yes | The Ethereum address to send to | |
| value | Yes | The amount to send in ether |
Implementation Reference
- src/tools/core.ts:860-950 (handler)Handler function that implements the core logic for the sendTransaction tool. It requires a wallet, supports mock mode for simulation (real sends rejected), serializes BigInts, and returns formatted responses or errors.async ({ to, value, data, mockMode = false, provider, chainId }) => { try { // First check if a wallet exists const wallet = await ethersService.getWallet(provider); if (!wallet) { return { isError: true, content: [{ type: "text", text: "No wallet available to send transaction. Please create or load a wallet first." }] }; } // Helper function to handle BigInt serialization const serializeData = (obj: any): any => { if (obj === null || obj === undefined) return null; if (typeof obj === 'bigint') return obj.toString(); if (typeof obj !== 'object') return obj; if (Array.isArray(obj)) { return obj.map(item => serializeData(item)); } const result: Record<string, any> = {}; for (const [key, value] of Object.entries(obj)) { result[key] = serializeData(value); } return result; }; // Create transaction object const tx = { to, value: ethers.parseEther(value), data: data || "0x" }; if (mockMode) { // Simulate the transaction without actually sending it const ethProvider = await ethersService.getProvider(provider, chainId); const feeData = await ethProvider.getFeeData(); const fromAddress = wallet.address; // Get nonce for the wallet const nonce = await ethProvider.getTransactionCount(fromAddress); const networkInfo = await ethProvider.getNetwork(); // Create a mock transaction response with all BigInt values converted to strings const mockTxResult = serializeData({ hash: `0x${Math.random().toString(16).substring(2).padStart(64, '0')}`, from: fromAddress, to, value: tx.value, nonce, gasLimit: 21000, // Basic ETH transfer gasPrice: feeData.gasPrice, maxFeePerGas: feeData.maxFeePerGas, maxPriorityFeePerGas: feeData.maxPriorityFeePerGas, data: data || "0x", chainId: networkInfo.chainId, type: 2, // EIP-1559 mockTransaction: true }); return { content: [{ type: "text", text: `MOCK TRANSACTION (not sent): \n${JSON.stringify(mockTxResult, null, 2)}` }] }; } // If not in mock mode, we should reject since we don't want to actually send transactions from tests return { isError: true, content: [{ type: "text", text: "Non-mock transactions are not supported in this implementation. Set mockMode: true to simulate transactions." }] }; } catch (error) { return { isError: true, content: [{ type: "text", text: `Error sending transaction: ${error instanceof Error ? error.message : String(error)}` }] }; } }
- src/tools/core.ts:842-859 (schema)Zod schema defining the input parameters for the sendTransaction tool: to (address), value (ether amount), optional data, mockMode, provider, and chainId.{ to: z.string().describe( "The Ethereum address to send to" ), value: z.string().describe( "The amount to send in ether" ), data: z.string().optional().describe( "Optional. The hex data to include in the transaction" ), mockMode: z.boolean().optional().default(false).describe( "Optional. If true, just simulates the transaction without sending it. Default is false." ), provider: z.string().optional().describe(PROVIDER_DESCRIPTION), chainId: z.number().optional().describe( "Optional. The chain ID to use. If provided with a named network and they don't match, the RPC's chain ID will be used." ) },
- src/tools/core.ts:840-951 (registration)Registration of the sendTransaction tool on the MCP server using server.tool(), including inline schema validation and handler function.server.tool( "sendTransaction", { to: z.string().describe( "The Ethereum address to send to" ), value: z.string().describe( "The amount to send in ether" ), data: z.string().optional().describe( "Optional. The hex data to include in the transaction" ), mockMode: z.boolean().optional().default(false).describe( "Optional. If true, just simulates the transaction without sending it. Default is false." ), provider: z.string().optional().describe(PROVIDER_DESCRIPTION), chainId: z.number().optional().describe( "Optional. The chain ID to use. If provided with a named network and they don't match, the RPC's chain ID will be used." ) }, async ({ to, value, data, mockMode = false, provider, chainId }) => { try { // First check if a wallet exists const wallet = await ethersService.getWallet(provider); if (!wallet) { return { isError: true, content: [{ type: "text", text: "No wallet available to send transaction. Please create or load a wallet first." }] }; } // Helper function to handle BigInt serialization const serializeData = (obj: any): any => { if (obj === null || obj === undefined) return null; if (typeof obj === 'bigint') return obj.toString(); if (typeof obj !== 'object') return obj; if (Array.isArray(obj)) { return obj.map(item => serializeData(item)); } const result: Record<string, any> = {}; for (const [key, value] of Object.entries(obj)) { result[key] = serializeData(value); } return result; }; // Create transaction object const tx = { to, value: ethers.parseEther(value), data: data || "0x" }; if (mockMode) { // Simulate the transaction without actually sending it const ethProvider = await ethersService.getProvider(provider, chainId); const feeData = await ethProvider.getFeeData(); const fromAddress = wallet.address; // Get nonce for the wallet const nonce = await ethProvider.getTransactionCount(fromAddress); const networkInfo = await ethProvider.getNetwork(); // Create a mock transaction response with all BigInt values converted to strings const mockTxResult = serializeData({ hash: `0x${Math.random().toString(16).substring(2).padStart(64, '0')}`, from: fromAddress, to, value: tx.value, nonce, gasLimit: 21000, // Basic ETH transfer gasPrice: feeData.gasPrice, maxFeePerGas: feeData.maxFeePerGas, maxPriorityFeePerGas: feeData.maxPriorityFeePerGas, data: data || "0x", chainId: networkInfo.chainId, type: 2, // EIP-1559 mockTransaction: true }); return { content: [{ type: "text", text: `MOCK TRANSACTION (not sent): \n${JSON.stringify(mockTxResult, null, 2)}` }] }; } // If not in mock mode, we should reject since we don't want to actually send transactions from tests return { isError: true, content: [{ type: "text", text: "Non-mock transactions are not supported in this implementation. Set mockMode: true to simulate transactions." }] }; } catch (error) { return { isError: true, content: [{ type: "text", text: `Error sending transaction: ${error instanceof Error ? error.message : String(error)}` }] }; } } );