pinpet-sdk 0.1.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,1087 @@
1
+ const { ComputeBudgetProgram, PublicKey, Transaction, SystemProgram, SYSVAR_RENT_PUBKEY } = require('@solana/web3.js');
2
+ const { createAssociatedTokenAccountInstruction, getAssociatedTokenAddress, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } = require('@solana/spl-token');
3
+ const anchor = require('@coral-xyz/anchor');
4
+ // 统一使用 buffer 包,所有平台一致
5
+ const { Buffer } = require('buffer');
6
+
7
+ // 环境检测和条件加载
8
+ const IS_NODE = typeof process !== 'undefined' && process.versions && process.versions.node;
9
+
10
+ let fs, path;
11
+ if (IS_NODE) {
12
+ try {
13
+ fs = require('fs');
14
+ path = require('path');
15
+ } catch (e) {
16
+ console.warn('File system modules not available in trading module');
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Trading Module
22
+ * Handles buy/sell and long/short trading operations
23
+ */
24
+ class TradingModule {
25
+ constructor(sdk) {
26
+ this.sdk = sdk;
27
+ }
28
+
29
+ /**
30
+ * Buy tokens
31
+ * @param {Object} params - Buy parameters
32
+ * @param {string|PublicKey} params.mintAccount - Token mint account address
33
+ * @param {anchor.BN} params.buyTokenAmount - Amount of tokens to buy
34
+ * @param {anchor.BN} params.maxSolAmount - Maximum SOL to spend
35
+ * @param {PublicKey} params.payer - Payer public key
36
+ * @param {Object} options - Optional parameters
37
+ * @param {number} options.computeUnits - Compute units limit, default 1400000
38
+ * @returns {Promise<Object>} Object containing transaction, signers and account info
39
+ *
40
+ * @example
41
+ * const result = await sdk.trading.buy({
42
+ * mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
43
+ * buyTokenAmount: new anchor.BN("1000000000"),
44
+ * maxSolAmount: new anchor.BN("2000000000"),
45
+ * payer: wallet.publicKey
46
+ * });
47
+ */
48
+ async buy({ mintAccount, buyTokenAmount, maxSolAmount, payer }, options = {}) {
49
+ const { computeUnits = 1400000 } = options;
50
+
51
+ // 1. Parameter validation and conversion
52
+ const mint = typeof mintAccount === 'string' ? new PublicKey(mintAccount) : mintAccount;
53
+
54
+ if (!anchor.BN.isBN(buyTokenAmount) || !anchor.BN.isBN(maxSolAmount)) {
55
+ throw new Error('buyTokenAmount and maxSolAmount must be anchor.BN type');
56
+ }
57
+
58
+ const currentPrice = await this.sdk.data.price(mintAccount);
59
+
60
+ // 2. Get orders data
61
+ const ordersData = await this.sdk.data.orders(mint.toString(), {
62
+ type: 'up_orders',
63
+ limit: this.sdk.MAX_ORDERS_COUNT + 1
64
+ });
65
+
66
+ // 3. Build transaction data
67
+ const lpPairs = this.sdk.buildLpPairs(ordersData.data.orders, 'up_orders', currentPrice, this.sdk.MAX_ORDERS_COUNT);
68
+ const orderAccounts = this.sdk.buildOrderAccounts(ordersData.data.orders, this.sdk.MAX_ORDERS_COUNT);
69
+
70
+ // 4. Calculate PDA accounts
71
+ const accounts = this._calculatePDAAccounts(mint);
72
+
73
+ // 5. Get user token account
74
+ const userTokenAccount = await getAssociatedTokenAddress(
75
+ mint,
76
+ payer
77
+ );
78
+
79
+ // 6. Check if user token account exists, create if not
80
+ const userTokenAccountInfo = await this.sdk.connection.getAccountInfo(userTokenAccount);
81
+ const createAtaIx = userTokenAccountInfo === null
82
+ ? createAssociatedTokenAccountInstruction(
83
+ payer,
84
+ userTokenAccount,
85
+ payer,
86
+ mint,
87
+ TOKEN_PROGRAM_ID,
88
+ ASSOCIATED_TOKEN_PROGRAM_ID
89
+ )
90
+ : null;
91
+
92
+ // 7. 构建订单账户参数
93
+ const orderAccountsParams = this._buildOrderAccountsParams(orderAccounts);
94
+
95
+ // 8. 构建交易指令
96
+
97
+ const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
98
+ units: computeUnits
99
+ });
100
+
101
+ const buyIx = await this.sdk.program.methods
102
+ .buy(lpPairs, buyTokenAmount, maxSolAmount)
103
+ .accounts({
104
+ payer: payer,
105
+ mintAccount: mint,
106
+ curveAccount: accounts.curveAccount,
107
+ poolTokenAccount: accounts.poolTokenAccount,
108
+ poolSolAccount: accounts.poolSolAccount,
109
+ userTokenAccount: userTokenAccount,
110
+ tokenProgram: TOKEN_PROGRAM_ID,
111
+ systemProgram: SystemProgram.programId,
112
+ rent: SYSVAR_RENT_PUBKEY,
113
+ associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
114
+ feeRecipientAccount: this.sdk.feeRecipient,
115
+ baseFeeRecipientAccount: this.sdk.baseFeeRecipient,
116
+ ...orderAccountsParams
117
+ })
118
+ .instruction();
119
+
120
+ // 9. Create transaction and add instructions
121
+ const transaction = new Transaction();
122
+ transaction.add(modifyComputeUnits);
123
+
124
+ // If user token account doesn't exist, create it first
125
+ if (createAtaIx) {
126
+ transaction.add(createAtaIx);
127
+ }
128
+
129
+ transaction.add(buyIx);
130
+
131
+ // 9. Return transaction object and related info
132
+ return {
133
+ transaction,
134
+ signers: [], // Buy transaction doesn't need additional signers, only payer signature
135
+ accounts: {
136
+ mint: mint,
137
+ curveAccount: accounts.curveAccount,
138
+ poolTokenAccount: accounts.poolTokenAccount,
139
+ poolSolAccount: accounts.poolSolAccount,
140
+ userTokenAccount: userTokenAccount,
141
+ payer: payer
142
+ },
143
+ orderData: {
144
+ ordersUsed: ordersData.data.orders.length,
145
+ lpPairsCount: lpPairs.filter(p => !p.solAmount.isZero()).length,
146
+ lpPairs: lpPairs,
147
+ orderAccounts: orderAccounts
148
+ }
149
+ };
150
+
151
+
152
+ }
153
+
154
+ /**
155
+ * Sell tokens
156
+ * @param {Object} params - Sell parameters
157
+ * @param {string|PublicKey} params.mintAccount - Token mint account address
158
+ * @param {anchor.BN} params.sellTokenAmount - Amount of tokens to sell
159
+ * @param {anchor.BN} params.minSolOutput - Minimum SOL output
160
+ * @param {PublicKey} params.payer - Payer public key
161
+ * @param {Object} options - Optional parameters
162
+ * @param {number} options.computeUnits - Compute units limit, default 1400000
163
+ * @returns {Promise<Object>} Object containing transaction, signers and account info
164
+ *
165
+ * @example
166
+ * const result = await sdk.trading.sell({
167
+ * mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
168
+ * sellTokenAmount: new anchor.BN("1000000000"),
169
+ * minSolOutput: new anchor.BN("2000000000"),
170
+ * payer: wallet.publicKey
171
+ * });
172
+ */
173
+ async sell({ mintAccount, sellTokenAmount, minSolOutput, payer }, options = {}) {
174
+ const { computeUnits = 1400000 } = options;
175
+
176
+ // 1. 参数验证和转换
177
+ const mint = typeof mintAccount === 'string' ? new PublicKey(mintAccount) : mintAccount;
178
+
179
+ if (!anchor.BN.isBN(sellTokenAmount) || !anchor.BN.isBN(minSolOutput)) {
180
+ throw new Error('sellTokenAmount and minSolOutput must be anchor.BN type');
181
+ }
182
+
183
+ const currentPrice = await this.sdk.data.price(mintAccount);
184
+
185
+ // 2. Get orders data (sell uses long orders)
186
+ const ordersData = await this.sdk.data.orders(mint.toString(), {
187
+ type: 'down_orders',
188
+ limit: this.sdk.MAX_ORDERS_COUNT + 1
189
+ });
190
+
191
+ // 3. Build transaction data
192
+ const lpPairs = this.sdk.buildLpPairs(ordersData.data.orders, 'down_orders', currentPrice, this.sdk.MAX_ORDERS_COUNT);
193
+ const orderAccounts = this.sdk.buildOrderAccounts(ordersData.data.orders, this.sdk.MAX_ORDERS_COUNT);
194
+
195
+ // 4. Calculate PDA accounts
196
+ const accounts = this._calculatePDAAccounts(mint);
197
+
198
+ // 5. Get user token account
199
+ const userTokenAccount = await getAssociatedTokenAddress(
200
+ mint,
201
+ payer
202
+ );
203
+
204
+ // 6. Check if user token account exists, create if not
205
+ const userTokenAccountInfo = await this.sdk.connection.getAccountInfo(userTokenAccount);
206
+ const createAtaIx = userTokenAccountInfo === null
207
+ ? createAssociatedTokenAccountInstruction(
208
+ payer,
209
+ userTokenAccount,
210
+ payer,
211
+ mint,
212
+ TOKEN_PROGRAM_ID,
213
+ ASSOCIATED_TOKEN_PROGRAM_ID
214
+ )
215
+ : null;
216
+
217
+ // 7. Build order accounts parameters
218
+ const orderAccountsParams = this._buildOrderAccountsParams(orderAccounts);
219
+
220
+ // 8. Build transaction instructions
221
+ const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
222
+ units: computeUnits
223
+ });
224
+
225
+ const sellIx = await this.sdk.program.methods
226
+ .sell(lpPairs, sellTokenAmount, minSolOutput)
227
+ .accounts({
228
+ payer: payer,
229
+ mintAccount: mint,
230
+ curveAccount: accounts.curveAccount,
231
+ poolTokenAccount: accounts.poolTokenAccount,
232
+ poolSolAccount: accounts.poolSolAccount,
233
+ userTokenAccount: userTokenAccount,
234
+ tokenProgram: TOKEN_PROGRAM_ID,
235
+ systemProgram: SystemProgram.programId,
236
+ rent: SYSVAR_RENT_PUBKEY,
237
+ associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
238
+ feeRecipientAccount: this.sdk.feeRecipient,
239
+ baseFeeRecipientAccount: this.sdk.baseFeeRecipient,
240
+ ...orderAccountsParams
241
+ })
242
+ .instruction();
243
+
244
+ // 9. Create transaction and add instructions
245
+ const transaction = new Transaction();
246
+ transaction.add(modifyComputeUnits);
247
+
248
+ // If user token account doesn't exist, create it first
249
+ if (createAtaIx) {
250
+ transaction.add(createAtaIx);
251
+ }
252
+
253
+ transaction.add(sellIx);
254
+
255
+ // 9. Return transaction object and related info
256
+ return {
257
+ transaction,
258
+ signers: [], // Sell transaction doesn't need additional signers, only payer signature
259
+ accounts: {
260
+ mint: mint,
261
+ curveAccount: accounts.curveAccount,
262
+ poolTokenAccount: accounts.poolTokenAccount,
263
+ poolSolAccount: accounts.poolSolAccount,
264
+ userTokenAccount: userTokenAccount,
265
+ payer: payer
266
+ },
267
+ orderData: {
268
+ ordersUsed: ordersData.data.orders.length,
269
+ lpPairsCount: lpPairs.filter(p => !p.solAmount.isZero()).length,
270
+ lpPairs: lpPairs,
271
+ orderAccounts: orderAccounts
272
+ }
273
+ };
274
+ }
275
+
276
+ /**
277
+ * Margin Long
278
+ * @param {Object} params - Long parameters
279
+ * @param {string|PublicKey} params.mintAccount - Token mint account address
280
+ * @param {anchor.BN} params.buyTokenAmount - Amount of tokens to buy
281
+ * @param {anchor.BN} params.maxSolAmount - Maximum SOL to spend
282
+ * @param {anchor.BN} params.marginSol - Margin amount
283
+ * @param {anchor.BN} params.closePrice - Close price
284
+ * @param {PublicKey} params.payer - Payer public key
285
+ * @param {Object} options - Optional parameters
286
+ * @param {number} options.computeUnits - Compute units limit, default 1400000
287
+ * @returns {Promise<Object>} Object containing transaction, signers and account info
288
+ *
289
+ * @example
290
+ * const result = await sdk.trading.long({
291
+ * mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
292
+ * buyTokenAmount: new anchor.BN("10000000"),
293
+ * maxSolAmount: new anchor.BN("1100000000"),
294
+ * marginSol: new anchor.BN("2200000000"),
295
+ * closePrice: new anchor.BN("1000000000000000"),
296
+ * payer: wallet.publicKey
297
+ * });
298
+ */
299
+ async long({ mintAccount, buyTokenAmount, maxSolAmount, marginSol, closePrice, prevOrder, nextOrder, payer }, options = {}) {
300
+ const { computeUnits = 1400000 } = options;
301
+
302
+ // 1. 参数验证和转换
303
+ const mint = typeof mintAccount === 'string' ? new PublicKey(mintAccount) : mintAccount;
304
+
305
+ if (!anchor.BN.isBN(buyTokenAmount) || !anchor.BN.isBN(maxSolAmount) ||
306
+ !anchor.BN.isBN(marginSol) || !anchor.BN.isBN(closePrice)) {
307
+ throw new Error('All parameters must be anchor.BN type');
308
+ }
309
+ const currentPrice = await this.sdk.data.price(mintAccount);
310
+
311
+ // 2. Get orders data (long uses short orders)
312
+ const ordersData = await this.sdk.data.orders(mint.toString(), {
313
+ type: 'up_orders',
314
+ limit: this.sdk.MAX_ORDERS_COUNT + 1
315
+ });
316
+
317
+ // 3. Build transaction data
318
+ const lpPairs = this.sdk.buildLpPairs(ordersData.data.orders, 'up_orders', currentPrice, this.sdk.MAX_ORDERS_COUNT);
319
+ const orderAccounts = this.sdk.buildOrderAccounts(ordersData.data.orders, this.sdk.MAX_ORDERS_COUNT);
320
+
321
+ // 4. Calculate PDA accounts
322
+ const accounts = this._calculatePDAAccounts(mint);
323
+
324
+ // 5. Build order accounts parameters
325
+ const orderAccountsParams = this._buildOrderAccountsParams(orderAccounts);
326
+
327
+ // 6. Generate uniqueSeed (random number)
328
+ const uniqueSeed = new anchor.BN(Date.now());
329
+
330
+ // 7. Generate long order account PDA
331
+ const [selfOrderAddress] = PublicKey.findProgramAddressSync(
332
+ [
333
+ Buffer.from("self_margin_order"),
334
+ payer.toBuffer(),
335
+ mint.toBuffer(),
336
+ uniqueSeed.toArrayLike(Buffer, 'le', 8)
337
+ ],
338
+ this.sdk.programId
339
+ );
340
+
341
+ // 安全的调试日志写入
342
+ this._writeDebugLog('orderPda.txt', `${selfOrderAddress.toString()}\n`);
343
+
344
+ const prevOrderStr = prevOrder ? prevOrder.toString() : 'null';
345
+ const nextOrderStr = nextOrder ? nextOrder.toString() : 'null';
346
+ this._writeDebugLog('orderOpen.txt',
347
+ `long ${prevOrderStr} -> ${selfOrderAddress.toString()} -> ${nextOrderStr} buyTokenAmount=${buyTokenAmount} closePrice=${closePrice}\n`
348
+ );
349
+
350
+ // // 8. Get prevOrder and nextOrder (simplified calculation)
351
+ // // Use simplified logic here, in actual project you can call simulator as needed
352
+ // const prevOrder = null; // Can calculate as needed
353
+ // const nextOrder = null; // Can calculate as needed
354
+
355
+ // 9. Build transaction instructions
356
+ const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
357
+ units: computeUnits
358
+ });
359
+
360
+ const longIx = await this.sdk.program.methods
361
+ .long(
362
+ uniqueSeed,
363
+ lpPairs,
364
+ buyTokenAmount,
365
+ maxSolAmount,
366
+ marginSol,
367
+ closePrice
368
+ )
369
+ .accounts({
370
+ payer: payer,
371
+ mintAccount: mint,
372
+ curveAccount: accounts.curveAccount,
373
+ poolTokenAccount: accounts.poolTokenAccount,
374
+ poolSolAccount: accounts.poolSolAccount,
375
+ selfOrder: selfOrderAddress,
376
+ tokenProgram: TOKEN_PROGRAM_ID,
377
+ systemProgram: SystemProgram.programId,
378
+ rent: SYSVAR_RENT_PUBKEY,
379
+ associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
380
+ feeRecipientAccount: this.sdk.feeRecipient,
381
+ baseFeeRecipientAccount: this.sdk.baseFeeRecipient,
382
+ ...orderAccountsParams,
383
+ prevOrder: prevOrder,
384
+ nextOrder: nextOrder
385
+ })
386
+ .instruction();
387
+
388
+ // 10. Create transaction and add instructions
389
+ const transaction = new Transaction();
390
+ transaction.add(modifyComputeUnits);
391
+ transaction.add(longIx);
392
+
393
+ // 11. Return transaction object and related info
394
+ return {
395
+ transaction,
396
+ signers: [], // Long transaction doesn't need additional signers, only payer signature
397
+ accounts: {
398
+ mint: mint,
399
+ curveAccount: accounts.curveAccount,
400
+ poolTokenAccount: accounts.poolTokenAccount,
401
+ poolSolAccount: accounts.poolSolAccount,
402
+ payer: payer,
403
+ selfOrder: selfOrderAddress
404
+ },
405
+ orderData: {
406
+ ordersUsed: ordersData.data.orders.length,
407
+ lpPairsCount: lpPairs.filter(p => !p.solAmount.isZero()).length,
408
+ lpPairs: lpPairs,
409
+ orderAccounts: orderAccounts,
410
+ uniqueSeed: uniqueSeed,
411
+ prevOrder: prevOrder,
412
+ nextOrder: nextOrder
413
+ }
414
+ };
415
+ }
416
+
417
+ /**
418
+ * 保证金做空 / Margin Short
419
+ * @param {Object} params - 做空参数 / Short parameters
420
+ * @param {string|PublicKey} params.mintAccount - 代币铸造账户地址 / Token mint account address
421
+ * @param {anchor.BN} params.borrowSellTokenAmount - 借出卖出的代币数量 / Borrowed token amount to sell
422
+ * @param {anchor.BN} params.minSolOutput - 最小 SOL 输出 / Minimum SOL output
423
+ * @param {anchor.BN} params.marginSol - 保证金数量 / Margin amount
424
+ * @param {anchor.BN} params.closePrice - 平仓价格 / Close price
425
+ * @param {PublicKey|null} params.prevOrder - 前一个订单 / Previous order
426
+ * @param {PublicKey|null} params.nextOrder - 下一个订单 / Next order
427
+ * @param {PublicKey} params.payer - 支付者公钥 / Payer public key
428
+ * @param {Object} options - 可选参数 / Optional parameters
429
+ * @param {number} options.computeUnits - 计算单元限制,默认 1400000 / Compute units limit, default 1400000
430
+ * @returns {Promise<Object>} 包含交易对象、签名者和账户信息的对象 / Object containing transaction, signers and account info
431
+ *
432
+ * @example
433
+ * const result = await sdk.trading.short({
434
+ * mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
435
+ * borrowSellTokenAmount: new anchor.BN("1000000000"),
436
+ * minSolOutput: new anchor.BN("100"),
437
+ * marginSol: new anchor.BN("2200000000"),
438
+ * closePrice: new anchor.BN("1000000000000000"),
439
+ * prevOrder: null,
440
+ * nextOrder: null,
441
+ * payer: wallet.publicKey
442
+ * });
443
+ */
444
+ async short({ mintAccount, borrowSellTokenAmount, minSolOutput, marginSol, closePrice, prevOrder, nextOrder, payer }, options = {}) {
445
+ const { computeUnits = 1400000 } = options;
446
+
447
+ // 1. 参数验证和转换 / Parameter validation and conversion
448
+ const mint = typeof mintAccount === 'string' ? new PublicKey(mintAccount) : mintAccount;
449
+
450
+ if (!anchor.BN.isBN(borrowSellTokenAmount) || !anchor.BN.isBN(minSolOutput) ||
451
+ !anchor.BN.isBN(marginSol) || !anchor.BN.isBN(closePrice)) {
452
+ throw new Error('所有参数必须是 anchor.BN 类型 / All parameters must be anchor.BN type');
453
+ }
454
+ const currentPrice = await this.sdk.data.price(mintAccount);
455
+
456
+ // 2. 获取订单数据(做空使用做多订单)/ Get orders data (short uses long orders)
457
+ const ordersData = await this.sdk.data.orders(mint.toString(), {
458
+ type: 'down_orders',
459
+ limit: this.sdk.MAX_ORDERS_COUNT + 1
460
+ });
461
+
462
+ // 3. 构建交易数据 / Build transaction data
463
+ const lpPairs = this.sdk.buildLpPairs(ordersData.data.orders, 'down_orders', currentPrice, this.sdk.MAX_ORDERS_COUNT);
464
+ const orderAccounts = this.sdk.buildOrderAccounts(ordersData.data.orders, this.sdk.MAX_ORDERS_COUNT);
465
+
466
+ // 4. 计算 PDA 账户 / Calculate PDA accounts
467
+ const accounts = this._calculatePDAAccounts(mint);
468
+
469
+ //console.log('curveAccount', accounts.curveAccount);
470
+
471
+ // 5. 构建订单账户参数 / Build order accounts parameters
472
+ const orderAccountsParams = this._buildOrderAccountsParams(orderAccounts);
473
+
474
+ // 6. 生成 uniqueSeed(随机数)/ Generate uniqueSeed (random number)
475
+ const uniqueSeed = new anchor.BN(Date.now());
476
+
477
+ // 7. 生成做空订单账户 PDA / Generate short order account PDA
478
+ const [selfOrderAddress] = PublicKey.findProgramAddressSync(
479
+ [
480
+ Buffer.from("self_margin_order"),
481
+ payer.toBuffer(),
482
+ mint.toBuffer(),
483
+ uniqueSeed.toArrayLike(Buffer, 'le', 8)
484
+ ],
485
+ this.sdk.programId
486
+ );
487
+
488
+ // 安全的调试日志写入
489
+ this._writeDebugLog('orderPda.txt', `${selfOrderAddress.toString()}\n`);
490
+
491
+ const prevOrderStr = prevOrder ? prevOrder.toString() : 'null';
492
+ const nextOrderStr = nextOrder ? nextOrder.toString() : 'null';
493
+ this._writeDebugLog('orderOpen.txt',
494
+ `short ${prevOrderStr} -> ${selfOrderAddress.toString()} -> ${nextOrderStr} borrowSellTokenAmount=${borrowSellTokenAmount} closePrice=${closePrice}\n`
495
+ );
496
+
497
+ // 8. 获取用户代币账户 / Get user token account
498
+ const userTokenAccount = await getAssociatedTokenAddress(
499
+ mint,
500
+ payer
501
+ );
502
+
503
+ // 9. 构建交易指令 / Build transaction instructions
504
+ const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
505
+ units: computeUnits
506
+ });
507
+
508
+ const shortIx = await this.sdk.program.methods
509
+ .short(
510
+ uniqueSeed,
511
+ lpPairs,
512
+ borrowSellTokenAmount,
513
+ minSolOutput,
514
+ marginSol,
515
+ closePrice
516
+ )
517
+ .accounts({
518
+ payer: payer,
519
+ mintAccount: mint,
520
+ curveAccount: accounts.curveAccount,
521
+ poolTokenAccount: accounts.poolTokenAccount,
522
+ poolSolAccount: accounts.poolSolAccount,
523
+ userTokenAccount: userTokenAccount,
524
+ selfOrder: selfOrderAddress,
525
+ tokenProgram: TOKEN_PROGRAM_ID,
526
+ systemProgram: SystemProgram.programId,
527
+ rent: SYSVAR_RENT_PUBKEY,
528
+ associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
529
+ feeRecipientAccount: this.sdk.feeRecipient,
530
+ baseFeeRecipientAccount: this.sdk.baseFeeRecipient,
531
+ ...orderAccountsParams,
532
+ prevOrder: prevOrder,
533
+ nextOrder: nextOrder
534
+ })
535
+ .instruction();
536
+
537
+ // 10. 创建交易并添加指令 / Create transaction and add instructions
538
+ const transaction = new Transaction();
539
+ transaction.add(modifyComputeUnits);
540
+ transaction.add(shortIx);
541
+
542
+ // 11. 返回交易对象和相关信息 / Return transaction object and related info
543
+ return {
544
+ transaction,
545
+ signers: [], // 做空交易不需要额外的签名者,只需要 payer 签名 / Short transaction doesn't need additional signers, only payer signature
546
+ accounts: {
547
+ mint: mint,
548
+ curveAccount: accounts.curveAccount,
549
+ poolTokenAccount: accounts.poolTokenAccount,
550
+ poolSolAccount: accounts.poolSolAccount,
551
+ userTokenAccount: userTokenAccount,
552
+ payer: payer,
553
+ selfOrder: selfOrderAddress
554
+ },
555
+ orderData: {
556
+ ordersUsed: ordersData.data.orders.length,
557
+ lpPairsCount: lpPairs.filter(p => !p.solAmount.isZero()).length,
558
+ lpPairs: lpPairs,
559
+ orderAccounts: orderAccounts,
560
+ uniqueSeed: uniqueSeed,
561
+ prevOrder: prevOrder,
562
+ nextOrder: nextOrder
563
+ }
564
+ };
565
+ }
566
+
567
+ /**
568
+ * 平仓做多(增强版,支持自定义收款账户)/ Close Long Position (Plus version with custom user SOL account)
569
+ * @param {Object} params - 平仓参数 / Close position parameters
570
+ * @param {string|PublicKey} params.mintAccount - 代币铸造账户地址 / Token mint account address
571
+ * @param {string|PublicKey} params.closeOrder - 需要关闭的订单地址 / Order address to close
572
+ * @param {anchor.BN} params.sellTokenAmount - 希望卖出的token数量 / Amount of tokens to sell
573
+ * @param {anchor.BN} params.minSolOutput - 卖出后最少得到的sol数量 / Minimum SOL output after selling
574
+ * @param {PublicKey} params.payer - 支付者公钥 / Payer public key
575
+ * @param {PublicKey} params.closeUserAccount - 平仓定单的用户账户 / User SOL account to receive funds
576
+ * @param {Object} options - 可选参数 / Optional parameters
577
+ * @param {number} options.computeUnits - 计算单元限制,默认1400000 / Compute units limit, default 1400000
578
+ * @returns {Promise<Object>} 包含交易对象、签名者和账户信息的对象 / Object containing transaction, signers and account info
579
+ *
580
+ * @example
581
+ * const result = await sdk.trading.closeLongPlus({
582
+ * mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
583
+ * closeOrder: "E2T72D4wZdxHRjELN5VnRdcCvS4FPcYBBT3UBEoaC5cA",
584
+ * sellTokenAmount: new anchor.BN("1000000000"),
585
+ * minSolOutput: new anchor.BN("100000000"),
586
+ * payer: wallet.publicKey,
587
+ * closeUserAccount: userSolAccount
588
+ * });
589
+ */
590
+ async closeLongPlus({ mintAccount, closeOrder, sellTokenAmount, minSolOutput, payer, closeUserAccount }, options = {}) {
591
+ const { computeUnits = 1400000 } = options;
592
+
593
+ // 1. 参数验证和转换 / Parameter validation and conversion
594
+ const mint = typeof mintAccount === 'string' ? new PublicKey(mintAccount) : mintAccount;
595
+ let closeOrderPubkey = typeof closeOrder === 'string' ? new PublicKey(closeOrder) : closeOrder;
596
+
597
+ if (!anchor.BN.isBN(sellTokenAmount) || !anchor.BN.isBN(minSolOutput)) {
598
+ throw new Error('sellTokenAmount 和 minSolOutput 必须是 anchor.BN 类型 / sellTokenAmount and minSolOutput must be anchor.BN type');
599
+ }
600
+
601
+ // 我的关闭损订单数据以便查找前后节点 / Get orders data to find prev/next nodes
602
+ const ordersStopData = await this.sdk.data.orders(mint.toString(), {
603
+ type: 'down_orders',
604
+ page: 1,
605
+ limit: this.sdk.FIND_MAX_ORDERS_COUNT
606
+ });
607
+
608
+ // console.log('this.sdk.FIND_MAX_ORDERS_COUNT=',this.sdk.FIND_MAX_ORDERS_COUNT)
609
+ // console.log(`#_# closeLong: Found ${ordersStopData.data.orders.length} orders`);
610
+
611
+ // 3. 使用 findPrevNext 查找前后订单 / Use findPrevNext to find prev/next orders
612
+ const prevNext = this.sdk.findPrevNext(ordersStopData.data.orders, closeOrderPubkey.toString());
613
+ let prevOrder = prevNext.prevOrder ? new PublicKey(prevNext.prevOrder.order_pda) : null;
614
+ let nextOrder = prevNext.nextOrder ? new PublicKey(prevNext.nextOrder.order_pda) : null;
615
+
616
+ // console.log(`closeLong: Found previous order: ${prevOrder ? prevOrder.toString() : 'null'}`);
617
+ // console.log(`closeLong: Found next order: ${nextOrder ? nextOrder.toString() : 'null'}`);
618
+
619
+
620
+ // 安全的调试日志写入
621
+ const prevOrderStr = prevOrder ? prevOrder.toString() : 'null';
622
+ const nextOrderStr = nextOrder ? nextOrder.toString() : 'null';
623
+ this._writeDebugLog('orderOpen.txt', `closeLong ${prevOrderStr} -> ${closeOrderPubkey.toString()} -> ${nextOrderStr}\n`);
624
+
625
+
626
+ // 获取止损订单数据以便查找前后节点 / Get orders data to find prev/next nodes
627
+ const ordersData = await this.sdk.data.orders(mint.toString(), {
628
+ type: 'down_orders',
629
+ limit: this.sdk.FIND_MAX_ORDERS_COUNT
630
+ });
631
+
632
+ // 计算订单在数组中的位置索引 / Calculate order position indices in array
633
+ let prev_order_id = this.sdk.findOrderIndex(ordersData.data.orders, prevOrder);
634
+ let close_order_id = this.sdk.findOrderIndex(ordersData.data.orders, closeOrderPubkey);
635
+ let next_order_id = this.sdk.findOrderIndex(ordersData.data.orders, nextOrder);
636
+
637
+ // 处理越界情况:如果索引 >= MAX_ORDERS_COUNT,设为 200
638
+ // Handle out-of-bounds: if index >= MAX_ORDERS_COUNT, set to 200
639
+ if (prev_order_id >= this.sdk.MAX_ORDERS_COUNT) {
640
+ prev_order_id = 200;
641
+ }
642
+ if (close_order_id >= this.sdk.MAX_ORDERS_COUNT) {
643
+ close_order_id = 200;
644
+ }
645
+ if (next_order_id >= this.sdk.MAX_ORDERS_COUNT) {
646
+ next_order_id = 200;
647
+ }
648
+
649
+ // 根据索引值调整PDA地址:如果索引为不为200,对应PDA设为null
650
+ // Adjust PDA addresses based on index values: if index is 200, set corresponding PDA to null
651
+ if (prev_order_id != 200) {
652
+ prevOrder = null;
653
+ }
654
+ if (close_order_id != 200) {
655
+ closeOrderPubkey = null;
656
+ }
657
+ if (next_order_id != 200) {
658
+ nextOrder = null;
659
+ }
660
+
661
+ console.log(`closeLong: Order indices - prev: ${prev_order_id}, close: ${close_order_id}, next: ${next_order_id}`);
662
+
663
+ // 4. 获取当前价格并构建 lpPairs / Get current price and build lpPairs
664
+ const currentPrice = await this.sdk.data.price(mintAccount);
665
+ const lpPairs = this.sdk.buildLpPairs(ordersData.data.orders, 'down_orders', currentPrice, this.sdk.MAX_ORDERS_COUNT);
666
+
667
+ // 5. 计算 PDA 账户 / Calculate PDA accounts
668
+ const accounts = this._calculatePDAAccounts(mint);
669
+
670
+ // 6. 构建订单账户数组(基于 lpPairs 对应的订单)/ Build order accounts array
671
+ const orderAccounts = this.sdk.buildOrderAccounts(ordersData.data.orders, this.sdk.MAX_ORDERS_COUNT);
672
+
673
+ // 7. 构建订单账户参数 / Build order accounts parameters
674
+ const orderAccountsParams = this._buildOrderAccountsParams(orderAccounts);
675
+
676
+ //console.log(`closeLong orderAccountsParams=`, orderAccountsParams);
677
+
678
+ // 8. 构建交易指令 / Build transaction instructions
679
+ const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
680
+ units: computeUnits
681
+ });
682
+
683
+ const closeLongIx = await this.sdk.program.methods
684
+ .closeLong(
685
+ lpPairs, // lp_pairs: LP配对数组 / LP pairs array
686
+ sellTokenAmount, // sell_token_amount: 希望卖出的token数量 / Amount of tokens to sell
687
+ minSolOutput, // min_sol_output: 卖出后最少得到的sol数量 / Minimum SOL output after selling
688
+ prev_order_id, // prev_order_id: 前一个订单的数组索引 / Previous order array index
689
+ close_order_id, // close_order_id: 要关闭订单的数组索引 / Close order array index
690
+ next_order_id, // next_order_id: 下一个订单的数组索引 / Next order array index
691
+ closeOrder.toString() // close_order_address: 要关闭订单的PDA地址字符串 / Close order PDA address string
692
+ )
693
+ .accounts({
694
+ payer: payer,
695
+ mintAccount: mint,
696
+ curveAccount: accounts.curveAccount,
697
+ poolTokenAccount: accounts.poolTokenAccount,
698
+ poolSolAccount: accounts.poolSolAccount,
699
+ userSolAccount: closeUserAccount, // 用户SOL账户,接收平仓后的SOL / User SOL account to receive funds after closing
700
+ tokenProgram: TOKEN_PROGRAM_ID,
701
+ systemProgram: SystemProgram.programId,
702
+ rent: SYSVAR_RENT_PUBKEY,
703
+ feeRecipientAccount: this.sdk.feeRecipient,
704
+ baseFeeRecipientAccount: this.sdk.baseFeeRecipient,
705
+ ...orderAccountsParams,
706
+ prevOrder: prevOrder, // 前一个订单 / Previous order
707
+ closeOrder: closeOrderPubkey, // 要关闭的订单 / Order to close
708
+ nextOrder: nextOrder // 下一个订单 / Next order
709
+ })
710
+ .instruction();
711
+
712
+ //console.log(`#_# closeLongPlus - closeOrder `, closeOrderPubkey.toString(), ` prevOrder `, prevOrder?.toString(), ` nextOrder `, nextOrder?.toString())
713
+
714
+
715
+ // 9. 创建交易并添加指令 / Create transaction and add instructions
716
+ const transaction = new Transaction();
717
+ transaction.add(modifyComputeUnits);
718
+ transaction.add(closeLongIx);
719
+
720
+ // 10. 返回交易对象和相关信息 / Return transaction object and related info
721
+ return {
722
+ transaction,
723
+ signers: [], // 平仓做多交易不需要额外的签名者,只需要 payer 签名 / Close long transaction doesn't need additional signers, only payer signature
724
+ accounts: {
725
+ mint: mint,
726
+ curveAccount: accounts.curveAccount,
727
+ poolTokenAccount: accounts.poolTokenAccount,
728
+ poolSolAccount: accounts.poolSolAccount,
729
+ payer: payer,
730
+ closeOrder: closeOrderPubkey,
731
+ prevOrder: prevOrder,
732
+ nextOrder: nextOrder
733
+ },
734
+ orderData: {
735
+ ordersUsed: ordersData.data.orders.length,
736
+ lpPairsCount: lpPairs.filter(p => !p.solAmount.isZero()).length,
737
+ lpPairs: lpPairs,
738
+ orderAccounts: orderAccounts,
739
+ prevOrder: prevOrder,
740
+ nextOrder: nextOrder,
741
+ closeOrderAddress: closeOrderPubkey
742
+ }
743
+ };
744
+ }
745
+
746
+ /**
747
+ * 平仓做多 / Close Long Position
748
+ * @param {Object} params - 平仓参数 / Close position parameters
749
+ * @param {string|PublicKey} params.mintAccount - 代币铸造账户地址 / Token mint account address
750
+ * @param {string|PublicKey} params.closeOrder - 需要关闭的订单地址 / Order address to close
751
+ * @param {anchor.BN} params.sellTokenAmount - 希望卖出的token数量 / Amount of tokens to sell
752
+ * @param {anchor.BN} params.minSolOutput - 卖出后最少得到的sol数量 / Minimum SOL output after selling
753
+ * @param {PublicKey} params.payer - 支付者公钥 / Payer public key
754
+ * @param {Object} options - 可选参数 / Optional parameters
755
+ * @param {number} options.computeUnits - 计算单元限制,默认1400000 / Compute units limit, default 1400000
756
+ * @returns {Promise<Object>} 包含交易对象、签名者和账户信息的对象 / Object containing transaction, signers and account info
757
+ *
758
+ * @example
759
+ * const result = await sdk.trading.closeLong({
760
+ * mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
761
+ * closeOrder: "E2T72D4wZdxHRjELN5VnRdcCvS4FPcYBBT3UBEoaC5cA",
762
+ * sellTokenAmount: new anchor.BN("1000000000"),
763
+ * minSolOutput: new anchor.BN("100000000"),
764
+ * payer: wallet.publicKey
765
+ * });
766
+ */
767
+ async closeLong({ mintAccount, closeOrder, sellTokenAmount, minSolOutput, payer }, options = {}) {
768
+ // 直接调用 closeLongPlus,使用 payer 作为 closeUserAccount
769
+ return this.closeLongPlus({
770
+ mintAccount,
771
+ closeOrder,
772
+ sellTokenAmount,
773
+ minSolOutput,
774
+ payer,
775
+ closeUserAccount: payer
776
+ }, options);
777
+ }
778
+
779
+ /**
780
+ * 平仓做空(增强版,支持自定义收款账户)/ Close Short Position (Plus version with custom user SOL account)
781
+ * @param {Object} params - 平仓参数 Close position parameters
782
+ * @param {string|PublicKey} params.mintAccount - 代币铸造账户地址 Token mint account address
783
+ * @param {string|PublicKey} params.closeOrder - 需要关闭的订单地址 Order address to close
784
+ * @param {anchor.BN} params.buyTokenAmount - 希望买入的token数量 Amount of tokens to buy
785
+ * @param {anchor.BN} params.maxSolAmount - 愿意给出的最大sol数量 Maximum SOL amount to spend
786
+ * @param {PublicKey} params.payer - 支付者公钥 Payer public key
787
+ * @param {PublicKey} params.closeUserAccount - 平仓定单的用户账户 / User SOL account to receive funds
788
+ * @param {Object} options - 可选参数 Optional parameters
789
+ * @param {number} options.computeUnits - 计算单元限制,默认1400000 Compute units limit, default 1400000
790
+ * @returns {Promise<Object>} 包含交易对象、签名者和账户信息的对象 Object containing transaction, signers and account info
791
+ *
792
+ * @example
793
+ * const result = await sdk.trading.closeShortPlus({
794
+ * mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
795
+ * closeOrder: "E2T72D4wZdxHRjELN5VnRdcCvS4FPcYBBT3UBEoaC5cA",
796
+ * buyTokenAmount: new anchor.BN("1000000000"),
797
+ * maxSolAmount: new anchor.BN("100000000"),
798
+ * payer: wallet.publicKey,
799
+ * closeUserAccount: userSolAccount
800
+ * });
801
+ */
802
+ async closeShortPlus({ mintAccount, closeOrder, buyTokenAmount, maxSolAmount, payer, closeUserAccount }, options = {}) {
803
+ const { computeUnits = 1400000 } = options;
804
+
805
+ // 1. 参数验证和转换 Parameter validation and conversion
806
+ const mint = typeof mintAccount === 'string' ? new PublicKey(mintAccount) : mintAccount;
807
+ let closeOrderPubkey = typeof closeOrder === 'string' ? new PublicKey(closeOrder) : closeOrder;
808
+
809
+ if (!anchor.BN.isBN(buyTokenAmount) || !anchor.BN.isBN(maxSolAmount)) {
810
+ throw new Error('buyTokenAmount 和 maxSolAmount 必须是 anchor.BN 类型 buyTokenAmount and maxSolAmount must be anchor.BN type');
811
+ }
812
+
813
+ // 我的关闭损订单数据以便查找前后节点 / Get orders data to find prev/next nodes
814
+ const ordersStopData = await this.sdk.data.orders(mint.toString(), {
815
+ type: 'up_orders',
816
+ limit: this.sdk.FIND_MAX_ORDERS_COUNT
817
+ });
818
+
819
+ // 3. 使用 findPrevNext 查找前后订单 Use findPrevNext to find prev/next orders
820
+ const prevNext = this.sdk.findPrevNext(ordersStopData.data.orders, closeOrderPubkey.toString());
821
+ let prevOrder = prevNext.prevOrder ? new PublicKey(prevNext.prevOrder.order_pda) : null;
822
+ let nextOrder = prevNext.nextOrder ? new PublicKey(prevNext.nextOrder.order_pda) : null;
823
+
824
+ console.log(`closeShort: Found previous order: ${prevOrder ? prevOrder.toString() : 'null'}`);
825
+ console.log(`closeShort: Found next order: ${nextOrder ? nextOrder.toString() : 'null'}`);
826
+
827
+ // 安全的调试日志写入
828
+ const prevOrderStr = prevOrder ? prevOrder.toString() : 'null';
829
+ const nextOrderStr = nextOrder ? nextOrder.toString() : 'null';
830
+ this._writeDebugLog('orderOpen.txt', `closeShort ${prevOrderStr} -> ${closeOrderPubkey.toString()} -> ${nextOrderStr}\n`);
831
+
832
+ // 2. 获取订单数据以便查找前后节点 Get orders data to find prev/next nodes
833
+ const ordersData = await this.sdk.data.orders(mint.toString(), {
834
+ type: 'up_orders',
835
+ limit: this.sdk.FIND_MAX_ORDERS_COUNT
836
+ });
837
+
838
+ // 计算订单在数组中的位置索引 / Calculate order position indices in array
839
+ let prev_order_id = this.sdk.findOrderIndex(ordersData.data.orders, prevOrder);
840
+ let close_order_id = this.sdk.findOrderIndex(ordersData.data.orders, closeOrderPubkey);
841
+ let next_order_id = this.sdk.findOrderIndex(ordersData.data.orders, nextOrder);
842
+
843
+ // 处理越界情况:如果索引 >= MAX_ORDERS_COUNT,设为 200
844
+ // Handle out-of-bounds: if index >= MAX_ORDERS_COUNT, set to 200
845
+ if (prev_order_id >= this.sdk.MAX_ORDERS_COUNT) {
846
+ prev_order_id = 200;
847
+ }
848
+ if (close_order_id >= this.sdk.MAX_ORDERS_COUNT) {
849
+ close_order_id = 200;
850
+ }
851
+ if (next_order_id >= this.sdk.MAX_ORDERS_COUNT) {
852
+ next_order_id = 200;
853
+ }
854
+
855
+ // 根据索引值调整PDA地址:如果索引为不为200,对应PDA设为null
856
+ // Adjust PDA addresses based on index values: if index is 200, set corresponding PDA to null
857
+ if (prev_order_id != 200) {
858
+ prevOrder = null;
859
+ }
860
+ if (close_order_id != 200) {
861
+ closeOrderPubkey = null;
862
+ }
863
+ if (next_order_id != 200) {
864
+ nextOrder = null;
865
+ }
866
+
867
+ console.log(`closeShort: Order indices - prev: ${prev_order_id}, close: ${close_order_id}, next: ${next_order_id}`);
868
+
869
+ // 4. 获取当前价格并构建 lpPairs / Get current price and build lpPairs
870
+ const currentPrice = await this.sdk.data.price(mintAccount);
871
+ const lpPairs = this.sdk.buildLpPairs(ordersData.data.orders, 'up_orders', currentPrice, this.sdk.MAX_ORDERS_COUNT);
872
+
873
+ // 5. 计算 PDA 账户 Calculate PDA accounts
874
+ const accounts = this._calculatePDAAccounts(mint);
875
+
876
+ // 6. 构建订单账户数组(基于 lpPairs 对应的订单)Build order accounts array
877
+ const orderAccounts = this.sdk.buildOrderAccounts(ordersData.data.orders, this.sdk.MAX_ORDERS_COUNT);
878
+
879
+ // 7. 构建订单账户参数 Build order accounts parameters
880
+ const orderAccountsParams = this._buildOrderAccountsParams(orderAccounts);
881
+
882
+ //console.log(`closeShort: Order accounts params: ${JSON.stringify(orderAccountsParams)}`);
883
+
884
+ // 8. 获取用户代币账户 Get user token account
885
+ const userTokenAccount = await getAssociatedTokenAddress(
886
+ mint,
887
+ payer
888
+ );
889
+
890
+ // 9. 检查用户代币账户是否存在,如果不存在则创建 Check if user token account exists, create if not
891
+ const userTokenAccountInfo = await this.sdk.connection.getAccountInfo(userTokenAccount);
892
+ const createAtaIx = userTokenAccountInfo === null
893
+ ? createAssociatedTokenAccountInstruction(
894
+ payer,
895
+ userTokenAccount,
896
+ payer,
897
+ mint,
898
+ TOKEN_PROGRAM_ID,
899
+ ASSOCIATED_TOKEN_PROGRAM_ID
900
+ )
901
+ : null;
902
+
903
+ // 10. 构建交易指令 Build transaction instructions
904
+ const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
905
+ units: computeUnits
906
+ });
907
+
908
+ const closeShortIx = await this.sdk.program.methods
909
+ .closeShort(
910
+ lpPairs, // lp_pairs: LP配对数组 LP pairs array
911
+ buyTokenAmount, // buy_token_amount: 希望买入的token数量 Amount of tokens to buy
912
+ maxSolAmount, // max_sol_amount: 愿意给出的最大sol数量 Maximum SOL amount to spend
913
+ prev_order_id, // prev_order_id: 前一个订单的数组索引 / Previous order array index
914
+ close_order_id, // close_order_id: 要关闭订单的数组索引 / Close order array index
915
+ next_order_id, // next_order_id: 下一个订单的数组索引 / Next order array index
916
+ closeOrder.toString() // close_order_address: 要关闭订单的PDA地址字符串 / Close order PDA address string
917
+ )
918
+ .accounts({
919
+ payer: payer,
920
+ mintAccount: mint,
921
+ curveAccount: accounts.curveAccount,
922
+ poolTokenAccount: accounts.poolTokenAccount,
923
+ poolSolAccount: accounts.poolSolAccount,
924
+ userTokenAccount: userTokenAccount,
925
+ userSolAccount: closeUserAccount, // 用户SOL账户,接收平仓后的SOL / User SOL account to receive funds after closing
926
+ tokenProgram: TOKEN_PROGRAM_ID,
927
+ systemProgram: SystemProgram.programId,
928
+ rent: SYSVAR_RENT_PUBKEY,
929
+ associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
930
+ feeRecipientAccount: this.sdk.feeRecipient,
931
+ baseFeeRecipientAccount: this.sdk.baseFeeRecipient,
932
+ ...orderAccountsParams,
933
+ prevOrder: prevOrder, // 前一个订单 Previous order
934
+ closeOrder: closeOrderPubkey, // 要关闭的订单 Order to close
935
+ nextOrder: nextOrder // 下一个订单 Next order
936
+ })
937
+ .instruction();
938
+
939
+ // 11. 创建交易并添加指令 Create transaction and add instructions
940
+ const transaction = new Transaction();
941
+ transaction.add(modifyComputeUnits);
942
+
943
+ // 如果用户代币账户不存在,先创建账户 If user token account doesn't exist, create it first
944
+ if (createAtaIx) {
945
+ transaction.add(createAtaIx);
946
+ }
947
+
948
+ transaction.add(closeShortIx);
949
+
950
+ // 12. 返回交易对象和相关信息 Return transaction object and related info
951
+ return {
952
+ transaction,
953
+ signers: [], // 平仓做空交易不需要额外的签名者,只需要 payer 签名 Close short transaction doesn't need additional signers, only payer signature
954
+ accounts: {
955
+ mint: mint,
956
+ curveAccount: accounts.curveAccount,
957
+ poolTokenAccount: accounts.poolTokenAccount,
958
+ poolSolAccount: accounts.poolSolAccount,
959
+ userTokenAccount: userTokenAccount,
960
+ payer: payer,
961
+ closeOrder: closeOrderPubkey,
962
+ prevOrder: prevOrder,
963
+ nextOrder: nextOrder
964
+ },
965
+ orderData: {
966
+ ordersUsed: ordersData.data.orders.length,
967
+ lpPairsCount: lpPairs.filter(p => !p.solAmount.isZero()).length,
968
+ lpPairs: lpPairs,
969
+ orderAccounts: orderAccounts,
970
+ prevOrder: prevOrder,
971
+ nextOrder: nextOrder,
972
+ closeOrderAddress: closeOrderPubkey
973
+ }
974
+ };
975
+ }
976
+
977
+ /**
978
+ * 平仓做空 Close Short Position
979
+ * @param {Object} params - 平仓参数 Close position parameters
980
+ * @param {string|PublicKey} params.mintAccount - 代币铸造账户地址 Token mint account address
981
+ * @param {string|PublicKey} params.closeOrder - 需要关闭的订单地址 Order address to close
982
+ * @param {anchor.BN} params.buyTokenAmount - 希望买入的token数量 Amount of tokens to buy
983
+ * @param {anchor.BN} params.maxSolAmount - 愿意给出的最大sol数量 Maximum SOL amount to spend
984
+ * @param {PublicKey} params.payer - 支付者公钥 Payer public key
985
+ * @param {Object} options - 可选参数 Optional parameters
986
+ * @param {number} options.computeUnits - 计算单元限制,默认1400000 Compute units limit, default 1400000
987
+ * @returns {Promise<Object>} 包含交易对象、签名者和账户信息的对象 Object containing transaction, signers and account info
988
+ *
989
+ * @example
990
+ * const result = await sdk.trading.closeShort({
991
+ * mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
992
+ * closeOrder: "E2T72D4wZdxHRjELN5VnRdcCvS4FPcYBBT3UBEoaC5cA",
993
+ * buyTokenAmount: new anchor.BN("1000000000"),
994
+ * maxSolAmount: new anchor.BN("100000000"),
995
+ * payer: wallet.publicKey
996
+ * });
997
+ */
998
+ async closeShort({ mintAccount, closeOrder, buyTokenAmount, maxSolAmount, payer }, options = {}) {
999
+ // 直接调用 closeShortPlus,使用 payer 作为 closeUserAccount
1000
+ return this.closeShortPlus({
1001
+ mintAccount,
1002
+ closeOrder,
1003
+ buyTokenAmount,
1004
+ maxSolAmount,
1005
+ payer,
1006
+ closeUserAccount: payer
1007
+ }, options);
1008
+ }
1009
+
1010
+ // ========== Debug File Management Methods ==========
1011
+
1012
+ /**
1013
+ * 安全地写入调试日志
1014
+ * Safely write debug log
1015
+ * @private
1016
+ * @param {string} fileName - 文件名
1017
+ * @param {string} content - 内容
1018
+ */
1019
+ _writeDebugLog(fileName, content) {
1020
+ if (!IS_NODE || !this.sdk.debugLogPath || typeof this.sdk.debugLogPath !== 'string' || !fs || !path) {
1021
+ return; // 浏览器环境或文件系统不可用时直接返回
1022
+ }
1023
+
1024
+ try {
1025
+ const fullPath = path.join(this.sdk.debugLogPath, fileName);
1026
+ fs.appendFileSync(fullPath, content);
1027
+ } catch (error) {
1028
+ console.warn(`Warning: Failed to write debug log to ${fileName}:`, error.message);
1029
+ }
1030
+ }
1031
+
1032
+ // ========== PDA Calculation Methods ==========
1033
+
1034
+ /**
1035
+ * 计算 PDA 账户
1036
+ * @private
1037
+ * @param {PublicKey} mintAccount - 代币铸造账户
1038
+ * @returns {Object} PDA 账户对象
1039
+ */
1040
+ _calculatePDAAccounts(mintAccount) {
1041
+ // 计算曲线账户 PDA
1042
+ const [curveAccount] = PublicKey.findProgramAddressSync(
1043
+ [Buffer.from('borrowing_curve'), mintAccount.toBuffer()],
1044
+ this.sdk.programId
1045
+ );
1046
+
1047
+ // 计算池子代币账户 PDA
1048
+ const [poolTokenAccount] = PublicKey.findProgramAddressSync(
1049
+ [Buffer.from('pool_token'), mintAccount.toBuffer()],
1050
+ this.sdk.programId
1051
+ );
1052
+
1053
+ // 计算池子 SOL 账户 PDA
1054
+ const [poolSolAccount] = PublicKey.findProgramAddressSync(
1055
+ [Buffer.from('pool_sol'), mintAccount.toBuffer()],
1056
+ this.sdk.programId
1057
+ );
1058
+
1059
+ return {
1060
+ curveAccount,
1061
+ poolTokenAccount,
1062
+ poolSolAccount
1063
+ };
1064
+ }
1065
+
1066
+
1067
+
1068
+ /**
1069
+ * 构建订单账户参数
1070
+ * @private
1071
+ * @param {Array} orderAccounts - 订单账户数组
1072
+ * @returns {Object} 订单账户参数对象
1073
+ */
1074
+ _buildOrderAccountsParams(orderAccounts) {
1075
+ const params = {};
1076
+
1077
+ for (let i = 0; i < this.sdk.MAX_ORDERS_COUNT; i++) {
1078
+ const account = orderAccounts[i];
1079
+ params[`order${i}`] = account ? new PublicKey(account) : null;
1080
+ //console.log("_buildOrderAccountsParams order",i,account);
1081
+ }
1082
+
1083
+ return params;
1084
+ }
1085
+ }
1086
+
1087
+ module.exports = TradingModule;