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.
- package/README.md +674 -0
- package/dist/index.d.ts +342 -0
- package/dist/pinpet-sdk.cjs.js +50648 -0
- package/dist/pinpet-sdk.cjs.js.map +1 -0
- package/dist/pinpet-sdk.esm.js +39265 -0
- package/dist/pinpet-sdk.esm.js.map +1 -0
- package/dist/pinpet-sdk.js +39277 -0
- package/dist/pinpet-sdk.js.map +1 -0
- package/package.json +67 -0
- package/src/idl/pinpet.json +3511 -0
- package/src/index.js +43 -0
- package/src/modules/chain.js +1102 -0
- package/src/modules/close.js +0 -0
- package/src/modules/fast.js +431 -0
- package/src/modules/param.js +171 -0
- package/src/modules/simulator/buy.js_del +711 -0
- package/src/modules/simulator/buy_sell_token.js +300 -0
- package/src/modules/simulator/calcLiq.js +701 -0
- package/src/modules/simulator/long_shrot_stop.js +602 -0
- package/src/modules/simulator/sell.js_del +323 -0
- package/src/modules/simulator/stop_loss_utils.js +223 -0
- package/src/modules/simulator/utils.js +44 -0
- package/src/modules/simulator.js +133 -0
- package/src/modules/token.js +140 -0
- package/src/modules/trading.js +1087 -0
- package/src/sdk.js +370 -0
- package/src/types/index.d.ts +342 -0
- package/src/utils/constants.js +49 -0
- package/src/utils/curve_amm.js +884 -0
- package/src/utils/orderUtils.js +465 -0
|
@@ -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;
|