100x-sdk 1.0.1

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.
@@ -0,0 +1,734 @@
1
+ const axios = require('axios');
2
+ const CurveAMM = require('../utils/curve_amm');
3
+
4
+ /**
5
+ * Fast API Module
6
+ * Access data through API interfaces from centralized servers, quickly obtain transaction parameters
7
+ * Enables smooth trading operations
8
+ */
9
+ class FastModule {
10
+ constructor(sdk) {
11
+ this.sdk = sdk;
12
+ this.baseUrl = sdk.fastApiUrl;
13
+
14
+ // Only create axios instance if baseUrl is provided
15
+ // This allows SDK to be initialized with chain-only mode
16
+ if (this.baseUrl) {
17
+ // Create axios instance
18
+ this.httpClient = axios.create({
19
+ baseURL: this.baseUrl,
20
+ timeout: 10000,
21
+ headers: {
22
+ 'Accept': 'application/json',
23
+ 'Content-Type': 'application/json'
24
+ }
25
+ });
26
+ } else {
27
+ this.httpClient = null;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Check if FastModule is properly configured
33
+ * @private
34
+ */
35
+ _ensureConfigured() {
36
+ if (!this.baseUrl || !this.httpClient) {
37
+ throw new Error('FastModule requires fastApiUrl configuration. Either provide fastApiUrl in options or use defaultDataSource: "chain"');
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Get token list (获取代币列表)
43
+ * @param {Object} options - Query parameters
44
+ * @param {number} options.limit - Items per page, default 10 (每页数量,默认 10)
45
+ * @param {number} options.before_timestamp - Cursor for pagination (分页游标,Unix 时间戳)
46
+ * @returns {Promise<Object>} Token list data
47
+ *
48
+ * @example
49
+ * // Get latest 5 tokens
50
+ * const result = await sdk.fast.mints({ limit: 5 });
51
+ * // Return format:
52
+ * // {
53
+ * // "code": 200,
54
+ * // "msg": "success",
55
+ * // "data": {
56
+ * // "tokens": [
57
+ * // {
58
+ * // "payer": "8iGFeUkRpyRx8w5uoUMbfZepUr6BfTdPuJmqGoNBntdb",
59
+ * // "mint_account": "7K7WLJ5rKgnjG3xtknAoRmFqWoRS6cNR3TsDEN7MnLJu",
60
+ * // "curve_account": "58XWvk4q9NeD2fbSjy5jRyrG7ub6V8kQFvkf8EuDFVPx",
61
+ * // "pool_token_account": "69cooFpg3Ykg8T6iaUeCT4HhKUvZGwDbjUHUAsQa3oD",
62
+ * // "pool_sol_account": "5vWvG42FrKPPwdZDs7TLVncTLf6VqyQnrhgjuGtWiseH",
63
+ * // "fee_recipient": "GesAj2dTn2wdNcxj4x8qsqS9aNRVPBPkE76aaqg7skxu",
64
+ * // "base_fee_recipient": "5YHi1HsxobLiTD6NQfHJQpoPoRjMuNyXp4RroTvR6dKi",
65
+ * // "params_account": "4yTBVZZQLiYi2p2xgwQXPb329z9jsQnhoPJyGmF3R4hb",
66
+ * // "swap_fee": 1000,
67
+ * // "borrow_fee": 1200,
68
+ * // "fee_discount_flag": 0,
69
+ * // "name": "AutoCreatedToken",
70
+ * // "symbol": "ACT",
71
+ * // "uri": "https://example.com/token-metadata.json",
72
+ * // "up_orderbook": "E63BemzshShTMk3Cbm7wYzm5muS6inokKM1hZWM8aDwp",
73
+ * // "down_orderbook": "CSva3cHwTYsJiK21H9Li4HL9pVzvA1hhy98noYBsbvxb",
74
+ * // "latest_price": "2795899347623485554",
75
+ * // "created_at": 1763804750,
76
+ * // "created_slot": 3156632,
77
+ * // "updated_at": 1763804750,
78
+ * // "extras": {}
79
+ * // }
80
+ * // ],
81
+ * // "total": 5,
82
+ * // "next_cursor": "1763774111"
83
+ * // }
84
+ * // }
85
+ *
86
+ * // Use pagination with cursor:
87
+ * const page1 = await sdk.fast.mints({ limit: 5 });
88
+ * const page2 = await sdk.fast.mints({ limit: 5, before_timestamp: page1.data.next_cursor });
89
+ *
90
+ * // Access token data:
91
+ * const tokens = result.data.tokens; // 完整代币对象数组
92
+ * const mintAddresses = tokens.map(t => t.mint_account); // 提取代币地址
93
+ */
94
+ async mints(options = {}) {
95
+ const params = {
96
+ limit: options.limit || 10
97
+ };
98
+
99
+ // Add cursor if provided
100
+ if (options.before_timestamp) {
101
+ params.before_timestamp = options.before_timestamp;
102
+ }
103
+
104
+ return this._directApiCall({
105
+ endpoint: '/api/tokens/latest',
106
+ method: 'GET'
107
+ }, params);
108
+ }
109
+
110
+ /**
111
+ * Get token details (获取代币详情信息)
112
+ * @param {string} mint - Token mint address (代币地址)
113
+ * @returns {Promise<Object>} Token details data
114
+ *
115
+ * @example
116
+ * // Get single token details
117
+ * const info = await sdk.fast.mint_info('Dj5FhAmbQ2qNDUXQV3ST2kJXTH3C3B6mrwfYXPhGb6ea');
118
+ * // Return format:
119
+ * // {
120
+ * // "code": 200,
121
+ * // "msg": "success",
122
+ * // "data": {
123
+ * // "payer": "8iGFeUkRpyRx8w5uoUMbfZepUr6BfTdPuJmqGoNBntdb",
124
+ * // "mint_account": "Dj5FhAmbQ2qNDUXQV3ST2kJXTH3C3B6mrwfYXPhGb6ea",
125
+ * // "curve_account": "3hhFqoPi4C4urNfxCW5iwdSZPg6bQbNc4mwWtuagDsEn",
126
+ * // "pool_token_account": "DTLk1p97Z5tFaZGRSV6qSkogf3gBfj57Jt1xMCAwwBGJ",
127
+ * // "pool_sol_account": "8oxE8wS1Zu4nktRyZqCiTXi6wEburBtRLszLHuZoyJ4b",
128
+ * // "fee_recipient": "GesAj2dTn2wdNcxj4x8qsqS9aNRVPBPkE76aaqg7skxu",
129
+ * // "base_fee_recipient": "5YHi1HsxobLiTD6NQfHJQpoPoRjMuNyXp4RroTvR6dKi",
130
+ * // "params_account": "4yTBVZZQLiYi2p2xgwQXPb329z9jsQnhoPJyGmF3R4hb",
131
+ * // "swap_fee": 1000,
132
+ * // "borrow_fee": 1200,
133
+ * // "fee_discount_flag": 0,
134
+ * // "name": "AutoCreatedToken",
135
+ * // "symbol": "ACT",
136
+ * // "uri": "https://example.com/token-metadata.json",
137
+ * // "up_orderbook": "8gYvmYKFNk6PN78yET2BB6xtyCKaEMgn65b6TA8VFMkM",
138
+ * // "down_orderbook": "6kk8XC3z1WUFJD8GHqqgi79GmoKd9houJ8GUEXk8ki6N",
139
+ * // "latest_price": "2795899347623485554",
140
+ * // "created_at": 1763777802,
141
+ * // "created_slot": 3090283,
142
+ * // "updated_at": 1763777802,
143
+ * // "extras": {}
144
+ * // }
145
+ * // }
146
+ *
147
+ * // Access token data:
148
+ * const tokenData = info.data;
149
+ * const mintAccount = tokenData.mint_account;
150
+ * const latestPrice = tokenData.latest_price;
151
+ */
152
+ async mint_info(mint) {
153
+ // Validate input
154
+ if (!mint || typeof mint !== 'string') {
155
+ throw new Error('mint_info: 代币地址必须是有效的字符串');
156
+ }
157
+
158
+ return this._directApiCall({
159
+ endpoint: '/api/tokens/mint',
160
+ method: 'GET',
161
+ usePathParam: true
162
+ }, { mint });
163
+ }
164
+
165
+ /**
166
+ * 直接API调用方法
167
+ * @private
168
+ * @param {Object} config - 数据配置
169
+ * @param {Object} params - 请求参数
170
+ * @returns {Promise<Object>} API响应数据
171
+ */
172
+ async _directApiCall(config, params = {}) {
173
+ // Ensure FastModule is properly configured before making API calls
174
+ this._ensureConfigured();
175
+
176
+ try {
177
+ let url = config.endpoint;
178
+ let queryParams = { ...params };
179
+
180
+ // Handle path parameters (e.g., /api/tokens/mint/:mint)
181
+ if (config.usePathParam && params.mint) {
182
+ url = `${config.endpoint}/${params.mint}`;
183
+ delete queryParams.mint; // Remove from query params
184
+ }
185
+
186
+ const requestConfig = {
187
+ method: config.method || 'GET',
188
+ url: url
189
+ };
190
+
191
+ // 根据请求方法设置参数
192
+ if (config.method === 'POST') {
193
+ requestConfig.data = queryParams; // POST 请求使用 data
194
+ } else {
195
+ requestConfig.params = queryParams; // GET 请求使用 params
196
+ }
197
+
198
+ const response = await this.httpClient.request(requestConfig);
199
+
200
+ // 检查新旧API响应格式
201
+ // 新格式: { code: 200, msg: "success", data: {...} }
202
+ // 旧格式: { success: true, data: {...}, message: "..." }
203
+ const isNewFormat = response.data && typeof response.data.code === 'number';
204
+ const isOldFormat = response.data && typeof response.data.success === 'boolean';
205
+
206
+ if (isNewFormat) {
207
+ // 新 API 格式
208
+ if (response.data.code !== 200) {
209
+ throw new Error(`API请求失败: ${response.data.msg || '未知错误'}`);
210
+ }
211
+ } else if (isOldFormat) {
212
+ // 旧 API 格式(向后兼容)
213
+ if (!response.data.success) {
214
+ throw new Error(`API请求失败: ${response.data.message || '未知错误'}`);
215
+ }
216
+ } else {
217
+ // 未知格式
218
+ throw new Error('API返回格式无法识别');
219
+ }
220
+
221
+ return response.data;
222
+
223
+ } catch (error) {
224
+ if (error.response) {
225
+ // API返回错误
226
+ const errorMsg = error.response.data?.msg || error.response.data?.message || error.message;
227
+ throw new Error(`API请求失败 [${error.response.status}]: ${errorMsg}`);
228
+ } else if (error.request) {
229
+ // 网络错误
230
+ throw new Error(`网络请求失败: 无法连接到 ${this.baseUrl}`);
231
+ } else {
232
+ // 其他错误
233
+ throw new Error(`请求处理失败: ${error.message}`);
234
+ }
235
+ }
236
+ }
237
+
238
+
239
+
240
+ /**
241
+ * 获取订单数据 Get Orders Data (查询活跃订单 Active Orders)
242
+ * @param {string} mint - 代币地址 Token mint address
243
+ * @param {Object} options - 查询参数 Query parameters
244
+ * @param {string} options.type - 订单类型 Order type: "up_orders" (做空/short) 或 "down_orders" (做多/long)
245
+ * @param {number} options.page - 页码,默认1 Page number, default 1
246
+ * @param {number} options.limit - 每页数量,默认500 Items per page, default 500
247
+ * @returns {Promise<Object>} 订单数据,包含订单列表及分页信息 Order data with order list and pagination info
248
+ *
249
+ * @example
250
+ * // 获取做多订单 Get long orders
251
+ * const ordersData = await sdk.fast.orders('T3NFPvYvpULCTgrhHb4b4Sj5J1qtSNvyKZfE8hCvuKM', { type: 'down_orders' });
252
+ *
253
+ * // 返回值示例 Return value example (compatible with chain.js format):
254
+ * // {
255
+ * // "success": true,
256
+ * // "data": {
257
+ * // "orders": [
258
+ * // {
259
+ * // "index": 0, // 订单在 OrderBook 中的索引位置
260
+ * // "user": "7621yjkZJ1jxBHw3oCPoazFfMx82NWBSARk2AGV7EBig", // 用户地址
261
+ * // "lock_lp_start_price": "2656104380242311276", // LP 开始价格(字符串)
262
+ * // "lock_lp_end_price": "2325402748045870207", // LP 结束价格(字符串)
263
+ * // "open_price": "2795899347623485554", // 开仓价格(字符串)
264
+ * // "order_id": "0", // 订单 ID(字符串)
265
+ * // "lock_lp_sol_amount": 1880793407, // LP 锁定 SOL 数量(lamports)
266
+ * // "lock_lp_token_amount": 75677963031921, // LP 锁定代币数量(最小单位)
267
+ * // "next_lp_sol_amount": 0, // Next LP SOL 数量
268
+ * // "next_lp_token_amount": 0, // Next LP Token 数量
269
+ * // "margin_init_sol_amount": 418211213, // 初始保证金
270
+ * // "margin_sol_amount": 418211213, // 保证金 SOL 数量(lamports)
271
+ * // "borrow_amount": 2276435100, // 借款数量(lamports)
272
+ * // "position_asset_amount": 75677963031921, // 持仓资产数量(最小单位)
273
+ * // "realized_sol_amount": 0, // 已实现收益
274
+ * // "version": 2, // 版本号
275
+ * // "start_time": 1764047379, // 开始时间(Unix 时间戳)
276
+ * // "end_time": 1764652179, // 结束时间(Unix 时间戳)
277
+ * // "next_order": 1, // 链表中下一个订单索引 (65535=none)
278
+ * // "prev_order": 65535, // 链表中上一个订单索引 (65535=none)
279
+ * // "borrow_fee": 1200, // 借款费用(基点,1200 = 12%)
280
+ * // "order_type": "down_orders" // 订单类型(字符串)
281
+ * // }
282
+ * // ],
283
+ * // "total": 10, // 总订单数量 (total_count)
284
+ * // "order_type": "down_orders", // 订单类型(字符串)
285
+ * // "mint_account": "T3NFPvYvpULCTgrhHb4b4Sj5J1qtSNvyKZfE8hCvuKM", // 查询的代币地址
286
+ * // "page": 1, // 当前页码
287
+ * // "limit": 3, // 每页限制
288
+ * // "has_next": true, // 是否有下一页
289
+ * // "has_prev": false // 是否有上一页
290
+ * // },
291
+ * // "message": "Operation successful" // 操作结果消息
292
+ * // }
293
+ *
294
+ * // 访问订单数据 Access order data:
295
+ * const orders = ordersData.data.orders; // 订单数组
296
+ * const totalOrders = ordersData.data.total; // 总订单数量
297
+ * const firstOrder = orders[0]; // 第一个订单
298
+ * const userAddress = firstOrder.user; // 用户地址
299
+ * const marginAmount = firstOrder.margin_sol_amount; // 保证金数量(lamports)
300
+ * const borrowFee = firstOrder.borrow_fee; // 借款费用(基点)
301
+ *
302
+ * // 获取做空订单 Get short orders:
303
+ * const shortOrders = await sdk.fast.orders(mint, { type: 'up_orders' });
304
+ *
305
+ * // 分页获取订单 Get paginated orders:
306
+ * const pageTwo = await sdk.fast.orders(mint, { type: 'down_orders', page: 2, limit: 100 });
307
+ */
308
+ async orders(mint, options = {}) {
309
+ // Ensure FastModule is properly configured
310
+ this._ensureConfigured();
311
+
312
+ try {
313
+ // Parameter validation
314
+ if (!mint || typeof mint !== 'string') {
315
+ throw new Error('orders: mint address must be a valid string');
316
+ }
317
+
318
+ // Extract parameters with defaults
319
+ const type = options.type || 'down_orders';
320
+ const page = options.page || 1;
321
+ const pageSize = options.limit || 55000;
322
+
323
+ // Validate order type
324
+ if (!['up_orders', 'down_orders'].includes(type)) {
325
+ throw new Error('orders: order type must be "up_orders" or "down_orders"');
326
+ }
327
+
328
+ // Convert type to direction for new API
329
+ // "up_orders" = short orders = direction 'up'
330
+ // "down_orders" = long orders = direction 'dn'
331
+ const direction = type === 'up_orders' ? 'up' : 'dn';
332
+
333
+ // Build API endpoint path: /api/orderbook/{mint}/{direction}
334
+ const url = `/api/orderbook/${mint}/${direction}`;
335
+
336
+ // Build query parameters
337
+ const queryParams = {
338
+ page: page,
339
+ page_size: pageSize
340
+ };
341
+
342
+ // Make API request
343
+ const requestConfig = {
344
+ method: 'GET',
345
+ url: url,
346
+ params: queryParams
347
+ };
348
+
349
+ const response = await this.httpClient.request(requestConfig);
350
+
351
+ // Check new API response format
352
+ const isNewFormat = response.data && typeof response.data.code === 'number';
353
+
354
+ if (!isNewFormat) {
355
+ throw new Error('API返回格式无法识别');
356
+ }
357
+
358
+ if (response.data.code !== 200) {
359
+ throw new Error(`API请求失败: ${response.data.msg || '未知错误'}`);
360
+ }
361
+
362
+ // Transform new API format to match chain.js format for compatibility
363
+ const apiData = response.data.data;
364
+
365
+ // Process orders: convert order_type from number to string, ensure all fields are properly formatted
366
+ const processedOrders = apiData.orders.map(order => ({
367
+ // Keep index from API (physical slot index in OrderBook)
368
+ index: order.index,
369
+
370
+ // User address
371
+ user: order.user,
372
+
373
+ // Price fields (ensure strings)
374
+ lock_lp_start_price: String(order.lock_lp_start_price),
375
+ lock_lp_end_price: String(order.lock_lp_end_price),
376
+ open_price: String(order.open_price),
377
+
378
+ // Order ID (ensure string)
379
+ order_id: order.order_id != null ? String(order.order_id) : undefined,
380
+
381
+ // Amount fields (ensure strings) - Fix precision issue
382
+ lock_lp_sol_amount: String(order.lock_lp_sol_amount),
383
+ lock_lp_token_amount: String(order.lock_lp_token_amount),
384
+ next_lp_sol_amount: String(order.next_lp_sol_amount),
385
+ next_lp_token_amount: String(order.next_lp_token_amount),
386
+
387
+ // Margin fields (ensure strings) - Fix precision issue
388
+ margin_init_sol_amount: String(order.margin_init_sol_amount),
389
+ margin_sol_amount: String(order.margin_sol_amount),
390
+
391
+ // Position fields (ensure strings) - Fix precision issue
392
+ borrow_amount: String(order.borrow_amount),
393
+ position_asset_amount: String(order.position_asset_amount),
394
+ realized_sol_amount: String(order.realized_sol_amount),
395
+
396
+ // Version
397
+ version: order.version,
398
+
399
+ // Time fields
400
+ start_time: order.start_time,
401
+ end_time: order.end_time,
402
+
403
+ // Linked list navigation
404
+ next_order: order.next_order,
405
+ prev_order: order.prev_order,
406
+
407
+ // Fee
408
+ borrow_fee: order.borrow_fee,
409
+
410
+ // Convert order_type number to string to match chain.js format
411
+ // order_type 1 = down_orders (long), order_type 2 = up_orders (short)
412
+ order_type: order.order_type === 1 ? 'down_orders' : 'up_orders',
413
+
414
+ // Add mint field for compatibility with chain.js (not in API response, use function parameter)
415
+ mint: mint
416
+ }));
417
+
418
+ // Sort orders by lock_lp_start_price
419
+ // down_orders (做多): descending order (从大到小)
420
+ // up_orders (做空): ascending order (从小到大)
421
+ processedOrders.sort((a, b) => {
422
+ const priceA = BigInt(a.lock_lp_start_price);
423
+ const priceB = BigInt(b.lock_lp_start_price);
424
+
425
+ if (type === 'down_orders') {
426
+ // 做多订单:从大到小排列
427
+ return priceB > priceA ? 1 : (priceB < priceA ? -1 : 0);
428
+ } else {
429
+ // 做空订单:从小到大排列
430
+ return priceA > priceB ? 1 : (priceA < priceB ? -1 : 0);
431
+ }
432
+ });
433
+
434
+ // Return in chain.js compatible format
435
+ return {
436
+ success: true,
437
+ data: {
438
+ orders: processedOrders,
439
+ total: apiData.total_count, // Use total_count from API
440
+ order_type: type, // Keep original type parameter for compatibility
441
+ mint_account: mint, // Add mint_account for compatibility
442
+ page: apiData.page,
443
+ limit: apiData.page_size, // Map page_size to limit
444
+ has_next: apiData.page < apiData.total_pages, // Calculate has_next
445
+ has_prev: apiData.page > 1 // Calculate has_prev
446
+ },
447
+ message: "Operation successful"
448
+ };
449
+
450
+ } catch (error) {
451
+ if (error.response) {
452
+ // API returned error
453
+ const errorMsg = error.response.data?.msg || error.response.data?.message || error.message;
454
+ throw new Error(`API请求失败 [${error.response.status}]: ${errorMsg}`);
455
+ } else if (error.request) {
456
+ // Network error
457
+ throw new Error(`网络请求失败: 无法连接到 ${this.baseUrl}`);
458
+ } else {
459
+ // Other errors
460
+ throw new Error(`请求处理失败: ${error.message}`);
461
+ }
462
+ }
463
+ }
464
+
465
+ /**
466
+ * Get token price (获取代币价格)
467
+ * @param {string} mint - Token mint address (代币地址)
468
+ * @returns {Promise<string>} Latest price string (最新价格字符串)
469
+ *
470
+ * @example
471
+ * // Get token latest price
472
+ * const price = await sdk.fast.price('Dj5FhAmbQ2qNDUXQV3ST2kJXTH3C3B6mrwfYXPhGb6ea');
473
+ * console.log('Latest price:', price); // "2795899347623485554"
474
+ */
475
+ async price(mint) {
476
+ // Validate input
477
+ if (!mint || typeof mint !== 'string') {
478
+ throw new Error('price: 代币地址必须是有效的字符串 mint address must be a valid string');
479
+ }
480
+
481
+ // Call mint_info API
482
+ const result = await this.mint_info(mint);
483
+
484
+ // Check return data (新格式: { code: 200, msg: "success", data: {...} })
485
+ if (!result || !result.data) {
486
+ throw new Error('price: 无法获取代币信息 Unable to fetch token information');
487
+ }
488
+
489
+ // Extract latest price (新格式直接返回对象,不是数组)
490
+ let latestPrice = result.data.latest_price;
491
+
492
+ if (!latestPrice) {
493
+ // If no price data, calculate initial price
494
+ const initialPrice = CurveAMM.getInitialPrice();
495
+ if (initialPrice === null) {
496
+ throw new Error('price: 无法计算初始价格 Unable to calculate initial price');
497
+ }
498
+ latestPrice = initialPrice.toString();
499
+ }
500
+
501
+ return latestPrice;
502
+ }
503
+
504
+ /**
505
+ * Get User Active Orders (获取用户活跃订单)
506
+ * Compatible with chain.js user_orders() method
507
+ *
508
+ * @param {string} user - User wallet address (用户钱包地址)
509
+ * @param {string} mint - Token mint address (optional, null/undefined for all tokens) (代币地址,可选)
510
+ * @param {Object} options - Query parameters (查询参数)
511
+ * @param {string} options.direction - Order direction (optional: 'up'=short, 'dn'=long, omit=all) (订单方向)
512
+ * @param {number} options.page - Page number, default 1 (页码,默认1)
513
+ * @param {number} options.limit - Items per page, default 20 (每页数量,默认20)
514
+ * @param {string} options.order_by - Sort order: 'start_time_desc' or 'start_time_asc', default 'start_time_desc' (排序方式)
515
+ * @returns {Promise<Object>} User orders data compatible with chain.js format
516
+ *
517
+ * @example
518
+ * // Get user's all active orders for a specific token
519
+ * const userOrders = await sdk.fast.user_orders(
520
+ * '7621yjkZJ1jxBHw3oCPoazFfMx82NWBSARk2AGV7EBig',
521
+ * 'T3NFPvYvpULCTgrhHb4b4Sj5J1qtSNvyKZfE8hCvuKM',
522
+ * { page: 1, limit: 20 }
523
+ * );
524
+ *
525
+ * // Get user's long orders for a specific token
526
+ * const longOrders = await sdk.fast.user_orders(
527
+ * '7621yjkZJ1jxBHw3oCPoazFfMx82NWBSARk2AGV7EBig',
528
+ * 'T3NFPvYvpULCTgrhHb4b4Sj5J1qtSNvyKZfE8hCvuKM',
529
+ * { direction: 'dn' }
530
+ * );
531
+ *
532
+ * // Get user's all active orders across all tokens (mint = null)
533
+ * const allOrders = await sdk.fast.user_orders(
534
+ * '7621yjkZJ1jxBHw3oCPoazFfMx82NWBSARk2AGV7EBig',
535
+ * null,
536
+ * { page: 1, limit: 20 }
537
+ * );
538
+ *
539
+ * // Return format (compatible with chain.js):
540
+ * // {
541
+ * // "success": true,
542
+ * // "data": {
543
+ * // "orders": [
544
+ * // {
545
+ * // "order_type": 1, // Order type: 1=long, 2=short
546
+ * // "mint": "T3NFPvYvpULCTgrhHb4b4Sj5J1qtSNvyKZfE8hCvuKM", // Token address
547
+ * // "user": "7621yjkZJ1jxBHw3oCPoazFfMx82NWBSARk2AGV7EBig", // User address
548
+ * // "lock_lp_start_price": "2656104380242311276", // LP start price (string)
549
+ * // "lock_lp_end_price": "2325402748045870207", // LP end price (string)
550
+ * // "open_price": "2795899347623485554", // Open price (string)
551
+ * // "order_id": "0", // Order ID (u64 as string)
552
+ * // "lock_lp_sol_amount": 1880793407, // LP locked SOL (lamports)
553
+ * // "lock_lp_token_amount": 75677963031921, // LP locked token (min unit)
554
+ * // "next_lp_sol_amount": 0, // Next LP SOL amount
555
+ * // "next_lp_token_amount": 0, // Next LP token amount
556
+ * // "margin_init_sol_amount": 418211213, // Initial margin
557
+ * // "margin_sol_amount": 418211213, // Current margin
558
+ * // "borrow_amount": 2276435100, // Borrow amount
559
+ * // "position_asset_amount": 75677963031921, // Position asset amount
560
+ * // "realized_sol_amount": 0, // Realized SOL amount
561
+ * // "start_time": 1764047379, // Start timestamp
562
+ * // "end_time": 1764652179, // End timestamp
563
+ * // "next_order": 1, // Next order index (65535=none)
564
+ * // "prev_order": 65535, // Previous order index (65535=none)
565
+ * // "borrow_fee": 1200, // Borrow fee (basis points)
566
+ * // "index": 0 // Order index in OrderBook
567
+ * // }
568
+ * // ],
569
+ * // "total": 37, // Total order count
570
+ * // "user": "7621yjkZJ1jxBHw3oCPoazFfMx82NWBSARk2AGV7EBig", // User address
571
+ * // "mint_account": "T3NFPvYvpULCTgrhHb4b4Sj5J1qtSNvyKZfE8hCvuKM", // Token address (null if not specified)
572
+ * // "page": 1, // Current page
573
+ * // "limit": 20, // Items per page
574
+ * // "has_next": true, // Has next page
575
+ * // "has_prev": false // Has previous page
576
+ * // },
577
+ * // "message": "Operation successful"
578
+ * // }
579
+ *
580
+ * // Use order data:
581
+ * const orders = userOrders.data.orders; // Order array
582
+ * const totalCount = userOrders.data.total; // Total count
583
+ */
584
+ async user_orders(user, mint, options = {}) {
585
+ this._ensureConfigured();
586
+
587
+ try {
588
+ // Parameter validation
589
+ if (!user || typeof user !== 'string') {
590
+ throw new Error('user_orders: user address must be a valid string');
591
+ }
592
+
593
+ // Extract parameters
594
+ const page = options.page || 1;
595
+ const pageSize = options.limit || 20;
596
+ const direction = options.direction; // Optional: 'up', 'dn', or undefined
597
+ const orderBy = options.order_by || 'start_time_desc';
598
+
599
+ // Build API path: /api/orderbook/user/{user}/active
600
+ const url = `/api/orderbook/user/${user}/active`;
601
+
602
+ // Build query parameters
603
+ const queryParams = {
604
+ page: page,
605
+ page_size: pageSize
606
+ };
607
+
608
+ // Optional parameter: mint (if provided and valid)
609
+ if (mint && typeof mint === 'string') {
610
+ queryParams.mint = mint;
611
+ }
612
+
613
+ // Optional parameter: direction
614
+ if (direction) {
615
+ if (!['up', 'dn'].includes(direction)) {
616
+ throw new Error('user_orders: direction must be "up" or "dn"');
617
+ }
618
+ queryParams.direction = direction;
619
+ }
620
+
621
+ // Make API request
622
+ const requestConfig = {
623
+ method: 'GET',
624
+ url: url,
625
+ params: queryParams
626
+ };
627
+
628
+ const response = await this.httpClient.request(requestConfig);
629
+
630
+ // Check new API response format
631
+ const isNewFormat = response.data && typeof response.data.code === 'number';
632
+
633
+ if (!isNewFormat) {
634
+ throw new Error('API返回格式无法识别');
635
+ }
636
+
637
+ if (response.data.code !== 200) {
638
+ throw new Error(`API请求失败: ${response.data.msg || '未知错误'}`);
639
+ }
640
+
641
+ // Transform to chain.js compatible format
642
+ const apiData = response.data.data;
643
+
644
+ // Process orders: add missing fields for chain.js compatibility
645
+ let processedOrders = apiData.orders.map(order => ({
646
+ // Keep order_type as number (1=long, 2=short) - compatible with chain.js
647
+ order_type: order.order_type,
648
+
649
+ // Token and user addresses
650
+ mint: order.mint,
651
+ user: order.user,
652
+
653
+ // Price fields (ensure strings)
654
+ lock_lp_start_price: String(order.lock_lp_start_price),
655
+ lock_lp_end_price: String(order.lock_lp_end_price),
656
+ open_price: String(order.open_price),
657
+
658
+ // Order ID (ensure string)
659
+ order_id: order.order_id != null ? String(order.order_id) : undefined,
660
+
661
+ // Amount fields (ensure strings) - Fix precision issue
662
+ lock_lp_sol_amount: String(order.lock_lp_sol_amount),
663
+ lock_lp_token_amount: String(order.lock_lp_token_amount),
664
+ next_lp_sol_amount: String(order.next_lp_sol_amount),
665
+ next_lp_token_amount: String(order.next_lp_token_amount),
666
+
667
+ // Margin fields (ensure strings) - Fix precision issue
668
+ margin_init_sol_amount: String(order.margin_init_sol_amount),
669
+ margin_sol_amount: String(order.margin_sol_amount),
670
+
671
+ // Position fields (ensure strings) - Fix precision issue
672
+ borrow_amount: String(order.borrow_amount),
673
+ position_asset_amount: String(order.position_asset_amount),
674
+ realized_sol_amount: String(order.realized_sol_amount),
675
+
676
+ // Time fields
677
+ start_time: order.start_time,
678
+ end_time: order.end_time,
679
+
680
+ // Linked list navigation fields (add from API response)
681
+ next_order: order.next_order,
682
+ prev_order: order.prev_order,
683
+
684
+ // Fee field
685
+ borrow_fee: order.borrow_fee,
686
+
687
+ // Index field (add from API response)
688
+ index: order.index
689
+ }));
690
+
691
+ // Client-side sorting (API doesn't support sorting)
692
+ if (orderBy === 'start_time_desc') {
693
+ processedOrders.sort((a, b) => b.start_time - a.start_time);
694
+ } else if (orderBy === 'start_time_asc') {
695
+ processedOrders.sort((a, b) => a.start_time - b.start_time);
696
+ }
697
+
698
+ // Calculate pagination info
699
+ const totalPages = Math.ceil(apiData.total / apiData.page_size);
700
+
701
+ // Return in chain.js compatible format
702
+ return {
703
+ success: true,
704
+ data: {
705
+ orders: processedOrders,
706
+ total: apiData.total,
707
+ user: user,
708
+ mint_account: mint || null, // If mint not specified, return null
709
+ page: apiData.page,
710
+ limit: apiData.page_size,
711
+ has_next: apiData.page < totalPages,
712
+ has_prev: apiData.page > 1
713
+ },
714
+ message: "Operation successful"
715
+ };
716
+
717
+ } catch (error) {
718
+ if (error.response) {
719
+ const errorMsg = error.response.data?.msg || error.response.data?.message || error.message;
720
+ throw new Error(`API请求失败 [${error.response.status}]: ${errorMsg}`);
721
+ } else if (error.request) {
722
+ throw new Error(`网络请求失败: 无法连接到 ${this.baseUrl}`);
723
+ } else {
724
+ throw new Error(`请求处理失败: ${error.message}`);
725
+ }
726
+ }
727
+ }
728
+
729
+
730
+
731
+
732
+ }
733
+
734
+ module.exports = FastModule;