100x-sdk 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +364 -0
- package/dist/100x-sdk.cjs.js +57601 -0
- package/dist/100x-sdk.cjs.js.map +1 -0
- package/dist/100x-sdk.esm.js +46234 -0
- package/dist/100x-sdk.esm.js.map +1 -0
- package/dist/100x-sdk.js +46245 -0
- package/dist/100x-sdk.js.map +1 -0
- package/dist/index.d.ts +386 -0
- package/package.json +69 -0
- package/src/idl/fun100x_localnet.json +3735 -0
- package/src/idl/fun100x_main.json +3735 -0
- package/src/index.js +50 -0
- package/src/modules/chain.js +1310 -0
- package/src/modules/fast.js +734 -0
- package/src/modules/param.js +171 -0
- package/src/modules/simulator/buy_sell_token.js +326 -0
- package/src/modules/simulator/calcLiq.js +778 -0
- package/src/modules/simulator/calc_sol_liq.js +268 -0
- package/src/modules/simulator/close_indices.js +217 -0
- package/src/modules/simulator/long_shrot_stop.js +1028 -0
- package/src/modules/simulator/stop_loss_utils.js +378 -0
- package/src/modules/simulator/utils.js +63 -0
- package/src/modules/simulator.js +386 -0
- package/src/modules/token.js +455 -0
- package/src/modules/tools.js +370 -0
- package/src/modules/trading.js +848 -0
- package/src/sdk.js +206 -0
- package/src/types/index.d.ts +386 -0
- package/src/utils/constants.js +57 -0
- package/src/utils/curve_amm.js +1215 -0
- package/src/utils/orderUtils.js +17 -0
|
@@ -0,0 +1,848 @@
|
|
|
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
|
+
const { MAX_CANDIDATE_INDICES } = require('./simulator/utils');
|
|
7
|
+
|
|
8
|
+
// 环境检测和条件加载
|
|
9
|
+
const IS_NODE = typeof process !== 'undefined' && process.versions && process.versions.node;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Trading Module
|
|
13
|
+
* Handles buy/sell and long/short trading operations
|
|
14
|
+
*/
|
|
15
|
+
class TradingModule {
|
|
16
|
+
constructor(sdk) {
|
|
17
|
+
this.sdk = sdk;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Buy tokens
|
|
22
|
+
* @param {Object} params - Buy parameters
|
|
23
|
+
* @param {string|PublicKey} params.mintAccount - Token mint account address
|
|
24
|
+
* @param {anchor.BN} params.buyTokenAmount - Amount of tokens to buy
|
|
25
|
+
* @param {anchor.BN} params.maxSolAmount - Maximum SOL to spend
|
|
26
|
+
* @param {PublicKey} params.payer - Payer public key
|
|
27
|
+
* @param {Object} options - Optional parameters
|
|
28
|
+
* @param {number} options.computeUnits - Compute units limit, default 1400000
|
|
29
|
+
* @returns {Promise<Object>} Object containing transaction, signers and account info
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const result = await sdk.trading.buy({
|
|
33
|
+
* mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
|
|
34
|
+
* buyTokenAmount: new anchor.BN("1000000000"),
|
|
35
|
+
* maxSolAmount: new anchor.BN("2000000000"),
|
|
36
|
+
* payer: wallet.publicKey
|
|
37
|
+
* });
|
|
38
|
+
*/
|
|
39
|
+
async buy({ mintAccount, buyTokenAmount, maxSolAmount, payer }, options = {}) {
|
|
40
|
+
const { computeUnits = 1400000 } = options;
|
|
41
|
+
|
|
42
|
+
// 1. Parameter validation and conversion
|
|
43
|
+
const mint = typeof mintAccount === 'string' ? new PublicKey(mintAccount) : mintAccount;
|
|
44
|
+
|
|
45
|
+
if (!anchor.BN.isBN(buyTokenAmount) || !anchor.BN.isBN(maxSolAmount)) {
|
|
46
|
+
throw new Error('buyTokenAmount and maxSolAmount must be anchor.BN type');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 2. Calculate PDA accounts
|
|
50
|
+
const accounts = this._calculatePDAAccounts(mint);
|
|
51
|
+
|
|
52
|
+
// 3. Calculate orderbook PDAs
|
|
53
|
+
const [upOrderbook] = PublicKey.findProgramAddressSync(
|
|
54
|
+
[Buffer.from('up_orderbook'), mint.toBuffer()],
|
|
55
|
+
this.sdk.programId
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const [downOrderbook] = PublicKey.findProgramAddressSync(
|
|
59
|
+
[Buffer.from('down_orderbook'), mint.toBuffer()],
|
|
60
|
+
this.sdk.programId
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// 4. Get user token account
|
|
64
|
+
const userTokenAccount = await getAssociatedTokenAddress(
|
|
65
|
+
mint,
|
|
66
|
+
payer
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// 5. Check if user token account exists, create if not
|
|
70
|
+
const userTokenAccountInfo = await this.sdk.connection.getAccountInfo(userTokenAccount);
|
|
71
|
+
const createAtaIx = userTokenAccountInfo === null
|
|
72
|
+
? createAssociatedTokenAccountInstruction(
|
|
73
|
+
payer,
|
|
74
|
+
userTokenAccount,
|
|
75
|
+
payer,
|
|
76
|
+
mint,
|
|
77
|
+
TOKEN_PROGRAM_ID,
|
|
78
|
+
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
79
|
+
)
|
|
80
|
+
: null;
|
|
81
|
+
|
|
82
|
+
// 6. Get fee recipient accounts from curve account (skipBalances: only need addresses)
|
|
83
|
+
const curveAccountInfo = await this.sdk.chain.getCurveAccount(mint, { skipBalances: true });
|
|
84
|
+
const feeRecipientAccount = new PublicKey(curveAccountInfo.feeRecipient);
|
|
85
|
+
const baseFeeRecipientAccount = new PublicKey(curveAccountInfo.baseFeeRecipient);
|
|
86
|
+
|
|
87
|
+
// 6.5. Calculate cooldown PDA
|
|
88
|
+
const [cooldownPDA] = PublicKey.findProgramAddressSync(
|
|
89
|
+
[
|
|
90
|
+
Buffer.from('trade_cooldown'),
|
|
91
|
+
mint.toBuffer(),
|
|
92
|
+
payer.toBuffer()
|
|
93
|
+
],
|
|
94
|
+
this.sdk.programId
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// 7. Build transaction instructions
|
|
98
|
+
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
|
|
99
|
+
units: computeUnits
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const buyIx = await this.sdk.program.methods
|
|
103
|
+
.buy(buyTokenAmount, maxSolAmount)
|
|
104
|
+
.accounts({
|
|
105
|
+
payer: payer,
|
|
106
|
+
mintAccount: mint,
|
|
107
|
+
curveAccount: accounts.curveAccount,
|
|
108
|
+
poolTokenAccount: accounts.poolTokenAccount,
|
|
109
|
+
poolSolAccount: accounts.poolSolAccount,
|
|
110
|
+
upOrderbook: upOrderbook,
|
|
111
|
+
downOrderbook: downOrderbook,
|
|
112
|
+
userTokenAccount: userTokenAccount,
|
|
113
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
114
|
+
systemProgram: SystemProgram.programId,
|
|
115
|
+
rent: SYSVAR_RENT_PUBKEY,
|
|
116
|
+
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
117
|
+
feeRecipientAccount: feeRecipientAccount,
|
|
118
|
+
baseFeeRecipientAccount: baseFeeRecipientAccount,
|
|
119
|
+
cooldown: cooldownPDA
|
|
120
|
+
})
|
|
121
|
+
.instruction();
|
|
122
|
+
|
|
123
|
+
// 8. Create transaction and add instructions
|
|
124
|
+
const transaction = new Transaction();
|
|
125
|
+
transaction.add(modifyComputeUnits);
|
|
126
|
+
|
|
127
|
+
// If user token account doesn't exist, create it first
|
|
128
|
+
if (createAtaIx) {
|
|
129
|
+
transaction.add(createAtaIx);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
transaction.add(buyIx);
|
|
133
|
+
|
|
134
|
+
// 9. Return transaction object and related info
|
|
135
|
+
return {
|
|
136
|
+
transaction,
|
|
137
|
+
signers: [], // Buy transaction doesn't need additional signers, only payer signature
|
|
138
|
+
accounts: {
|
|
139
|
+
mint: mint,
|
|
140
|
+
curveAccount: accounts.curveAccount,
|
|
141
|
+
poolTokenAccount: accounts.poolTokenAccount,
|
|
142
|
+
poolSolAccount: accounts.poolSolAccount,
|
|
143
|
+
upOrderbook: upOrderbook,
|
|
144
|
+
downOrderbook: downOrderbook,
|
|
145
|
+
userTokenAccount: userTokenAccount,
|
|
146
|
+
payer: payer,
|
|
147
|
+
feeRecipientAccount: feeRecipientAccount,
|
|
148
|
+
baseFeeRecipientAccount: baseFeeRecipientAccount,
|
|
149
|
+
cooldown: cooldownPDA
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Sell tokens
|
|
158
|
+
* @param {Object} params - Sell parameters
|
|
159
|
+
* @param {string|PublicKey} params.mintAccount - Token mint account address
|
|
160
|
+
* @param {anchor.BN} params.sellTokenAmount - Amount of tokens to sell
|
|
161
|
+
* @param {anchor.BN} params.minSolOutput - Minimum SOL output
|
|
162
|
+
* @param {PublicKey} params.payer - Payer public key
|
|
163
|
+
* @param {Object} options - Optional parameters
|
|
164
|
+
* @param {number} options.computeUnits - Compute units limit, default 1400000
|
|
165
|
+
* @returns {Promise<Object>} Object containing transaction, signers and account info
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* const result = await sdk.trading.sell({
|
|
169
|
+
* mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
|
|
170
|
+
* sellTokenAmount: new anchor.BN("1000000000"),
|
|
171
|
+
* minSolOutput: new anchor.BN("2000000000"),
|
|
172
|
+
* payer: wallet.publicKey
|
|
173
|
+
* });
|
|
174
|
+
*/
|
|
175
|
+
async sell({ mintAccount, sellTokenAmount, minSolOutput, payer }, options = {}) {
|
|
176
|
+
const { computeUnits = 1400000 } = options;
|
|
177
|
+
|
|
178
|
+
// 1. Parameter validation and conversion
|
|
179
|
+
const mint = typeof mintAccount === 'string' ? new PublicKey(mintAccount) : mintAccount;
|
|
180
|
+
|
|
181
|
+
if (!anchor.BN.isBN(sellTokenAmount) || !anchor.BN.isBN(minSolOutput)) {
|
|
182
|
+
throw new Error('sellTokenAmount and minSolOutput must be anchor.BN type');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 2. Calculate PDA accounts
|
|
186
|
+
const accounts = this._calculatePDAAccounts(mint);
|
|
187
|
+
|
|
188
|
+
// 3. Calculate orderbook PDAs
|
|
189
|
+
const [upOrderbook] = PublicKey.findProgramAddressSync(
|
|
190
|
+
[Buffer.from('up_orderbook'), mint.toBuffer()],
|
|
191
|
+
this.sdk.programId
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const [downOrderbook] = PublicKey.findProgramAddressSync(
|
|
195
|
+
[Buffer.from('down_orderbook'), mint.toBuffer()],
|
|
196
|
+
this.sdk.programId
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// 4. Get user token account
|
|
200
|
+
const userTokenAccount = await getAssociatedTokenAddress(
|
|
201
|
+
mint,
|
|
202
|
+
payer
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// 5. Check if user token account exists, create if not
|
|
206
|
+
const userTokenAccountInfo = await this.sdk.connection.getAccountInfo(userTokenAccount);
|
|
207
|
+
const createAtaIx = userTokenAccountInfo === null
|
|
208
|
+
? createAssociatedTokenAccountInstruction(
|
|
209
|
+
payer,
|
|
210
|
+
userTokenAccount,
|
|
211
|
+
payer,
|
|
212
|
+
mint,
|
|
213
|
+
TOKEN_PROGRAM_ID,
|
|
214
|
+
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
215
|
+
)
|
|
216
|
+
: null;
|
|
217
|
+
|
|
218
|
+
// 6. Get fee recipient accounts from curve account (skipBalances: only need addresses)
|
|
219
|
+
const curveAccountInfo = await this.sdk.chain.getCurveAccount(mint, { skipBalances: true });
|
|
220
|
+
const feeRecipientAccount = new PublicKey(curveAccountInfo.feeRecipient);
|
|
221
|
+
const baseFeeRecipientAccount = new PublicKey(curveAccountInfo.baseFeeRecipient);
|
|
222
|
+
|
|
223
|
+
// 6.5. Calculate cooldown PDA
|
|
224
|
+
const [cooldownPDA] = PublicKey.findProgramAddressSync(
|
|
225
|
+
[
|
|
226
|
+
Buffer.from('trade_cooldown'),
|
|
227
|
+
mint.toBuffer(),
|
|
228
|
+
payer.toBuffer()
|
|
229
|
+
],
|
|
230
|
+
this.sdk.programId
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// 7. Build transaction instructions
|
|
234
|
+
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
|
|
235
|
+
units: computeUnits
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const sellIx = await this.sdk.program.methods
|
|
239
|
+
.sell(sellTokenAmount, minSolOutput)
|
|
240
|
+
.accounts({
|
|
241
|
+
payer: payer,
|
|
242
|
+
mintAccount: mint,
|
|
243
|
+
curveAccount: accounts.curveAccount,
|
|
244
|
+
poolTokenAccount: accounts.poolTokenAccount,
|
|
245
|
+
poolSolAccount: accounts.poolSolAccount,
|
|
246
|
+
upOrderbook: upOrderbook,
|
|
247
|
+
downOrderbook: downOrderbook,
|
|
248
|
+
userTokenAccount: userTokenAccount,
|
|
249
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
250
|
+
systemProgram: SystemProgram.programId,
|
|
251
|
+
rent: SYSVAR_RENT_PUBKEY,
|
|
252
|
+
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
253
|
+
feeRecipientAccount: feeRecipientAccount,
|
|
254
|
+
baseFeeRecipientAccount: baseFeeRecipientAccount,
|
|
255
|
+
cooldown: cooldownPDA
|
|
256
|
+
})
|
|
257
|
+
.instruction();
|
|
258
|
+
|
|
259
|
+
// 8. Create transaction and add instructions
|
|
260
|
+
const transaction = new Transaction();
|
|
261
|
+
transaction.add(modifyComputeUnits);
|
|
262
|
+
|
|
263
|
+
// If user token account doesn't exist, create it first
|
|
264
|
+
if (createAtaIx) {
|
|
265
|
+
transaction.add(createAtaIx);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
transaction.add(sellIx);
|
|
269
|
+
|
|
270
|
+
// 9. Return transaction object and related info
|
|
271
|
+
return {
|
|
272
|
+
transaction,
|
|
273
|
+
signers: [], // Sell transaction doesn't need additional signers, only payer signature
|
|
274
|
+
accounts: {
|
|
275
|
+
mint: mint,
|
|
276
|
+
curveAccount: accounts.curveAccount,
|
|
277
|
+
poolTokenAccount: accounts.poolTokenAccount,
|
|
278
|
+
poolSolAccount: accounts.poolSolAccount,
|
|
279
|
+
upOrderbook: upOrderbook,
|
|
280
|
+
downOrderbook: downOrderbook,
|
|
281
|
+
userTokenAccount: userTokenAccount,
|
|
282
|
+
payer: payer,
|
|
283
|
+
feeRecipientAccount: feeRecipientAccount,
|
|
284
|
+
baseFeeRecipientAccount: baseFeeRecipientAccount,
|
|
285
|
+
cooldown: cooldownPDA
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Margin Long
|
|
292
|
+
* @param {Object} params - Long parameters
|
|
293
|
+
* @param {string|PublicKey} params.mintAccount - Token mint account address
|
|
294
|
+
* @param {anchor.BN} params.buyTokenAmount - Amount of tokens to buy (确定值)
|
|
295
|
+
* @param {anchor.BN} params.maxSolAmount - Maximum SOL to spend (实际有可能会少点)
|
|
296
|
+
* @param {anchor.BN} params.marginSol - Margin amount (保证金数量 SOL)
|
|
297
|
+
* @param {anchor.BN} params.closePrice - Close price (平仓价格/止损价格)
|
|
298
|
+
* @param {Array<number>} params.closeInsertIndices - Close insert indices array (平仓时插入订单簿的位置索引数组)
|
|
299
|
+
* @param {PublicKey} params.payer - Payer public key
|
|
300
|
+
* @param {Object} options - Optional parameters
|
|
301
|
+
* @param {number} options.computeUnits - Compute units limit, default 1400000
|
|
302
|
+
* @returns {Promise<Object>} Object containing transaction, signers and account info
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* const result = await sdk.trading.long({
|
|
306
|
+
* mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
|
|
307
|
+
* buyTokenAmount: new anchor.BN("10000000"),
|
|
308
|
+
* maxSolAmount: new anchor.BN("1100000000"),
|
|
309
|
+
* marginSol: new anchor.BN("2200000000"),
|
|
310
|
+
* closePrice: new anchor.BN("1000000000000000"),
|
|
311
|
+
* closeInsertIndices: [0, 1, 2], // 插入位置索引数组
|
|
312
|
+
* payer: wallet.publicKey
|
|
313
|
+
* });
|
|
314
|
+
*/
|
|
315
|
+
async long({ mintAccount, buyTokenAmount, maxSolAmount, marginSol, closePrice, closeInsertIndices, payer }, options = {}) {
|
|
316
|
+
const { computeUnits = 1400000 } = options;
|
|
317
|
+
|
|
318
|
+
// 1. 参数验证和转换
|
|
319
|
+
const mint = typeof mintAccount === 'string' ? new PublicKey(mintAccount) : mintAccount;
|
|
320
|
+
|
|
321
|
+
if (!anchor.BN.isBN(buyTokenAmount) || !anchor.BN.isBN(maxSolAmount) ||
|
|
322
|
+
!anchor.BN.isBN(marginSol) || !anchor.BN.isBN(closePrice)) {
|
|
323
|
+
throw new Error('All amount parameters must be anchor.BN type');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (!Array.isArray(closeInsertIndices) || closeInsertIndices.length === 0) {
|
|
327
|
+
throw new Error('closeInsertIndices must be a non-empty array');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (closeInsertIndices.length > 20) {
|
|
331
|
+
throw new Error('closeInsertIndices array cannot exceed 20 elements');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// 2. Calculate PDA accounts
|
|
335
|
+
const accounts = this._calculatePDAAccounts(mint);
|
|
336
|
+
|
|
337
|
+
// 3. Calculate OrderBook PDA addresses
|
|
338
|
+
const [upOrderbook] = PublicKey.findProgramAddressSync(
|
|
339
|
+
[Buffer.from("up_orderbook"), mint.toBuffer()],
|
|
340
|
+
this.sdk.programId
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
const [downOrderbook] = PublicKey.findProgramAddressSync(
|
|
344
|
+
[Buffer.from("down_orderbook"), mint.toBuffer()],
|
|
345
|
+
this.sdk.programId
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
// 4. Build transaction instructions
|
|
349
|
+
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
|
|
350
|
+
units: computeUnits
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const longIx = await this.sdk.program.methods
|
|
354
|
+
.long(
|
|
355
|
+
buyTokenAmount,
|
|
356
|
+
maxSolAmount,
|
|
357
|
+
marginSol,
|
|
358
|
+
closePrice,
|
|
359
|
+
closeInsertIndices
|
|
360
|
+
)
|
|
361
|
+
.accounts({
|
|
362
|
+
payer: payer,
|
|
363
|
+
mintAccount: mint,
|
|
364
|
+
curveAccount: accounts.curveAccount,
|
|
365
|
+
poolTokenAccount: accounts.poolTokenAccount,
|
|
366
|
+
poolSolAccount: accounts.poolSolAccount,
|
|
367
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
368
|
+
systemProgram: SystemProgram.programId,
|
|
369
|
+
rent: SYSVAR_RENT_PUBKEY,
|
|
370
|
+
feeRecipientAccount: this.sdk.feeRecipient,
|
|
371
|
+
baseFeeRecipientAccount: this.sdk.baseFeeRecipient,
|
|
372
|
+
upOrderbook: upOrderbook,
|
|
373
|
+
downOrderbook: downOrderbook
|
|
374
|
+
})
|
|
375
|
+
.instruction();
|
|
376
|
+
|
|
377
|
+
// 5. Create transaction and add instructions
|
|
378
|
+
const transaction = new Transaction();
|
|
379
|
+
transaction.add(modifyComputeUnits);
|
|
380
|
+
transaction.add(longIx);
|
|
381
|
+
|
|
382
|
+
// 6. Return transaction object and related info
|
|
383
|
+
return {
|
|
384
|
+
transaction,
|
|
385
|
+
signers: [], // Long transaction doesn't need additional signers, only payer signature
|
|
386
|
+
accounts: {
|
|
387
|
+
mint: mint,
|
|
388
|
+
curveAccount: accounts.curveAccount,
|
|
389
|
+
poolTokenAccount: accounts.poolTokenAccount,
|
|
390
|
+
poolSolAccount: accounts.poolSolAccount,
|
|
391
|
+
payer: payer,
|
|
392
|
+
upOrderbook: upOrderbook,
|
|
393
|
+
downOrderbook: downOrderbook
|
|
394
|
+
},
|
|
395
|
+
orderData: {
|
|
396
|
+
closeInsertIndices: closeInsertIndices
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* 保证金做空 / Margin Short
|
|
403
|
+
* @param {Object} params - 做空参数 / Short parameters
|
|
404
|
+
* @param {string|PublicKey} params.mintAccount - 代币铸造账户地址 / Token mint account address
|
|
405
|
+
* @param {anchor.BN} params.borrowSellTokenAmount - 借出卖出的代币数量 (希望卖出的token数量) / Borrowed token amount to sell
|
|
406
|
+
* @param {anchor.BN} params.minSolOutput - 最小 SOL 输出 (卖出后最少得到的sol数量) / Minimum SOL output
|
|
407
|
+
* @param {anchor.BN} params.marginSol - 保证金数量 (SOL) / Margin amount
|
|
408
|
+
* @param {anchor.BN} params.closePrice - 平仓价格 (止损价格) / Close price
|
|
409
|
+
* @param {Array<number>} params.closeInsertIndices - Close insert indices array (平仓时插入订单簿的位置索引数组)
|
|
410
|
+
* @param {PublicKey} params.payer - 支付者公钥 / Payer public key
|
|
411
|
+
* @param {Object} options - 可选参数 / Optional parameters
|
|
412
|
+
* @param {number} options.computeUnits - 计算单元限制,默认 1400000 / Compute units limit, default 1400000
|
|
413
|
+
* @returns {Promise<Object>} 包含交易对象、签名者和账户信息的对象 / Object containing transaction, signers and account info
|
|
414
|
+
*
|
|
415
|
+
* @example
|
|
416
|
+
* const result = await sdk.trading.short({
|
|
417
|
+
* mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
|
|
418
|
+
* borrowSellTokenAmount: new anchor.BN("1000000000"),
|
|
419
|
+
* minSolOutput: new anchor.BN("100"),
|
|
420
|
+
* marginSol: new anchor.BN("2200000000"),
|
|
421
|
+
* closePrice: new anchor.BN("1000000000000000"),
|
|
422
|
+
* closeInsertIndices: [0, 1, 2], // 插入位置索引数组
|
|
423
|
+
* payer: wallet.publicKey
|
|
424
|
+
* });
|
|
425
|
+
*/
|
|
426
|
+
async short({ mintAccount, borrowSellTokenAmount, minSolOutput, marginSol, closePrice, closeInsertIndices, payer }, options = {}) {
|
|
427
|
+
const { computeUnits = 1400000 } = options;
|
|
428
|
+
|
|
429
|
+
// 1. 参数验证和转换 / Parameter validation and conversion
|
|
430
|
+
const mint = typeof mintAccount === 'string' ? new PublicKey(mintAccount) : mintAccount;
|
|
431
|
+
|
|
432
|
+
if (!anchor.BN.isBN(borrowSellTokenAmount) || !anchor.BN.isBN(minSolOutput) ||
|
|
433
|
+
!anchor.BN.isBN(marginSol) || !anchor.BN.isBN(closePrice)) {
|
|
434
|
+
throw new Error('所有金额参数必须是 anchor.BN 类型 / All amount parameters must be anchor.BN type');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (!Array.isArray(closeInsertIndices) || closeInsertIndices.length === 0) {
|
|
438
|
+
throw new Error('closeInsertIndices must be a non-empty array');
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (closeInsertIndices.length > 20) {
|
|
442
|
+
throw new Error('closeInsertIndices array cannot exceed 20 elements');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// 2. 计算 PDA 账户 / Calculate PDA accounts
|
|
446
|
+
const accounts = this._calculatePDAAccounts(mint);
|
|
447
|
+
|
|
448
|
+
// 3. Calculate OrderBook PDA addresses
|
|
449
|
+
const [upOrderbook] = PublicKey.findProgramAddressSync(
|
|
450
|
+
[Buffer.from("up_orderbook"), mint.toBuffer()],
|
|
451
|
+
this.sdk.programId
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
const [downOrderbook] = PublicKey.findProgramAddressSync(
|
|
455
|
+
[Buffer.from("down_orderbook"), mint.toBuffer()],
|
|
456
|
+
this.sdk.programId
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
// 4. 构建交易指令 / Build transaction instructions
|
|
460
|
+
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
|
|
461
|
+
units: computeUnits
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
const shortIx = await this.sdk.program.methods
|
|
465
|
+
.short(
|
|
466
|
+
borrowSellTokenAmount,
|
|
467
|
+
minSolOutput,
|
|
468
|
+
marginSol,
|
|
469
|
+
closePrice,
|
|
470
|
+
closeInsertIndices
|
|
471
|
+
)
|
|
472
|
+
.accounts({
|
|
473
|
+
payer: payer,
|
|
474
|
+
mintAccount: mint,
|
|
475
|
+
curveAccount: accounts.curveAccount,
|
|
476
|
+
poolTokenAccount: accounts.poolTokenAccount,
|
|
477
|
+
poolSolAccount: accounts.poolSolAccount,
|
|
478
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
479
|
+
systemProgram: SystemProgram.programId,
|
|
480
|
+
rent: SYSVAR_RENT_PUBKEY,
|
|
481
|
+
feeRecipientAccount: this.sdk.feeRecipient,
|
|
482
|
+
baseFeeRecipientAccount: this.sdk.baseFeeRecipient,
|
|
483
|
+
upOrderbook: upOrderbook,
|
|
484
|
+
downOrderbook: downOrderbook
|
|
485
|
+
})
|
|
486
|
+
.instruction();
|
|
487
|
+
|
|
488
|
+
// 5. 创建交易并添加指令 / Create transaction and add instructions
|
|
489
|
+
const transaction = new Transaction();
|
|
490
|
+
transaction.add(modifyComputeUnits);
|
|
491
|
+
transaction.add(shortIx);
|
|
492
|
+
|
|
493
|
+
// 6. 返回交易对象和相关信息 / Return transaction object and related info
|
|
494
|
+
return {
|
|
495
|
+
transaction,
|
|
496
|
+
signers: [], // 做空交易不需要额外的签名者,只需要 payer 签名 / Short transaction doesn't need additional signers, only payer signature
|
|
497
|
+
accounts: {
|
|
498
|
+
mint: mint,
|
|
499
|
+
curveAccount: accounts.curveAccount,
|
|
500
|
+
poolTokenAccount: accounts.poolTokenAccount,
|
|
501
|
+
poolSolAccount: accounts.poolSolAccount,
|
|
502
|
+
payer: payer,
|
|
503
|
+
upOrderbook: upOrderbook,
|
|
504
|
+
downOrderbook: downOrderbook
|
|
505
|
+
},
|
|
506
|
+
orderData: {
|
|
507
|
+
closeInsertIndices: closeInsertIndices
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* 平仓做多 / Close Long Position
|
|
514
|
+
* @param {Object} params - 平仓参数 / Close position parameters
|
|
515
|
+
* @param {string|PublicKey} params.mintAccount - 代币铸造账户地址 / Token mint account address
|
|
516
|
+
* @param {anchor.BN} params.sellTokenAmount - 希望卖出的token数量 / Amount of tokens to sell
|
|
517
|
+
* @param {anchor.BN} params.minSolOutput - 卖出后最少得到的sol数量 / Minimum SOL output after selling
|
|
518
|
+
* @param {number|anchor.BN} params.closeOrderId - 订单的唯一编号 / Order unique ID
|
|
519
|
+
* @param {Array<number>} params.closeOrderIndices - 平仓时订单的位置索引数组 / Close order position indices array
|
|
520
|
+
* @param {PublicKey} params.payer - 支付者公钥 / Payer public key
|
|
521
|
+
* @param {PublicKey} params.userSolAccount - 开仓用户的SOL账户(接收资金)/ User SOL account to receive funds (must be order opener)
|
|
522
|
+
* @param {Object} options - 可选参数 / Optional parameters
|
|
523
|
+
* @param {number} options.computeUnits - 计算单元限制,默认1400000 / Compute units limit, default 1400000
|
|
524
|
+
* @returns {Promise<Object>} 包含交易对象、签名者和账户信息的对象 / Object containing transaction, signers and account info
|
|
525
|
+
*
|
|
526
|
+
* @example
|
|
527
|
+
* const result = await sdk.trading.closeLong({
|
|
528
|
+
* mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
|
|
529
|
+
* sellTokenAmount: new anchor.BN("1000000000"),
|
|
530
|
+
* minSolOutput: new anchor.BN("100000000"),
|
|
531
|
+
* closeOrderId: 12345, // 订单唯一编号
|
|
532
|
+
* closeOrderIndices: [10, 11, 12], // 候选索引数组
|
|
533
|
+
* payer: wallet.publicKey,
|
|
534
|
+
* userSolAccount: orderOwnerPublicKey
|
|
535
|
+
* });
|
|
536
|
+
*/
|
|
537
|
+
async closeLong({ mintAccount, sellTokenAmount, minSolOutput, closeOrderId, closeOrderIndices, payer, userSolAccount }, options = {}) {
|
|
538
|
+
const { computeUnits = 1400000 } = options;
|
|
539
|
+
|
|
540
|
+
// 1. 参数验证和转换 / Parameter validation and conversion
|
|
541
|
+
const mint = typeof mintAccount === 'string' ? new PublicKey(mintAccount) : mintAccount;
|
|
542
|
+
|
|
543
|
+
if (!anchor.BN.isBN(sellTokenAmount) || !anchor.BN.isBN(minSolOutput)) {
|
|
544
|
+
throw new Error('sellTokenAmount and minSolOutput must be anchor.BN type');
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// 转换 closeOrderId 为 anchor.BN (u64)
|
|
548
|
+
const closeOrderIdBN = anchor.BN.isBN(closeOrderId) ? closeOrderId : new anchor.BN(closeOrderId);
|
|
549
|
+
|
|
550
|
+
// 验证 closeOrderIndices 参数
|
|
551
|
+
if (!Array.isArray(closeOrderIndices) || closeOrderIndices.length === 0) {
|
|
552
|
+
throw new Error('closeOrderIndices must be a non-empty array');
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (closeOrderIndices.length > MAX_CANDIDATE_INDICES) {
|
|
556
|
+
throw new Error(`closeOrderIndices array cannot exceed ${MAX_CANDIDATE_INDICES} elements`);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// 验证和转换 userSolAccount - 支持字符串或 PublicKey 对象
|
|
560
|
+
let userSolAccountPubkey;
|
|
561
|
+
if (!userSolAccount) {
|
|
562
|
+
throw new Error('userSolAccount is required');
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (typeof userSolAccount === 'string') {
|
|
566
|
+
try {
|
|
567
|
+
userSolAccountPubkey = new PublicKey(userSolAccount);
|
|
568
|
+
} catch (error) {
|
|
569
|
+
throw new Error('userSolAccount must be a valid PublicKey string');
|
|
570
|
+
}
|
|
571
|
+
} else if (userSolAccount instanceof PublicKey) {
|
|
572
|
+
userSolAccountPubkey = userSolAccount;
|
|
573
|
+
} else if (userSolAccount.constructor && userSolAccount.constructor.name === 'PublicKey') {
|
|
574
|
+
// 处理跨包 PublicKey 实例 - 提取字符串后重新创建
|
|
575
|
+
try {
|
|
576
|
+
userSolAccountPubkey = new PublicKey(userSolAccount.toString());
|
|
577
|
+
} catch (error) {
|
|
578
|
+
throw new Error('userSolAccount must be a valid PublicKey');
|
|
579
|
+
}
|
|
580
|
+
} else {
|
|
581
|
+
throw new Error('userSolAccount must be a valid PublicKey or PublicKey string');
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// 2. 计算 PDA 账户 / Calculate PDA accounts
|
|
585
|
+
const accounts = this._calculatePDAAccounts(mint);
|
|
586
|
+
|
|
587
|
+
// 3. 计算 OrderBook PDA 地址 / Calculate OrderBook PDA addresses
|
|
588
|
+
const [upOrderbook] = PublicKey.findProgramAddressSync(
|
|
589
|
+
[Buffer.from('up_orderbook'), mint.toBuffer()],
|
|
590
|
+
this.sdk.programId
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
const [downOrderbook] = PublicKey.findProgramAddressSync(
|
|
594
|
+
[Buffer.from('down_orderbook'), mint.toBuffer()],
|
|
595
|
+
this.sdk.programId
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
// 4. 从 curve account 获取手续费接收账户 / Get fee recipient accounts from curve account (skipBalances: only need addresses)
|
|
599
|
+
const curveAccountInfo = await this.sdk.chain.getCurveAccount(mint, { skipBalances: true });
|
|
600
|
+
const feeRecipientAccount = new PublicKey(curveAccountInfo.feeRecipient);
|
|
601
|
+
const baseFeeRecipientAccount = new PublicKey(curveAccountInfo.baseFeeRecipient);
|
|
602
|
+
|
|
603
|
+
// 5. 构建交易指令 / Build transaction instructions
|
|
604
|
+
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
|
|
605
|
+
units: computeUnits
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
const closeLongIx = await this.sdk.program.methods
|
|
609
|
+
.closeLong(
|
|
610
|
+
sellTokenAmount, // sell_token_amount: 希望卖出的token数量 / Amount of tokens to sell
|
|
611
|
+
minSolOutput, // min_sol_output: 卖出后最少得到的sol数量 / Minimum SOL output
|
|
612
|
+
closeOrderIdBN, // close_order_id: 订单的唯一编号 / Order unique ID
|
|
613
|
+
closeOrderIndices // close_order_indices: 平仓时订单的位置索引数组 / Close order indices array
|
|
614
|
+
)
|
|
615
|
+
.accounts({
|
|
616
|
+
payer: payer,
|
|
617
|
+
mintAccount: mint,
|
|
618
|
+
curveAccount: accounts.curveAccount,
|
|
619
|
+
poolTokenAccount: accounts.poolTokenAccount,
|
|
620
|
+
poolSolAccount: accounts.poolSolAccount,
|
|
621
|
+
userSolAccount: userSolAccountPubkey, // 开仓用户的SOL账户 / User SOL account (must be order opener)
|
|
622
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
623
|
+
systemProgram: SystemProgram.programId,
|
|
624
|
+
rent: SYSVAR_RENT_PUBKEY,
|
|
625
|
+
feeRecipientAccount: feeRecipientAccount,
|
|
626
|
+
baseFeeRecipientAccount: baseFeeRecipientAccount,
|
|
627
|
+
upOrderbook: upOrderbook,
|
|
628
|
+
downOrderbook: downOrderbook
|
|
629
|
+
})
|
|
630
|
+
.instruction();
|
|
631
|
+
|
|
632
|
+
// 6. 创建交易并添加指令 / Create transaction and add instructions
|
|
633
|
+
const transaction = new Transaction();
|
|
634
|
+
transaction.add(modifyComputeUnits);
|
|
635
|
+
transaction.add(closeLongIx);
|
|
636
|
+
|
|
637
|
+
// 7. 返回交易对象和相关信息 / Return transaction object and related info
|
|
638
|
+
return {
|
|
639
|
+
transaction,
|
|
640
|
+
signers: [], // 平仓做多交易不需要额外的签名者,只需要 payer 签名 / Close long transaction doesn't need additional signers, only payer signature
|
|
641
|
+
accounts: {
|
|
642
|
+
mint: mint,
|
|
643
|
+
curveAccount: accounts.curveAccount,
|
|
644
|
+
poolTokenAccount: accounts.poolTokenAccount,
|
|
645
|
+
poolSolAccount: accounts.poolSolAccount,
|
|
646
|
+
payer: payer,
|
|
647
|
+
userSolAccount: userSolAccount,
|
|
648
|
+
upOrderbook: upOrderbook,
|
|
649
|
+
downOrderbook: downOrderbook,
|
|
650
|
+
feeRecipientAccount: feeRecipientAccount,
|
|
651
|
+
baseFeeRecipientAccount: baseFeeRecipientAccount
|
|
652
|
+
},
|
|
653
|
+
orderData: {
|
|
654
|
+
closeOrderId: closeOrderIdBN.toString(),
|
|
655
|
+
closeOrderIndices: closeOrderIndices
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* 平仓做空 / Close Short Position
|
|
663
|
+
* @param {Object} params - 平仓参数 / Close position parameters
|
|
664
|
+
* @param {string|PublicKey} params.mintAccount - 代币铸造账户地址 / Token mint account address
|
|
665
|
+
* @param {anchor.BN} params.buyTokenAmount - 希望买入的token数量 / Amount of tokens to buy
|
|
666
|
+
* @param {anchor.BN} params.maxSolAmount - 愿意给出的最大sol数量 / Maximum SOL amount to spend
|
|
667
|
+
* @param {number|anchor.BN} params.closeOrderId - 订单的唯一编号 / Order unique ID
|
|
668
|
+
* @param {Array<number>} params.closeOrderIndices - 平仓时订单的位置索引数组 / Close order position indices array
|
|
669
|
+
* @param {PublicKey} params.payer - 支付者公钥 / Payer public key
|
|
670
|
+
* @param {PublicKey} params.userSolAccount - 开仓用户的SOL账户(接收资金)/ User SOL account to receive funds (must be order opener)
|
|
671
|
+
* @param {Object} options - 可选参数 / Optional parameters
|
|
672
|
+
* @param {number} options.computeUnits - 计算单元限制,默认1400000 / Compute units limit, default 1400000
|
|
673
|
+
* @returns {Promise<Object>} 包含交易对象、签名者和账户信息的对象 / Object containing transaction, signers and account info
|
|
674
|
+
*
|
|
675
|
+
* @example
|
|
676
|
+
* const result = await sdk.trading.closeShort({
|
|
677
|
+
* mintAccount: "HZBos3RNhExDcAtzmdKXhTd4sVcQFBiT3FDBgmBBMk7",
|
|
678
|
+
* buyTokenAmount: new anchor.BN("1000000000"),
|
|
679
|
+
* maxSolAmount: new anchor.BN("100000000"),
|
|
680
|
+
* closeOrderId: 12345, // 订单唯一编号
|
|
681
|
+
* closeOrderIndices: [10, 11, 12], // 候选索引数组
|
|
682
|
+
* payer: wallet.publicKey,
|
|
683
|
+
* userSolAccount: orderOwnerPublicKey
|
|
684
|
+
* });
|
|
685
|
+
*/
|
|
686
|
+
async closeShort({ mintAccount, buyTokenAmount, maxSolAmount, closeOrderId, closeOrderIndices, payer, userSolAccount }, options = {}) {
|
|
687
|
+
const { computeUnits = 1400000 } = options;
|
|
688
|
+
|
|
689
|
+
// 1. 参数验证和转换 / Parameter validation and conversion
|
|
690
|
+
const mint = typeof mintAccount === 'string' ? new PublicKey(mintAccount) : mintAccount;
|
|
691
|
+
|
|
692
|
+
if (!anchor.BN.isBN(buyTokenAmount) || !anchor.BN.isBN(maxSolAmount)) {
|
|
693
|
+
throw new Error('buyTokenAmount and maxSolAmount must be anchor.BN type');
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// 转换 closeOrderId 为 anchor.BN (u64)
|
|
697
|
+
const closeOrderIdBN = anchor.BN.isBN(closeOrderId) ? closeOrderId : new anchor.BN(closeOrderId);
|
|
698
|
+
|
|
699
|
+
// 验证 closeOrderIndices 参数
|
|
700
|
+
if (!Array.isArray(closeOrderIndices) || closeOrderIndices.length === 0) {
|
|
701
|
+
throw new Error('closeOrderIndices must be a non-empty array');
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (closeOrderIndices.length > MAX_CANDIDATE_INDICES) {
|
|
705
|
+
throw new Error(`closeOrderIndices array cannot exceed ${MAX_CANDIDATE_INDICES} elements`);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// 验证和转换 userSolAccount - 支持字符串或 PublicKey 对象
|
|
709
|
+
let userSolAccountPubkey;
|
|
710
|
+
if (!userSolAccount) {
|
|
711
|
+
throw new Error('userSolAccount is required');
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
if (typeof userSolAccount === 'string') {
|
|
715
|
+
try {
|
|
716
|
+
userSolAccountPubkey = new PublicKey(userSolAccount);
|
|
717
|
+
} catch (error) {
|
|
718
|
+
throw new Error('userSolAccount must be a valid PublicKey string');
|
|
719
|
+
}
|
|
720
|
+
} else if (userSolAccount instanceof PublicKey) {
|
|
721
|
+
userSolAccountPubkey = userSolAccount;
|
|
722
|
+
} else if (userSolAccount.constructor && userSolAccount.constructor.name === 'PublicKey') {
|
|
723
|
+
// 处理跨包 PublicKey 实例 - 提取字符串后重新创建
|
|
724
|
+
try {
|
|
725
|
+
userSolAccountPubkey = new PublicKey(userSolAccount.toString());
|
|
726
|
+
} catch (error) {
|
|
727
|
+
throw new Error('userSolAccount must be a valid PublicKey');
|
|
728
|
+
}
|
|
729
|
+
} else {
|
|
730
|
+
throw new Error('userSolAccount must be a valid PublicKey or PublicKey string');
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// 2. 计算 PDA 账户 / Calculate PDA accounts
|
|
734
|
+
const accounts = this._calculatePDAAccounts(mint);
|
|
735
|
+
|
|
736
|
+
// 3. 计算 OrderBook PDA 地址 / Calculate OrderBook PDA addresses
|
|
737
|
+
const [upOrderbook] = PublicKey.findProgramAddressSync(
|
|
738
|
+
[Buffer.from('up_orderbook'), mint.toBuffer()],
|
|
739
|
+
this.sdk.programId
|
|
740
|
+
);
|
|
741
|
+
|
|
742
|
+
const [downOrderbook] = PublicKey.findProgramAddressSync(
|
|
743
|
+
[Buffer.from('down_orderbook'), mint.toBuffer()],
|
|
744
|
+
this.sdk.programId
|
|
745
|
+
);
|
|
746
|
+
|
|
747
|
+
// 4. 从 curve account 获取手续费接收账户 / Get fee recipient accounts from curve account (skipBalances: only need addresses)
|
|
748
|
+
const curveAccountInfo = await this.sdk.chain.getCurveAccount(mint, { skipBalances: true });
|
|
749
|
+
const feeRecipientAccount = new PublicKey(curveAccountInfo.feeRecipient);
|
|
750
|
+
const baseFeeRecipientAccount = new PublicKey(curveAccountInfo.baseFeeRecipient);
|
|
751
|
+
|
|
752
|
+
// 5. 构建交易指令 / Build transaction instructions
|
|
753
|
+
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
|
|
754
|
+
units: computeUnits
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
const closeShortIx = await this.sdk.program.methods
|
|
758
|
+
.closeShort(
|
|
759
|
+
buyTokenAmount, // buy_token_amount: 希望买入的token数量 / Amount of tokens to buy
|
|
760
|
+
maxSolAmount, // max_sol_amount: 愿意给出的最大sol数量 / Maximum SOL amount to spend
|
|
761
|
+
closeOrderIdBN, // close_order_id: 订单的唯一编号 / Order unique ID
|
|
762
|
+
closeOrderIndices // close_order_indices: 平仓时订单的位置索引数组 / Close order indices array
|
|
763
|
+
)
|
|
764
|
+
.accounts({
|
|
765
|
+
payer: payer,
|
|
766
|
+
mintAccount: mint,
|
|
767
|
+
curveAccount: accounts.curveAccount,
|
|
768
|
+
poolTokenAccount: accounts.poolTokenAccount,
|
|
769
|
+
poolSolAccount: accounts.poolSolAccount,
|
|
770
|
+
userSolAccount: userSolAccountPubkey, // 开仓用户的SOL账户 / User SOL account (must be order opener)
|
|
771
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
772
|
+
systemProgram: SystemProgram.programId,
|
|
773
|
+
rent: SYSVAR_RENT_PUBKEY,
|
|
774
|
+
feeRecipientAccount: feeRecipientAccount,
|
|
775
|
+
baseFeeRecipientAccount: baseFeeRecipientAccount,
|
|
776
|
+
upOrderbook: upOrderbook,
|
|
777
|
+
downOrderbook: downOrderbook
|
|
778
|
+
})
|
|
779
|
+
.instruction();
|
|
780
|
+
|
|
781
|
+
// 6. 创建交易并添加指令 / Create transaction and add instructions
|
|
782
|
+
const transaction = new Transaction();
|
|
783
|
+
transaction.add(modifyComputeUnits);
|
|
784
|
+
transaction.add(closeShortIx);
|
|
785
|
+
|
|
786
|
+
// 7. 返回交易对象和相关信息 / Return transaction object and related info
|
|
787
|
+
return {
|
|
788
|
+
transaction,
|
|
789
|
+
signers: [], // 平仓做空交易不需要额外的签名者,只需要 payer 签名 / Close short transaction doesn't need additional signers, only payer signature
|
|
790
|
+
accounts: {
|
|
791
|
+
mint: mint,
|
|
792
|
+
curveAccount: accounts.curveAccount,
|
|
793
|
+
poolTokenAccount: accounts.poolTokenAccount,
|
|
794
|
+
poolSolAccount: accounts.poolSolAccount,
|
|
795
|
+
payer: payer,
|
|
796
|
+
userSolAccount: userSolAccount,
|
|
797
|
+
upOrderbook: upOrderbook,
|
|
798
|
+
downOrderbook: downOrderbook,
|
|
799
|
+
feeRecipientAccount: feeRecipientAccount,
|
|
800
|
+
baseFeeRecipientAccount: baseFeeRecipientAccount
|
|
801
|
+
},
|
|
802
|
+
orderData: {
|
|
803
|
+
closeOrderId: closeOrderIdBN.toString(),
|
|
804
|
+
closeOrderIndices: closeOrderIndices
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// ========== PDA Calculation Methods ==========
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* 计算 PDA 账户
|
|
813
|
+
* @private
|
|
814
|
+
* @param {PublicKey} mintAccount - 代币铸造账户
|
|
815
|
+
* @returns {Object} PDA 账户对象
|
|
816
|
+
*/
|
|
817
|
+
_calculatePDAAccounts(mintAccount) {
|
|
818
|
+
// 计算曲线账户 PDA
|
|
819
|
+
const [curveAccount] = PublicKey.findProgramAddressSync(
|
|
820
|
+
[Buffer.from('borrowing_curve'), mintAccount.toBuffer()],
|
|
821
|
+
this.sdk.programId
|
|
822
|
+
);
|
|
823
|
+
|
|
824
|
+
// 计算池子代币账户 PDA
|
|
825
|
+
const [poolTokenAccount] = PublicKey.findProgramAddressSync(
|
|
826
|
+
[Buffer.from('pool_token'), mintAccount.toBuffer()],
|
|
827
|
+
this.sdk.programId
|
|
828
|
+
);
|
|
829
|
+
|
|
830
|
+
// 计算池子 SOL 账户 PDA
|
|
831
|
+
const [poolSolAccount] = PublicKey.findProgramAddressSync(
|
|
832
|
+
[Buffer.from('pool_sol'), mintAccount.toBuffer()],
|
|
833
|
+
this.sdk.programId
|
|
834
|
+
);
|
|
835
|
+
|
|
836
|
+
return {
|
|
837
|
+
curveAccount,
|
|
838
|
+
poolTokenAccount,
|
|
839
|
+
poolSolAccount
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
module.exports = TradingModule;
|