UNPKG

@bbigu0898/fast-swap-mcp

Version:

Fast Swap MCP Server - Supports quickly initiating token buy/sell tasks, querying transaction results, and managing take-profit/stop-loss tasks.

927 lines • 54.8 kB
#!/usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const types_js_1 = require("@modelcontextprotocol/sdk/types.js"); const client_js_1 = require("./client.js"); const types_js_2 = require("./types.js"); class FastSwapMcpServer { server; client; constructor() { this.server = new index_js_1.Server({ name: 'fast-swap-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, }); this.client = new client_js_1.DbotClient(); this.setupToolHandlers(); this.setupErrorHandling(); } setupErrorHandling() { this.server.onerror = (error) => { console.error('[MCP Error]', error); }; process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } /** * Format API error message */ formatApiError(response, operation, request) { let errorText = `āŒ ${operation} failed:\n\n`; // Show error status errorText += `šŸ” Error Status: ${response.err ? 'Failed' : 'Unknown Error'}\n`; // Show response content if (response.res) { errorText += `šŸ“„ API Response: ${JSON.stringify(response.res, null, 2)}\n`; } // Show request parameters (exclude sensitive info) if (request) { const safeRequest = { ...request }; if (safeRequest.walletId) { safeRequest.walletId = safeRequest.walletId.substring(0, 8) + '***'; } errorText += `šŸ“‹ Request Parameters: ${JSON.stringify(safeRequest, null, 2)}\n`; } // Show documentation link errorText += `\nšŸ“š Documentation: ${response.docs || 'https://dbotx.com/docs'}`; return errorText; } /** * Format network error message */ formatNetworkError(error, operation, request) { let errorText = `āŒ ${operation} failed:\n\n`; if (error.response) { // HTTP error response errorText += `🌐 HTTP Status: ${error.response.status} ${error.response.statusText}\n`; errorText += `šŸ“„ Error Response: ${JSON.stringify(error.response.data, null, 2)}\n`; } else if (error.request) { // Network request failed errorText += `šŸ”Œ Network Error: No response received, please check network connection\n`; errorText += `šŸ“” Request Details: ${error.message}\n`; } else { // Other errors errorText += `āš ļø Unknown Error: ${error.message}\n`; } // Show request parameters (exclude sensitive info) if (request) { const safeRequest = { ...request }; if (safeRequest.walletId) { safeRequest.walletId = safeRequest.walletId.substring(0, 8) + '***'; } if (safeRequest.walletIdList) { safeRequest.walletIdList = safeRequest.walletIdList.map((id) => id.substring(0, 8) + '***'); } errorText += `šŸ“‹ Request Parameters: ${JSON.stringify(safeRequest, null, 2)}\n`; } errorText += `\nšŸ’” Suggestions:\n`; errorText += `- Check if API key is correct\n`; errorText += `- Check if network connection is normal\n`; errorText += `- Check if parameter format is correct\n`; errorText += `- Check if wallet ID is valid\n`; return errorText; } setupToolHandlers() { this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => { return { tools: [ { name: 'create_fast_swap', description: 'Create fast buy/sell trading orders, supporting multi-chain trading (solana/ethereum/base/bsc/tron), supporting take profit and stop loss settings for both buy and sell', inputSchema: { type: 'object', properties: { chain: { type: 'string', enum: ['solana', 'ethereum', 'base', 'bsc', 'tron'], description: 'Chain (solana/ethereum/base/bsc/tron), defaults to solana if not specified', default: 'solana', }, pair: { type: 'string', description: 'Token address or trading pair address to buy/sell', }, walletId: { type: 'string', description: 'Wallet ID to use, can be obtained via "Wallet Info API" (optional, will use DBOT_WALLET_ID environment variable if not provided)', }, type: { type: 'string', enum: ['buy', 'sell'], description: 'Transaction type, value is buy or sell', }, amountOrPercent: { type: 'number', description: 'For buy type, fill in buy amount (ETH/SOL/BNB/TRX), for sell type, fill in sell percentage (0.00-1.00), defaults to 0.001', default: 0.001, }, customFeeAndTip: { type: 'boolean', description: '"true" means both priority fee and tip are valid, system will execute transaction according to filled values, "false" means only priority fee is valid in high speed mode, only tip is valid in anti-sandwich mode. Defaults to false', default: false, }, priorityFee: { type: 'string', description: 'Priority fee (SOL), valid for Solana, empty string means using auto priority fee. Defaults to "0.0001"', default: '0.0001', }, gasFeeDelta: { type: 'number', description: 'Additional gas (Gwei), valid for EVM chains. Defaults to 5', default: 5, }, maxFeePerGas: { type: 'number', description: 'Will not trade if base gas exceeds this value (Gwei), valid for EVM chains. Defaults to 100', default: 100, }, jitoEnabled: { type: 'boolean', description: 'Whether to enable anti-sandwich mode (Solana & Ethereum & Bsc). Defaults to true', default: true, }, jitoTip: { type: 'number', description: 'Tip used in anti-sandwich mode (Solana). Defaults to 0.001', default: 0.001, }, maxSlippage: { type: 'number', description: 'Maximum slippage (0.00-1.00), for buy it means difference between actual transaction price and expected price, for sell it means difference between expected price and actual transaction price. Defaults to 0.1', default: 0.1, }, concurrentNodes: { type: 'number', description: 'Number of concurrent nodes (1-3). Defaults to 2', default: 2, }, retries: { type: 'number', description: 'Number of retries after failure (0-10). Defaults to 1', default: 1, }, migrateSellPercent: { type: 'number', description: 'Migration sell percentage (0.00-1.00). Defaults to 1.0', default: 1.0, }, minDevSellPercent: { type: 'number', description: 'Minimum developer sell percentage (0.00-1.00). Defaults to 0.5', default: 0.5, }, devSellPercent: { type: 'number', description: 'Developer sell percentage (0.00-1.00). Defaults to 1.0', default: 1.0, }, stopEarnPercent: { type: 'number', description: 'Take profit percentage (0.00 and above), optional', }, stopLossPercent: { type: 'number', description: 'Stop loss percentage (0.00-1.00), optional', }, stopEarnGroup: { type: 'array', description: 'Take profit group settings, maximum 6 groups, optional', items: { type: 'object', properties: { pricePercent: { type: 'number' }, amountPercent: { type: 'number' }, }, }, maxItems: 6, }, stopLossGroup: { type: 'array', description: 'Stop loss group settings, maximum 6 groups, optional', items: { type: 'object', properties: { pricePercent: { type: 'number' }, amountPercent: { type: 'number' }, }, }, maxItems: 6, }, trailingStopGroup: { type: 'array', description: 'Trailing stop settings, maximum 1 group, optional', items: { type: 'object', properties: { pricePercent: { type: 'number' }, amountPercent: { type: 'number' }, }, }, maxItems: 1, }, pnlOrderExpireDelta: { type: 'number', description: 'Take profit/stop loss order valid duration (milliseconds), maximum value is 432000000. Defaults to 43200000', default: 43200000, }, pnlOrderExpireExecute: { type: 'boolean', description: 'Whether to execute when take profit/stop loss order expires. Defaults to false', default: false, }, pnlOrderUseMidPrice: { type: 'boolean', description: 'Whether take profit/stop loss order uses mid price. Defaults to false', default: false, }, pnlCustomConfigEnabled: { type: 'boolean', description: 'Whether to enable custom take profit/stop loss configuration. Defaults to true', default: true, }, pnlCustomConfig: { type: 'object', description: 'Custom take profit/stop loss configuration, optional', properties: { customFeeAndTip: { type: 'boolean', default: false }, priorityFee: { type: 'string', default: '0.0001' }, gasFeeDelta: { type: 'number', default: 5 }, maxFeePerGas: { type: 'number', default: 100 }, jitoEnabled: { type: 'boolean', default: true }, jitoTip: { type: 'number', default: 0.001 }, maxSlippage: { type: 'number', default: 0.1 }, concurrentNodes: { type: 'number', default: 2 }, retries: { type: 'number', default: 1 }, }, }, }, required: ['pair', 'type'], }, }, { name: 'create_fast_swaps', description: 'Create fast buy/sell trading orders, supporting multi-chain trading (solana/ethereum/base/bsc/tron), supporting take profit and stop loss settings for both buy and sell, using multiple wallets to trade simultaneously', inputSchema: { type: 'object', properties: { chain: { type: 'string', enum: ['solana', 'ethereum', 'base', 'bsc', 'tron'], description: 'Chain (solana/ethereum/base/bsc/tron), defaults to solana if not specified', default: 'solana', }, pair: { type: 'string', description: 'Token address or trading pair address to buy/sell', }, walletIdList: { type: 'array', description: 'List of wallet IDs to use, maximum 5 wallets, can be obtained via "Wallet Info API" (optional, will use DBOT_WALLET_ID environment variable if not provided)', items: { type: 'string' }, maxItems: 5, }, type: { type: 'string', enum: ['buy', 'sell'], description: 'Transaction type, value is buy or sell', }, customFeeAndTip: { type: 'boolean', description: '"true" means both priority fee and tip are valid, system will execute transaction according to filled values, "false" means only priority fee is valid in high speed mode, only tip is valid in anti-sandwich mode. Defaults to false', default: false, }, priorityFee: { type: 'string', description: 'Priority fee (SOL), valid for Solana, empty string means using auto priority fee. Defaults to empty string', default: '', }, gasFeeDelta: { type: 'number', description: 'Additional gas (Gwei), valid for EVM chains. Defaults to 5', default: 5, }, maxFeePerGas: { type: 'number', description: 'Will not trade if base gas exceeds this value (Gwei), valid for EVM chains. Defaults to 100', default: 100, }, jitoEnabled: { type: 'boolean', description: 'Whether to enable anti-sandwich mode (Solana & Ethereum & Bsc). Defaults to false', default: false, }, jitoTip: { type: 'number', description: 'Tip used in anti-sandwich mode (Solana). Defaults to 0.001', default: 0.001, }, maxSlippage: { type: 'number', description: 'Maximum slippage (0.00-1.00), for buy it means difference between actual transaction price and expected price, for sell it means difference between expected price and actual transaction price. Defaults to 0.1', default: 0.1, }, concurrentNodes: { type: 'number', description: 'Number of concurrent nodes (1-3). Defaults to 2', default: 2, }, retries: { type: 'number', description: 'Number of retries after failure (0-10). Defaults to 1', default: 1, }, minAmount: { type: 'number', description: 'Minimum buy amount (ETH/SOL/BNB/TRX), optional', }, maxAmount: { type: 'number', description: 'Maximum buy amount (ETH/SOL/BNB/TRX), optional', }, sellPercent: { type: 'number', description: 'Sell percentage (0.00-1.00). Defaults to 1.0', default: 1.0, }, stopEarnPercent: { type: 'number', description: 'Take profit percentage (0.00 and above), optional', }, stopLossPercent: { type: 'number', description: 'Stop loss percentage (0.00-1.00), optional', }, stopEarnGroup: { type: 'array', description: 'Take profit group settings, maximum 6 groups, optional', items: { type: 'object', properties: { pricePercent: { type: 'number' }, amountPercent: { type: 'number' }, }, }, maxItems: 6, }, stopLossGroup: { type: 'array', description: 'Stop loss group settings, maximum 6 groups, optional', items: { type: 'object', properties: { pricePercent: { type: 'number' }, amountPercent: { type: 'number' }, }, }, maxItems: 6, }, trailingStopGroup: { type: 'array', description: 'Trailing stop settings, maximum 1 group, optional', items: { type: 'object', properties: { pricePercent: { type: 'number' }, amountPercent: { type: 'number' }, }, }, maxItems: 1, }, pnlOrderExpireDelta: { type: 'number', description: 'Take profit/stop loss order valid duration (milliseconds), maximum value is 432000000. Defaults to 43200000', default: 43200000, }, pnlOrderExpireExecute: { type: 'boolean', description: 'Whether to execute when take profit/stop loss order expires. Defaults to false', default: false, }, pnlOrderUseMidPrice: { type: 'boolean', description: 'Whether take profit/stop loss order uses mid price. Defaults to false', default: false, }, pnlCustomConfigEnabled: { type: 'boolean', description: 'Whether to enable custom take profit/stop loss configuration. Defaults to true', default: true, }, pnlCustomConfig: { type: 'object', description: 'Custom take profit/stop loss configuration, optional', properties: { customFeeAndTip: { type: 'boolean', default: false }, priorityFee: { type: 'string', default: '0.0001' }, gasFeeDelta: { type: 'number', default: 5 }, maxFeePerGas: { type: 'number', default: 100 }, jitoEnabled: { type: 'boolean', default: true }, jitoTip: { type: 'number', default: 0.001 }, maxSlippage: { type: 'number', default: 0.1 }, concurrentNodes: { type: 'number', default: 2 }, retries: { type: 'number', default: 1 }, }, }, }, required: ['pair', 'walletIdList', 'type'], }, }, { name: 'get_swap_order_info', description: 'Query fast buy/sell order information', inputSchema: { type: 'object', properties: { ids: { type: 'string', description: 'List of order IDs, multiple IDs separated by commas', }, }, required: ['ids'], }, }, { name: 'get_swap_records', description: 'Get all fast buy/sell records for the user', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Page number', default: 0, }, size: { type: 'number', description: 'Items per page (maximum 20)', default: 10, maximum: 20, }, chain: { type: 'string', enum: ['solana', 'ethereum', 'base', 'bsc', 'tron', ''], description: 'Chain name (defaults to empty, query all chains)', default: '', }, }, }, }, { name: 'swap_tpsl_tasks', description: 'Get all take profit/stop loss tasks created by fast buy/sell. Note: All parameters are optional, you can query the first 20 tasks without any parameters', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Page number', default: 0, }, size: { type: 'number', description: 'Items per page (maximum 20)', default: 10, maximum: 20, }, chain: { type: 'string', enum: ['solana', 'ethereum', 'base', 'bsc', 'tron'], description: 'Chain name (defaults to empty, query all chains)', }, state: { type: 'string', enum: ['init', 'processing', 'done', 'fail', 'expired'], description: 'Task status (defaults to init)', }, sourceId: { type: 'string', description: 'Fast buy/sell record ID', }, token: { type: 'string', description: 'Token address', }, sortBy: { type: 'string', description: 'Sort field', }, sort: { type: 'number', description: 'Sort direction: 1 for ascending, -1 for descending (defaults to -1)', }, }, }, }, { name: 'edit_fastswap_tpsl_order', description: 'Edit take profit/stop loss orders created by fast buy/sell, can modify trigger price, transaction amount, slippage and other parameters. Only generate parameters that user wants to modify and required parameters, do not pass parameters that are not specified or null.', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'Limit order ID' }, enabled: { type: 'boolean', description: 'Task enabled status, true means enabled, false means disabled' }, groupId: { type: 'string', description: 'Group ID, used for order group management' }, triggerPriceUsd: { type: 'string', description: 'Price to trigger buy/sell (USD)' }, triggerDirection: { type: 'string', enum: ['up', 'down'], description: '"down" means execute buy/sell when price is below trigger price, "up" means execute buy/sell when price is above trigger price' }, currencyAmountUI: { type: 'number', description: 'For buy type, fill in buy amount (ETH/SOL/BNB/TRX), for sell type, fill in sell percentage (0.00-1.00)' }, customFeeAndTip: { type: 'boolean', description: '"true" means both priority fee and tip are valid, system will execute transaction according to filled values (null means auto priority fee/auto tip), "false" means only priority fee is valid in high speed mode, only tip is valid in anti-sandwich mode' }, priorityFee: { type: 'string', description: 'Priority fee (SOL), valid for Solana, empty string means using auto priority fee' }, gasFeeDelta: { type: 'number', description: 'Additional gas (Gwei), valid for EVM chains' }, maxFeePerGas: { type: 'number', description: 'Will not trade if base gas exceeds this value (Gwei), valid for EVM chains' }, jitoEnabled: { type: 'boolean', description: '"true" means enable anti-sandwich mode (Solana & Ethereum & Bsc)' }, jitoTip: { type: 'number', description: 'Tip used in anti-sandwich mode (Solana)' }, expireDelta: { type: 'number', description: 'Task valid duration, maximum value is 432000000 (milliseconds)' }, expireExecute: { type: 'boolean', description: '"true" means if task has not triggered when expired, will buy or sell token at real-time price' }, useMidPrice: { type: 'boolean', description: '"true" means enable anti-spike mode, will use mid price within 1 second as trigger price, will try best but cannot 100% guarantee to avoid spikes' }, maxSlippage: { type: 'number', description: 'Maximum slippage (0.00-1.00), for buy it means difference between actual transaction price and expected price, for sell it means difference between expected price and actual transaction price, ratio=1/(1-slippage), 0.5 means accept up to 2x price difference, 1 means no price difference limit' }, concurrentNodes: { type: 'number', description: 'Number of concurrent nodes (1-3)' }, retries: { type: 'number', description: 'Number of retries after failure (0-10)' } }, required: ['id'], }, }, { name: 'enable_fastswap_tpsl_order', description: 'Enable/disable take profit/stop loss orders created by fast buy/sell', inputSchema: { type: 'object', properties: { id: { type: 'string' }, enabled: { type: 'boolean' }, }, required: ['id', 'enabled'], }, }, { name: 'delete_fastswap_tpsl_order', description: 'Delete take profit/stop loss orders created by fast buy/sell', inputSchema: { type: 'object', properties: { id: { type: 'string' }, }, required: ['id'], }, }, ], }; }); this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; switch (name) { case 'create_fast_swap': return this.handleCreateFastSwap(args); case 'create_fast_swaps': return this.handleCreateFastSwaps(args); case 'get_swap_order_info': return this.handleGetSwapOrderInfo(args); case 'get_swap_records': return this.handleGetSwapRecords(args); case 'swap_tpsl_tasks': return this.handleSwapTpslTasks(args); case 'edit_fastswap_tpsl_order': return this.handleEditLimitOrder(args); case 'enable_fastswap_tpsl_order': return this.handleEnableLimitOrder(args); case 'delete_fastswap_tpsl_order': return this.handleDeleteLimitOrder(args); default: throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Tool ${name} not found`); } }); } async handleCreateFastSwap(args) { try { // Get wallet ID from environment variable if (!args.walletId) { args.walletId = process.env.DBOT_WALLET_ID; if (!args.walletId) { throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, 'Please set DBOT_WALLET_ID environment variable'); } } const request = types_js_2.CreateFastSwapRequestSchema.parse(args); const response = await this.client.createFastSwap(request); if (response.err) { return { content: [ { type: 'text', text: this.formatApiError(response, 'Create fast trade order', request), }, ], }; } const orderId = response.res?.id || 'Unknown'; return { content: [ { type: 'text', text: `āœ… Trade order created successfully!\n\nOrder ID: ${orderId}\nChain: ${request.chain}\nTransaction Type: ${request.type}\nToken: ${request.pair}\n\nāš ļø Note: Please immediately use get_swap_order_info tool with order ID to check order status and transaction result.\n\nDocumentation: ${response.docs}`, }, ], }; } catch (error) { if (error instanceof types_js_1.McpError) { throw error; } // Check if it's a network error if (error && typeof error === 'object' && ('response' in error || 'request' in error)) { return { content: [ { type: 'text', text: this.formatNetworkError(error, 'Create fast trade order', args), }, ], }; } throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Parameter validation failed: ${error instanceof Error ? error.message : String(error)}`); } } async handleCreateFastSwaps(args) { try { // Check wallet ID list if (!args.walletIdList || args.walletIdList.length === 0) { const walletId = process.env.DBOT_WALLET_ID; if (!walletId) { throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, 'Please set DBOT_WALLET_ID environment variable'); } args.walletIdList = [walletId]; } const request = types_js_2.CreateFastSwapsRequestSchema.parse(args); const response = await this.client.createFastSwaps(request); if (response.err) { return { content: [ { type: 'text', text: this.formatApiError(response, 'Create batch fast trade orders', request), }, ], }; } const orderIds = response.res.map(r => r.id || 'Unknown').join(', '); return { content: [ { type: 'text', text: `āœ… Batch trade orders created successfully!\n\nOrder ID List: ${orderIds}\nChain: ${request.chain}\nTrade Type: ${request.type}\nToken: ${request.pair}\n\nāš ļø Note: Please use get_swap_order_info tool with order IDs immediately to check order status and transaction results.\n\nDocs: ${response.docs}`, }, ], }; } catch (error) { if (error instanceof types_js_1.McpError) { throw error; } // Check if it's a network error if (error && typeof error === 'object' && ('response' in error || 'request' in error)) { return { content: [ { type: 'text', text: this.formatNetworkError(error, 'Create batch fast trade orders', args), }, ], }; } throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Parameter validation failed: ${error instanceof Error ? error.message : String(error)}`); } } async handleGetSwapOrderInfo(args) { const { ids } = args; if (!ids || typeof ids !== 'string') { throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, 'ids parameter must be a string'); } try { const orderIds = ids.split(',').map((id) => id.trim()).filter(Boolean); const response = await this.client.getSwapOrderInfo(orderIds); if (response.err) { return { content: [ { type: 'text', text: this.formatApiError(response, 'Query order information', { ids: orderIds }), }, ], }; } const orders = response.res || []; let result = `šŸ“Š Order Information Query Results (${orders.length} orders in total):\n\n`; orders.forEach((order, index) => { result += `${index + 1}. Order ID: ${order.id}\n`; result += ` Status: ${order.state}\n`; result += ` Chain: ${order.chain}\n`; result += ` Type: ${order.tradeType}\n`; if (order.txPriceUsd) { result += ` Price: $${order.txPriceUsd}\n`; } if (order.swapHash) { result += ` Transaction Hash: ${order.swapHash}\n`; } if (order.errorCode || order.errorMessage) { result += ` Error: ${order.errorCode} - ${order.errorMessage}\n`; } result += '\n'; }); result += `\nDocs: ${response.docs}`; return { content: [ { type: 'text', text: result, }, ], }; } catch (error) { return { content: [ { type: 'text', text: this.formatNetworkError(error, 'Query order information', args), }, ], }; } } async handleGetSwapRecords(args) { try { const params = { page: args.page ?? 0, size: args.size ?? 10, chain: args.chain ?? '', }; const response = await this.client.getSwapRecords(params); if (response.err) { return { content: [ { type: 'text', text: this.formatApiError(response, 'Query fast buy/sell records', params), }, ], }; } const records = response.res || []; let result = `šŸ“œ Fast Buy/Sell Records (${records.length} records in total):\n\n`; if (records.length === 0) { result += 'No records found.\n'; } else { records.forEach((record, index) => { result += `[${index + 1}] ${new Date(record.createAt).toLocaleString()}\n`; result += ` Order ID: ${record.id}\n`; result += ` Status: ${record.state} | Chain: ${record.chain} | Type: ${record.type}\n`; result += ` Trading Pair: ${record.pair}\n`; if (record.send?.info && record.receive?.info) { const sendToken = record.send.info.symbol; const receiveToken = record.receive.info.symbol; const sendAmount = record.send.amount / Math.pow(10, record.send.info.decimals); const receiveAmount = record.receive.amount / Math.pow(10, record.receive.info.decimals); result += ` Trade: ${sendAmount.toFixed(4)} ${sendToken} -> ${receiveAmount.toFixed(4)} ${receiveToken}\n`; } if (record.errorMessage) { result += ` Error: ${record.errorMessage}\n`; } result += '\n'; }); } result += `\nDocs: ${response.docs}`; return { content: [ { type: 'text', text: result, }, ], }; } catch (error) { return { content: [ { type: 'text', text: this.formatNetworkError(error, 'Query fast buy/sell records', args), }, ], }; } } async handleSwapTpslTasks(args) { try { // Set default parameters const params = { page: args.page ?? 0, size: args.size ?? 20, chain: args.chain ?? '', state: args.state ?? 'init', sourceId: args.sourceId ?? '', token: args.token ?? '', sortBy: args.sortBy ?? '', sort: args.sort ?? -1, }; const response = await this.client.getSwapTpslTasks(params); if (response.err) { return { content: [ { type: 'text', text: this.formatApiError(response, 'Query take profit/stop loss tasks', params), }, ], }; } // API returns task array directly, not a paginated object const tasks = Array.isArray(response.res) ? response.res : []; const totalElements = tasks.length; let result = `šŸ“ˆ Take Profit/Stop Loss Task List (${totalElements} tasks in total, showing ${tasks.length}):\n\n`; tasks.forEach((task, index) => { result += `${index + 1}. Task ID: ${task.id}\n`; result += ` Status: ${task.state} ${task.enabled ? '(Enabled)' : '(Disabled)'}\n`; result += ` Chain: ${task.chain}\n`; result += ` Token: ${task.pair}\n`; result += ` Trade Type: ${task.tradeType}\n`; result += ` Trigger Direction: ${task.triggerDirection === 'up' ? 'When price rises' : 'When price falls'}\n`; result += ` Trigger Price: $${task.triggerPriceUsd}\n`; result += ` Take Profit/Stop Loss Percentage: ${(task.triggerPercent * 100).toFixed(2)}%\n`; if (task.basePriceUsd) { result += ` Buy Price: $${task.basePriceUsd}\n`; } if (task.txPriceUsd) { result += ` Transaction Price: $${task.txPriceUsd}\n`; } if (task.walletName) { result += ` Wallet: ${task.walletName}\n`; } result += ` Source: ${task.source === 'swap_order' ? 'Fast Buy/Sell' : 'Copy Trade'}\n`; if (task.errorCode || task.errorMessage) { result += ` Error: ${task.errorCode} - ${task.errorMessage}\n`; } result +=