hchain-mcp 1.0.0

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.
Files changed (56) hide show
  1. package/.env.example +5 -0
  2. package/AGENT-MCP-RULES.md +248 -0
  3. package/OnchainOS-API/345/257/271/346/216/245/350/247/204/350/214/203.md +329 -0
  4. package/README.md +106 -0
  5. package/dist/adapters/onchainos-ws.d.ts +31 -0
  6. package/dist/adapters/onchainos-ws.js +103 -0
  7. package/dist/adapters/onchainos.d.ts +343 -0
  8. package/dist/adapters/onchainos.js +275 -0
  9. package/dist/adapters/shared.d.ts +23 -0
  10. package/dist/adapters/shared.js +107 -0
  11. package/dist/http.d.ts +9 -0
  12. package/dist/http.js +124 -0
  13. package/dist/index.d.ts +6 -0
  14. package/dist/index.js +54 -0
  15. package/dist/tools/balance.d.ts +4 -0
  16. package/dist/tools/balance.js +69 -0
  17. package/dist/tools/defi.d.ts +4 -0
  18. package/dist/tools/defi.js +268 -0
  19. package/dist/tools/gateway.d.ts +4 -0
  20. package/dist/tools/gateway.js +107 -0
  21. package/dist/tools/intent.d.ts +4 -0
  22. package/dist/tools/intent.js +111 -0
  23. package/dist/tools/market.d.ts +4 -0
  24. package/dist/tools/market.js +612 -0
  25. package/dist/tools/payments.d.ts +4 -0
  26. package/dist/tools/payments.js +100 -0
  27. package/dist/tools/skills.d.ts +4 -0
  28. package/dist/tools/skills.js +464 -0
  29. package/dist/tools/trade.d.ts +4 -0
  30. package/dist/tools/trade.js +195 -0
  31. package/dist/tools/txhistory.d.ts +4 -0
  32. package/dist/tools/txhistory.js +49 -0
  33. package/dist/tools/ws.d.ts +4 -0
  34. package/dist/tools/ws.js +64 -0
  35. package/docs/DEFI.md +65 -0
  36. package/docs/GATEWAY.md +45 -0
  37. package/docs/MARKET.md +109 -0
  38. package/docs/PAYMENTS.md +83 -0
  39. package/docs/TRADE.md +103 -0
  40. package/package.json +25 -0
  41. package/src/adapters/onchainos-ws.ts +126 -0
  42. package/src/adapters/onchainos.ts +488 -0
  43. package/src/adapters/shared.ts +100 -0
  44. package/src/http.ts +132 -0
  45. package/src/index.ts +57 -0
  46. package/src/tools/balance.ts +67 -0
  47. package/src/tools/defi.ts +224 -0
  48. package/src/tools/gateway.ts +115 -0
  49. package/src/tools/intent.ts +106 -0
  50. package/src/tools/market.ts +543 -0
  51. package/src/tools/payments.ts +105 -0
  52. package/src/tools/skills.ts +489 -0
  53. package/src/tools/trade.ts +197 -0
  54. package/src/tools/txhistory.ts +51 -0
  55. package/src/tools/ws.ts +72 -0
  56. package/tsconfig.json +18 -0
@@ -0,0 +1,488 @@
1
+ /**
2
+ * Onchain OS REST API 适配层
3
+ * Base: https://web3.okx.com
4
+ * 按官方文档逐端点对接,杜绝幻觉
5
+ */
6
+ import crypto from "node:crypto";
7
+ import type { Auth } from "./shared.js";
8
+
9
+ const BASE = "https://web3.okx.com";
10
+
11
+ function ts(): string { return new Date().toISOString().replace(/(\.\d{3})\d*Z/, "$1Z"); }
12
+
13
+ function buildQuery(p: Record<string, string | number | boolean | undefined>): string {
14
+ const s = new URLSearchParams();
15
+ for (const [k, v] of Object.entries(p)) if (v !== undefined && v !== "") s.append(k, String(v));
16
+ return s.size ? "?" + s.toString() : "";
17
+ }
18
+
19
+ async function request<T>(
20
+ method: "GET" | "POST", path: string,
21
+ opts: { params?: Record<string, string | number | boolean | undefined>; body?: unknown; auth?: Auth } = {},
22
+ ): Promise<T> {
23
+ const query = opts.params ? buildQuery(opts.params) : "";
24
+ const fullPath = path + query;
25
+ const bodyStr = opts.body ? JSON.stringify(opts.body) : "";
26
+ const headers: Record<string, string> = { "Content-Type": "application/json", "Accept": "application/json" };
27
+
28
+ if (opts.auth) {
29
+ const t = ts();
30
+ const sign = crypto.createHmac("sha256", opts.auth.secret).update(t + method + fullPath + bodyStr).digest("base64");
31
+ headers["OK-ACCESS-KEY"] = opts.auth.apiKey;
32
+ headers["OK-ACCESS-SIGN"] = sign;
33
+ headers["OK-ACCESS-TIMESTAMP"] = t;
34
+ headers["OK-ACCESS-PASSPHRASE"] = opts.auth.passphrase;
35
+ }
36
+
37
+ const res = await fetch(BASE + fullPath, { method, headers, ...(bodyStr ? { body: bodyStr } : {}) });
38
+ if (!res.ok) { const t = await res.text().catch(() => ""); throw new Error(`HTTP ${res.status}: ${t}`); }
39
+ const json = await res.json() as { code: string; msg?: string; data?: T };
40
+ if (json.code && json.code !== "0") throw new Error(`OKX ${json.code}: ${json.msg ?? ""}`);
41
+ return (json.data ?? json) as T;
42
+ }
43
+
44
+ // ═══════════════════════════════════════════════════════════════
45
+ // 按官方文档逐模块添加
46
+ // ═══════════════════════════════════════════════════════════════
47
+
48
+ // ── Balance API v6 ─────────────────────────────────────────────
49
+
50
+ export const balanceApi = {
51
+ /** GET /api/v6/dex/balance/supported/chain — 返回 name/logoUrl/shortName/chainIndex */
52
+ supportedChain: (auth: Auth) =>
53
+ request("GET", "/api/v6/dex/balance/supported/chain", { auth }),
54
+
55
+ /** GET /api/v6/dex/balance/total-value-by-address?address=&chains=&assetType=&excludeRiskToken= */
56
+ totalValue: (auth: Auth, address: string, chains: string, assetType?: string, excludeRiskToken?: boolean) =>
57
+ request("GET", "/api/v6/dex/balance/total-value-by-address", {
58
+ params: { address, chains, assetType, excludeRiskToken },
59
+ auth,
60
+ }),
61
+
62
+ /** GET /api/v6/dex/balance/all-token-balances-by-address?address=&chains=&excludeRiskToken= */
63
+ allTokenBalances: (auth: Auth, address: string, chains: string, excludeRiskToken?: string) =>
64
+ request("GET", "/api/v6/dex/balance/all-token-balances-by-address", {
65
+ params: { address, chains, excludeRiskToken },
66
+ auth,
67
+ }),
68
+
69
+ /** POST /api/v6/dex/balance/token-balances-by-address — body: { address, tokenContractAddresses: [{chainIndex, tokenContractAddress}], excludeRiskToken? } */
70
+ specificTokenBalance: (
71
+ auth: Auth,
72
+ address: string,
73
+ tokenContractAddresses: Array<{ chainIndex: string; tokenContractAddress: string }>,
74
+ excludeRiskToken?: string,
75
+ ) =>
76
+ request("POST", "/api/v6/dex/balance/token-balances-by-address", {
77
+ body: { address, tokenContractAddresses, ...(excludeRiskToken !== undefined ? { excludeRiskToken } : {}) },
78
+ auth,
79
+ }),
80
+ };
81
+
82
+ // ── Gateway API v6 (交易上链) ──────────────────────────────────
83
+
84
+ export const gatewayApi = {
85
+ /** GET /api/v6/dex/pre-transaction/supported/chain */
86
+ supportedChain: (auth: Auth) =>
87
+ request("GET", "/api/v6/dex/pre-transaction/supported/chain", { auth }),
88
+
89
+ /** GET /api/v6/dex/pre-transaction/gas-price?chainIndex= */
90
+ gasPrice: (auth: Auth, chainIndex: string) =>
91
+ request("GET", "/api/v6/dex/pre-transaction/gas-price", { params: { chainIndex }, auth }),
92
+
93
+ /** POST /api/v6/dex/pre-transaction/gas-limit */
94
+ gasLimit: (auth: Auth, body: { chainIndex: string; fromAddress: string; toAddress: string; txAmount?: string; extJson?: { inputData?: string } }) =>
95
+ request("POST", "/api/v6/dex/pre-transaction/gas-limit", { body, auth }),
96
+
97
+ /** POST /api/v6/dex/pre-transaction/simulate — 模拟执行, 返回 intention/assetChange/gasUsed/failReason/risks */
98
+ simulate: (auth: Auth, body: { fromAddress: string; toAddress: string; chainIndex: string; txAmount?: string; extJson: { inputData: string }; priorityFee?: string; gasPrice?: string }) =>
99
+ request("POST", "/api/v6/dex/pre-transaction/simulate", { body, auth }),
100
+
101
+ /** POST /api/v6/dex/pre-transaction/broadcast-transaction — 返回 orderId/txHash */
102
+ broadcast: (auth: Auth, body: { signedTx: string; chainIndex: string; address: string; extraData?: string }) =>
103
+ request("POST", "/api/v6/dex/pre-transaction/broadcast-transaction", { body, auth }),
104
+ };
105
+
106
+ // ── Post-transaction API v6 ───────────────────────────────────
107
+
108
+ export const postTxApi = {
109
+ /** GET /api/v6/dex/post-transaction/orders — 广播订单列表 */
110
+ orders: (auth: Auth, address: string, chainIndex: string, txStatus?: string, orderId?: string, cursor?: string, limit?: string) =>
111
+ request("GET", "/api/v6/dex/post-transaction/orders", {
112
+ params: { address, chainIndex, txStatus, orderId, cursor, limit },
113
+ auth,
114
+ }),
115
+
116
+ /** GET /api/v6/dex/post-transaction/supported/chain — 交易历史支持的链 */
117
+ supportedChain: (auth: Auth) =>
118
+ request("GET", "/api/v6/dex/post-transaction/supported/chain", { auth }),
119
+
120
+ /** GET /api/v6/dex/post-transaction/transactions-by-address?address=&chains=&tokenContractAddress=&begin=&end=&cursor=&limit= */
121
+ transactions: (auth: Auth, address: string, chains: string, tokenContractAddress?: string, begin?: string, end?: string, cursor?: string, limit?: string) =>
122
+ request("GET", "/api/v6/dex/post-transaction/transactions-by-address", {
123
+ params: { address, chains, tokenContractAddress, begin, end, cursor, limit },
124
+ auth,
125
+ }),
126
+
127
+ /** GET /api/v6/dex/post-transaction/transaction-detail-by-txhash?chainIndex=&txHash=&itype= */
128
+ transactionDetail: (auth: Auth, chainIndex: string, txHash: string, itype?: string) =>
129
+ request("GET", "/api/v6/dex/post-transaction/transaction-detail-by-txhash", {
130
+ params: { chainIndex, txHash, itype },
131
+ auth,
132
+ }),
133
+ };
134
+
135
+ // ── DeFi API v6 ──────────────────────────────────────────────
136
+
137
+ export const defiApi = {
138
+ /** GET /api/v6/defi/product/supported-chains — 返回 chainIndex(String)+network */
139
+ supportedChains: (auth: Auth) =>
140
+ request("GET", "/api/v6/defi/product/supported-chains", { auth }),
141
+
142
+ /** GET /api/v6/defi/product/supported-platforms — 返回 analysisPlatformId+platformName+investmentCount */
143
+ supportedPlatforms: (auth: Auth) =>
144
+ request("GET", "/api/v6/defi/product/supported-platforms", { auth }),
145
+
146
+ /** POST /api/v6/defi/product/search — body: { tokenKeywordList, platformKeywordList?, pageNum?, chainIndex?, productGroup? } */
147
+ searchProducts: (auth: Auth, body: { tokenKeywordList: string[]; platformKeywordList?: string[]; pageNum?: number; chainIndex?: string; productGroup?: string }) =>
148
+ request("POST", "/api/v6/defi/product/search", { body, auth }),
149
+
150
+ /** GET /api/v6/defi/product/detail?investmentId= */
151
+ productDetail: (auth: Auth, investmentId: string) =>
152
+ request("GET", "/api/v6/defi/product/detail", { params: { investmentId }, auth }),
153
+
154
+ /** GET /api/v6/defi/product/rate/chart?investmentId=&timeRange= — APY 折线图 */
155
+ rateChart: (auth: Auth, investmentId: string, timeRange?: string) =>
156
+ request("GET", "/api/v6/defi/product/rate/chart", { params: { investmentId, timeRange }, auth }),
157
+
158
+ /** GET /api/v6/defi/product/tvl/chart?investmentId=&timeRange= — TVL 折线图 */
159
+ tvlChart: (auth: Auth, investmentId: string, timeRange?: string) =>
160
+ request("GET", "/api/v6/defi/product/tvl/chart", { params: { investmentId, timeRange }, auth }),
161
+
162
+ /** GET /api/v6/defi/product/depth-price/chart?investmentId=&chartType=&timeRange= — V3 深度/价格图 */
163
+ depthPriceChart: (auth: Auth, investmentId: string, chartType?: string, timeRange?: string) =>
164
+ request("GET", "/api/v6/defi/product/depth-price/chart", { params: { investmentId, chartType, timeRange }, auth }),
165
+
166
+ /** POST /api/v6/defi/product/detail/prepare — 交易前准备, 返回 investWithTokenList/receiveTokenInfo */
167
+ prepareTransaction: (auth: Auth, investmentId: string) =>
168
+ request("POST", "/api/v6/defi/product/detail/prepare", { body: { investmentId }, auth }),
169
+
170
+ /** POST /api/v6/defi/calculator/enter/info — V3 Pool 双币分配计算 */
171
+ calcEnterInfo: (auth: Auth, body: { inputAmount: string; inputTokenAddress: string; tokenDecimal: string; investmentId: string; address: string; tickLower: string; tickUpper: string }) =>
172
+ request("POST", "/api/v6/defi/calculator/enter/info", { body, auth }),
173
+
174
+ /** POST /api/v6/defi/transaction/enter — 申购/存款/借款, 返回 dataList(APPROVE→DEPOSIT) */
175
+ enter: (auth: Auth, body: Record<string, unknown>) =>
176
+ request("POST", "/api/v6/defi/transaction/enter", { body, auth }),
177
+
178
+ /** POST /api/v6/defi/transaction/exit — 赎回/还款, 返回 dataList */
179
+ exit: (auth: Auth, body: Record<string, unknown>) =>
180
+ request("POST", "/api/v6/defi/transaction/exit", { body, auth }),
181
+
182
+ /** POST /api/v6/defi/transaction/claim — 领取奖励, 返回 dataList */
183
+ claim: (auth: Auth, body: Record<string, unknown>) =>
184
+ request("POST", "/api/v6/defi/transaction/claim", { body, auth }),
185
+
186
+ /** POST /api/v6/defi/user/asset/platform/list — 用户持仓概览(协议维度) */
187
+ userPlatformList: (auth: Auth, body: { walletAddressList: Array<{ chainIndex: string; walletAddress: string; pubKey?: string }>; tag?: string }) =>
188
+ request("POST", "/api/v6/defi/user/asset/platform/list", { body, auth }),
189
+
190
+ /** POST /api/v6/defi/user/asset/platform/detail — 用户持仓明细(投资品维度) */
191
+ userPlatformDetail: (auth: Auth, body: Record<string, unknown>) =>
192
+ request("POST", "/api/v6/defi/user/asset/platform/detail", { body, auth }),
193
+ };
194
+
195
+ // ── Payments API (Agent Payments Protocol) ──────────────────
196
+ // 路径来源: API 参考 (HTTP端 + Agent端)
197
+
198
+ export const paymentsApi = {
199
+ // ── Agent 端 A2A (单次支付) ────────────────────────────
200
+
201
+ /** POST /api/v6/pay/a2a/payment/create — Seller 创建付款, 返回 paymentId+challenge+付款链接 */
202
+ create: (auth: Auth, body: { type: string; amount: string; symbol: string; recipient: string; description?: string; externalId?: string; expiresIn?: number; realm?: string; deliveries?: Record<string, unknown> }) =>
203
+ request("POST", "/api/v6/pay/a2a/payment/create", { body, auth }),
204
+
205
+ /** GET /api/v6/pay/a2a/p/{paymentId} — Buyer 拉取付款详情, PUBLIC */
206
+ detail: (paymentId: string) =>
207
+ request("GET", `/api/v6/pay/a2a/p/${paymentId}`),
208
+
209
+ /** POST /api/v6/pay/a2a/p/{paymentId}/credential — Buyer 提交 EIP-3009 签名凭证, PUBLIC */
210
+ submit: (paymentId: string, body: Record<string, unknown>) =>
211
+ request("POST", `/api/v6/pay/a2a/p/${paymentId}/credential`, { body }),
212
+
213
+ /** GET /api/v6/pay/a2a/p/{paymentId}/status — 查询支付状态, PUBLIC */
214
+ status: (paymentId: string) =>
215
+ request("GET", `/api/v6/pay/a2a/p/${paymentId}/status`),
216
+
217
+ // ── HTTP 端 x402 (单次/批量) ──────────────────────────
218
+
219
+ /** GET /api/v6/pay/x402/supported — 支持的网络/代币/scheme */
220
+ supported: (auth: Auth) =>
221
+ request("GET", "/api/v6/pay/x402/supported", { auth }),
222
+
223
+ /** POST /api/v6/pay/x402/verify — 验签 */
224
+ verify: (auth: Auth, body: Record<string, unknown>) =>
225
+ request("POST", "/api/v6/pay/x402/verify", { body, auth }),
226
+
227
+ /** POST /api/v6/pay/x402/settle — 上链结算 */
228
+ settle: (auth: Auth, body: Record<string, unknown>) =>
229
+ request("POST", "/api/v6/pay/x402/settle", { body, auth }),
230
+
231
+ /** GET /api/v6/pay/x402/settle/status?txHash= — 轮询结算状态 */
232
+ settleStatus: (auth: Auth, txHash: string) =>
233
+ request("GET", "/api/v6/pay/x402/settle/status", { params: { txHash }, auth }),
234
+ };
235
+
236
+ // ── Trade / DEX API v6 (经典兑换) ────────────────────────────
237
+ // 来源: Solana/EVM/Sui/Ton 搭建指南 + API 参考
238
+
239
+ export const tradeApi = {
240
+ /** GET /api/v6/dex/aggregator/supported/chain?chainIndex= — 返回 chainIndex/chainName/dexTokenApproveAddress */
241
+ supportedChain: (auth: Auth, chainIndex?: string) =>
242
+ request("GET", "/api/v6/dex/aggregator/supported/chain", { params: { chainIndex }, auth }),
243
+
244
+ allTokens: (auth: Auth, chainIndex: string) =>
245
+ request("GET", "/api/v6/dex/aggregator/all-tokens", { params: { chainIndex }, auth }),
246
+
247
+ liquidity: (auth: Auth, chainIndex: string) =>
248
+ request("GET", "/api/v6/dex/aggregator/get-liquidity", { params: { chainIndex }, auth }),
249
+
250
+ approveTransaction: (auth: Auth, chainIndex: string, tokenContractAddress: string, approveAmount: string) =>
251
+ request("GET", "/api/v6/dex/aggregator/approve-transaction", { params: { chainIndex, tokenContractAddress, approveAmount }, auth }),
252
+
253
+ quote: (auth: Auth, params: Record<string, string>) =>
254
+ request("GET", "/api/v6/dex/aggregator/quote", { params, auth }),
255
+
256
+ swap: (auth: Auth, params: Record<string, string>) =>
257
+ request("GET", "/api/v6/dex/aggregator/swap", { params, auth }),
258
+
259
+ swapInstruction: (auth: Auth, params: Record<string, string>) =>
260
+ request("GET", "/api/v6/dex/aggregator/swap-instruction", { params, auth }),
261
+
262
+ swapHistory: (auth: Auth, chainIndex: string, txHash: string, isFromMyProject?: boolean) =>
263
+ request("GET", "/api/v6/dex/aggregator/history", { params: { chainIndex, txHash, isFromMyProject }, auth }),
264
+ };
265
+
266
+ // ── Intent Swap API v6 ──────────────────────────────────────
267
+
268
+ export const intentApi = {
269
+ /** POST /api/v6/dex/aggregator/intent/create-order — 创建意图订单, 返回 orderUid */
270
+ createOrder: (auth: Auth, body: Record<string, unknown>) =>
271
+ request("POST", "/api/v6/dex/aggregator/intent/create-order", { body, auth }),
272
+
273
+ /** GET /api/v6/dex/aggregator/intent/order-list?userWalletAddress=&orderUid=&cursor=&limit= — 订单列表 */
274
+ orderList: (auth: Auth, params: { userWalletAddress?: string; orderUid?: string; cursor?: string; limit?: number }) =>
275
+ request("GET", "/api/v6/dex/aggregator/intent/order-list", { params, auth }),
276
+
277
+ /** GET /api/v6/dex/aggregator/intent/order-status?orderUid= — 查订单状态 */
278
+ orderStatus: (auth: Auth, orderUid: string) =>
279
+ request("GET", "/api/v6/dex/aggregator/intent/order-status", { params: { orderUid }, auth }),
280
+
281
+ /** POST /api/v6/dex/aggregator/intent/cancel-signdata — 获取取消签名数据 */
282
+ cancelSignData: (auth: Auth, userWalletAddress: string, orderUid: string) =>
283
+ request("POST", "/api/v6/dex/aggregator/intent/cancel-signdata", { body: { userWalletAddress, orderUid }, auth }),
284
+
285
+ /** POST /api/v6/dex/aggregator/intent/cancel-order — 取消订单 */
286
+ cancelOrder: (auth: Auth, userWalletAddress: string, orderUid: string, signature: string) =>
287
+ request("POST", "/api/v6/dex/aggregator/intent/cancel-order", { body: { userWalletAddress, orderUid, signature }, auth }),
288
+
289
+ /** GET /api/v6/dex/aggregator/intent/auction-info?auctionId=&txHash= — 查拍卖结果 */
290
+ auctionInfo: (auth: Auth, params: { auctionId?: string; txHash?: string }) =>
291
+ request("GET", "/api/v6/dex/aggregator/intent/auction-info", { params, auth }),
292
+ };
293
+
294
+ // ── Market API v6 (行情 — x402 付费) ──────────────────────
295
+
296
+ export const marketApi = {
297
+ /** GET /api/v6/dex/market/supported/chain?chainIndex= */
298
+ supportedChain: (auth: Auth, chainIndex?: string) =>
299
+ request("GET", "/api/v6/dex/market/supported/chain", { params: { chainIndex }, auth }),
300
+
301
+ /** POST /api/v6/dex/market/price — body: [{chainIndex, tokenContractAddress}] */
302
+ price: (auth: Auth, body: Array<{ chainIndex: string; tokenContractAddress: string }>) =>
303
+ request("POST", "/api/v6/dex/market/price", { body, auth }),
304
+
305
+ /** GET /api/v6/dex/market/candles?chainIndex=&tokenContractAddress=&bar=&after=&before=&limit= */
306
+ candles: (auth: Auth, params: { chainIndex: string; tokenContractAddress: string; bar?: string; after?: string; before?: string; limit?: string }) =>
307
+ request("GET", "/api/v6/dex/market/candles", { params, auth }),
308
+
309
+ /** GET /api/v6/dex/market/historical-candles?chainIndex=&tokenContractAddress=&bar=&after=&before=&limit= */
310
+ historicalCandles: (auth: Auth, params: { chainIndex: string; tokenContractAddress: string; bar?: string; after?: string; before?: string; limit?: string }) =>
311
+ request("GET", "/api/v6/dex/market/historical-candles", { params, auth }),
312
+
313
+ // ── 综合币价 ──────────────────────────────────────
314
+
315
+ /** POST /api/v6/dex/index/current-price — body: [{chainIndex, tokenContractAddress}] */
316
+ indexCurrentPrice: (auth: Auth, body: Array<{ chainIndex: string; tokenContractAddress: string }>) =>
317
+ request("POST", "/api/v6/dex/index/current-price", { body, auth }),
318
+
319
+ /** GET /api/v6/dex/index/historical-price?chainIndex=&tokenContractAddress=&limit=&cursor=&begin=&end=&period= */
320
+ indexHistoricalPrice: (auth: Auth, params: { chainIndex: string; tokenContractAddress?: string; limit?: string; cursor?: string; begin?: string; end?: string; period?: string }) =>
321
+ request("GET", "/api/v6/dex/index/historical-price", { params, auth }),
322
+
323
+ // ── 代币 API ──────────────────────────────────────
324
+
325
+ /** GET /api/v6/dex/market/token/search?chains=&search=&cursor=&limit= — 搜索代币 */
326
+ searchToken: (auth: Auth, chains: string, search: string, cursor?: string, limit?: string) =>
327
+ request("GET", "/api/v6/dex/market/token/search", { params: { chains, search, cursor, limit }, auth }),
328
+
329
+ /** POST /api/v6/dex/market/token/basic-info — body: [{chainIndex, tokenContractAddress}] */
330
+ tokenBasicInfo: (auth: Auth, body: Array<{ chainIndex: string; tokenContractAddress: string }>) =>
331
+ request("POST", "/api/v6/dex/market/token/basic-info", { body, auth }),
332
+
333
+ /** GET /api/v6/dex/market/token/top-liquidity?chainIndex=&tokenContractAddress= — 流动性池 */
334
+ tokenTopLiquidity: (auth: Auth, chainIndex: string, tokenContractAddress: string) =>
335
+ request("GET", "/api/v6/dex/market/token/top-liquidity", { params: { chainIndex, tokenContractAddress }, auth }),
336
+
337
+ /** POST /api/v6/dex/market/price-info — 批量交易信息, body: [{chainIndex, tokenContractAddress}], max 100 */
338
+ priceInfo: (auth: Auth, body: Array<{ chainIndex: string; tokenContractAddress: string }>) =>
339
+ request("POST", "/api/v6/dex/market/price-info", { body, auth }),
340
+
341
+ /** GET /api/v6/dex/market/token/advanced-info?chainIndex=&tokenContractAddress= — 代币安全分析(貔貅/开发者/狙击手) */
342
+ tokenAdvancedInfo: (auth: Auth, chainIndex: string, tokenContractAddress: string) =>
343
+ request("GET", "/api/v6/dex/market/token/advanced-info", { params: { chainIndex, tokenContractAddress }, auth }),
344
+
345
+ /** GET /api/v6/dex/market/token/hot-token?rankingType=&chainIndex=&rankBy=... — 热门代币(最多100) */
346
+ tokenHot: (auth: Auth, params: Record<string, string | number | boolean | undefined>) =>
347
+ request("GET", "/api/v6/dex/market/token/hot-token", { params, auth }),
348
+
349
+ /** GET /api/v6/dex/market/token/holder?chainIndex=&tokenContractAddress=&tagFilter=&cursor=&limit= — 前100持有人 */
350
+ tokenHolder: (auth: Auth, params: { chainIndex: string; tokenContractAddress: string; tagFilter?: string; cursor?: string; limit?: string }) =>
351
+ request("GET", "/api/v6/dex/market/token/holder", { params, auth }),
352
+
353
+ /** GET /api/v6/dex/market/token/cluster/supported/chain — 聚类支持链 */
354
+ tokenClusterSupportedChain: (auth: Auth) =>
355
+ request("GET", "/api/v6/dex/market/token/cluster/supported/chain", { auth }),
356
+
357
+ /** GET /api/v6/dex/market/token/cluster/overview?chainIndex=&tokenContractAddress= — 持仓集中度 */
358
+ tokenClusterOverview: (auth: Auth, chainIndex: string, tokenContractAddress: string) =>
359
+ request("GET", "/api/v6/dex/market/token/cluster/overview", { params: { chainIndex, tokenContractAddress }, auth }),
360
+
361
+ /** GET /api/v6/dex/market/token/cluster/list?chainIndex=&tokenContractAddress= — 聚类列表(top100集群) */
362
+ tokenClusterList: (auth: Auth, chainIndex: string, tokenContractAddress: string) =>
363
+ request("GET", "/api/v6/dex/market/token/cluster/list", { params: { chainIndex, tokenContractAddress }, auth }),
364
+
365
+ /** GET /api/v6/dex/market/token/cluster/top-holders?chainIndex=&tokenContractAddress=&rangeFilter= — 前10/50/100持仓 */
366
+ tokenClusterTopHolders: (auth: Auth, chainIndex: string, tokenContractAddress: string, rangeFilter: string) =>
367
+ request("GET", "/api/v6/dex/market/token/cluster/top-holders", { params: { chainIndex, tokenContractAddress, rangeFilter }, auth }),
368
+
369
+ /** GET /api/v6/dex/market/token/top-trader?chainIndex=&tokenContractAddress=&tagFilter=&cursor=&limit= — 前100盈利地址 */
370
+ tokenTopTrader: (auth: Auth, params: { chainIndex: string; tokenContractAddress: string; tagFilter?: string; cursor?: string; limit?: string }) =>
371
+ request("GET", "/api/v6/dex/market/token/top-trader", { params, auth }),
372
+
373
+ // ── 信号 API ──────────────────────────────────────
374
+
375
+ /** GET /api/v6/dex/market/signal/supported/chain — 信号支持链 */
376
+ signalSupportedChain: (auth: Auth) =>
377
+ request("GET", "/api/v6/dex/market/signal/supported/chain", { auth }),
378
+
379
+ /** POST /api/v6/dex/market/signal/list — 获取信号 */
380
+ signalList: (auth: Auth, body: Record<string, unknown>) =>
381
+ request("POST", "/api/v6/dex/market/signal/list", { body, auth }),
382
+
383
+ /** GET /api/v6/dex/market/leaderboard/supported/chain — 聪明钱榜单支持链 */
384
+ leaderboardSupportedChain: (auth: Auth) =>
385
+ request("GET", "/api/v6/dex/market/leaderboard/supported/chain", { auth }),
386
+
387
+ /** GET /api/v6/dex/market/leaderboard/list?chainIndex=&timeFrame=&sortBy=&walletType=... — 聪明钱榜单 */
388
+ leaderboardList: (auth: Auth, params: Record<string, string>) =>
389
+ request("GET", "/api/v6/dex/market/leaderboard/list", { params, auth }),
390
+
391
+ // ── Memepump / 扫链 ──────────────────────────────────
392
+
393
+ /** GET /api/v6/dex/market/memepump/supported/chainsProtocol — 支持的链+协议 */
394
+ memepumpSupported: (auth: Auth) =>
395
+ request("GET", "/api/v6/dex/market/memepump/supported/chainsProtocol", { auth }),
396
+
397
+ /** GET /api/v6/dex/market/memepump/tokenList?chainIndex=&stage=&... — 代币列表(最多30) */
398
+ memepumpTokenList: (auth: Auth, params: Record<string, string | boolean | undefined>) =>
399
+ request("GET", "/api/v6/dex/market/memepump/tokenList", { params, auth }),
400
+
401
+ /** GET /api/v6/dex/market/memepump/tokenDetails?chainIndex=&tokenContractAddress=&walletAddress= */
402
+ memepumpTokenDetails: (auth: Auth, chainIndex: string, tokenContractAddress: string, walletAddress?: string) =>
403
+ request("GET", "/api/v6/dex/market/memepump/tokenDetails", { params: { chainIndex, tokenContractAddress, walletAddress }, auth }),
404
+
405
+ /** GET /api/v6/dex/market/memepump/tokenDevInfo?chainIndex=&tokenContractAddress= */
406
+ memepumpTokenDevInfo: (auth: Auth, chainIndex: string, tokenContractAddress: string) =>
407
+ request("GET", "/api/v6/dex/market/memepump/tokenDevInfo", { params: { chainIndex, tokenContractAddress }, auth }),
408
+
409
+ /** GET /api/v6/dex/market/memepump/similarToken?chainIndex=&tokenContractAddress= */
410
+ memepumpSimilarToken: (auth: Auth, chainIndex: string, tokenContractAddress: string) =>
411
+ request("GET", "/api/v6/dex/market/memepump/similarToken", { params: { chainIndex, tokenContractAddress }, auth }),
412
+
413
+ /** GET /api/v6/dex/market/memepump/tokenBundleInfo?chainIndex=&tokenContractAddress= */
414
+ memepumpBundleInfo: (auth: Auth, chainIndex: string, tokenContractAddress: string) =>
415
+ request("GET", "/api/v6/dex/market/memepump/tokenBundleInfo", { params: { chainIndex, tokenContractAddress }, auth }),
416
+
417
+ /** GET /api/v6/dex/market/memepump/apedWallet?chainIndex=&tokenContractAddress=&walletAddress= */
418
+ memepumpApedWallet: (auth: Auth, chainIndex: string, tokenContractAddress: string, walletAddress?: string) =>
419
+ request("GET", "/api/v6/dex/market/memepump/apedWallet", { params: { chainIndex, tokenContractAddress, walletAddress }, auth }),
420
+
421
+ // ── Portfolio / 地址分析 ─────────────────────────────
422
+
423
+ /** GET /api/v6/dex/market/portfolio/supported/chain */
424
+ portfolioSupportedChain: (auth: Auth) =>
425
+ request("GET", "/api/v6/dex/market/portfolio/supported/chain", { auth }),
426
+
427
+ /** GET /api/v6/dex/market/portfolio/overview?chainIndex=&walletAddress=&timeFrame= */
428
+ portfolioOverview: (auth: Auth, chainIndex: string, walletAddress: string, timeFrame: string) =>
429
+ request("GET", "/api/v6/dex/market/portfolio/overview", { params: { chainIndex, walletAddress, timeFrame }, auth }),
430
+
431
+ /** GET /api/v6/dex/market/portfolio/recent-pnl?chainIndex=&walletAddress=&cursor=&limit= */
432
+ portfolioRecentPnl: (auth: Auth, params: { chainIndex: string; walletAddress: string; cursor?: string; limit?: string }) =>
433
+ request("GET", "/api/v6/dex/market/portfolio/recent-pnl", { params, auth }),
434
+
435
+ /** GET /api/v6/dex/market/portfolio/token/latest-pnl?chainIndex=&walletAddress=&tokenContractAddress= */
436
+ portfolioTokenLatestPnl: (auth: Auth, chainIndex: string, walletAddress: string, tokenContractAddress: string) =>
437
+ request("GET", "/api/v6/dex/market/portfolio/token/latest-pnl", { params: { chainIndex, walletAddress, tokenContractAddress }, auth }),
438
+
439
+ /** GET /api/v6/dex/market/portfolio/dex-history?chainIndex=&walletAddress=&begin=&end=&... */
440
+ portfolioDexHistory: (auth: Auth, params: { chainIndex: string; walletAddress: string; begin: string; end: string; tokenContractAddress?: string; type?: string; cursor?: string; limit?: string }) =>
441
+ request("GET", "/api/v6/dex/market/portfolio/dex-history", { params, auth }),
442
+
443
+ /** GET /api/v6/dex/market/address-tracker/trades?trackerType=&walletAddress=&tradeType=... */
444
+ addressTrackerTrades: (auth: Auth, params: { trackerType: string; walletAddress?: string; tradeType?: string; chainIndex?: string; minVolume?: string; maxVolume?: string; minHolders?: string; minMarketCap?: string; maxMarketCap?: string; minLiquidity?: string; maxLiquidity?: string }) =>
445
+ request("GET", "/api/v6/dex/market/address-tracker/trades", { params, auth }),
446
+
447
+ // ── Social / 社媒 ──────────────────────────────────
448
+
449
+ /** GET /api/v6/dex/market/social/news/latest */
450
+ socialNewsLatest: (auth: Auth, params: Record<string, string>) =>
451
+ request("GET", "/api/v6/dex/market/social/news/latest", { params, auth }),
452
+
453
+ /** GET /api/v6/dex/market/social/news/by-symbol */
454
+ socialNewsBySymbol: (auth: Auth, params: Record<string, string>) =>
455
+ request("GET", "/api/v6/dex/market/social/news/by-symbol", { params, auth }),
456
+
457
+ /** GET /api/v6/dex/market/social/news/search */
458
+ socialNewsSearch: (auth: Auth, params: Record<string, string>) =>
459
+ request("GET", "/api/v6/dex/market/social/news/search", { params, auth }),
460
+
461
+ /** GET /api/v6/dex/market/social/news/detail?articleId= */
462
+ socialNewsDetail: (auth: Auth, articleId: string, language?: string) =>
463
+ request("GET", "/api/v6/dex/market/social/news/detail", { params: { articleId, language }, auth }),
464
+
465
+ /** GET /api/v6/dex/market/social/news/platforms */
466
+ socialNewsPlatforms: (auth: Auth) =>
467
+ request("GET", "/api/v6/dex/market/social/news/platforms", { auth }),
468
+
469
+ /** GET /api/v6/dex/market/social/sentiment/symbol?tokenSymbols=&timeFrame=&trendPoints= */
470
+ socialSentimentSymbol: (auth: Auth, params: Record<string, string>) =>
471
+ request("GET", "/api/v6/dex/market/social/sentiment/symbol", { params, auth }),
472
+
473
+ /** GET /api/v6/dex/market/social/sentiment/ranking?timeFrame=&sortBy=&limit= */
474
+ socialSentimentRanking: (auth: Auth, params: Record<string, string>) =>
475
+ request("GET", "/api/v6/dex/market/social/sentiment/ranking", { params, auth }),
476
+
477
+ /** GET /api/v6/dex/market/social/vibe/timeline?chainIndex=&tokenAddress=&timeFrame= */
478
+ socialVibeTimeline: (auth: Auth, params: Record<string, string>) =>
479
+ request("GET", "/api/v6/dex/market/social/vibe/timeline", { params, auth }),
480
+
481
+ /** GET /api/v6/dex/market/social/vibe/top-kols?chainIndex=&tokenAddress=&sortBy=&timeFrame=&limit= */
482
+ socialVibeTopKols: (auth: Auth, params: Record<string, string>) =>
483
+ request("GET", "/api/v6/dex/market/social/vibe/top-kols", { params, auth }),
484
+
485
+ /** GET /api/v6/dex/market/trades?chainIndex=&tokenContractAddress=&after=&limit=&tagFilter=&walletAddressFilter= */
486
+ trades: (auth: Auth, params: { chainIndex: string; tokenContractAddress: string; after?: string; limit?: string; tagFilter?: string; walletAddressFilter?: string }) =>
487
+ request("GET", "/api/v6/dex/market/trades", { params, auth }),
488
+ };
@@ -0,0 +1,100 @@
1
+ /**
2
+ * h-mcp — 共享工具层
3
+ * 规范: OnchainOS-API对接规范.md §四/§六 + AGENT-MCP-RULES.md §4/§8
4
+ */
5
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
6
+
7
+ // ── 类型 ────────────────────────────────────────────────────
8
+ export interface Auth { apiKey: string; secret: string; passphrase: string; }
9
+
10
+ export interface NextStep {
11
+ action: string;
12
+ tool: string;
13
+ params?: Record<string, unknown>;
14
+ condition?: string;
15
+ }
16
+
17
+ // ── toResult ────────────────────────────────────────────────
18
+ export function toResult<T>(data: T, opts?: { warnings?: string[]; nextSteps?: NextStep[] }): CallToolResult {
19
+ const body: Record<string, unknown> = { success: true, data, tsIso: new Date().toISOString() };
20
+ if (opts?.warnings?.length) body.warnings = opts.warnings;
21
+ if (opts?.nextSteps?.length) body.nextSteps = opts.nextSteps;
22
+ return { content: [{ type: "text", text: JSON.stringify(body) }] };
23
+ }
24
+
25
+ // ── toError ─────────────────────────────────────────────────
26
+ export function toError(e: unknown): CallToolResult {
27
+ const msg = e instanceof Error ? e.message : String(e);
28
+ let error: { code: string; message: string; fix: string; retryAfter?: number };
29
+
30
+ // OKX 业务错误码 (HTTP 200, code != "0")
31
+ if (msg.includes("OKX 50001")) {
32
+ error = { code: "SERVICE_UNAVAILABLE", message: msg, fix: "服务暂不可用, 等待几秒后重试", retryAfter: 3000 };
33
+ } else if (msg.includes("OKX 50011")) {
34
+ error = { code: "RATE_LIMITED", message: msg, fix: "超出频率限制, 请降低请求频率", retryAfter: 5000 };
35
+ } else if (msg.includes("OKX 50103") || msg.includes("OKX 50104") || msg.includes("OKX 50105") || msg.includes("OKX 50106") || msg.includes("OKX 50107") || msg.includes("OKX 50111") || msg.includes("OKX 50112") || msg.includes("OKX 50113")) {
36
+ error = { code: "AUTH_ERROR", message: msg, fix: "API Key 签名错误, 请检查 OKX_API_KEY/OKX_SECRET_KEY/OKX_PASSPHRASE" };
37
+ } else if (msg.includes("OKX 81001")) {
38
+ error = { code: "BAD_PARAMETER", message: msg, fix: "参数不正确, 请检查参数名/类型/必填项" };
39
+ } else if (msg.includes("OKX 81108")) {
40
+ error = { code: "WALLET_TYPE_MISMATCH", message: msg, fix: "钱包类型不匹配, 请确认链和地址格式一致" };
41
+ } else if (msg.includes("OKX 81104")) {
42
+ error = { code: "CHAIN_NOT_SUPPORT", message: msg, fix: "该链不支持此操作, 请调用 supported_chain 确认可用链" };
43
+ } else if (msg.includes("OKX 81152")) {
44
+ error = { code: "COIN_NOT_EXIST", message: msg, fix: "代币不存在, 请用 onchainos_token_search 搜索正确地址" };
45
+ } else if (msg.includes("OKX 81451")) {
46
+ error = { code: "NODE_FAILED", message: msg, fix: "节点返回失败, 请稍后重试", retryAfter: 2000 };
47
+ } else if (msg.includes("OKX 84001") || msg.includes("OKX 84003")) {
48
+ error = { code: "PROTOCOL_NOT_SUPPORT", message: msg, fix: "协议不支持, 请用 onchainos_defi_supported_platforms 确认支持列表" };
49
+ } else if (msg.includes("OKX 84007") || msg.includes("OKX 84024")) {
50
+ error = { code: "PRODUCT_NOT_SUPPORT", message: msg, fix: "投资品不支持或不存在, 请用 onchainos_defi_search_products 重新搜索" };
51
+ } else if (msg.includes("OKX 84010")) {
52
+ error = { code: "TOKEN_NOT_SUPPORT", message: msg, fix: "代币不支持, 请确认 tokenAddress 正确" };
53
+ } else if (msg.includes("OKX 84014")) {
54
+ error = { code: "BALANCE_FAILED", message: msg, fix: "余额验证失败, 请确认钱包余额充足" };
55
+ } else if (msg.includes("OKX 84016")) {
56
+ error = { code: "CONTRACT_FAILED", message: msg, fix: "智能合约执行失败, 请检查参数和链上状态" };
57
+ } else if (msg.includes("OKX 84019")) {
58
+ error = { code: "ADDRESS_MISMATCH", message: msg, fix: "地址格式不匹配, 请用 onchainos_wallet_validate_address 校验" };
59
+ } else if (msg.includes("OKX 84021")) {
60
+ error = { code: "SYNCING", message: msg, fix: "资产正在同步中, 请等待片刻后重试", retryAfter: 3000 };
61
+ } else if (msg.includes("OKX 84025")) {
62
+ error = { code: "NO_REWARD", message: msg, fix: "当前无可领取的奖励" };
63
+ } else if (msg.includes("OKX 84029")) {
64
+ error = { code: "LOCKED", message: msg, fix: "本金仍在锁定期, 无法领取" };
65
+ } else if (msg.includes("OKX 84030")) {
66
+ error = { code: "EXPIRED", message: msg, fix: "本金领取已过期" };
67
+ } else if (msg.includes("OKX 84032")) {
68
+ error = { code: "V3_ONLY", message: msg, fix: "此接口仅适用于 V3 DEX Pool 投资品" };
69
+ }
70
+ // HTTP 传输层错误
71
+ else if (msg.includes("429") || msg.includes("RATE")) {
72
+ error = { code: "RATE_LIMITED", message: msg, fix: "请等待 1-2 秒后重试", retryAfter: 1000 };
73
+ } else if (msg.includes("503")) {
74
+ error = { code: "UNAVAILABLE", message: msg, fix: "服务暂时不可用,请等待 5 秒后重试", retryAfter: 5000 };
75
+ } else if (msg.includes("400")) {
76
+ error = { code: "BAD_REQUEST", message: msg, fix: "请检查参数格式和必填字段" };
77
+ } else if (msg.includes("422")) {
78
+ error = { code: "BUSINESS_REJECT", message: msg, fix: "参数合法但业务不可行,请检查余额/授权等条件" };
79
+ } else if (msg.includes("500")) {
80
+ error = { code: "SYSTEM_ERROR", message: msg, fix: "系统错误,不建议重试" };
81
+ } else {
82
+ error = { code: "ERROR", message: msg, fix: "请检查网络和 API 配置" };
83
+ }
84
+ return {
85
+ content: [{ type: "text", text: JSON.stringify({ success: false, error, tsIso: new Date().toISOString() }) }],
86
+ isError: true,
87
+ };
88
+ }
89
+
90
+ // ── AUTH_REQUIRED ───────────────────────────────────────────
91
+ export function AUTH_REQUIRED(scope: "READ" | "TRADE" = "READ"): CallToolResult {
92
+ return {
93
+ content: [{ type: "text", text: JSON.stringify({
94
+ success: false,
95
+ error: { code: "AUTH_REQUIRED", message: `需要 OKX API Key(${scope} 权限)`, fix: "请设置环境变量 OKX_API_KEY / OKX_SECRET_KEY / OKX_PASSPHRASE" },
96
+ tsIso: new Date().toISOString(),
97
+ }) }],
98
+ isError: true,
99
+ };
100
+ }