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,370 @@
|
|
|
1
|
+
const anchor = require('@coral-xyz/anchor');
|
|
2
|
+
const { PublicKey, SystemProgram, SYSVAR_RENT_PUBKEY, Transaction } = require('@solana/web3.js');
|
|
3
|
+
const { getAssociatedTokenAddress, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } = require('@solana/spl-token');
|
|
4
|
+
// 统一使用 buffer 包,所有平台一致
|
|
5
|
+
const { Buffer } = require('buffer');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Tools Module
|
|
9
|
+
* Provides trading utility functions: approve trade and close cooldown PDA
|
|
10
|
+
*/
|
|
11
|
+
class ToolsModule {
|
|
12
|
+
/**
|
|
13
|
+
* Constructor
|
|
14
|
+
* @param {Fun100xSdk} sdk - SDK instance
|
|
15
|
+
*/
|
|
16
|
+
constructor(sdk) {
|
|
17
|
+
this.sdk = sdk;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Approve current token balance for trading
|
|
22
|
+
*
|
|
23
|
+
* Use Cases:
|
|
24
|
+
* 1. After receiving tokens from another address and wanting to trade immediately
|
|
25
|
+
* 2. Reactivating cooldown PDA
|
|
26
|
+
*
|
|
27
|
+
* @param {Object} params - Parameters
|
|
28
|
+
* @param {PublicKey|string} params.mint - Token mint address
|
|
29
|
+
* @param {Keypair} params.wallet - User wallet for signing
|
|
30
|
+
* @returns {Promise<Object>} Transaction object and related account info
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const result = await sdk.tools.approveTrade({
|
|
34
|
+
* mint: 'xxxxx',
|
|
35
|
+
* wallet: userKeypair
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* const signature = await this.sdk.connection.sendTransaction(
|
|
39
|
+
* result.transaction,
|
|
40
|
+
* [wallet]
|
|
41
|
+
* );
|
|
42
|
+
*/
|
|
43
|
+
async approveTrade(params) {
|
|
44
|
+
const { mint, wallet } = params;
|
|
45
|
+
|
|
46
|
+
// Validate parameters
|
|
47
|
+
if (!mint) {
|
|
48
|
+
throw new Error('mint parameter is required');
|
|
49
|
+
}
|
|
50
|
+
if (!wallet) {
|
|
51
|
+
throw new Error('wallet parameter is required');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Convert mint to PublicKey
|
|
55
|
+
const mintPubkey = typeof mint === 'string' ? new PublicKey(mint) : mint;
|
|
56
|
+
const walletPubkey = wallet.publicKey;
|
|
57
|
+
|
|
58
|
+
// Calculate user token account
|
|
59
|
+
const userTokenAccount = await getAssociatedTokenAddress(
|
|
60
|
+
mintPubkey,
|
|
61
|
+
walletPubkey
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// Calculate trade cooldown PDA
|
|
65
|
+
const [cooldown] = PublicKey.findProgramAddressSync(
|
|
66
|
+
[
|
|
67
|
+
Buffer.from('trade_cooldown'),
|
|
68
|
+
mintPubkey.toBuffer(),
|
|
69
|
+
walletPubkey.toBuffer()
|
|
70
|
+
],
|
|
71
|
+
this.sdk.programId
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Build instruction
|
|
75
|
+
const approveTradeIx = await this.sdk.program.methods
|
|
76
|
+
.approveTrade()
|
|
77
|
+
.accounts({
|
|
78
|
+
payer: walletPubkey,
|
|
79
|
+
mintAccount: mintPubkey,
|
|
80
|
+
userTokenAccount: userTokenAccount,
|
|
81
|
+
cooldown: cooldown,
|
|
82
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
83
|
+
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
84
|
+
systemProgram: SystemProgram.programId,
|
|
85
|
+
rent: SYSVAR_RENT_PUBKEY,
|
|
86
|
+
})
|
|
87
|
+
.instruction();
|
|
88
|
+
|
|
89
|
+
// Build transaction
|
|
90
|
+
const transaction = new Transaction().add(approveTradeIx);
|
|
91
|
+
|
|
92
|
+
// Return result
|
|
93
|
+
return {
|
|
94
|
+
transaction,
|
|
95
|
+
signers: [],
|
|
96
|
+
accounts: {
|
|
97
|
+
payer: walletPubkey,
|
|
98
|
+
mintAccount: mintPubkey,
|
|
99
|
+
userTokenAccount: userTokenAccount,
|
|
100
|
+
cooldown: cooldown,
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Manually close TradeCooldown PDA and reclaim rent
|
|
107
|
+
*
|
|
108
|
+
* Conditions:
|
|
109
|
+
* 1. Can only close your own PDA (verified through seeds)
|
|
110
|
+
*
|
|
111
|
+
* Use Cases:
|
|
112
|
+
* - User wants to reclaim rent
|
|
113
|
+
* - Clean up unused PDAs
|
|
114
|
+
* - Admin batch cleanup of expired PDAs
|
|
115
|
+
*
|
|
116
|
+
* Notes:
|
|
117
|
+
* - No need to verify token balance, can be recreated via approve_trade after closing
|
|
118
|
+
* - After PDA is closed, next buy or approve will automatically recreate it
|
|
119
|
+
*
|
|
120
|
+
* @param {Object} params - Parameters
|
|
121
|
+
* @param {PublicKey|string} params.mint - Token mint address
|
|
122
|
+
* @param {Keypair} params.wallet - User wallet for signing
|
|
123
|
+
* @returns {Promise<Object>} Transaction object and related account info
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* const result = await sdk.tools.closeTradeCooldown({
|
|
127
|
+
* mint: 'xxxxx',
|
|
128
|
+
* wallet: userKeypair
|
|
129
|
+
* });
|
|
130
|
+
*
|
|
131
|
+
* const signature = await this.sdk.connection.sendTransaction(
|
|
132
|
+
* result.transaction,
|
|
133
|
+
* [wallet]
|
|
134
|
+
* );
|
|
135
|
+
*/
|
|
136
|
+
async closeTradeCooldown(params) {
|
|
137
|
+
const { mint, wallet } = params;
|
|
138
|
+
|
|
139
|
+
// Validate parameters
|
|
140
|
+
if (!mint) {
|
|
141
|
+
throw new Error('mint parameter is required');
|
|
142
|
+
}
|
|
143
|
+
if (!wallet) {
|
|
144
|
+
throw new Error('wallet parameter is required');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Convert mint to PublicKey
|
|
148
|
+
const mintPubkey = typeof mint === 'string' ? new PublicKey(mint) : mint;
|
|
149
|
+
const walletPubkey = wallet.publicKey;
|
|
150
|
+
|
|
151
|
+
// Calculate user token account
|
|
152
|
+
const userTokenAccount = await getAssociatedTokenAddress(
|
|
153
|
+
mintPubkey,
|
|
154
|
+
walletPubkey
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// Calculate trade cooldown PDA
|
|
158
|
+
const [cooldown] = PublicKey.findProgramAddressSync(
|
|
159
|
+
[
|
|
160
|
+
Buffer.from('trade_cooldown'),
|
|
161
|
+
mintPubkey.toBuffer(),
|
|
162
|
+
walletPubkey.toBuffer()
|
|
163
|
+
],
|
|
164
|
+
this.sdk.programId
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Get cooldown account to fetch bump
|
|
168
|
+
// Use manual fetch method to avoid provider issues
|
|
169
|
+
const cooldownAccountInfo = await this.sdk.connection.getAccountInfo(cooldown);
|
|
170
|
+
if (!cooldownAccountInfo) {
|
|
171
|
+
throw new Error(`Cooldown PDA does not exist for mint: ${mint} and wallet: ${wallet.publicKey.toString()}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const accountsCoder = new anchor.BorshAccountsCoder(this.sdk.program.idl);
|
|
175
|
+
let cooldownAccountData;
|
|
176
|
+
try {
|
|
177
|
+
cooldownAccountData = accountsCoder.decode('TradeCooldown', cooldownAccountInfo.data);
|
|
178
|
+
} catch (e1) {
|
|
179
|
+
try {
|
|
180
|
+
cooldownAccountData = accountsCoder.decode('tradeCooldown', cooldownAccountInfo.data);
|
|
181
|
+
} catch (e2) {
|
|
182
|
+
throw new Error(`Cannot decode cooldown account: ${e1.message}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Build instruction
|
|
187
|
+
const closeTradeCooldownIx = await this.sdk.program.methods
|
|
188
|
+
.closeTradeCooldown()
|
|
189
|
+
.accounts({
|
|
190
|
+
payer: walletPubkey,
|
|
191
|
+
mintAccount: mintPubkey,
|
|
192
|
+
userTokenAccount: userTokenAccount,
|
|
193
|
+
cooldown: cooldown,
|
|
194
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
195
|
+
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
196
|
+
systemProgram: SystemProgram.programId,
|
|
197
|
+
})
|
|
198
|
+
.instruction();
|
|
199
|
+
|
|
200
|
+
// Build transaction
|
|
201
|
+
const transaction = new Transaction().add(closeTradeCooldownIx);
|
|
202
|
+
|
|
203
|
+
// Return result
|
|
204
|
+
return {
|
|
205
|
+
transaction,
|
|
206
|
+
signers: [],
|
|
207
|
+
accounts: {
|
|
208
|
+
payer: walletPubkey,
|
|
209
|
+
mintAccount: mintPubkey,
|
|
210
|
+
userTokenAccount: userTokenAccount,
|
|
211
|
+
cooldown: cooldown,
|
|
212
|
+
},
|
|
213
|
+
cooldownInfo: {
|
|
214
|
+
lastTradeTime: cooldownAccountData.lastTradeTime,
|
|
215
|
+
approvalTokenAmount: cooldownAccountData.approvalTokenAmount,
|
|
216
|
+
bump: cooldownAccountData.bump,
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Validate cooldown PDA approval_token_amount matches user's current token balance
|
|
223
|
+
*
|
|
224
|
+
* Use Cases:
|
|
225
|
+
* - Before trading, verify if the cooldown PDA is in sync with user's token balance
|
|
226
|
+
* - Check if user needs to call approveTrade after receiving tokens
|
|
227
|
+
* - Validate cooldown state for security checks
|
|
228
|
+
*
|
|
229
|
+
* @param {Object} params - Parameters
|
|
230
|
+
* @param {PublicKey|string} params.mint - Token mint address
|
|
231
|
+
* @param {Keypair|Object} params.wallet - User wallet (can be Keypair or object with publicKey)
|
|
232
|
+
* @param {anchor.BN|number|string} [params.tokenBalance] - Optional: user's current token balance, if not provided will fetch from chain
|
|
233
|
+
* @returns {Promise<Object>} Validation result with detailed info
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* // Auto-fetch token balance from chain
|
|
237
|
+
* const result = await sdk.tools.validateCooldown({
|
|
238
|
+
* mint: 'xxxxx',
|
|
239
|
+
* wallet: userKeypair
|
|
240
|
+
* });
|
|
241
|
+
*
|
|
242
|
+
* // Provide token balance manually
|
|
243
|
+
* const result = await sdk.tools.validateCooldown({
|
|
244
|
+
* mint: 'xxxxx',
|
|
245
|
+
* wallet: userKeypair,
|
|
246
|
+
* tokenBalance: new anchor.BN('1000000')
|
|
247
|
+
* });
|
|
248
|
+
*
|
|
249
|
+
* console.log(result.isValid); // true/false
|
|
250
|
+
* console.log(result.cooldownInfo.approvalTokenAmount);
|
|
251
|
+
* console.log(result.tokenBalance);
|
|
252
|
+
*/
|
|
253
|
+
async validateCooldown(params) {
|
|
254
|
+
const { mint, wallet, tokenBalance } = params;
|
|
255
|
+
|
|
256
|
+
// Validate parameters
|
|
257
|
+
if (!mint) {
|
|
258
|
+
throw new Error('mint parameter is required');
|
|
259
|
+
}
|
|
260
|
+
if (!wallet) {
|
|
261
|
+
throw new Error('wallet parameter is required');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Convert mint to PublicKey
|
|
265
|
+
const mintPubkey = typeof mint === 'string' ? new PublicKey(mint) : mint;
|
|
266
|
+
const walletPubkey = wallet.publicKey || wallet;
|
|
267
|
+
|
|
268
|
+
// Calculate user token account
|
|
269
|
+
const userTokenAccount = await getAssociatedTokenAddress(
|
|
270
|
+
mintPubkey,
|
|
271
|
+
walletPubkey
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
// Calculate trade cooldown PDA
|
|
275
|
+
const [cooldown] = PublicKey.findProgramAddressSync(
|
|
276
|
+
[
|
|
277
|
+
Buffer.from('trade_cooldown'),
|
|
278
|
+
mintPubkey.toBuffer(),
|
|
279
|
+
walletPubkey.toBuffer()
|
|
280
|
+
],
|
|
281
|
+
this.sdk.programId
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
// Get cooldown account data
|
|
285
|
+
const cooldownAccountInfo = await this.sdk.connection.getAccountInfo(cooldown);
|
|
286
|
+
if (!cooldownAccountInfo) {
|
|
287
|
+
// Cooldown PDA does not exist, return status without throwing error
|
|
288
|
+
return {
|
|
289
|
+
isValid: false,
|
|
290
|
+
exists: false,
|
|
291
|
+
reason: 'COOLDOWN_NOT_EXISTS',
|
|
292
|
+
message: 'Cooldown PDA does not exist. User has never traded this token or needs to call approveTrade first.',
|
|
293
|
+
cooldownInfo: null,
|
|
294
|
+
tokenBalance: null,
|
|
295
|
+
accounts: {
|
|
296
|
+
mintAccount: mintPubkey,
|
|
297
|
+
userTokenAccount: userTokenAccount,
|
|
298
|
+
cooldown: cooldown,
|
|
299
|
+
wallet: walletPubkey,
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const accountsCoder = new anchor.BorshAccountsCoder(this.sdk.program.idl);
|
|
305
|
+
let cooldownAccountData;
|
|
306
|
+
try {
|
|
307
|
+
cooldownAccountData = accountsCoder.decode('TradeCooldown', cooldownAccountInfo.data);
|
|
308
|
+
} catch (e1) {
|
|
309
|
+
try {
|
|
310
|
+
cooldownAccountData = accountsCoder.decode('tradeCooldown', cooldownAccountInfo.data);
|
|
311
|
+
} catch (e2) {
|
|
312
|
+
throw new Error(`Cannot decode cooldown account: ${e1.message}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Get user's current token balance
|
|
317
|
+
let currentTokenBalance;
|
|
318
|
+
if (tokenBalance !== undefined && tokenBalance !== null) {
|
|
319
|
+
// Use provided token balance
|
|
320
|
+
if (anchor.BN.isBN(tokenBalance)) {
|
|
321
|
+
currentTokenBalance = tokenBalance;
|
|
322
|
+
} else {
|
|
323
|
+
currentTokenBalance = new anchor.BN(tokenBalance.toString());
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
// Fetch token balance from chain
|
|
327
|
+
const userTokenAccountInfo = await this.sdk.connection.getAccountInfo(userTokenAccount);
|
|
328
|
+
if (!userTokenAccountInfo) {
|
|
329
|
+
throw new Error(`User token account does not exist for mint: ${mint} and wallet: ${walletPubkey.toString()}`);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
// Fetch SPL token account balance
|
|
334
|
+
const tokenAccountInfo = await this.sdk.connection.getTokenAccountBalance(userTokenAccount);
|
|
335
|
+
currentTokenBalance = new anchor.BN(tokenAccountInfo.value.amount);
|
|
336
|
+
} catch (e) {
|
|
337
|
+
throw new Error(`Cannot fetch token balance: ${e.message}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Compare approval_token_amount with current token balance
|
|
342
|
+
// Valid if approval_token_amount >= current token balance
|
|
343
|
+
const approvalTokenAmount = new anchor.BN(cooldownAccountData.approvalTokenAmount.toString());
|
|
344
|
+
const isValid = approvalTokenAmount.gte(currentTokenBalance);
|
|
345
|
+
|
|
346
|
+
// Return result
|
|
347
|
+
return {
|
|
348
|
+
isValid,
|
|
349
|
+
exists: true,
|
|
350
|
+
reason: isValid ? 'VALID' : 'AMOUNT_MISMATCH',
|
|
351
|
+
message: isValid
|
|
352
|
+
? 'Cooldown validation passed. approval_token_amount >= token_balance'
|
|
353
|
+
: 'Cooldown validation failed. approval_token_amount < token_balance. User needs to call approveTrade.',
|
|
354
|
+
cooldownInfo: {
|
|
355
|
+
approvalTokenAmount: cooldownAccountData.approvalTokenAmount,
|
|
356
|
+
lastTradeTime: cooldownAccountData.lastTradeTime,
|
|
357
|
+
bump: cooldownAccountData.bump,
|
|
358
|
+
},
|
|
359
|
+
tokenBalance: currentTokenBalance,
|
|
360
|
+
accounts: {
|
|
361
|
+
mintAccount: mintPubkey,
|
|
362
|
+
userTokenAccount: userTokenAccount,
|
|
363
|
+
cooldown: cooldown,
|
|
364
|
+
wallet: walletPubkey,
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
module.exports = ToolsModule;
|