@michaleffffff/mcp-trading-server 2.4.0 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server.js +3 -3
- package/dist/services/marketService.js +3 -3
- package/dist/services/poolService.js +1 -3
- package/dist/services/tradeService.js +81 -110
- package/dist/tools/accountInfo.js +2 -3
- package/dist/tools/accountTransfer.js +7 -8
- package/dist/tools/adjustMargin.js +2 -1
- package/dist/tools/cancelAllOrders.js +4 -17
- package/dist/tools/cancelOrder.js +2 -12
- package/dist/tools/checkApproval.js +3 -4
- package/dist/tools/closeAllPositions.js +2 -3
- package/dist/tools/closePosition.js +12 -10
- package/dist/tools/executeTrade.js +20 -15
- package/dist/tools/getAccountVipInfo.js +1 -1
- package/dist/tools/getBaseDetail.js +4 -58
- package/dist/tools/getKline.js +3 -2
- package/dist/tools/getKlineLatestBar.js +2 -1
- package/dist/tools/getMarketList.js +1 -1
- package/dist/tools/getMarketPrice.js +1 -1
- package/dist/tools/getNetworkFee.js +1 -1
- package/dist/tools/getOraclePrice.js +1 -1
- package/dist/tools/getUserTradingFeeRate.js +3 -3
- package/dist/tools/manageLiquidity.js +9 -22
- package/dist/tools/marketInfo.js +4 -4
- package/dist/tools/orderQueries.js +3 -4
- package/dist/tools/poolConfig.js +1 -1
- package/dist/tools/positionHistory.js +3 -4
- package/dist/tools/searchMarket.js +1 -1
- package/dist/tools/setTpSl.js +11 -12
- package/dist/tools/updateOrderTpSl.js +9 -10
- package/package.json +1 -1
- package/dist/utils/schema.js +0 -84
package/dist/server.js
CHANGED
|
@@ -81,7 +81,7 @@ function zodSchemaToJsonSchema(zodSchema) {
|
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
83
|
// ─── MCP Server ───
|
|
84
|
-
const server = new Server({ name: "myx-mcp-trading-server", version: "2.4.
|
|
84
|
+
const server = new Server({ name: "myx-mcp-trading-server", version: "2.4.1" }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
|
|
85
85
|
// List tools
|
|
86
86
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
87
87
|
return {
|
|
@@ -105,7 +105,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
105
105
|
try {
|
|
106
106
|
let validatedArgs = args ?? {};
|
|
107
107
|
if (tool.schema) {
|
|
108
|
-
validatedArgs = z.object(tool.schema).parse(args ?? {});
|
|
108
|
+
validatedArgs = z.object(tool.schema).strict().parse(args ?? {});
|
|
109
109
|
}
|
|
110
110
|
logger.toolExecution(name, validatedArgs);
|
|
111
111
|
const result = await tool.handler(validatedArgs);
|
|
@@ -181,7 +181,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
181
181
|
async function main() {
|
|
182
182
|
const transport = new StdioServerTransport();
|
|
183
183
|
await server.connect(transport);
|
|
184
|
-
logger.info("🚀 MYX Trading MCP Server v2.4.
|
|
184
|
+
logger.info("🚀 MYX Trading MCP Server v2.4.1 running (stdio, pure on-chain, prod ready)");
|
|
185
185
|
}
|
|
186
186
|
main().catch((err) => {
|
|
187
187
|
logger.error("Fatal Server Startup Error", err);
|
|
@@ -14,7 +14,7 @@ export async function getOraclePrice(client, poolId, chainIdOverride) {
|
|
|
14
14
|
}
|
|
15
15
|
export async function searchMarket(client, keyword, limit = 1000) {
|
|
16
16
|
const chainId = getChainId();
|
|
17
|
-
const searchRes = await client.markets.searchMarket({ chainId,
|
|
17
|
+
const searchRes = await client.markets.searchMarket({ chainId, keyword, limit });
|
|
18
18
|
const raw = searchRes;
|
|
19
19
|
let dataList = [];
|
|
20
20
|
if (raw && raw.contractInfo && Array.isArray(raw.contractInfo.list)) {
|
|
@@ -44,7 +44,7 @@ export async function searchMarket(client, keyword, limit = 1000) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
return activeMarkets.map(m => {
|
|
47
|
-
const ticker = tickers.find(t => t.poolId.toLowerCase() === m.poolId.toLowerCase());
|
|
47
|
+
const ticker = tickers.find(t => String(t.poolId).toLowerCase() === String(m.poolId).toLowerCase());
|
|
48
48
|
return {
|
|
49
49
|
symbol: m.baseQuoteSymbol || `${m.baseSymbol}/${m.quoteSymbol}`,
|
|
50
50
|
name: m.symbolName || m.name,
|
|
@@ -76,5 +76,5 @@ export async function getPoolList(client) {
|
|
|
76
76
|
*/
|
|
77
77
|
export async function getPoolLevelConfig(client, poolId, chainIdOverride) {
|
|
78
78
|
const chainId = chainIdOverride ?? getChainId();
|
|
79
|
-
return client.
|
|
79
|
+
return client.markets.getPoolLevelConfig(poolId, chainId);
|
|
80
80
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { pool, quote, base } from "@myx-trade/sdk";
|
|
2
2
|
import { getChainId, resolveClient } from "../auth/resolveClient.js";
|
|
3
|
-
import { ensureUnits } from "../utils/units.js";
|
|
4
3
|
/**
|
|
5
4
|
* 创建合约市场池子
|
|
6
5
|
*/
|
|
@@ -28,8 +27,7 @@ export async function getPoolDetail(poolId, chainIdOverride) {
|
|
|
28
27
|
*/
|
|
29
28
|
export async function getLiquidityInfo(client, poolId, marketPrice, chainIdOverride) {
|
|
30
29
|
const chainId = chainIdOverride ?? getChainId();
|
|
31
|
-
|
|
32
|
-
return client.utils.getLiquidityInfo({ chainId, poolId, marketPrice: price30 });
|
|
30
|
+
return client.utils.getLiquidityInfo({ chainId, poolId, marketPrice });
|
|
33
31
|
}
|
|
34
32
|
/**
|
|
35
33
|
* Quote 池 deposit
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Direction, OrderType, TriggerType } from "@myx-trade/sdk";
|
|
2
2
|
import { parseUnits } from "ethers";
|
|
3
|
-
import { getChainId, getQuoteToken
|
|
3
|
+
import { getChainId, getQuoteToken } from "../auth/resolveClient.js";
|
|
4
4
|
import { ensureUnits } from "../utils/units.js";
|
|
5
5
|
import { normalizeAddress } from "../utils/address.js";
|
|
6
6
|
/**
|
|
@@ -20,115 +20,79 @@ function resolveDirection(direction) {
|
|
|
20
20
|
* 开仓 / 加仓
|
|
21
21
|
*/
|
|
22
22
|
export async function openPosition(client, address, args) {
|
|
23
|
-
const maxTradeAmount = process.env.MAX_TRADE_AMOUNT;
|
|
24
|
-
if (maxTradeAmount && Number(args.collateralAmount) > Number(maxTradeAmount)) {
|
|
25
|
-
throw new Error(`Security Exception: collateralAmount (${args.collateralAmount}) exceeds MAX_TRADE_AMOUNT (${maxTradeAmount}). Update .env if you need to trade larger sizes.`);
|
|
26
|
-
}
|
|
27
23
|
const chainId = getChainId();
|
|
28
|
-
const quoteToken = normalizeAddress(args.quoteToken || getQuoteToken(), "quoteToken");
|
|
29
|
-
const quoteDecimals = args.quoteDecimals ?? getQuoteDecimals();
|
|
30
|
-
const rawLeverage = args.leverage ?? 10;
|
|
31
|
-
if (!Number.isFinite(rawLeverage) || rawLeverage <= 0) {
|
|
32
|
-
throw new Error("leverage must be a positive number.");
|
|
33
|
-
}
|
|
34
|
-
const leverage = Math.floor(rawLeverage);
|
|
35
|
-
let calcPrice30;
|
|
36
|
-
let orderPrice30;
|
|
37
|
-
if (args.price) {
|
|
38
|
-
const limitPrice30 = ensureUnits(args.price, 30, "price");
|
|
39
|
-
calcPrice30 = limitPrice30;
|
|
40
|
-
orderPrice30 = limitPrice30;
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
const oracleData = await client.utils.getOraclePrice(args.poolId, chainId);
|
|
44
|
-
calcPrice30 = ensureUnits(oracleData.price, 30, "oracle price");
|
|
45
|
-
orderPrice30 = calcPrice30;
|
|
46
|
-
console.error(`[Market Order] Fetched oracle price for size math: ${calcPrice30}`);
|
|
47
|
-
}
|
|
48
|
-
// 保证金转为 quote token 精度
|
|
49
|
-
const collateralWei = ensureUnits(args.collateralAmount, quoteDecimals, "collateralAmount");
|
|
50
|
-
// 仓位大小 (size): 在 MYX 这一版中,size 表示 Base Token(WETH)的数量,精度通常为 18
|
|
51
|
-
// 计算公式:size = (collateral * leverage) / price
|
|
52
|
-
// 为了防止浮点溢出,我们可以全部转为 BigInt 计算
|
|
53
|
-
const collateralBig = BigInt(collateralWei);
|
|
54
|
-
const leverageBig = BigInt(leverage);
|
|
55
|
-
const priceBig = BigInt(calcPrice30);
|
|
56
|
-
let size = "0";
|
|
57
|
-
if (priceBig > 0n) {
|
|
58
|
-
// collateral: 6位, price: 30位, 目标size: 18位
|
|
59
|
-
// (collateral * leverage * 10^(18 + 30 - 6)) / price
|
|
60
|
-
const exponent = BigInt(18 + 30 - quoteDecimals);
|
|
61
|
-
size = ((collateralBig * leverageBig * (10n ** exponent)) / priceBig).toString();
|
|
62
|
-
}
|
|
63
24
|
const dir = resolveDirection(args.direction);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
25
|
+
const executionFeeToken = normalizeAddress(args.executionFeeToken, "executionFeeToken");
|
|
26
|
+
// Fetch pool detail to get decimals
|
|
27
|
+
const poolResponse = await client.markets.getMarketDetail({ chainId, poolId: args.poolId });
|
|
28
|
+
const poolData = poolResponse?.data || (poolResponse?.marketId ? poolResponse : null);
|
|
29
|
+
if (!poolData) {
|
|
30
|
+
console.error(`[ERROR] poolResponse for ${args.poolId}:`, JSON.stringify(poolResponse, null, 2));
|
|
31
|
+
throw new Error(`Could not find pool metadata for ID: ${args.poolId}`);
|
|
32
|
+
}
|
|
33
|
+
const baseDecimals = poolData.baseDecimals || 18;
|
|
34
|
+
const quoteDecimals = poolData.quoteDecimals || 6;
|
|
68
35
|
const orderParams = {
|
|
69
36
|
chainId,
|
|
70
37
|
address,
|
|
71
38
|
poolId: args.poolId,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
triggerType: triggerType,
|
|
39
|
+
positionId: args.positionId,
|
|
40
|
+
orderType: args.orderType,
|
|
41
|
+
triggerType: args.triggerType,
|
|
76
42
|
direction: dir,
|
|
77
|
-
collateralAmount:
|
|
78
|
-
size,
|
|
79
|
-
price:
|
|
80
|
-
timeInForce:
|
|
81
|
-
postOnly:
|
|
82
|
-
slippagePct: args.slippagePct
|
|
83
|
-
executionFeeToken
|
|
84
|
-
leverage,
|
|
43
|
+
collateralAmount: ensureUnits(args.collateralAmount, quoteDecimals, "collateralAmount"),
|
|
44
|
+
size: ensureUnits(args.size, baseDecimals, "size"),
|
|
45
|
+
price: ensureUnits(args.price, 30, "price"),
|
|
46
|
+
timeInForce: args.timeInForce,
|
|
47
|
+
postOnly: args.postOnly,
|
|
48
|
+
slippagePct: String(args.slippagePct),
|
|
49
|
+
executionFeeToken,
|
|
50
|
+
leverage: args.leverage,
|
|
85
51
|
};
|
|
86
|
-
|
|
87
|
-
|
|
52
|
+
if (args.tpSize)
|
|
53
|
+
orderParams.tpSize = ensureUnits(args.tpSize, baseDecimals, "tpSize");
|
|
54
|
+
if (args.tpPrice)
|
|
88
55
|
orderParams.tpPrice = ensureUnits(args.tpPrice, 30, "tpPrice");
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (args.slPrice)
|
|
56
|
+
if (args.slSize)
|
|
57
|
+
orderParams.slSize = ensureUnits(args.slSize, baseDecimals, "slSize");
|
|
58
|
+
if (args.slPrice)
|
|
92
59
|
orderParams.slPrice = ensureUnits(args.slPrice, 30, "slPrice");
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
// tradingFee: SDK 第二个参数
|
|
96
|
-
const tradingFeeStr = (args.tradingFee || "0").toString();
|
|
97
|
-
// 获取 marketId,SDK 0.1.267 要求 as 3rd parameter
|
|
98
|
-
const marketDetailRes = await client.markets.getMarketDetail({ chainId, poolId: args.poolId });
|
|
99
|
-
const marketId = marketDetailRes?.marketId || marketDetailRes?.data?.marketId;
|
|
100
|
-
if (!marketId) {
|
|
101
|
-
throw new Error(`Could not find marketId for poolId: ${args.poolId}`);
|
|
102
|
-
}
|
|
103
|
-
return client.order.createIncreaseOrder(orderParams, tradingFeeStr, marketId);
|
|
60
|
+
return client.order.createIncreaseOrder(orderParams, args.tradingFee, args.marketId);
|
|
104
61
|
}
|
|
105
62
|
/**
|
|
106
63
|
* 平仓 / 减仓
|
|
107
64
|
*/
|
|
108
65
|
export async function closePosition(client, address, args) {
|
|
109
66
|
const chainId = getChainId();
|
|
110
|
-
const
|
|
67
|
+
const executionFeeToken = normalizeAddress(args.executionFeeToken, "executionFeeToken");
|
|
111
68
|
if (args.direction === undefined || args.direction === null) {
|
|
112
69
|
throw new Error("direction is required (0=LONG, 1=SHORT), must match position direction.");
|
|
113
70
|
}
|
|
114
|
-
const price30 = ensureUnits(args.price, 30, "price");
|
|
115
|
-
const sizeWei = ensureUnits(args.size, 18, "size");
|
|
116
71
|
const dir = resolveDirection(args.direction);
|
|
72
|
+
// Fetch pool detail to get decimals
|
|
73
|
+
const poolResponse = await client.markets.getMarketDetail({ chainId, poolId: args.poolId });
|
|
74
|
+
const poolData = poolResponse?.data || (poolResponse?.marketId ? poolResponse : null);
|
|
75
|
+
if (!poolData) {
|
|
76
|
+
console.error(`[ERROR] poolResponse for ${args.poolId}:`, JSON.stringify(poolResponse, null, 2));
|
|
77
|
+
throw new Error(`Could not find pool metadata for ID: ${args.poolId}`);
|
|
78
|
+
}
|
|
79
|
+
const baseDecimals = poolData.baseDecimals || 18;
|
|
80
|
+
const quoteDecimals = poolData.quoteDecimals || 6;
|
|
117
81
|
return client.order.createDecreaseOrder({
|
|
118
82
|
chainId,
|
|
119
83
|
address,
|
|
120
84
|
poolId: args.poolId,
|
|
121
85
|
positionId: args.positionId,
|
|
122
|
-
orderType:
|
|
123
|
-
triggerType:
|
|
86
|
+
orderType: args.orderType,
|
|
87
|
+
triggerType: args.triggerType,
|
|
124
88
|
direction: dir,
|
|
125
|
-
collateralAmount: "
|
|
126
|
-
size:
|
|
127
|
-
price:
|
|
128
|
-
timeInForce:
|
|
129
|
-
postOnly:
|
|
130
|
-
slippagePct: args.slippagePct
|
|
131
|
-
executionFeeToken
|
|
89
|
+
collateralAmount: ensureUnits(args.collateralAmount, quoteDecimals, "collateralAmount"),
|
|
90
|
+
size: ensureUnits(args.size, baseDecimals, "size"),
|
|
91
|
+
price: ensureUnits(args.price, 30, "price"),
|
|
92
|
+
timeInForce: args.timeInForce,
|
|
93
|
+
postOnly: args.postOnly,
|
|
94
|
+
slippagePct: String(args.slippagePct),
|
|
95
|
+
executionFeeToken,
|
|
132
96
|
leverage: args.leverage,
|
|
133
97
|
});
|
|
134
98
|
}
|
|
@@ -137,7 +101,7 @@ export async function closePosition(client, address, args) {
|
|
|
137
101
|
*/
|
|
138
102
|
export async function setPositionTpSl(client, address, args) {
|
|
139
103
|
const chainId = getChainId();
|
|
140
|
-
const
|
|
104
|
+
const executionFeeToken = normalizeAddress(args.executionFeeToken, "executionFeeToken");
|
|
141
105
|
if (!args.tpPrice && !args.slPrice) {
|
|
142
106
|
throw new Error("At least one of tpPrice or slPrice must be provided.");
|
|
143
107
|
}
|
|
@@ -149,19 +113,19 @@ export async function setPositionTpSl(client, address, args) {
|
|
|
149
113
|
positionId: args.positionId,
|
|
150
114
|
direction: dir,
|
|
151
115
|
leverage: args.leverage,
|
|
152
|
-
executionFeeToken
|
|
153
|
-
tpTriggerType: args.
|
|
154
|
-
slTriggerType: args.
|
|
155
|
-
slippagePct: args.slippagePct
|
|
116
|
+
executionFeeToken,
|
|
117
|
+
tpTriggerType: args.tpTriggerType,
|
|
118
|
+
slTriggerType: args.slTriggerType,
|
|
119
|
+
slippagePct: args.slippagePct,
|
|
156
120
|
};
|
|
157
|
-
if (args.tpPrice)
|
|
158
|
-
params.tpPrice =
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (args.slPrice)
|
|
162
|
-
params.slPrice =
|
|
163
|
-
|
|
164
|
-
|
|
121
|
+
if (args.tpPrice)
|
|
122
|
+
params.tpPrice = args.tpPrice;
|
|
123
|
+
if (args.tpSize)
|
|
124
|
+
params.tpSize = args.tpSize;
|
|
125
|
+
if (args.slPrice)
|
|
126
|
+
params.slPrice = args.slPrice;
|
|
127
|
+
if (args.slSize)
|
|
128
|
+
params.slSize = args.slSize;
|
|
165
129
|
return client.order.createPositionTpSlOrder(params);
|
|
166
130
|
}
|
|
167
131
|
/**
|
|
@@ -170,15 +134,22 @@ export async function setPositionTpSl(client, address, args) {
|
|
|
170
134
|
export async function adjustMargin(client, address, args) {
|
|
171
135
|
const chainId = getChainId();
|
|
172
136
|
const quoteToken = normalizeAddress(args.quoteToken || getQuoteToken(), "quoteToken");
|
|
173
|
-
const
|
|
174
|
-
|
|
137
|
+
const adjustAmount = String(args.adjustAmount ?? "").trim();
|
|
138
|
+
if (!/^-?\d+$/.test(adjustAmount)) {
|
|
139
|
+
throw new Error("adjustAmount must be an integer string in quote token raw units.");
|
|
140
|
+
}
|
|
141
|
+
const params = {
|
|
175
142
|
poolId: args.poolId,
|
|
176
143
|
positionId: args.positionId,
|
|
177
|
-
adjustAmount
|
|
144
|
+
adjustAmount,
|
|
178
145
|
quoteToken,
|
|
179
146
|
chainId,
|
|
180
147
|
address,
|
|
181
|
-
}
|
|
148
|
+
};
|
|
149
|
+
if (args.poolOracleType !== undefined) {
|
|
150
|
+
params.poolOracleType = Number(args.poolOracleType);
|
|
151
|
+
}
|
|
152
|
+
return client.position.adjustCollateral(params);
|
|
182
153
|
}
|
|
183
154
|
/**
|
|
184
155
|
* 平掉所有仓位
|
|
@@ -232,17 +203,17 @@ export async function closeAllPositions(client, address) {
|
|
|
232
203
|
*/
|
|
233
204
|
export async function updateOrderTpSl(client, address, args) {
|
|
234
205
|
const chainId = getChainId();
|
|
235
|
-
const quoteToken = normalizeAddress(args.quoteToken
|
|
206
|
+
const quoteToken = normalizeAddress(args.quoteToken, "quoteToken");
|
|
236
207
|
const params = {
|
|
237
208
|
orderId: args.orderId,
|
|
238
|
-
tpSize: args.tpSize
|
|
239
|
-
tpPrice: args.tpPrice
|
|
240
|
-
slSize: args.slSize
|
|
241
|
-
slPrice: args.slPrice
|
|
242
|
-
useOrderCollateral: args.useOrderCollateral
|
|
209
|
+
tpSize: args.tpSize,
|
|
210
|
+
tpPrice: args.tpPrice,
|
|
211
|
+
slSize: args.slSize,
|
|
212
|
+
slPrice: args.slPrice,
|
|
213
|
+
useOrderCollateral: args.useOrderCollateral,
|
|
243
214
|
executionFeeToken: quoteToken,
|
|
244
|
-
size: args.size
|
|
245
|
-
price: args.price
|
|
215
|
+
size: args.size,
|
|
216
|
+
price: args.price,
|
|
246
217
|
};
|
|
247
|
-
return client.order.updateOrderTpSl(params, quoteToken, chainId, address, args.marketId, args.isTpSlOrder
|
|
218
|
+
return client.order.updateOrderTpSl(params, quoteToken, chainId, address, args.marketId, args.isTpSlOrder);
|
|
248
219
|
}
|
|
@@ -41,14 +41,13 @@ export const getTradeFlowTool = {
|
|
|
41
41
|
description: "Get account trade flow / transaction history.",
|
|
42
42
|
schema: {
|
|
43
43
|
poolId: z.string().optional().describe("Optional pool ID filter."),
|
|
44
|
-
|
|
45
|
-
limit: z.coerce.number().optional().describe("Results per page (default 20)"),
|
|
44
|
+
limit: z.number().int().positive().optional().describe("Results per page (default 20)"),
|
|
46
45
|
},
|
|
47
46
|
handler: async (args) => {
|
|
48
47
|
try {
|
|
49
48
|
const { client, address } = await resolveClient();
|
|
50
49
|
const chainId = getChainId();
|
|
51
|
-
const query = { chainId, poolId: args.poolId,
|
|
50
|
+
const query = { chainId, poolId: args.poolId, limit: args.limit ?? 20 };
|
|
52
51
|
const result = await client.account.getTradeFlow(query, address);
|
|
53
52
|
const enhancedData = (result?.data || []).map((flow) => ({
|
|
54
53
|
...flow,
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { resolveClient, getChainId
|
|
2
|
+
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
3
3
|
import { normalizeAddress } from "../utils/address.js";
|
|
4
|
-
import { booleanLike } from "../utils/schema.js";
|
|
5
4
|
export const accountDepositTool = {
|
|
6
5
|
name: "account_deposit",
|
|
7
6
|
description: "Deposit funds from wallet into the MYX trading account.",
|
|
8
7
|
schema: {
|
|
9
|
-
amount: z.string().describe("Amount to deposit (
|
|
10
|
-
tokenAddress: z.string().
|
|
8
|
+
amount: z.string().regex(/^\d+$/).describe("Amount to deposit (token raw units)"),
|
|
9
|
+
tokenAddress: z.string().describe("Token address"),
|
|
11
10
|
},
|
|
12
11
|
handler: async (args) => {
|
|
13
12
|
try {
|
|
14
13
|
const { client } = await resolveClient();
|
|
15
14
|
const chainId = getChainId();
|
|
16
|
-
const tokenAddress = normalizeAddress(args.tokenAddress
|
|
15
|
+
const tokenAddress = normalizeAddress(args.tokenAddress, "tokenAddress");
|
|
17
16
|
const result = await client.account.deposit({
|
|
18
17
|
amount: args.amount,
|
|
19
18
|
tokenAddress,
|
|
@@ -31,8 +30,8 @@ export const accountWithdrawTool = {
|
|
|
31
30
|
description: "Withdraw funds from MYX trading account back to wallet.",
|
|
32
31
|
schema: {
|
|
33
32
|
poolId: z.string().describe("Pool ID to withdraw from"),
|
|
34
|
-
amount: z.string().describe("Amount to withdraw (
|
|
35
|
-
isQuoteToken:
|
|
33
|
+
amount: z.string().regex(/^\d+$/).describe("Amount to withdraw (token raw units)"),
|
|
34
|
+
isQuoteToken: z.boolean().describe("Whether to withdraw as quote token"),
|
|
36
35
|
},
|
|
37
36
|
handler: async (args) => {
|
|
38
37
|
try {
|
|
@@ -43,7 +42,7 @@ export const accountWithdrawTool = {
|
|
|
43
42
|
receiver: address,
|
|
44
43
|
amount: args.amount,
|
|
45
44
|
poolId: args.poolId,
|
|
46
|
-
isQuoteToken: args.isQuoteToken
|
|
45
|
+
isQuoteToken: args.isQuoteToken,
|
|
47
46
|
});
|
|
48
47
|
return { content: [{ type: "text", text: JSON.stringify({ status: "success", data: result }, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2) }] };
|
|
49
48
|
}
|
|
@@ -7,8 +7,9 @@ export const adjustMarginTool = {
|
|
|
7
7
|
schema: {
|
|
8
8
|
poolId: z.string().describe("Pool ID"),
|
|
9
9
|
positionId: z.string().describe("Position ID"),
|
|
10
|
-
adjustAmount: z.string().describe("
|
|
10
|
+
adjustAmount: z.string().regex(/^-?\d+$/).describe("Quote token raw units. Positive = add, negative = remove."),
|
|
11
11
|
quoteToken: z.string().optional().describe("Quote token address"),
|
|
12
|
+
poolOracleType: z.number().optional().describe("Oracle type: 1 for Chainlink, 2 for Pyth"),
|
|
12
13
|
},
|
|
13
14
|
handler: async (args) => {
|
|
14
15
|
try {
|
|
@@ -2,28 +2,15 @@ import { z } from "zod";
|
|
|
2
2
|
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
3
3
|
export const cancelAllOrdersTool = {
|
|
4
4
|
name: "cancel_all_orders",
|
|
5
|
-
description: "Cancel
|
|
5
|
+
description: "Cancel multiple open orders by orderIds.",
|
|
6
6
|
schema: {
|
|
7
|
-
orderIds: z.array(z.string()).
|
|
7
|
+
orderIds: z.array(z.string()).min(1).describe("Order IDs to cancel."),
|
|
8
8
|
},
|
|
9
9
|
handler: async (args) => {
|
|
10
10
|
try {
|
|
11
|
-
const { client
|
|
11
|
+
const { client } = await resolveClient();
|
|
12
12
|
const chainId = getChainId();
|
|
13
|
-
|
|
14
|
-
if (orderIds.length === 0) {
|
|
15
|
-
const openOrders = await client.order.getOrders(address);
|
|
16
|
-
const rows = Array.isArray(openOrders?.data) ? openOrders.data : [];
|
|
17
|
-
orderIds = rows
|
|
18
|
-
.map((o) => o.orderId ?? o.id ?? o.order_id)
|
|
19
|
-
.filter((id) => id !== undefined && id !== null)
|
|
20
|
-
.map((id) => String(id));
|
|
21
|
-
}
|
|
22
|
-
if (orderIds.length === 0) {
|
|
23
|
-
return {
|
|
24
|
-
content: [{ type: "text", text: JSON.stringify({ status: "success", data: { message: "No open orders to cancel.", cancelled: 0 } }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }],
|
|
25
|
-
};
|
|
26
|
-
}
|
|
13
|
+
const orderIds = args.orderIds;
|
|
27
14
|
const result = await client.order.cancelAllOrders(orderIds, chainId);
|
|
28
15
|
return {
|
|
29
16
|
content: [{ type: "text", text: JSON.stringify({ status: "success", data: { cancelled: orderIds.length, orderIds, result } }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }],
|
|
@@ -4,23 +4,13 @@ export const cancelOrderTool = {
|
|
|
4
4
|
name: "cancel_order",
|
|
5
5
|
description: "Cancel an open order by its order ID.",
|
|
6
6
|
schema: {
|
|
7
|
-
orderId: z.string().
|
|
8
|
-
orderIds: z.array(z.string()).optional().describe("Array of order IDs to cancel"),
|
|
7
|
+
orderId: z.string().describe("Order ID to cancel"),
|
|
9
8
|
},
|
|
10
9
|
handler: async (args) => {
|
|
11
10
|
try {
|
|
12
11
|
const { client } = await resolveClient();
|
|
13
12
|
const chainId = getChainId();
|
|
14
|
-
const
|
|
15
|
-
if (!ids || ids.length === 0)
|
|
16
|
-
throw new Error("orderId or orderIds is required.");
|
|
17
|
-
let result;
|
|
18
|
-
if (ids.length === 1) {
|
|
19
|
-
result = await client.order.cancelOrder(ids[0], chainId);
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
result = await client.order.cancelOrders(ids, chainId);
|
|
23
|
-
}
|
|
13
|
+
const result = await client.order.cancelOrder(args.orderId, chainId);
|
|
24
14
|
return { content: [{ type: "text", text: JSON.stringify({ status: "success", data: result }, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2) }] };
|
|
25
15
|
}
|
|
26
16
|
catch (error) {
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { resolveClient, getChainId, getQuoteToken } from "../auth/resolveClient.js";
|
|
3
3
|
import { normalizeAddress } from "../utils/address.js";
|
|
4
|
-
import { booleanLike } from "../utils/schema.js";
|
|
5
4
|
const MAX_UINT256 = "115792089237316195423570985008687907853269984665640564039457584007913129639935";
|
|
6
5
|
export const checkApprovalTool = {
|
|
7
6
|
name: "check_approval",
|
|
8
7
|
description: "Check if token spending approval is needed. Supports auto-approve exact amount (default) or optional unlimited approval.",
|
|
9
8
|
schema: {
|
|
10
|
-
amount: z.string().describe("Amount to check approval for (
|
|
9
|
+
amount: z.string().regex(/^\d+$/).describe("Amount to check approval for (token raw units)"),
|
|
11
10
|
quoteToken: z.string().optional().describe("Token address to check. Uses default if omitted."),
|
|
12
|
-
autoApprove:
|
|
13
|
-
approveMax:
|
|
11
|
+
autoApprove: z.boolean().optional().describe("If true, automatically approve when needed."),
|
|
12
|
+
approveMax: z.boolean().optional().describe("If true with autoApprove, approve unlimited MaxUint256 (default false: approve exact amount only)."),
|
|
14
13
|
},
|
|
15
14
|
handler: async (args) => {
|
|
16
15
|
try {
|
|
@@ -2,13 +2,12 @@ import { z } from "zod";
|
|
|
2
2
|
import { resolveClient, getChainId, getQuoteToken } from "../auth/resolveClient.js";
|
|
3
3
|
import { getOraclePrice } from "../services/marketService.js";
|
|
4
4
|
import { ensureUnits } from "../utils/units.js";
|
|
5
|
-
import { bpsLikeString } from "../utils/schema.js";
|
|
6
5
|
export const closeAllPositionsTool = {
|
|
7
6
|
name: "close_all_positions",
|
|
8
7
|
description: "Emergency: close ALL open positions in a pool at once. Use for risk management.",
|
|
9
8
|
schema: {
|
|
10
9
|
poolId: z.string().describe("Pool ID to close all positions in"),
|
|
11
|
-
slippagePct:
|
|
10
|
+
slippagePct: z.string().regex(/^\d+$/).optional().describe("Slippage in 4-digit precision raw units (1 = 0.01%)"),
|
|
12
11
|
},
|
|
13
12
|
handler: async (args) => {
|
|
14
13
|
try {
|
|
@@ -61,7 +60,7 @@ export const closeAllPositionsTool = {
|
|
|
61
60
|
postOnly: false,
|
|
62
61
|
slippagePct,
|
|
63
62
|
executionFeeToken: pos.quoteToken || pos.quote_token || getQuoteToken(),
|
|
64
|
-
leverage: pos.leverage
|
|
63
|
+
leverage: pos.userLeverage ?? pos.leverage ?? 1,
|
|
65
64
|
};
|
|
66
65
|
});
|
|
67
66
|
const result = await client.order.closeAllPositions(chainId, closeParams);
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { resolveClient } from "../auth/resolveClient.js";
|
|
3
3
|
import { closePosition as closePos } from "../services/tradeService.js";
|
|
4
|
-
import { bpsLikeString } from "../utils/schema.js";
|
|
5
4
|
export const closePositionTool = {
|
|
6
5
|
name: "close_position",
|
|
7
|
-
description: "
|
|
6
|
+
description: "Create a decrease order using SDK-native parameters.",
|
|
8
7
|
schema: {
|
|
9
8
|
poolId: z.string().describe("Pool ID"),
|
|
10
9
|
positionId: z.string().describe("Position ID to close"),
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
size: z.string().describe("
|
|
16
|
-
price: z.string().describe("
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
orderType: z.number().int().min(0).max(3).describe("OrderType enum value"),
|
|
11
|
+
triggerType: z.number().int().min(0).max(2).describe("TriggerType enum value"),
|
|
12
|
+
direction: z.union([z.literal(0), z.literal(1)]).describe("0 = LONG, 1 = SHORT"),
|
|
13
|
+
collateralAmount: z.union([z.string(), z.number()]).describe("Collateral amount (human-readable or raw units)"),
|
|
14
|
+
size: z.union([z.string(), z.number()]).describe("Position size (human-readable or raw units)"),
|
|
15
|
+
price: z.union([z.string(), z.number()]).describe("Price (human-readable or 30-dec raw units)"),
|
|
16
|
+
timeInForce: z.number().int().describe("TimeInForce enum value"),
|
|
17
|
+
postOnly: z.boolean().describe("Post-only flag"),
|
|
18
|
+
slippagePct: z.union([z.string(), z.number()]).describe("Slippage in BPS (e.g. 100 = 1%)"),
|
|
19
|
+
executionFeeToken: z.string().describe("Execution fee token address"),
|
|
20
|
+
leverage: z.number().describe("Leverage"),
|
|
19
21
|
},
|
|
20
22
|
handler: async (args) => {
|
|
21
23
|
try {
|
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { resolveClient } from "../auth/resolveClient.js";
|
|
3
3
|
import { openPosition } from "../services/tradeService.js";
|
|
4
|
-
import { bpsLikeString } from "../utils/schema.js";
|
|
5
4
|
export const executeTradeTool = {
|
|
6
5
|
name: "execute_trade",
|
|
7
|
-
description: "
|
|
6
|
+
description: "Create an increase order using SDK-native parameters.",
|
|
8
7
|
schema: {
|
|
9
|
-
poolId: z.string().describe("Pool ID
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
8
|
+
poolId: z.string().describe("Pool ID"),
|
|
9
|
+
positionId: z.string().describe("Position ID ('0' for new position)"),
|
|
10
|
+
orderType: z.number().int().min(0).max(3).describe("OrderType enum value"),
|
|
11
|
+
triggerType: z.number().int().min(0).max(2).describe("TriggerType enum value"),
|
|
12
|
+
direction: z.union([z.literal(0), z.literal(1)]).describe("0 = LONG, 1 = SHORT"),
|
|
13
|
+
collateralAmount: z.union([z.string(), z.number()]).describe("Collateral amount (human-readable or raw units)"),
|
|
14
|
+
size: z.union([z.string(), z.number()]).describe("Position size (human-readable or raw units)"),
|
|
15
|
+
price: z.union([z.string(), z.number()]).describe("Price (human-readable or 30-dec raw units)"),
|
|
16
|
+
timeInForce: z.number().int().describe("TimeInForce enum value"),
|
|
17
|
+
postOnly: z.boolean().describe("Post-only flag"),
|
|
18
|
+
slippagePct: z.union([z.string(), z.number()]).describe("Slippage in BPS (e.g. 100 = 1%)"),
|
|
19
|
+
executionFeeToken: z.string().describe("Execution fee token address"),
|
|
20
|
+
leverage: z.number().describe("Leverage"),
|
|
21
|
+
tpSize: z.union([z.string(), z.number()]).optional().describe("TP size (human-readable or raw units)"),
|
|
22
|
+
tpPrice: z.union([z.string(), z.number()]).optional().describe("TP price (human-readable or 30-dec raw units)"),
|
|
23
|
+
slSize: z.union([z.string(), z.number()]).optional().describe("SL size (human-readable or raw units)"),
|
|
24
|
+
slPrice: z.union([z.string(), z.number()]).optional().describe("SL price (human-readable or 30-dec raw units)"),
|
|
25
|
+
tradingFee: z.string().regex(/^\d+$/).describe("Trading fee raw units"),
|
|
26
|
+
marketId: z.string().describe("Market ID"),
|
|
22
27
|
},
|
|
23
28
|
handler: async (args) => {
|
|
24
29
|
try {
|
|
@@ -4,7 +4,7 @@ export const getAccountVipInfoTool = {
|
|
|
4
4
|
name: "get_account_vip_info",
|
|
5
5
|
description: "Get account VIP/fee-tier information.",
|
|
6
6
|
schema: {
|
|
7
|
-
chainId: z.
|
|
7
|
+
chainId: z.number().int().positive().optional().describe("Optional chainId override"),
|
|
8
8
|
},
|
|
9
9
|
handler: async (args) => {
|
|
10
10
|
try {
|
|
@@ -1,71 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
3
|
-
function normalizeLower(value) {
|
|
4
|
-
return String(value ?? "").trim().toLowerCase();
|
|
5
|
-
}
|
|
6
|
-
function collectRows(input) {
|
|
7
|
-
if (Array.isArray(input)) {
|
|
8
|
-
return input.flatMap((item) => collectRows(item));
|
|
9
|
-
}
|
|
10
|
-
if (!input || typeof input !== "object") {
|
|
11
|
-
return [];
|
|
12
|
-
}
|
|
13
|
-
if (input.poolId || input.pool_id) {
|
|
14
|
-
return [input];
|
|
15
|
-
}
|
|
16
|
-
return Object.values(input).flatMap((value) => collectRows(value));
|
|
17
|
-
}
|
|
18
|
-
function findPoolIdByBaseAddress(rows, baseAddress) {
|
|
19
|
-
const target = normalizeLower(baseAddress);
|
|
20
|
-
if (!target)
|
|
21
|
-
return null;
|
|
22
|
-
for (const row of rows) {
|
|
23
|
-
const candidates = [
|
|
24
|
-
row.baseToken,
|
|
25
|
-
row.base_token,
|
|
26
|
-
row.baseAddress,
|
|
27
|
-
row.base_address,
|
|
28
|
-
row.base,
|
|
29
|
-
row.tokenAddress,
|
|
30
|
-
row.token_address,
|
|
31
|
-
];
|
|
32
|
-
const hit = candidates.some((candidate) => normalizeLower(candidate) === target);
|
|
33
|
-
if (!hit)
|
|
34
|
-
continue;
|
|
35
|
-
const poolId = row.poolId || row.pool_id;
|
|
36
|
-
if (poolId) {
|
|
37
|
-
return String(poolId);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
3
|
export const getBaseDetailTool = {
|
|
43
4
|
name: "get_base_detail",
|
|
44
|
-
description: "Get base
|
|
5
|
+
description: "Get base token details.",
|
|
45
6
|
schema: {
|
|
46
|
-
baseAddress: z.string().
|
|
47
|
-
|
|
48
|
-
chainId: z.coerce.number().optional().describe("Optional chainId override"),
|
|
7
|
+
baseAddress: z.string().describe("Base token address"),
|
|
8
|
+
chainId: z.number().int().positive().optional().describe("Optional chainId override"),
|
|
49
9
|
},
|
|
50
10
|
handler: async (args) => {
|
|
51
11
|
try {
|
|
52
12
|
const { client } = await resolveClient();
|
|
53
13
|
const chainId = args.chainId ?? getChainId();
|
|
54
|
-
|
|
55
|
-
if (!poolId && args.baseAddress) {
|
|
56
|
-
const sources = await Promise.allSettled([
|
|
57
|
-
client.api.getPoolList(),
|
|
58
|
-
client.api.getMarketList(),
|
|
59
|
-
]);
|
|
60
|
-
const rows = sources
|
|
61
|
-
.filter((result) => result.status === "fulfilled")
|
|
62
|
-
.flatMap((result) => collectRows(result.value));
|
|
63
|
-
poolId = findPoolIdByBaseAddress(rows, args.baseAddress) || undefined;
|
|
64
|
-
}
|
|
65
|
-
if (!poolId) {
|
|
66
|
-
throw new Error("poolId is required. If using baseAddress, no matching pool was found.");
|
|
67
|
-
}
|
|
68
|
-
const result = await client.markets.getBaseDetail({ chainId, poolId });
|
|
14
|
+
const result = await client.markets.getBaseDetail({ chainId, baseAddress: args.baseAddress });
|
|
69
15
|
return { content: [{ type: "text", text: JSON.stringify({ status: "success", data: result }, (_, v) => typeof v === "bigint" ? v.toString() : v, 2) }] };
|
|
70
16
|
}
|
|
71
17
|
catch (error) {
|
package/dist/tools/getKline.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
3
|
+
const klineIntervalSchema = z.enum(["1m", "5m", "15m", "30m", "1h", "4h", "1d", "1w", "1M"]);
|
|
3
4
|
export const getKlineTool = {
|
|
4
5
|
name: "get_kline",
|
|
5
6
|
description: "Get K-line / candlestick data for a pool. Essential for price analysis.",
|
|
6
7
|
schema: {
|
|
7
8
|
poolId: z.string().describe("Pool ID"),
|
|
8
|
-
interval:
|
|
9
|
-
limit: z.
|
|
9
|
+
interval: klineIntervalSchema.describe("K-line interval"),
|
|
10
|
+
limit: z.number().int().positive().optional().describe("Number of bars (default 100)"),
|
|
10
11
|
},
|
|
11
12
|
handler: async (args) => {
|
|
12
13
|
try {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
3
|
+
const klineIntervalSchema = z.enum(["1m", "5m", "15m", "30m", "1h", "4h", "1d", "1w", "1M"]);
|
|
3
4
|
export const getKlineLatestBarTool = {
|
|
4
5
|
name: "get_kline_latest_bar",
|
|
5
6
|
description: "Get the latest single K-line/candlestick bar for a pool.",
|
|
6
7
|
schema: {
|
|
7
8
|
poolId: z.string().describe("Pool ID"),
|
|
8
|
-
interval:
|
|
9
|
+
interval: klineIntervalSchema.describe("K-line interval"),
|
|
9
10
|
},
|
|
10
11
|
handler: async (args) => {
|
|
11
12
|
try {
|
|
@@ -5,7 +5,7 @@ export const getMarketListTool = {
|
|
|
5
5
|
name: "get_market_list",
|
|
6
6
|
description: "Get tradable markets/pools (state=2 Active). Supports configurable result limit; backend may still enforce its own cap.",
|
|
7
7
|
schema: {
|
|
8
|
-
limit: z.
|
|
8
|
+
limit: z.number().int().positive().optional().describe("Max results to request (default 1000)."),
|
|
9
9
|
},
|
|
10
10
|
handler: async (args) => {
|
|
11
11
|
try {
|
|
@@ -6,7 +6,7 @@ export const getMarketPriceTool = {
|
|
|
6
6
|
description: "Get the current market price for a specific pool.",
|
|
7
7
|
schema: {
|
|
8
8
|
poolId: z.string().describe("Pool ID to get price for"),
|
|
9
|
-
chainId: z.
|
|
9
|
+
chainId: z.number().int().positive().optional().describe("Optional chainId override"),
|
|
10
10
|
},
|
|
11
11
|
handler: async (args) => {
|
|
12
12
|
try {
|
|
@@ -5,7 +5,7 @@ export const getNetworkFeeTool = {
|
|
|
5
5
|
description: "Estimate network fee requirements for a market.",
|
|
6
6
|
schema: {
|
|
7
7
|
marketId: z.string().describe("Market ID"),
|
|
8
|
-
chainId: z.
|
|
8
|
+
chainId: z.number().int().positive().optional().describe("Optional chainId override"),
|
|
9
9
|
},
|
|
10
10
|
handler: async (args) => {
|
|
11
11
|
try {
|
|
@@ -6,7 +6,7 @@ export const getOraclePriceTool = {
|
|
|
6
6
|
description: "Get the current oracle price for a specific pool.",
|
|
7
7
|
schema: {
|
|
8
8
|
poolId: z.string().describe("Pool ID to get oracle price for"),
|
|
9
|
-
chainId: z.
|
|
9
|
+
chainId: z.number().int().positive().optional().describe("Optional chainId override"),
|
|
10
10
|
},
|
|
11
11
|
handler: async (args) => {
|
|
12
12
|
try {
|
|
@@ -4,9 +4,9 @@ export const getUserTradingFeeRateTool = {
|
|
|
4
4
|
name: "get_user_trading_fee_rate",
|
|
5
5
|
description: "Get maker/taker fee rates for a given assetClass and riskTier.",
|
|
6
6
|
schema: {
|
|
7
|
-
assetClass: z.
|
|
8
|
-
riskTier: z.
|
|
9
|
-
chainId: z.
|
|
7
|
+
assetClass: z.number().int().nonnegative().describe("Asset class ID"),
|
|
8
|
+
riskTier: z.number().int().nonnegative().describe("Risk tier"),
|
|
9
|
+
chainId: z.number().int().positive().optional().describe("Optional chainId override"),
|
|
10
10
|
},
|
|
11
11
|
handler: async (args) => {
|
|
12
12
|
try {
|
|
@@ -1,31 +1,20 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { quoteDeposit, quoteWithdraw, baseDeposit, baseWithdraw, getLpPrice, } from "../services/poolService.js";
|
|
3
|
-
import { parseSafeNumber } from "../utils/units.js";
|
|
4
|
-
import { lpSlippageLike } from "../utils/schema.js";
|
|
5
3
|
export const manageLiquidityTool = {
|
|
6
4
|
name: "manage_liquidity",
|
|
7
|
-
description: "Add or withdraw liquidity from a BASE or QUOTE pool.
|
|
5
|
+
description: "Add or withdraw liquidity from a BASE or QUOTE pool.",
|
|
8
6
|
schema: {
|
|
9
|
-
action: z.
|
|
10
|
-
|
|
11
|
-
}).describe("'deposit' or 'withdraw'"),
|
|
12
|
-
poolType: z.string().trim().toUpperCase().refine((value) => value === "BASE" || value === "QUOTE", {
|
|
13
|
-
message: "poolType must be 'BASE' or 'QUOTE'.",
|
|
14
|
-
}).describe("'BASE' or 'QUOTE'"),
|
|
7
|
+
action: z.enum(["deposit", "withdraw"]).describe("'deposit' or 'withdraw'"),
|
|
8
|
+
poolType: z.enum(["BASE", "QUOTE"]).describe("'BASE' or 'QUOTE'"),
|
|
15
9
|
poolId: z.string().describe("Pool ID"),
|
|
16
|
-
amount: z.
|
|
17
|
-
slippage:
|
|
18
|
-
chainId: z.
|
|
10
|
+
amount: z.number().positive().describe("Amount in human-readable units"),
|
|
11
|
+
slippage: z.number().min(0).describe("LP slippage ratio (e.g. 0.01 = 1%)"),
|
|
12
|
+
chainId: z.number().int().positive().optional().describe("Optional chainId override"),
|
|
19
13
|
},
|
|
20
14
|
handler: async (args) => {
|
|
21
15
|
try {
|
|
22
16
|
const { action, poolType, poolId } = args;
|
|
23
|
-
const amount =
|
|
24
|
-
const slippage = args.slippage;
|
|
25
|
-
if (amount <= 0)
|
|
26
|
-
throw new Error("amount must be a positive number.");
|
|
27
|
-
if (slippage < 0)
|
|
28
|
-
throw new Error("slippage must be a non-negative number.");
|
|
17
|
+
const { amount, slippage } = args;
|
|
29
18
|
let result;
|
|
30
19
|
if (poolType === "QUOTE") {
|
|
31
20
|
result = action === "deposit"
|
|
@@ -48,11 +37,9 @@ export const getLpPriceTool = {
|
|
|
48
37
|
name: "get_lp_price",
|
|
49
38
|
description: "Get the current internal net asset value (NAV) price of an LP token for a BASE or QUOTE pool. Note: This is NOT the underlying token's external Oracle market price (e.g. WETH's price), but rather the internal exchange rate / net worth of the LP token itself which fluctuates based on pool PnL and fees.",
|
|
50
39
|
schema: {
|
|
51
|
-
poolType: z.
|
|
52
|
-
message: "poolType must be 'BASE' or 'QUOTE'.",
|
|
53
|
-
}).describe("'BASE' or 'QUOTE'"),
|
|
40
|
+
poolType: z.enum(["BASE", "QUOTE"]).describe("'BASE' or 'QUOTE'"),
|
|
54
41
|
poolId: z.string().describe("Pool ID"),
|
|
55
|
-
chainId: z.
|
|
42
|
+
chainId: z.number().int().positive().optional().describe("Optional chainId override"),
|
|
56
43
|
},
|
|
57
44
|
handler: async (args) => {
|
|
58
45
|
try {
|
package/dist/tools/marketInfo.js
CHANGED
|
@@ -7,7 +7,7 @@ export const getMarketDetailTool = {
|
|
|
7
7
|
description: "Get detailed information for a specific trading pool (fee rates, open interest, etc.).",
|
|
8
8
|
schema: {
|
|
9
9
|
poolId: z.string().describe("Pool ID"),
|
|
10
|
-
chainId: z.
|
|
10
|
+
chainId: z.number().int().positive().optional().describe("Optional chainId override"),
|
|
11
11
|
},
|
|
12
12
|
handler: async (args) => {
|
|
13
13
|
try {
|
|
@@ -26,7 +26,7 @@ export const getPoolInfoTool = {
|
|
|
26
26
|
description: "Get pool on-chain information (reserves, utilization, etc.).",
|
|
27
27
|
schema: {
|
|
28
28
|
poolId: z.string().describe("Pool ID"),
|
|
29
|
-
chainId: z.
|
|
29
|
+
chainId: z.number().int().positive().optional().describe("Optional chainId override"),
|
|
30
30
|
},
|
|
31
31
|
handler: async (args) => {
|
|
32
32
|
try {
|
|
@@ -44,8 +44,8 @@ export const getLiquidityInfoTool = {
|
|
|
44
44
|
description: "Get pool liquidity utilization and depth information.",
|
|
45
45
|
schema: {
|
|
46
46
|
poolId: z.string().describe("Pool ID"),
|
|
47
|
-
marketPrice: z.string().describe("Current market price
|
|
48
|
-
chainId: z.
|
|
47
|
+
marketPrice: z.string().regex(/^\d+$/).describe("Current market price in 30-decimal raw units"),
|
|
48
|
+
chainId: z.number().int().positive().optional().describe("Optional chainId override"),
|
|
49
49
|
},
|
|
50
50
|
handler: async (args) => {
|
|
51
51
|
try {
|
|
@@ -24,17 +24,16 @@ export const getOpenOrdersTool = {
|
|
|
24
24
|
};
|
|
25
25
|
export const getOrderHistoryTool = {
|
|
26
26
|
name: "get_order_history",
|
|
27
|
-
description: "Get historical orders with optional pool filter
|
|
27
|
+
description: "Get historical orders with optional pool filter.",
|
|
28
28
|
schema: {
|
|
29
29
|
poolId: z.string().optional().describe("Filter by pool ID"),
|
|
30
|
-
|
|
31
|
-
limit: z.coerce.number().optional().describe("Results per page (default 20)"),
|
|
30
|
+
limit: z.number().int().positive().optional().describe("Results per page (default 20)"),
|
|
32
31
|
},
|
|
33
32
|
handler: async (args) => {
|
|
34
33
|
try {
|
|
35
34
|
const { client, address } = await resolveClient();
|
|
36
35
|
const chainId = getChainId();
|
|
37
|
-
const query = { chainId, poolId: args.poolId,
|
|
36
|
+
const query = { chainId, poolId: args.poolId, limit: args.limit ?? 20 };
|
|
38
37
|
const result = await client.order.getOrderHistory(query, address);
|
|
39
38
|
const enhancedData = (result?.data || []).map((order) => ({
|
|
40
39
|
...order,
|
package/dist/tools/poolConfig.js
CHANGED
|
@@ -6,7 +6,7 @@ export const getPoolLevelConfigTool = {
|
|
|
6
6
|
description: "Get the level configuration and trading limits for a specific pool.",
|
|
7
7
|
schema: {
|
|
8
8
|
poolId: z.string().describe("Pool ID"),
|
|
9
|
-
chainId: z.
|
|
9
|
+
chainId: z.number().int().positive().optional().describe("Optional chainId override"),
|
|
10
10
|
},
|
|
11
11
|
handler: async (args) => {
|
|
12
12
|
try {
|
|
@@ -3,17 +3,16 @@ import { resolveClient, getChainId } from "../auth/resolveClient.js";
|
|
|
3
3
|
import { getDirectionDesc, getCloseTypeDesc } from "../utils/mappings.js";
|
|
4
4
|
export const getPositionHistoryTool = {
|
|
5
5
|
name: "get_position_history",
|
|
6
|
-
description: "Get historical closed positions with optional pool filter
|
|
6
|
+
description: "Get historical closed positions with optional pool filter.",
|
|
7
7
|
schema: {
|
|
8
8
|
poolId: z.string().optional().describe("Filter by pool ID"),
|
|
9
|
-
|
|
10
|
-
limit: z.coerce.number().optional().describe("Results per page (default 20)"),
|
|
9
|
+
limit: z.number().int().positive().optional().describe("Results per page (default 20)"),
|
|
11
10
|
},
|
|
12
11
|
handler: async (args) => {
|
|
13
12
|
try {
|
|
14
13
|
const { client, address } = await resolveClient();
|
|
15
14
|
const chainId = getChainId();
|
|
16
|
-
const query = { chainId, poolId: args.poolId,
|
|
15
|
+
const query = { chainId, poolId: args.poolId, limit: args.limit ?? 20 };
|
|
17
16
|
const result = await client.position.getPositionHistory(query, address);
|
|
18
17
|
const enhancedData = (result?.data || []).map((pos) => ({
|
|
19
18
|
...pos,
|
|
@@ -6,7 +6,7 @@ export const searchMarketTool = {
|
|
|
6
6
|
description: "Search for an active market by keyword.",
|
|
7
7
|
schema: {
|
|
8
8
|
keyword: z.string().describe('Search keyword, e.g. "BTC", "ETH"'),
|
|
9
|
-
limit: z.
|
|
9
|
+
limit: z.number().int().positive().optional().describe("Max results (default 100)"),
|
|
10
10
|
},
|
|
11
11
|
handler: async (args) => {
|
|
12
12
|
try {
|
package/dist/tools/setTpSl.js
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { resolveClient } from "../auth/resolveClient.js";
|
|
3
3
|
import { setPositionTpSl } from "../services/tradeService.js";
|
|
4
|
-
import { bpsLikeString } from "../utils/schema.js";
|
|
5
4
|
export const setTpSlTool = {
|
|
6
5
|
name: "set_tp_sl",
|
|
7
|
-
description: "
|
|
6
|
+
description: "Create TP/SL order using SDK-native parameters.",
|
|
8
7
|
schema: {
|
|
9
8
|
poolId: z.string().describe("Pool ID"),
|
|
10
9
|
positionId: z.string().describe("Position ID"),
|
|
11
|
-
direction: z.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
direction: z.union([z.literal(0), z.literal(1)]).describe("0 = LONG, 1 = SHORT"),
|
|
11
|
+
leverage: z.number().describe("Leverage"),
|
|
12
|
+
executionFeeToken: z.string().describe("Execution fee token address"),
|
|
13
|
+
tpTriggerType: z.number().int().min(0).max(2).describe("TP trigger type"),
|
|
14
|
+
slTriggerType: z.number().int().min(0).max(2).describe("SL trigger type"),
|
|
15
|
+
slippagePct: z.string().regex(/^\d+$/).describe("Slippage with 4-dec precision raw units"),
|
|
16
|
+
tpPrice: z.string().regex(/^\d+$/).optional().describe("TP price raw units (30 decimals)"),
|
|
17
|
+
tpSize: z.string().regex(/^\d+$/).optional().describe("TP size raw units"),
|
|
18
|
+
slPrice: z.string().regex(/^\d+$/).optional().describe("SL price raw units (30 decimals)"),
|
|
19
|
+
slSize: z.string().regex(/^\d+$/).optional().describe("SL size raw units"),
|
|
21
20
|
},
|
|
22
21
|
handler: async (args) => {
|
|
23
22
|
try {
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { resolveClient } from "../auth/resolveClient.js";
|
|
3
3
|
import { updateOrderTpSl } from "../services/tradeService.js";
|
|
4
|
-
import { booleanLike } from "../utils/schema.js";
|
|
5
4
|
export const updateOrderTpSlTool = {
|
|
6
5
|
name: "update_order_tp_sl",
|
|
7
6
|
description: "Update an existing take profit or stop loss order.",
|
|
8
7
|
schema: {
|
|
9
8
|
orderId: z.string().describe("The ID of the order to update"),
|
|
10
9
|
marketId: z.string().describe("The market ID (config hash) for the order"),
|
|
11
|
-
size: z.string().
|
|
12
|
-
price: z.string().
|
|
13
|
-
tpPrice: z.string().
|
|
14
|
-
tpSize: z.string().
|
|
15
|
-
slPrice: z.string().
|
|
16
|
-
slSize: z.string().
|
|
17
|
-
useOrderCollateral:
|
|
18
|
-
isTpSlOrder:
|
|
19
|
-
quoteToken: z.string().
|
|
10
|
+
size: z.string().regex(/^\d+$/).describe("Order size raw units"),
|
|
11
|
+
price: z.string().regex(/^\d+$/).describe("Order price raw units (30 decimals)"),
|
|
12
|
+
tpPrice: z.string().regex(/^\d+$/).describe("TP price raw units (30 decimals)"),
|
|
13
|
+
tpSize: z.string().regex(/^\d+$/).describe("TP size raw units"),
|
|
14
|
+
slPrice: z.string().regex(/^\d+$/).describe("SL price raw units (30 decimals)"),
|
|
15
|
+
slSize: z.string().regex(/^\d+$/).describe("SL size raw units"),
|
|
16
|
+
useOrderCollateral: z.boolean().describe("Whether to use order collateral"),
|
|
17
|
+
isTpSlOrder: z.boolean().optional().describe("Whether this is a TP/SL order"),
|
|
18
|
+
quoteToken: z.string().describe("Quote token address"),
|
|
20
19
|
},
|
|
21
20
|
handler: async (args) => {
|
|
22
21
|
try {
|
package/package.json
CHANGED
package/dist/utils/schema.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
const trueValues = new Set(["true", "1", "yes", "y", "on"]);
|
|
3
|
-
const falseValues = new Set(["false", "0", "no", "n", "off"]);
|
|
4
|
-
const DECIMAL_RE = /^-?\d+(\.\d+)?$/;
|
|
5
|
-
const EPSILON = 1e-8;
|
|
6
|
-
/**
|
|
7
|
-
* Safe boolean coercion for MCP inputs:
|
|
8
|
-
* - boolean: true/false
|
|
9
|
-
* - number: 1/0
|
|
10
|
-
* - string: true/false/1/0/yes/no/on/off (case-insensitive)
|
|
11
|
-
*/
|
|
12
|
-
export const booleanLike = z.union([
|
|
13
|
-
z.boolean(),
|
|
14
|
-
z.number().refine((value) => value === 0 || value === 1, {
|
|
15
|
-
message: "Expected 0 or 1 for boolean value.",
|
|
16
|
-
}).transform((value) => value === 1),
|
|
17
|
-
z.string().trim().toLowerCase().refine((value) => trueValues.has(value) || falseValues.has(value), {
|
|
18
|
-
message: "Expected boolean-like value: true/false/1/0/yes/no/on/off.",
|
|
19
|
-
}).transform((value) => trueValues.has(value)),
|
|
20
|
-
]);
|
|
21
|
-
function isNearlyInteger(value) {
|
|
22
|
-
return Math.abs(value - Math.round(value)) < EPSILON;
|
|
23
|
-
}
|
|
24
|
-
function parseBpsLike(value) {
|
|
25
|
-
const raw = String(value).trim();
|
|
26
|
-
if (!raw)
|
|
27
|
-
throw new Error("slippage is required.");
|
|
28
|
-
if (!DECIMAL_RE.test(raw))
|
|
29
|
-
throw new Error("slippage must be numeric.");
|
|
30
|
-
const parsed = Number(raw);
|
|
31
|
-
if (!Number.isFinite(parsed))
|
|
32
|
-
throw new Error("slippage must be finite.");
|
|
33
|
-
if (parsed < 0)
|
|
34
|
-
throw new Error("slippage must be >= 0.");
|
|
35
|
-
if (isNearlyInteger(parsed)) {
|
|
36
|
-
return Math.round(parsed);
|
|
37
|
-
}
|
|
38
|
-
// Backward compatibility: legacy ratio input (e.g. 0.01 = 1%) -> bps
|
|
39
|
-
if (parsed > 0 && parsed < 1) {
|
|
40
|
-
const bps = parsed * 10000;
|
|
41
|
-
if (!isNearlyInteger(bps)) {
|
|
42
|
-
throw new Error("legacy slippage ratio is too precise; use integer bps.");
|
|
43
|
-
}
|
|
44
|
-
return Math.round(bps);
|
|
45
|
-
}
|
|
46
|
-
throw new Error("slippage must be integer bps (1 = 0.01%).");
|
|
47
|
-
}
|
|
48
|
-
function parseLpSlippageLike(value) {
|
|
49
|
-
const raw = String(value).trim();
|
|
50
|
-
if (!raw)
|
|
51
|
-
throw new Error("slippage is required.");
|
|
52
|
-
if (!DECIMAL_RE.test(raw))
|
|
53
|
-
throw new Error("slippage must be numeric.");
|
|
54
|
-
const parsed = Number(raw);
|
|
55
|
-
if (!Number.isFinite(parsed))
|
|
56
|
-
throw new Error("slippage must be finite.");
|
|
57
|
-
if (parsed < 0)
|
|
58
|
-
throw new Error("slippage must be >= 0.");
|
|
59
|
-
// SDK doc convention for LP: ratio (e.g. 0.01 = 1%)
|
|
60
|
-
if (parsed <= 1) {
|
|
61
|
-
return parsed;
|
|
62
|
-
}
|
|
63
|
-
// Backward compatibility for MCP callers that send BPS (e.g. 100 = 1%)
|
|
64
|
-
return parsed / 10000;
|
|
65
|
-
}
|
|
66
|
-
export const bpsLikeNumber = z.union([z.number(), z.string()]).transform((value, ctx) => {
|
|
67
|
-
try {
|
|
68
|
-
return parseBpsLike(value);
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
ctx.addIssue({ code: "custom", message: error.message || "Invalid slippage." });
|
|
72
|
-
return z.NEVER;
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
export const bpsLikeString = bpsLikeNumber.transform((value) => value.toString());
|
|
76
|
-
export const lpSlippageLike = z.union([z.number(), z.string()]).transform((value, ctx) => {
|
|
77
|
-
try {
|
|
78
|
-
return parseLpSlippageLike(value);
|
|
79
|
-
}
|
|
80
|
-
catch (error) {
|
|
81
|
-
ctx.addIssue({ code: "custom", message: error.message || "Invalid LP slippage." });
|
|
82
|
-
return z.NEVER;
|
|
83
|
-
}
|
|
84
|
-
});
|