swap_tokens
Execute token swaps on SailFish DEX by specifying input/output token addresses, amounts, slippage tolerance, and fees using the sender's private key for secure transactions.
Instructions
Swap tokens on SailFish DEX (token to token)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| amountIn | Yes | Amount of input token to swap | |
| fee | No | Fee tier (100=0.01%, 500=0.05%, 3000=0.3%, 10000=1%) | |
| privateKey | Yes | Private key of the sender wallet | |
| slippagePercentage | No | Slippage tolerance percentage (default: 0.5) | |
| tokenIn | Yes | Address of the input token | |
| tokenOut | Yes | Address of the output token |
Implementation Reference
- src/index.ts:1151-1187 (handler)MCP tool handler for 'swap_tokens': validates input parameters and calls the swapExactTokensForTokens helper function from swap module, returning the transaction result as JSON.case 'swap_tokens': { if (!args.privateKey || typeof args.privateKey !== 'string') { throw new McpError(ErrorCode.InvalidParams, 'Private key is required'); } if (!args.tokenIn || typeof args.tokenIn !== 'string') { throw new McpError(ErrorCode.InvalidParams, 'Input token address is required'); } if (!args.tokenOut || typeof args.tokenOut !== 'string') { throw new McpError(ErrorCode.InvalidParams, 'Output token address is required'); } if (!args.amountIn || typeof args.amountIn !== 'string') { throw new McpError(ErrorCode.InvalidParams, 'Input amount is required'); } const slippagePercentage = typeof args.slippagePercentage === 'number' ? args.slippagePercentage : 0.5; const fee = typeof args.fee === 'number' ? args.fee : 3000; // Default to 0.3% const result = await swap.swapExactTokensForTokens( args.privateKey, args.tokenIn, args.tokenOut, args.amountIn, slippagePercentage ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; }
- src/index.ts:517-549 (registration)Registration of the 'swap_tokens' tool in the MCP server's list of tools, including name, description, and input schema definition.{ name: 'swap_tokens', description: 'Swap tokens on SailFish DEX (token to token)', inputSchema: { type: 'object', properties: { privateKey: { type: 'string', description: 'Private key of the sender wallet', }, tokenIn: { type: 'string', description: 'Address of the input token', }, tokenOut: { type: 'string', description: 'Address of the output token', }, amountIn: { type: 'string', description: 'Amount of input token to swap', }, slippagePercentage: { type: 'number', description: 'Slippage tolerance percentage (default: 0.5)', }, fee: { type: 'number', description: 'Fee tier (100=0.01%, 500=0.05%, 3000=0.3%, 10000=1%)', }, }, required: ['privateKey', 'tokenIn', 'tokenOut', 'amountIn'], },
- src/index.ts:520-549 (schema)Input schema definition for the 'swap_tokens' tool, specifying parameters like privateKey, token addresses, amount, slippage, and fee.inputSchema: { type: 'object', properties: { privateKey: { type: 'string', description: 'Private key of the sender wallet', }, tokenIn: { type: 'string', description: 'Address of the input token', }, tokenOut: { type: 'string', description: 'Address of the output token', }, amountIn: { type: 'string', description: 'Amount of input token to swap', }, slippagePercentage: { type: 'number', description: 'Slippage tolerance percentage (default: 0.5)', }, fee: { type: 'number', description: 'Fee tier (100=0.01%, 500=0.05%, 3000=0.3%, 10000=1%)', }, }, required: ['privateKey', 'tokenIn', 'tokenOut', 'amountIn'], },
- src/swap.ts:801-907 (helper)Core helper function swapExactTokensForTokens that implements the token swap logic: gets quote, approves tokens, executes swap on Uniswap V3 router (direct or multi-hop), returns tx details.export async function swapExactTokensForTokens( privateKey: string, tokenIn: string, tokenOut: string, amountIn: string, slippagePercentage: number = 0.5 // Default 0.5% slippage ): Promise<{ hash: string; from: string; amountIn: string; amountOut: string; tokenIn: string; tokenOut: string; tokenInSymbol: string; tokenOutSymbol: string; route: routes.RouteInfo; }> { try { const provider = blockchain.getProvider(); const wallet = new ethers.Wallet(privateKey, provider); const fromAddress = wallet.address; // Get quote and route information const quote = await getSwapQuote(tokenIn, tokenOut, amountIn, slippagePercentage); const route = quote.route; // Convert amount to token units const amountInWei = ethers.parseUnits(amountIn, quote.tokenInDecimals); const minAmountOut = ethers.getBigInt(quote.minimumAmountOut); // Approve router to spend tokens const tokenContract = new ethers.Contract(tokenIn, ERC20_ABI, wallet); const approveTx = await tokenContract.approve(CONTRACTS.SwapRouter, amountInWei); await approveTx.wait(); // Create swap router contract const swapRouter = new ethers.Contract(CONTRACTS.SwapRouter, SWAP_ROUTER_ABI, wallet); // Execute swap based on route type const deadline = Math.floor(Date.now() / 1000) + 60 * 20; // 20 minutes from now let tx; if (route.type === 'direct') { // Direct route (single hop) const fee = parseInt(route.path[0].feeTier); const swapParams = { tokenIn, tokenOut, fee, recipient: fromAddress, deadline, amountIn: amountInWei, amountOutMinimum: minAmountOut, sqrtPriceLimitX96: 0 // No price limit }; tx = await swapRouter.exactInputSingle(swapParams); } else { // Indirect route (multi-hop) const intermediaryToken = route.intermediaryToken!; // Encode the path for multi-hop swap const path = ethers.solidityPacked( ['address', 'uint24', 'address', 'uint24', 'address'], [ tokenIn, parseInt(route.path[0].feeTier), intermediaryToken.address, parseInt(route.path[1].feeTier), tokenOut ] ); const swapParams = { path, recipient: fromAddress, deadline, amountIn: amountInWei, amountOutMinimum: minAmountOut }; tx = await swapRouter.exactInput(swapParams); } const receipt = await tx.wait(); if (!receipt) { throw new Error('Transaction failed'); } return { hash: tx.hash, from: fromAddress, amountIn, amountOut: quote.formattedMinimumAmountOut, tokenIn, tokenOut, tokenInSymbol: quote.tokenInSymbol, tokenOutSymbol: quote.tokenOutSymbol, route }; } catch (error) { console.error('Error swapping tokens:', error); throw error; } }