pump-trader 1.1.4 → 1.1.8
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/index.js +480 -236
- package/index.ts +1311 -247
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
TransactionInstruction,
|
|
6
6
|
SystemProgram,
|
|
7
7
|
ComputeBudgetProgram,
|
|
8
|
-
Keypair
|
|
8
|
+
Keypair,
|
|
9
9
|
} from "@solana/web3.js";
|
|
10
10
|
|
|
11
11
|
import {
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
getTokenMetadata,
|
|
18
18
|
TOKEN_2022_PROGRAM_ID,
|
|
19
19
|
TOKEN_PROGRAM_ID,
|
|
20
|
-
getMint
|
|
20
|
+
getMint,
|
|
21
21
|
} from "@solana/spl-token";
|
|
22
22
|
|
|
23
23
|
import BN from "bn.js";
|
|
@@ -30,28 +30,30 @@ const PROGRAM_IDS = {
|
|
|
30
30
|
PUMP_AMM: new PublicKey("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"),
|
|
31
31
|
METADATA: new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"),
|
|
32
32
|
FEE: new PublicKey("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"),
|
|
33
|
-
EVENT_AUTHORITY: new PublicKey(
|
|
33
|
+
EVENT_AUTHORITY: new PublicKey(
|
|
34
|
+
"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1",
|
|
35
|
+
),
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
const SOL_MINT = new PublicKey("So11111111111111111111111111111111111111112");
|
|
37
39
|
|
|
38
40
|
const SEEDS = {
|
|
39
41
|
FEE_CONFIG: new Uint8Array([
|
|
40
|
-
1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, 91, 170,
|
|
41
|
-
|
|
42
|
+
1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, 91, 170, 81,
|
|
43
|
+
137, 203, 151, 245, 210, 255, 59, 101, 93, 43, 182, 253, 109, 24, 176,
|
|
42
44
|
]),
|
|
43
45
|
AMM_FEE_CONFIG: Buffer.from([
|
|
44
|
-
12, 20, 222, 252, 130, 94, 198, 118, 148, 37, 8, 24, 187, 101, 64, 101,
|
|
45
|
-
|
|
46
|
+
12, 20, 222, 252, 130, 94, 198, 118, 148, 37, 8, 24, 187, 101, 64, 101, 244,
|
|
47
|
+
41, 141, 49, 86, 213, 113, 180, 212, 248, 9, 12, 24, 233, 168, 99,
|
|
46
48
|
]),
|
|
47
49
|
GLOBAL: Buffer.from("global"),
|
|
48
|
-
BONDING: Buffer.from("bonding-curve")
|
|
50
|
+
BONDING: Buffer.from("bonding-curve"),
|
|
49
51
|
};
|
|
50
52
|
|
|
51
53
|
const DISCRIMINATORS = {
|
|
52
54
|
BUY: Buffer.from([102, 6, 61, 18, 1, 218, 235, 234]),
|
|
53
55
|
SELL: Buffer.from([51, 230, 133, 164, 1, 127, 131, 173]),
|
|
54
|
-
TRADE_EVENT: Buffer.from([189, 219, 127, 211, 78, 230, 97, 238])
|
|
56
|
+
TRADE_EVENT: Buffer.from([189, 219, 127, 211, 78, 230, 97, 238]),
|
|
55
57
|
};
|
|
56
58
|
|
|
57
59
|
const AMM_FEE_BPS = 100n; // 1%
|
|
@@ -64,7 +66,7 @@ const PUMP_NEW_FEE_RECIPIENTS = [
|
|
|
64
66
|
"5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6",
|
|
65
67
|
"EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL",
|
|
66
68
|
"5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD",
|
|
67
|
-
"A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"
|
|
69
|
+
"A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW",
|
|
68
70
|
].map((value) => new PublicKey(value));
|
|
69
71
|
|
|
70
72
|
/* ================= 工具函数 ================= */
|
|
@@ -84,7 +86,9 @@ const readU32 = (buf, offsetObj) => {
|
|
|
84
86
|
|
|
85
87
|
const readString = (buf, offsetObj) => {
|
|
86
88
|
const len = readU32(buf, offsetObj);
|
|
87
|
-
const str = buf
|
|
89
|
+
const str = buf
|
|
90
|
+
.slice(offsetObj.offset, offsetObj.offset + len)
|
|
91
|
+
.toString("utf8");
|
|
88
92
|
offsetObj.offset += len;
|
|
89
93
|
return str;
|
|
90
94
|
};
|
|
@@ -94,10 +98,14 @@ const readString = (buf, offsetObj) => {
|
|
|
94
98
|
function parseMetadataAccount(data) {
|
|
95
99
|
const offsetObj = { offset: 1 }; // 跳过 key
|
|
96
100
|
|
|
97
|
-
const updateAuthority = new PublicKey(
|
|
101
|
+
const updateAuthority = new PublicKey(
|
|
102
|
+
data.slice(offsetObj.offset, offsetObj.offset + 32),
|
|
103
|
+
);
|
|
98
104
|
offsetObj.offset += 32;
|
|
99
105
|
|
|
100
|
-
const mint = new PublicKey(
|
|
106
|
+
const mint = new PublicKey(
|
|
107
|
+
data.slice(offsetObj.offset, offsetObj.offset + 32),
|
|
108
|
+
);
|
|
101
109
|
offsetObj.offset += 32;
|
|
102
110
|
|
|
103
111
|
const name = readString(data, offsetObj);
|
|
@@ -109,13 +117,13 @@ function parseMetadataAccount(data) {
|
|
|
109
117
|
mint: mint.toBase58(),
|
|
110
118
|
name,
|
|
111
119
|
symbol,
|
|
112
|
-
uri
|
|
120
|
+
uri,
|
|
113
121
|
};
|
|
114
122
|
}
|
|
115
123
|
|
|
116
124
|
function parsePoolKeys(data) {
|
|
117
125
|
if (!data || data.length < 280) {
|
|
118
|
-
throw new Error(
|
|
126
|
+
throw new Error("Invalid pool account data");
|
|
119
127
|
}
|
|
120
128
|
|
|
121
129
|
let offset = 8; // 跳过 discriminator
|
|
@@ -152,7 +160,8 @@ function parsePoolKeys(data) {
|
|
|
152
160
|
|
|
153
161
|
const isMayhemMode = data.readUInt8(offset) === 1;
|
|
154
162
|
offset += 1;
|
|
155
|
-
const isCashbackCoin =
|
|
163
|
+
const isCashbackCoin =
|
|
164
|
+
offset < data.length ? data.readUInt8(offset) === 1 : false;
|
|
156
165
|
|
|
157
166
|
return {
|
|
158
167
|
creator,
|
|
@@ -163,7 +172,7 @@ function parsePoolKeys(data) {
|
|
|
163
172
|
poolQuoteTokenAccount,
|
|
164
173
|
coinCreator,
|
|
165
174
|
isMayhemMode,
|
|
166
|
-
isCashbackCoin
|
|
175
|
+
isCashbackCoin,
|
|
167
176
|
};
|
|
168
177
|
}
|
|
169
178
|
|
|
@@ -173,7 +182,10 @@ export class PumpTrader {
|
|
|
173
182
|
constructor(rpc, privateKey) {
|
|
174
183
|
this.connection = new Connection(rpc, "confirmed");
|
|
175
184
|
this.wallet = Keypair.fromSecretKey(bs58.decode(privateKey));
|
|
176
|
-
this.global = PublicKey.findProgramAddressSync(
|
|
185
|
+
this.global = PublicKey.findProgramAddressSync(
|
|
186
|
+
[SEEDS.GLOBAL],
|
|
187
|
+
PROGRAM_IDS.PUMP,
|
|
188
|
+
)[0];
|
|
177
189
|
this.globalState = null;
|
|
178
190
|
this.tokenProgramCache = new Map(); // 缓存token program检测结果
|
|
179
191
|
}
|
|
@@ -195,29 +207,59 @@ export class PumpTrader {
|
|
|
195
207
|
|
|
196
208
|
try {
|
|
197
209
|
// 首先尝试获取 TOKEN_2022 的代币信息
|
|
198
|
-
const mintData = await getMint(
|
|
210
|
+
const mintData = await getMint(
|
|
211
|
+
this.connection,
|
|
212
|
+
mint,
|
|
213
|
+
"confirmed",
|
|
214
|
+
TOKEN_2022_PROGRAM_ID,
|
|
215
|
+
);
|
|
199
216
|
const result = {
|
|
200
217
|
type: "TOKEN_2022_PROGRAM_ID",
|
|
201
|
-
programId: TOKEN_2022_PROGRAM_ID
|
|
218
|
+
programId: TOKEN_2022_PROGRAM_ID,
|
|
202
219
|
};
|
|
203
220
|
this.tokenProgramCache.set(tokenAddr, result);
|
|
204
221
|
return result;
|
|
205
222
|
} catch (e) {
|
|
206
223
|
try {
|
|
207
224
|
// 如果失败,尝试标准 TOKEN_PROGRAM_ID
|
|
208
|
-
const mintData = await getMint(
|
|
225
|
+
const mintData = await getMint(
|
|
226
|
+
this.connection,
|
|
227
|
+
mint,
|
|
228
|
+
"confirmed",
|
|
229
|
+
TOKEN_PROGRAM_ID,
|
|
230
|
+
);
|
|
209
231
|
const result = {
|
|
210
232
|
type: "TOKEN_PROGRAM_ID",
|
|
211
|
-
programId: TOKEN_PROGRAM_ID
|
|
233
|
+
programId: TOKEN_PROGRAM_ID,
|
|
212
234
|
};
|
|
213
235
|
this.tokenProgramCache.set(tokenAddr, result);
|
|
214
236
|
return result;
|
|
215
237
|
} catch (error) {
|
|
216
|
-
throw new Error(
|
|
238
|
+
throw new Error(
|
|
239
|
+
`Failed to detect token program for ${tokenAddr}: ${error}`,
|
|
240
|
+
);
|
|
217
241
|
}
|
|
218
242
|
}
|
|
219
243
|
}
|
|
220
244
|
|
|
245
|
+
async detectQuoteTokenProgram(quoteMint) {
|
|
246
|
+
const quoteAddr = quoteMint.toBase58();
|
|
247
|
+
if (this.tokenProgramCache.has(quoteAddr)) {
|
|
248
|
+
return this.tokenProgramCache.get(quoteAddr).programId;
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
await getMint(
|
|
252
|
+
this.connection,
|
|
253
|
+
quoteMint,
|
|
254
|
+
"confirmed",
|
|
255
|
+
TOKEN_2022_PROGRAM_ID,
|
|
256
|
+
);
|
|
257
|
+
return TOKEN_2022_PROGRAM_ID;
|
|
258
|
+
} catch {
|
|
259
|
+
return TOKEN_PROGRAM_ID;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
221
263
|
/* ---------- 内盘/外盘检测 ---------- */
|
|
222
264
|
|
|
223
265
|
/**
|
|
@@ -282,7 +324,7 @@ export class PumpTrader {
|
|
|
282
324
|
initialVirtualSolReserves: readU64(),
|
|
283
325
|
initialRealTokenReserves: readU64(),
|
|
284
326
|
tokenTotalSupply: readU64(),
|
|
285
|
-
feeBasisPoints: readU64()
|
|
327
|
+
feeBasisPoints: readU64(),
|
|
286
328
|
};
|
|
287
329
|
|
|
288
330
|
return this.globalState;
|
|
@@ -293,14 +335,14 @@ export class PumpTrader {
|
|
|
293
335
|
getBondingPda(mint) {
|
|
294
336
|
return PublicKey.findProgramAddressSync(
|
|
295
337
|
[SEEDS.BONDING, mint.toBuffer()],
|
|
296
|
-
PROGRAM_IDS.PUMP
|
|
338
|
+
PROGRAM_IDS.PUMP,
|
|
297
339
|
)[0];
|
|
298
340
|
}
|
|
299
341
|
|
|
300
342
|
deriveBondingCurveV2(mint) {
|
|
301
343
|
return PublicKey.findProgramAddressSync(
|
|
302
344
|
[Buffer.from("bonding-curve-v2"), mint.toBuffer()],
|
|
303
|
-
PROGRAM_IDS.PUMP
|
|
345
|
+
PROGRAM_IDS.PUMP,
|
|
304
346
|
)[0];
|
|
305
347
|
}
|
|
306
348
|
|
|
@@ -316,7 +358,11 @@ export class PumpTrader {
|
|
|
316
358
|
{ pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
|
|
317
359
|
{ pubkey: args.mint, isSigner: false, isWritable: false },
|
|
318
360
|
{ pubkey: args.bonding, isSigner: false, isWritable: true },
|
|
319
|
-
{
|
|
361
|
+
{
|
|
362
|
+
pubkey: args.associatedBondingCurve,
|
|
363
|
+
isSigner: false,
|
|
364
|
+
isWritable: true,
|
|
365
|
+
},
|
|
320
366
|
{ pubkey: args.userAta, isSigner: false, isWritable: true },
|
|
321
367
|
{ pubkey: args.wallet, isSigner: true, isWritable: true },
|
|
322
368
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
@@ -324,12 +370,16 @@ export class PumpTrader {
|
|
|
324
370
|
{ pubkey: args.creatorVault, isSigner: false, isWritable: true },
|
|
325
371
|
{ pubkey: args.eventAuthority, isSigner: false, isWritable: false },
|
|
326
372
|
{ pubkey: args.pumpProgram, isSigner: false, isWritable: false },
|
|
327
|
-
{
|
|
373
|
+
{
|
|
374
|
+
pubkey: args.globalVolumeAccumulator,
|
|
375
|
+
isSigner: false,
|
|
376
|
+
isWritable: false,
|
|
377
|
+
},
|
|
328
378
|
{ pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
329
379
|
{ pubkey: args.feeConfig, isSigner: false, isWritable: false },
|
|
330
380
|
{ pubkey: args.feeProgram, isSigner: false, isWritable: false },
|
|
331
381
|
{ pubkey: args.bondingCurveV2, isSigner: false, isWritable: false },
|
|
332
|
-
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true }
|
|
382
|
+
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true },
|
|
333
383
|
];
|
|
334
384
|
}
|
|
335
385
|
|
|
@@ -340,7 +390,11 @@ export class PumpTrader {
|
|
|
340
390
|
{ pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
|
|
341
391
|
{ pubkey: args.mint, isSigner: false, isWritable: false },
|
|
342
392
|
{ pubkey: args.bonding, isSigner: false, isWritable: true },
|
|
343
|
-
{
|
|
393
|
+
{
|
|
394
|
+
pubkey: args.associatedBondingCurve,
|
|
395
|
+
isSigner: false,
|
|
396
|
+
isWritable: true,
|
|
397
|
+
},
|
|
344
398
|
{ pubkey: args.userAta, isSigner: false, isWritable: true },
|
|
345
399
|
{ pubkey: args.wallet, isSigner: true, isWritable: true },
|
|
346
400
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
@@ -349,16 +403,20 @@ export class PumpTrader {
|
|
|
349
403
|
{ pubkey: args.eventAuthority, isSigner: false, isWritable: false },
|
|
350
404
|
{ pubkey: args.pumpProgram, isSigner: false, isWritable: false },
|
|
351
405
|
{ pubkey: args.feeConfig, isSigner: false, isWritable: false },
|
|
352
|
-
{ pubkey: args.feeProgram, isSigner: false, isWritable: false }
|
|
406
|
+
{ pubkey: args.feeProgram, isSigner: false, isWritable: false },
|
|
353
407
|
];
|
|
354
408
|
|
|
355
409
|
if (args.isCashbackCoin) {
|
|
356
|
-
keys.push({
|
|
410
|
+
keys.push({
|
|
411
|
+
pubkey: args.userVolumeAccumulator,
|
|
412
|
+
isSigner: false,
|
|
413
|
+
isWritable: true,
|
|
414
|
+
});
|
|
357
415
|
}
|
|
358
416
|
|
|
359
417
|
keys.push(
|
|
360
418
|
{ pubkey: args.bondingCurveV2, isSigner: false, isWritable: false },
|
|
361
|
-
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true }
|
|
419
|
+
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true },
|
|
362
420
|
);
|
|
363
421
|
|
|
364
422
|
return keys;
|
|
@@ -394,25 +452,29 @@ export class PumpTrader {
|
|
|
394
452
|
|
|
395
453
|
calcBuy(solIn, state) {
|
|
396
454
|
const newVirtualSol = state.virtualSolReserves + solIn;
|
|
397
|
-
const newVirtualToken =
|
|
455
|
+
const newVirtualToken =
|
|
456
|
+
(state.virtualSolReserves * state.virtualTokenReserves) / newVirtualSol;
|
|
398
457
|
return state.virtualTokenReserves - newVirtualToken;
|
|
399
458
|
}
|
|
400
459
|
|
|
401
460
|
calcSell(tokenIn, state) {
|
|
402
461
|
const newVirtualToken = state.virtualTokenReserves + tokenIn;
|
|
403
|
-
const newVirtualSol =
|
|
462
|
+
const newVirtualSol =
|
|
463
|
+
(state.virtualSolReserves * state.virtualTokenReserves) / newVirtualToken;
|
|
404
464
|
return state.virtualSolReserves - newVirtualSol;
|
|
405
465
|
}
|
|
406
466
|
|
|
407
467
|
calculateAmmBuyOutput(quoteIn, reserves) {
|
|
408
|
-
const quoteInAfterFee =
|
|
468
|
+
const quoteInAfterFee =
|
|
469
|
+
(quoteIn * (BPS_DENOMINATOR - AMM_FEE_BPS)) / BPS_DENOMINATOR;
|
|
409
470
|
const numerator = reserves.baseAmount * quoteInAfterFee;
|
|
410
471
|
const denominator = reserves.quoteAmount + quoteInAfterFee;
|
|
411
472
|
return numerator / denominator;
|
|
412
473
|
}
|
|
413
474
|
|
|
414
475
|
calculateAmmSellOutput(baseIn, reserves) {
|
|
415
|
-
const baseInAfterFee =
|
|
476
|
+
const baseInAfterFee =
|
|
477
|
+
(baseIn * (BPS_DENOMINATOR - AMM_FEE_BPS)) / BPS_DENOMINATOR;
|
|
416
478
|
const numerator = reserves.quoteAmount * baseInAfterFee;
|
|
417
479
|
const denominator = reserves.baseAmount + baseInAfterFee;
|
|
418
480
|
return numerator / denominator;
|
|
@@ -438,13 +500,19 @@ export class PumpTrader {
|
|
|
438
500
|
async getAmmPrice(mint) {
|
|
439
501
|
const [poolCreator] = PublicKey.findProgramAddressSync(
|
|
440
502
|
[Buffer.from("pool-authority"), mint.toBuffer()],
|
|
441
|
-
PROGRAM_IDS.PUMP
|
|
503
|
+
PROGRAM_IDS.PUMP,
|
|
442
504
|
);
|
|
443
505
|
|
|
444
506
|
const indexBuffer = new BN(0).toArrayLike(Buffer, "le", 2);
|
|
445
507
|
const [pool] = PublicKey.findProgramAddressSync(
|
|
446
|
-
[
|
|
447
|
-
|
|
508
|
+
[
|
|
509
|
+
Buffer.from("pool"),
|
|
510
|
+
indexBuffer,
|
|
511
|
+
poolCreator.toBuffer(),
|
|
512
|
+
mint.toBuffer(),
|
|
513
|
+
SOL_MINT.toBuffer(),
|
|
514
|
+
],
|
|
515
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
448
516
|
);
|
|
449
517
|
|
|
450
518
|
const acc = await this.connection.getAccountInfo(pool);
|
|
@@ -453,7 +521,7 @@ export class PumpTrader {
|
|
|
453
521
|
const poolKeys = parsePoolKeys(acc.data);
|
|
454
522
|
const [baseInfo, quoteInfo] = await Promise.all([
|
|
455
523
|
this.connection.getTokenAccountBalance(poolKeys.poolBaseTokenAccount),
|
|
456
|
-
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount)
|
|
524
|
+
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
|
|
457
525
|
]);
|
|
458
526
|
|
|
459
527
|
return quoteInfo.value.uiAmount / baseInfo.value.uiAmount;
|
|
@@ -472,9 +540,12 @@ export class PumpTrader {
|
|
|
472
540
|
const mint = new PublicKey(tokenAddr);
|
|
473
541
|
const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner(
|
|
474
542
|
this.wallet.publicKey,
|
|
475
|
-
{ mint }
|
|
543
|
+
{ mint },
|
|
544
|
+
);
|
|
545
|
+
return (
|
|
546
|
+
tokenAccounts.value[0]?.account.data.parsed.info.tokenAmount.uiAmount ||
|
|
547
|
+
0
|
|
476
548
|
);
|
|
477
|
-
return tokenAccounts.value[0]?.account.data.parsed.info.tokenAmount.uiAmount || 0;
|
|
478
549
|
} else {
|
|
479
550
|
// 查询所有代币
|
|
480
551
|
return this.getAllTokenBalances();
|
|
@@ -488,13 +559,13 @@ export class PumpTrader {
|
|
|
488
559
|
async getAllTokenBalances() {
|
|
489
560
|
const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner(
|
|
490
561
|
this.wallet.publicKey,
|
|
491
|
-
{ programId: TOKEN_PROGRAM_ID }
|
|
562
|
+
{ programId: TOKEN_PROGRAM_ID },
|
|
492
563
|
);
|
|
493
564
|
|
|
494
565
|
const balances = tokenAccounts.value
|
|
495
566
|
.map((account) => {
|
|
496
567
|
const parsed = account.account.data.parsed;
|
|
497
|
-
if (parsed.type !==
|
|
568
|
+
if (parsed.type !== "account") return null;
|
|
498
569
|
|
|
499
570
|
const tokenAmount = parsed.info.tokenAmount;
|
|
500
571
|
if (Number(tokenAmount.amount) === 0) return null;
|
|
@@ -503,21 +574,22 @@ export class PumpTrader {
|
|
|
503
574
|
mint: parsed.info.mint,
|
|
504
575
|
amount: BigInt(tokenAmount.amount),
|
|
505
576
|
decimals: tokenAmount.decimals,
|
|
506
|
-
uiAmount: tokenAmount.uiAmount || 0
|
|
577
|
+
uiAmount: tokenAmount.uiAmount || 0,
|
|
507
578
|
};
|
|
508
579
|
})
|
|
509
580
|
.filter((item) => item !== null);
|
|
510
581
|
|
|
511
582
|
// 同时查询 TOKEN_2022_PROGRAM_ID
|
|
512
|
-
const token2022Accounts =
|
|
513
|
-
this.
|
|
514
|
-
|
|
515
|
-
|
|
583
|
+
const token2022Accounts =
|
|
584
|
+
await this.connection.getParsedTokenAccountsByOwner(
|
|
585
|
+
this.wallet.publicKey,
|
|
586
|
+
{ programId: TOKEN_2022_PROGRAM_ID },
|
|
587
|
+
);
|
|
516
588
|
|
|
517
589
|
const token2022Balances = token2022Accounts.value
|
|
518
590
|
.map((account) => {
|
|
519
591
|
const parsed = account.account.data.parsed;
|
|
520
|
-
if (parsed.type !==
|
|
592
|
+
if (parsed.type !== "account") return null;
|
|
521
593
|
|
|
522
594
|
const tokenAmount = parsed.info.tokenAmount;
|
|
523
595
|
if (Number(tokenAmount.amount) === 0) return null;
|
|
@@ -526,7 +598,7 @@ export class PumpTrader {
|
|
|
526
598
|
mint: parsed.info.mint,
|
|
527
599
|
amount: BigInt(tokenAmount.amount),
|
|
528
600
|
decimals: tokenAmount.decimals,
|
|
529
|
-
uiAmount: tokenAmount.uiAmount || 0
|
|
601
|
+
uiAmount: tokenAmount.uiAmount || 0,
|
|
530
602
|
};
|
|
531
603
|
})
|
|
532
604
|
.filter((item) => item !== null);
|
|
@@ -544,7 +616,7 @@ export class PumpTrader {
|
|
|
544
616
|
mint: b.mint,
|
|
545
617
|
amount: Number(b.amount),
|
|
546
618
|
decimals: b.decimals,
|
|
547
|
-
uiAmount: b.uiAmount
|
|
619
|
+
uiAmount: b.uiAmount,
|
|
548
620
|
}));
|
|
549
621
|
|
|
550
622
|
return uniqueBalances;
|
|
@@ -564,7 +636,7 @@ export class PumpTrader {
|
|
|
564
636
|
this.wallet.publicKey,
|
|
565
637
|
false,
|
|
566
638
|
program,
|
|
567
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
639
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
568
640
|
);
|
|
569
641
|
|
|
570
642
|
const acc = await this.connection.getAccountInfo(ata);
|
|
@@ -576,8 +648,8 @@ export class PumpTrader {
|
|
|
576
648
|
this.wallet.publicKey,
|
|
577
649
|
mint,
|
|
578
650
|
program,
|
|
579
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
580
|
-
)
|
|
651
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
652
|
+
),
|
|
581
653
|
);
|
|
582
654
|
}
|
|
583
655
|
|
|
@@ -590,7 +662,7 @@ export class PumpTrader {
|
|
|
590
662
|
owner,
|
|
591
663
|
false,
|
|
592
664
|
TOKEN_PROGRAM_ID,
|
|
593
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
665
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
594
666
|
);
|
|
595
667
|
|
|
596
668
|
const acc = await this.connection.getAccountInfo(wsolAta);
|
|
@@ -603,18 +675,18 @@ export class PumpTrader {
|
|
|
603
675
|
owner,
|
|
604
676
|
SOL_MINT,
|
|
605
677
|
TOKEN_PROGRAM_ID,
|
|
606
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
607
|
-
)
|
|
678
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
679
|
+
),
|
|
608
680
|
);
|
|
609
681
|
}
|
|
610
682
|
|
|
611
|
-
if (mode ===
|
|
683
|
+
if (mode === "buy") {
|
|
612
684
|
tx.add(
|
|
613
685
|
SystemProgram.transfer({
|
|
614
686
|
fromPubkey: owner,
|
|
615
687
|
toPubkey: wsolAta,
|
|
616
|
-
lamports: Number(lamports)
|
|
617
|
-
})
|
|
688
|
+
lamports: Number(lamports),
|
|
689
|
+
}),
|
|
618
690
|
);
|
|
619
691
|
tx.add(createSyncNativeInstruction(wsolAta));
|
|
620
692
|
}
|
|
@@ -628,7 +700,9 @@ export class PumpTrader {
|
|
|
628
700
|
if (!priorityOpt?.enableRandom || !priorityOpt.randomRange) {
|
|
629
701
|
return priorityOpt.base;
|
|
630
702
|
}
|
|
631
|
-
return
|
|
703
|
+
return (
|
|
704
|
+
priorityOpt.base + Math.floor(Math.random() * priorityOpt.randomRange)
|
|
705
|
+
);
|
|
632
706
|
}
|
|
633
707
|
|
|
634
708
|
calcSlippage({ tradeSize, reserve, slippageOpt }) {
|
|
@@ -676,25 +750,47 @@ export class PumpTrader {
|
|
|
676
750
|
|
|
677
751
|
/**
|
|
678
752
|
* 统一的自动买入接口,自动判断内盘/外盘
|
|
753
|
+
* @param {boolean} [useV2=false] - use buy_v2 instruction (supports USDC quote)
|
|
754
|
+
* @param {PublicKey} [quoteMint=SOL_MINT] - quote mint (SOL_MINT for SOL-paired, USDC mint for USDC-paired)
|
|
679
755
|
*/
|
|
680
|
-
async autoBuy(
|
|
756
|
+
async autoBuy(
|
|
757
|
+
tokenAddr,
|
|
758
|
+
totalSolIn,
|
|
759
|
+
tradeOpt,
|
|
760
|
+
useV2 = false,
|
|
761
|
+
quoteMint = SOL_MINT,
|
|
762
|
+
) {
|
|
681
763
|
const mode = await this.getTradeMode(tokenAddr);
|
|
682
764
|
if (mode === "bonding") {
|
|
765
|
+
if (useV2) {
|
|
766
|
+
return this.buyV2(tokenAddr, totalSolIn, tradeOpt, quoteMint);
|
|
767
|
+
}
|
|
683
768
|
return this.buy(tokenAddr, totalSolIn, tradeOpt);
|
|
684
769
|
} else {
|
|
685
|
-
return this.ammBuy(tokenAddr, totalSolIn, tradeOpt);
|
|
770
|
+
return this.ammBuy(tokenAddr, totalSolIn, tradeOpt, quoteMint);
|
|
686
771
|
}
|
|
687
772
|
}
|
|
688
773
|
|
|
689
774
|
/**
|
|
690
775
|
* 统一的自动卖出接口,自动判断内盘/外盘
|
|
776
|
+
* @param {boolean} [useV2=false] - use sell_v2 instruction
|
|
777
|
+
* @param {PublicKey} [quoteMint=SOL_MINT] - quote mint
|
|
691
778
|
*/
|
|
692
|
-
async autoSell(
|
|
779
|
+
async autoSell(
|
|
780
|
+
tokenAddr,
|
|
781
|
+
totalTokenIn,
|
|
782
|
+
tradeOpt,
|
|
783
|
+
useV2 = false,
|
|
784
|
+
quoteMint = SOL_MINT,
|
|
785
|
+
) {
|
|
693
786
|
const mode = await this.getTradeMode(tokenAddr);
|
|
694
787
|
if (mode === "bonding") {
|
|
788
|
+
if (useV2) {
|
|
789
|
+
return this.sellV2(tokenAddr, totalTokenIn, tradeOpt, quoteMint);
|
|
790
|
+
}
|
|
695
791
|
return this.sell(tokenAddr, totalTokenIn, tradeOpt);
|
|
696
792
|
} else {
|
|
697
|
-
return this.ammSell(tokenAddr, totalTokenIn, tradeOpt);
|
|
793
|
+
return this.ammSell(tokenAddr, totalTokenIn, tradeOpt, quoteMint);
|
|
698
794
|
}
|
|
699
795
|
}
|
|
700
796
|
|
|
@@ -715,27 +811,30 @@ export class PumpTrader {
|
|
|
715
811
|
bonding,
|
|
716
812
|
true,
|
|
717
813
|
tokenProgram.programId,
|
|
718
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
814
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
719
815
|
);
|
|
720
816
|
|
|
721
817
|
const [creatorVault] = PublicKey.findProgramAddressSync(
|
|
722
818
|
[Buffer.from("creator-vault"), creator.toBuffer()],
|
|
723
|
-
PROGRAM_IDS.PUMP
|
|
819
|
+
PROGRAM_IDS.PUMP,
|
|
724
820
|
);
|
|
725
821
|
|
|
726
822
|
const [globalVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
727
823
|
[Buffer.from("global_volume_accumulator")],
|
|
728
|
-
PROGRAM_IDS.PUMP
|
|
824
|
+
PROGRAM_IDS.PUMP,
|
|
729
825
|
);
|
|
730
826
|
|
|
731
827
|
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
732
|
-
[
|
|
733
|
-
|
|
828
|
+
[
|
|
829
|
+
Buffer.from("user_volume_accumulator"),
|
|
830
|
+
this.wallet.publicKey.toBuffer(),
|
|
831
|
+
],
|
|
832
|
+
PROGRAM_IDS.PUMP,
|
|
734
833
|
);
|
|
735
834
|
|
|
736
835
|
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
737
836
|
[Buffer.from("fee_config"), SEEDS.FEE_CONFIG],
|
|
738
|
-
PROGRAM_IDS.FEE
|
|
837
|
+
PROGRAM_IDS.FEE,
|
|
739
838
|
);
|
|
740
839
|
const feeRecipient = this.pickFeeRecipient();
|
|
741
840
|
|
|
@@ -746,14 +845,14 @@ export class PumpTrader {
|
|
|
746
845
|
const slippageBps = this.calcSlippage({
|
|
747
846
|
tradeSize: solIn,
|
|
748
847
|
reserve: state.virtualSolReserves,
|
|
749
|
-
slippageOpt: tradeOpt.slippage
|
|
848
|
+
slippageOpt: tradeOpt.slippage,
|
|
750
849
|
});
|
|
751
850
|
const maxSol = (solIn * BigInt(10_000 + slippageBps)) / 10_000n;
|
|
752
851
|
const priority = this.genPriority(tradeOpt.priority);
|
|
753
852
|
|
|
754
853
|
const tx = new Transaction().add(
|
|
755
854
|
ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
|
|
756
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority })
|
|
855
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
757
856
|
);
|
|
758
857
|
|
|
759
858
|
const userAta = await this.ensureAta(tx, mint, tokenProgram.programId);
|
|
@@ -778,32 +877,39 @@ export class PumpTrader {
|
|
|
778
877
|
feeProgram: PROGRAM_IDS.FEE,
|
|
779
878
|
bondingCurveV2,
|
|
780
879
|
feeRecipient,
|
|
781
|
-
tokenProgramId: tokenProgram.programId
|
|
880
|
+
tokenProgramId: tokenProgram.programId,
|
|
782
881
|
}),
|
|
783
|
-
data: Buffer.concat([
|
|
784
|
-
|
|
882
|
+
data: Buffer.concat([
|
|
883
|
+
DISCRIMINATORS.BUY,
|
|
884
|
+
u64(tokenOut),
|
|
885
|
+
u64(maxSol),
|
|
886
|
+
]),
|
|
887
|
+
}),
|
|
785
888
|
);
|
|
786
889
|
|
|
787
890
|
const { blockhash, lastValidBlockHeight } =
|
|
788
|
-
await this.connection.getLatestBlockhash(
|
|
891
|
+
await this.connection.getLatestBlockhash("finalized");
|
|
789
892
|
tx.recentBlockhash = blockhash;
|
|
790
893
|
tx.feePayer = this.wallet.publicKey;
|
|
791
894
|
tx.sign(this.wallet);
|
|
792
895
|
|
|
793
|
-
const signature = await this.connection.sendRawTransaction(
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
896
|
+
const signature = await this.connection.sendRawTransaction(
|
|
897
|
+
tx.serialize(),
|
|
898
|
+
{
|
|
899
|
+
skipPreflight: false,
|
|
900
|
+
maxRetries: 2,
|
|
901
|
+
},
|
|
902
|
+
);
|
|
797
903
|
|
|
798
904
|
pendingTransactions.push({
|
|
799
905
|
signature,
|
|
800
906
|
lastValidBlockHeight,
|
|
801
|
-
index: i
|
|
907
|
+
index: i,
|
|
802
908
|
});
|
|
803
909
|
} catch (e) {
|
|
804
910
|
failedTransactions.push({
|
|
805
911
|
index: i,
|
|
806
|
-
error: e.message
|
|
912
|
+
error: e.message,
|
|
807
913
|
});
|
|
808
914
|
}
|
|
809
915
|
}
|
|
@@ -822,12 +928,15 @@ export class PumpTrader {
|
|
|
822
928
|
if (state.complete) throw new Error("Bonding curve already completed");
|
|
823
929
|
|
|
824
930
|
const totalSolOut = this.calcSell(totalTokenIn, state);
|
|
825
|
-
const tokenChunks =
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
931
|
+
const tokenChunks =
|
|
932
|
+
totalSolOut <= tradeOpt.maxSolPerTx
|
|
933
|
+
? [totalTokenIn]
|
|
934
|
+
: this.splitIntoN(
|
|
935
|
+
totalTokenIn,
|
|
936
|
+
Number(
|
|
937
|
+
(totalSolOut + tradeOpt.maxSolPerTx - 1n) / tradeOpt.maxSolPerTx,
|
|
938
|
+
),
|
|
939
|
+
);
|
|
831
940
|
|
|
832
941
|
const pendingTransactions = []; // 待确认的交易
|
|
833
942
|
const failedTransactions = []; // 发送失败的交易
|
|
@@ -837,7 +946,7 @@ export class PumpTrader {
|
|
|
837
946
|
bonding,
|
|
838
947
|
true,
|
|
839
948
|
tokenProgram.programId,
|
|
840
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
949
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
841
950
|
);
|
|
842
951
|
|
|
843
952
|
const userAta = getAssociatedTokenAddressSync(
|
|
@@ -845,21 +954,24 @@ export class PumpTrader {
|
|
|
845
954
|
this.wallet.publicKey,
|
|
846
955
|
false,
|
|
847
956
|
tokenProgram.programId,
|
|
848
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
957
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
849
958
|
);
|
|
850
959
|
|
|
851
960
|
const [creatorVault] = PublicKey.findProgramAddressSync(
|
|
852
961
|
[Buffer.from("creator-vault"), creator.toBuffer()],
|
|
853
|
-
PROGRAM_IDS.PUMP
|
|
962
|
+
PROGRAM_IDS.PUMP,
|
|
854
963
|
);
|
|
855
964
|
|
|
856
965
|
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
857
966
|
[Buffer.from("fee_config"), SEEDS.FEE_CONFIG],
|
|
858
|
-
PROGRAM_IDS.FEE
|
|
967
|
+
PROGRAM_IDS.FEE,
|
|
859
968
|
);
|
|
860
969
|
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
861
|
-
[
|
|
862
|
-
|
|
970
|
+
[
|
|
971
|
+
Buffer.from("user_volume_accumulator"),
|
|
972
|
+
this.wallet.publicKey.toBuffer(),
|
|
973
|
+
],
|
|
974
|
+
PROGRAM_IDS.PUMP,
|
|
863
975
|
);
|
|
864
976
|
const feeRecipient = this.pickFeeRecipient();
|
|
865
977
|
|
|
@@ -870,14 +982,14 @@ export class PumpTrader {
|
|
|
870
982
|
const slippageBps = this.calcSlippage({
|
|
871
983
|
tradeSize: tokenIn,
|
|
872
984
|
reserve: state.virtualTokenReserves,
|
|
873
|
-
slippageOpt: tradeOpt.slippage
|
|
985
|
+
slippageOpt: tradeOpt.slippage,
|
|
874
986
|
});
|
|
875
987
|
const minSol = (solOut * BigInt(10_000 - slippageBps)) / 10_000n;
|
|
876
988
|
const priority = this.genPriority(tradeOpt.priority);
|
|
877
989
|
|
|
878
990
|
const tx = new Transaction().add(
|
|
879
991
|
ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
|
|
880
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority })
|
|
992
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
881
993
|
);
|
|
882
994
|
|
|
883
995
|
tx.add(
|
|
@@ -900,14 +1012,14 @@ export class PumpTrader {
|
|
|
900
1012
|
feeRecipient,
|
|
901
1013
|
isCashbackCoin: !!state.isCashbackCoin,
|
|
902
1014
|
userVolumeAccumulator,
|
|
903
|
-
tokenProgramId: tokenProgram.programId
|
|
1015
|
+
tokenProgramId: tokenProgram.programId,
|
|
904
1016
|
}),
|
|
905
1017
|
data: Buffer.concat([
|
|
906
1018
|
DISCRIMINATORS.SELL,
|
|
907
1019
|
u64(tokenIn),
|
|
908
|
-
u64(minSol > 0n ? minSol : 1n)
|
|
909
|
-
])
|
|
910
|
-
})
|
|
1020
|
+
u64(minSol > 0n ? minSol : 1n),
|
|
1021
|
+
]),
|
|
1022
|
+
}),
|
|
911
1023
|
);
|
|
912
1024
|
|
|
913
1025
|
const { blockhash, lastValidBlockHeight } =
|
|
@@ -916,16 +1028,18 @@ export class PumpTrader {
|
|
|
916
1028
|
tx.feePayer = this.wallet.publicKey;
|
|
917
1029
|
tx.sign(this.wallet);
|
|
918
1030
|
|
|
919
|
-
const signature = await this.connection.sendRawTransaction(
|
|
1031
|
+
const signature = await this.connection.sendRawTransaction(
|
|
1032
|
+
tx.serialize(),
|
|
1033
|
+
);
|
|
920
1034
|
pendingTransactions.push({
|
|
921
1035
|
signature,
|
|
922
1036
|
lastValidBlockHeight,
|
|
923
|
-
index: i
|
|
1037
|
+
index: i,
|
|
924
1038
|
});
|
|
925
1039
|
} catch (e) {
|
|
926
1040
|
failedTransactions.push({
|
|
927
1041
|
index: i,
|
|
928
|
-
error: e.message
|
|
1042
|
+
error: e.message,
|
|
929
1043
|
});
|
|
930
1044
|
}
|
|
931
1045
|
}
|
|
@@ -935,12 +1049,16 @@ export class PumpTrader {
|
|
|
935
1049
|
|
|
936
1050
|
/* ---------- 外盘交易 ---------- */
|
|
937
1051
|
|
|
938
|
-
async ammBuy(tokenAddr, totalSolIn, tradeOpt) {
|
|
1052
|
+
async ammBuy(tokenAddr, totalSolIn, tradeOpt, quoteMint = SOL_MINT) {
|
|
939
1053
|
const mint = new PublicKey(tokenAddr);
|
|
940
|
-
const poolInfo = await this.getAmmPoolInfo(mint);
|
|
1054
|
+
const poolInfo = await this.getAmmPoolInfo(mint, quoteMint);
|
|
941
1055
|
const reserves = await this.getAmmPoolReserves(poolInfo.poolKeys);
|
|
942
1056
|
const solChunks = this.splitByMax(totalSolIn, tradeOpt.maxSolPerTx);
|
|
943
1057
|
const tokenProgram = await this.detectTokenProgram(tokenAddr);
|
|
1058
|
+
const isSolQuote = quoteMint.equals(SOL_MINT);
|
|
1059
|
+
const quoteTokenProgramId = isSolQuote
|
|
1060
|
+
? TOKEN_PROGRAM_ID
|
|
1061
|
+
: await this.detectQuoteTokenProgram(quoteMint);
|
|
944
1062
|
const pendingTransactions = [];
|
|
945
1063
|
const failedTransactions = [];
|
|
946
1064
|
|
|
@@ -951,23 +1069,29 @@ export class PumpTrader {
|
|
|
951
1069
|
const slippageBps = this.calcSlippage({
|
|
952
1070
|
tradeSize: solIn,
|
|
953
1071
|
reserve: reserves.quoteAmount,
|
|
954
|
-
slippageOpt: tradeOpt.slippage
|
|
1072
|
+
slippageOpt: tradeOpt.slippage,
|
|
955
1073
|
});
|
|
956
1074
|
const maxQuoteIn = (solIn * BigInt(10_000 + slippageBps)) / 10_000n;
|
|
957
1075
|
const priority = this.genPriority(tradeOpt.priority);
|
|
958
1076
|
|
|
959
1077
|
const tx = new Transaction().add(
|
|
960
1078
|
ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }),
|
|
961
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority })
|
|
1079
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
962
1080
|
);
|
|
963
1081
|
|
|
964
|
-
const userBaseAta = await this.ensureAta(
|
|
965
|
-
const userQuoteAta = await this.ensureWSOLAta(
|
|
1082
|
+
const userBaseAta = await this.ensureAta(
|
|
966
1083
|
tx,
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
maxQuoteIn
|
|
1084
|
+
poolInfo.poolKeys.baseMint,
|
|
1085
|
+
tokenProgram.programId,
|
|
970
1086
|
);
|
|
1087
|
+
const userQuoteAta = isSolQuote
|
|
1088
|
+
? await this.ensureWSOLAta(
|
|
1089
|
+
tx,
|
|
1090
|
+
this.wallet.publicKey,
|
|
1091
|
+
"buy",
|
|
1092
|
+
maxQuoteIn,
|
|
1093
|
+
)
|
|
1094
|
+
: await this.ensureAta(tx, quoteMint, quoteTokenProgramId);
|
|
971
1095
|
|
|
972
1096
|
const buyIx = this.createAmmBuyInstruction(
|
|
973
1097
|
poolInfo,
|
|
@@ -975,38 +1099,43 @@ export class PumpTrader {
|
|
|
975
1099
|
userQuoteAta,
|
|
976
1100
|
baseAmountOut,
|
|
977
1101
|
maxQuoteIn,
|
|
978
|
-
tokenProgram.programId
|
|
1102
|
+
tokenProgram.programId,
|
|
979
1103
|
);
|
|
980
1104
|
|
|
981
1105
|
tx.add(buyIx);
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1106
|
+
if (isSolQuote) {
|
|
1107
|
+
tx.add(
|
|
1108
|
+
createCloseAccountInstruction(
|
|
1109
|
+
userQuoteAta,
|
|
1110
|
+
this.wallet.publicKey,
|
|
1111
|
+
this.wallet.publicKey,
|
|
1112
|
+
),
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
989
1115
|
|
|
990
1116
|
const { blockhash, lastValidBlockHeight } =
|
|
991
|
-
await this.connection.getLatestBlockhash(
|
|
1117
|
+
await this.connection.getLatestBlockhash("finalized");
|
|
992
1118
|
tx.recentBlockhash = blockhash;
|
|
993
1119
|
tx.feePayer = this.wallet.publicKey;
|
|
994
1120
|
tx.sign(this.wallet);
|
|
995
1121
|
|
|
996
|
-
const signature = await this.connection.sendRawTransaction(
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1122
|
+
const signature = await this.connection.sendRawTransaction(
|
|
1123
|
+
tx.serialize(),
|
|
1124
|
+
{
|
|
1125
|
+
skipPreflight: false,
|
|
1126
|
+
maxRetries: 2,
|
|
1127
|
+
},
|
|
1128
|
+
);
|
|
1000
1129
|
|
|
1001
1130
|
pendingTransactions.push({
|
|
1002
1131
|
signature,
|
|
1003
1132
|
lastValidBlockHeight,
|
|
1004
|
-
index: i
|
|
1133
|
+
index: i,
|
|
1005
1134
|
});
|
|
1006
1135
|
} catch (e) {
|
|
1007
1136
|
failedTransactions.push({
|
|
1008
1137
|
index: i,
|
|
1009
|
-
error: e.message
|
|
1138
|
+
error: e.message,
|
|
1010
1139
|
});
|
|
1011
1140
|
}
|
|
1012
1141
|
}
|
|
@@ -1014,19 +1143,26 @@ export class PumpTrader {
|
|
|
1014
1143
|
return { pendingTransactions, failedTransactions };
|
|
1015
1144
|
}
|
|
1016
1145
|
|
|
1017
|
-
async ammSell(tokenAddr, totalTokenIn, tradeOpt) {
|
|
1146
|
+
async ammSell(tokenAddr, totalTokenIn, tradeOpt, quoteMint = SOL_MINT) {
|
|
1018
1147
|
const mint = new PublicKey(tokenAddr);
|
|
1019
|
-
const poolInfo = await this.getAmmPoolInfo(mint);
|
|
1148
|
+
const poolInfo = await this.getAmmPoolInfo(mint, quoteMint);
|
|
1020
1149
|
const reserves = await this.getAmmPoolReserves(poolInfo.poolKeys);
|
|
1021
1150
|
const totalSolOut = this.calculateAmmSellOutput(totalTokenIn, reserves);
|
|
1022
1151
|
const tokenProgram = await this.detectTokenProgram(tokenAddr);
|
|
1023
|
-
|
|
1024
|
-
const
|
|
1025
|
-
?
|
|
1026
|
-
: this.
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1152
|
+
const isSolQuote = quoteMint.equals(SOL_MINT);
|
|
1153
|
+
const quoteTokenProgramId = isSolQuote
|
|
1154
|
+
? TOKEN_PROGRAM_ID
|
|
1155
|
+
: await this.detectQuoteTokenProgram(quoteMint);
|
|
1156
|
+
|
|
1157
|
+
const tokenChunks =
|
|
1158
|
+
totalSolOut <= tradeOpt.maxSolPerTx
|
|
1159
|
+
? [totalTokenIn]
|
|
1160
|
+
: this.splitIntoN(
|
|
1161
|
+
totalTokenIn,
|
|
1162
|
+
Number(
|
|
1163
|
+
(totalSolOut + tradeOpt.maxSolPerTx - 1n) / tradeOpt.maxSolPerTx,
|
|
1164
|
+
),
|
|
1165
|
+
);
|
|
1030
1166
|
|
|
1031
1167
|
const pendingTransactions = [];
|
|
1032
1168
|
const failedTransactions = [];
|
|
@@ -1038,18 +1174,24 @@ export class PumpTrader {
|
|
|
1038
1174
|
const slippageBps = this.calcSlippage({
|
|
1039
1175
|
tradeSize: tokenIn,
|
|
1040
1176
|
reserve: reserves.baseAmount,
|
|
1041
|
-
slippageOpt: tradeOpt.slippage
|
|
1177
|
+
slippageOpt: tradeOpt.slippage,
|
|
1042
1178
|
});
|
|
1043
1179
|
const minQuoteOut = (solOut * BigInt(10_000 - slippageBps)) / 10_000n;
|
|
1044
1180
|
const priority = this.genPriority(tradeOpt.priority);
|
|
1045
1181
|
|
|
1046
1182
|
const tx = new Transaction().add(
|
|
1047
1183
|
ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }),
|
|
1048
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority })
|
|
1184
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
1049
1185
|
);
|
|
1050
1186
|
|
|
1051
|
-
const userBaseAta = await this.ensureAta(
|
|
1052
|
-
|
|
1187
|
+
const userBaseAta = await this.ensureAta(
|
|
1188
|
+
tx,
|
|
1189
|
+
poolInfo.poolKeys.baseMint,
|
|
1190
|
+
tokenProgram.programId,
|
|
1191
|
+
);
|
|
1192
|
+
const userQuoteAta = isSolQuote
|
|
1193
|
+
? await this.ensureWSOLAta(tx, this.wallet.publicKey, "sell")
|
|
1194
|
+
: await this.ensureAta(tx, quoteMint, quoteTokenProgramId);
|
|
1053
1195
|
|
|
1054
1196
|
const sellIx = this.createAmmSellInstruction(
|
|
1055
1197
|
poolInfo,
|
|
@@ -1057,38 +1199,43 @@ export class PumpTrader {
|
|
|
1057
1199
|
userQuoteAta,
|
|
1058
1200
|
tokenIn,
|
|
1059
1201
|
minQuoteOut,
|
|
1060
|
-
tokenProgram.programId
|
|
1202
|
+
tokenProgram.programId,
|
|
1061
1203
|
);
|
|
1062
1204
|
|
|
1063
1205
|
tx.add(sellIx);
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1206
|
+
if (isSolQuote) {
|
|
1207
|
+
tx.add(
|
|
1208
|
+
createCloseAccountInstruction(
|
|
1209
|
+
userQuoteAta,
|
|
1210
|
+
this.wallet.publicKey,
|
|
1211
|
+
this.wallet.publicKey,
|
|
1212
|
+
),
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1071
1215
|
|
|
1072
1216
|
const { blockhash, lastValidBlockHeight } =
|
|
1073
|
-
await this.connection.getLatestBlockhash(
|
|
1217
|
+
await this.connection.getLatestBlockhash("finalized");
|
|
1074
1218
|
tx.recentBlockhash = blockhash;
|
|
1075
1219
|
tx.feePayer = this.wallet.publicKey;
|
|
1076
1220
|
tx.sign(this.wallet);
|
|
1077
1221
|
|
|
1078
|
-
const signature = await this.connection.sendRawTransaction(
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1222
|
+
const signature = await this.connection.sendRawTransaction(
|
|
1223
|
+
tx.serialize(),
|
|
1224
|
+
{
|
|
1225
|
+
skipPreflight: false,
|
|
1226
|
+
maxRetries: 2,
|
|
1227
|
+
},
|
|
1228
|
+
);
|
|
1082
1229
|
|
|
1083
1230
|
pendingTransactions.push({
|
|
1084
1231
|
signature,
|
|
1085
1232
|
lastValidBlockHeight,
|
|
1086
|
-
index: i
|
|
1233
|
+
index: i,
|
|
1087
1234
|
});
|
|
1088
1235
|
} catch (e) {
|
|
1089
1236
|
failedTransactions.push({
|
|
1090
1237
|
index: i,
|
|
1091
|
-
error: e.message
|
|
1238
|
+
error: e.message,
|
|
1092
1239
|
});
|
|
1093
1240
|
}
|
|
1094
1241
|
}
|
|
@@ -1098,10 +1245,10 @@ export class PumpTrader {
|
|
|
1098
1245
|
|
|
1099
1246
|
/* ---------- AMM 池信息 ---------- */
|
|
1100
1247
|
|
|
1101
|
-
async getAmmPoolInfo(mint) {
|
|
1248
|
+
async getAmmPoolInfo(mint, quoteMint = SOL_MINT) {
|
|
1102
1249
|
const [poolAuthority] = PublicKey.findProgramAddressSync(
|
|
1103
1250
|
[Buffer.from("pool-authority"), mint.toBuffer()],
|
|
1104
|
-
PROGRAM_IDS.PUMP
|
|
1251
|
+
PROGRAM_IDS.PUMP,
|
|
1105
1252
|
);
|
|
1106
1253
|
|
|
1107
1254
|
const [pool] = PublicKey.findProgramAddressSync(
|
|
@@ -1110,9 +1257,9 @@ export class PumpTrader {
|
|
|
1110
1257
|
new BN(0).toArrayLike(Buffer, "le", 2),
|
|
1111
1258
|
poolAuthority.toBuffer(),
|
|
1112
1259
|
mint.toBuffer(),
|
|
1113
|
-
|
|
1260
|
+
quoteMint.toBuffer(),
|
|
1114
1261
|
],
|
|
1115
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1262
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1116
1263
|
);
|
|
1117
1264
|
|
|
1118
1265
|
const acc = await this.connection.getAccountInfo(pool);
|
|
@@ -1122,13 +1269,17 @@ export class PumpTrader {
|
|
|
1122
1269
|
|
|
1123
1270
|
const [globalConfigPda] = PublicKey.findProgramAddressSync(
|
|
1124
1271
|
[Buffer.from("global_config")],
|
|
1125
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1272
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1126
1273
|
);
|
|
1127
1274
|
|
|
1128
|
-
const globalConfigAcc =
|
|
1275
|
+
const globalConfigAcc =
|
|
1276
|
+
await this.connection.getAccountInfo(globalConfigPda);
|
|
1129
1277
|
if (!globalConfigAcc) throw new Error("Global config not found");
|
|
1130
1278
|
|
|
1131
|
-
const globalConfig = this.parseAmmGlobalConfig(
|
|
1279
|
+
const globalConfig = this.parseAmmGlobalConfig(
|
|
1280
|
+
globalConfigAcc.data,
|
|
1281
|
+
globalConfigPda,
|
|
1282
|
+
);
|
|
1132
1283
|
|
|
1133
1284
|
return { pool, poolAuthority, poolKeys, globalConfig };
|
|
1134
1285
|
}
|
|
@@ -1145,7 +1296,9 @@ export class PumpTrader {
|
|
|
1145
1296
|
|
|
1146
1297
|
const protocolFeeRecipients = [];
|
|
1147
1298
|
for (let i = 0; i < 8; i++) {
|
|
1148
|
-
protocolFeeRecipients.push(
|
|
1299
|
+
protocolFeeRecipients.push(
|
|
1300
|
+
new PublicKey(data.slice(offset, offset + 32)),
|
|
1301
|
+
);
|
|
1149
1302
|
offset += 32;
|
|
1150
1303
|
}
|
|
1151
1304
|
|
|
@@ -1155,38 +1308,45 @@ export class PumpTrader {
|
|
|
1155
1308
|
async getAmmPoolReserves(poolKeys) {
|
|
1156
1309
|
const [baseInfo, quoteInfo] = await Promise.all([
|
|
1157
1310
|
this.connection.getTokenAccountBalance(poolKeys.poolBaseTokenAccount),
|
|
1158
|
-
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount)
|
|
1311
|
+
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
|
|
1159
1312
|
]);
|
|
1160
1313
|
|
|
1161
1314
|
return {
|
|
1162
1315
|
baseAmount: BigInt(baseInfo.value.amount),
|
|
1163
1316
|
quoteAmount: BigInt(quoteInfo.value.amount),
|
|
1164
1317
|
baseDecimals: baseInfo.value.decimals,
|
|
1165
|
-
quoteDecimals: quoteInfo.value.decimals
|
|
1318
|
+
quoteDecimals: quoteInfo.value.decimals,
|
|
1166
1319
|
};
|
|
1167
1320
|
}
|
|
1168
1321
|
|
|
1169
1322
|
deriveAmmPoolV2(baseMint) {
|
|
1170
1323
|
return PublicKey.findProgramAddressSync(
|
|
1171
1324
|
[Buffer.from("pool-v2"), baseMint.toBuffer()],
|
|
1172
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1325
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1173
1326
|
)[0];
|
|
1174
1327
|
}
|
|
1175
1328
|
|
|
1176
1329
|
/* ---------- AMM 指令构建 ---------- */
|
|
1177
1330
|
|
|
1178
|
-
createAmmBuyInstruction(
|
|
1331
|
+
createAmmBuyInstruction(
|
|
1332
|
+
poolInfo,
|
|
1333
|
+
userBaseAta,
|
|
1334
|
+
userQuoteAta,
|
|
1335
|
+
baseAmountOut,
|
|
1336
|
+
maxQuoteAmountIn,
|
|
1337
|
+
tokenProgramId,
|
|
1338
|
+
) {
|
|
1179
1339
|
const { pool, poolKeys, globalConfig } = poolInfo;
|
|
1180
1340
|
const poolV2 = this.deriveAmmPoolV2(poolKeys.baseMint);
|
|
1181
1341
|
|
|
1182
1342
|
const [eventAuthority] = PublicKey.findProgramAddressSync(
|
|
1183
1343
|
[Buffer.from("__event_authority")],
|
|
1184
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1344
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1185
1345
|
);
|
|
1186
1346
|
|
|
1187
1347
|
const [coinCreatorVaultAuthority] = PublicKey.findProgramAddressSync(
|
|
1188
1348
|
[Buffer.from("creator_vault"), poolKeys.coinCreator.toBuffer()],
|
|
1189
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1349
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1190
1350
|
);
|
|
1191
1351
|
|
|
1192
1352
|
const coinCreatorVaultAta = getAssociatedTokenAddressSync(
|
|
@@ -1194,22 +1354,25 @@ export class PumpTrader {
|
|
|
1194
1354
|
coinCreatorVaultAuthority,
|
|
1195
1355
|
true,
|
|
1196
1356
|
TOKEN_PROGRAM_ID,
|
|
1197
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1357
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1198
1358
|
);
|
|
1199
1359
|
|
|
1200
1360
|
const [globalVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
1201
1361
|
[Buffer.from("global_volume_accumulator")],
|
|
1202
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1362
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1203
1363
|
);
|
|
1204
1364
|
|
|
1205
1365
|
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
1206
|
-
[
|
|
1207
|
-
|
|
1366
|
+
[
|
|
1367
|
+
Buffer.from("user_volume_accumulator"),
|
|
1368
|
+
this.wallet.publicKey.toBuffer(),
|
|
1369
|
+
],
|
|
1370
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1208
1371
|
);
|
|
1209
1372
|
|
|
1210
1373
|
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
1211
1374
|
[Buffer.from("fee_config"), SEEDS.AMM_FEE_CONFIG],
|
|
1212
|
-
PROGRAM_IDS.FEE
|
|
1375
|
+
PROGRAM_IDS.FEE,
|
|
1213
1376
|
);
|
|
1214
1377
|
|
|
1215
1378
|
const protocolFeeRecipient = globalConfig.protocolFeeRecipients[0];
|
|
@@ -1218,7 +1381,7 @@ export class PumpTrader {
|
|
|
1218
1381
|
protocolFeeRecipient,
|
|
1219
1382
|
true,
|
|
1220
1383
|
TOKEN_PROGRAM_ID,
|
|
1221
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1384
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1222
1385
|
);
|
|
1223
1386
|
const newFeeRecipient = this.pickFeeRecipient();
|
|
1224
1387
|
const newFeeRecipientTokenAccount = getAssociatedTokenAddressSync(
|
|
@@ -1226,7 +1389,7 @@ export class PumpTrader {
|
|
|
1226
1389
|
newFeeRecipient,
|
|
1227
1390
|
true,
|
|
1228
1391
|
TOKEN_PROGRAM_ID,
|
|
1229
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1392
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1230
1393
|
);
|
|
1231
1394
|
|
|
1232
1395
|
const remainingKeys = [];
|
|
@@ -1236,14 +1399,22 @@ export class PumpTrader {
|
|
|
1236
1399
|
userVolumeAccumulator,
|
|
1237
1400
|
true,
|
|
1238
1401
|
TOKEN_PROGRAM_ID,
|
|
1239
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1402
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1240
1403
|
);
|
|
1241
|
-
remainingKeys.push({
|
|
1404
|
+
remainingKeys.push({
|
|
1405
|
+
pubkey: userVolumeAccumulatorWsolAta,
|
|
1406
|
+
isSigner: false,
|
|
1407
|
+
isWritable: true,
|
|
1408
|
+
});
|
|
1242
1409
|
}
|
|
1243
1410
|
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
1244
1411
|
remainingKeys.push(
|
|
1245
1412
|
{ pubkey: newFeeRecipient, isSigner: false, isWritable: false },
|
|
1246
|
-
{
|
|
1413
|
+
{
|
|
1414
|
+
pubkey: newFeeRecipientTokenAccount,
|
|
1415
|
+
isSigner: false,
|
|
1416
|
+
isWritable: true,
|
|
1417
|
+
},
|
|
1247
1418
|
);
|
|
1248
1419
|
|
|
1249
1420
|
return new TransactionInstruction({
|
|
@@ -1256,45 +1427,72 @@ export class PumpTrader {
|
|
|
1256
1427
|
{ pubkey: poolKeys.quoteMint, isSigner: false, isWritable: false },
|
|
1257
1428
|
{ pubkey: userBaseAta, isSigner: false, isWritable: true },
|
|
1258
1429
|
{ pubkey: userQuoteAta, isSigner: false, isWritable: true },
|
|
1259
|
-
{
|
|
1260
|
-
|
|
1430
|
+
{
|
|
1431
|
+
pubkey: poolKeys.poolBaseTokenAccount,
|
|
1432
|
+
isSigner: false,
|
|
1433
|
+
isWritable: true,
|
|
1434
|
+
},
|
|
1435
|
+
{
|
|
1436
|
+
pubkey: poolKeys.poolQuoteTokenAccount,
|
|
1437
|
+
isSigner: false,
|
|
1438
|
+
isWritable: true,
|
|
1439
|
+
},
|
|
1261
1440
|
{ pubkey: protocolFeeRecipient, isSigner: false, isWritable: false },
|
|
1262
|
-
{
|
|
1441
|
+
{
|
|
1442
|
+
pubkey: protocolFeeRecipientTokenAccount,
|
|
1443
|
+
isSigner: false,
|
|
1444
|
+
isWritable: true,
|
|
1445
|
+
},
|
|
1263
1446
|
{ pubkey: tokenProgramId, isSigner: false, isWritable: false },
|
|
1264
1447
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1265
1448
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1266
|
-
{
|
|
1449
|
+
{
|
|
1450
|
+
pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1451
|
+
isSigner: false,
|
|
1452
|
+
isWritable: false,
|
|
1453
|
+
},
|
|
1267
1454
|
{ pubkey: eventAuthority, isSigner: false, isWritable: false },
|
|
1268
1455
|
{ pubkey: PROGRAM_IDS.PUMP_AMM, isSigner: false, isWritable: false },
|
|
1269
1456
|
{ pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true },
|
|
1270
|
-
{
|
|
1457
|
+
{
|
|
1458
|
+
pubkey: coinCreatorVaultAuthority,
|
|
1459
|
+
isSigner: false,
|
|
1460
|
+
isWritable: false,
|
|
1461
|
+
},
|
|
1271
1462
|
{ pubkey: globalVolumeAccumulator, isSigner: false, isWritable: false },
|
|
1272
1463
|
{ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
1273
1464
|
{ pubkey: feeConfig, isSigner: false, isWritable: false },
|
|
1274
1465
|
{ pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
|
|
1275
|
-
...remainingKeys
|
|
1466
|
+
...remainingKeys,
|
|
1276
1467
|
],
|
|
1277
1468
|
data: Buffer.concat([
|
|
1278
1469
|
DISCRIMINATORS.BUY,
|
|
1279
1470
|
u64(baseAmountOut),
|
|
1280
1471
|
u64(maxQuoteAmountIn),
|
|
1281
|
-
Buffer.from([1, 1])
|
|
1282
|
-
])
|
|
1472
|
+
Buffer.from([1, 1]),
|
|
1473
|
+
]),
|
|
1283
1474
|
});
|
|
1284
1475
|
}
|
|
1285
1476
|
|
|
1286
|
-
createAmmSellInstruction(
|
|
1477
|
+
createAmmSellInstruction(
|
|
1478
|
+
poolInfo,
|
|
1479
|
+
userBaseAta,
|
|
1480
|
+
userQuoteAta,
|
|
1481
|
+
baseAmountIn,
|
|
1482
|
+
minQuoteAmountOut,
|
|
1483
|
+
tokenProgramId,
|
|
1484
|
+
) {
|
|
1287
1485
|
const { pool, poolKeys, globalConfig } = poolInfo;
|
|
1288
1486
|
const poolV2 = this.deriveAmmPoolV2(poolKeys.baseMint);
|
|
1289
1487
|
|
|
1290
1488
|
const [eventAuthority] = PublicKey.findProgramAddressSync(
|
|
1291
1489
|
[Buffer.from("__event_authority")],
|
|
1292
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1490
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1293
1491
|
);
|
|
1294
1492
|
|
|
1295
1493
|
const [coinCreatorVaultAuthority] = PublicKey.findProgramAddressSync(
|
|
1296
1494
|
[Buffer.from("creator_vault"), poolKeys.coinCreator.toBuffer()],
|
|
1297
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1495
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1298
1496
|
);
|
|
1299
1497
|
|
|
1300
1498
|
const coinCreatorVaultAta = getAssociatedTokenAddressSync(
|
|
@@ -1302,12 +1500,12 @@ export class PumpTrader {
|
|
|
1302
1500
|
coinCreatorVaultAuthority,
|
|
1303
1501
|
true,
|
|
1304
1502
|
TOKEN_PROGRAM_ID,
|
|
1305
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1503
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1306
1504
|
);
|
|
1307
1505
|
|
|
1308
1506
|
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
1309
1507
|
[Buffer.from("fee_config"), SEEDS.AMM_FEE_CONFIG],
|
|
1310
|
-
PROGRAM_IDS.FEE
|
|
1508
|
+
PROGRAM_IDS.FEE,
|
|
1311
1509
|
);
|
|
1312
1510
|
|
|
1313
1511
|
const protocolFeeRecipient = globalConfig.protocolFeeRecipients[0];
|
|
@@ -1316,7 +1514,7 @@ export class PumpTrader {
|
|
|
1316
1514
|
protocolFeeRecipient,
|
|
1317
1515
|
true,
|
|
1318
1516
|
TOKEN_PROGRAM_ID,
|
|
1319
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1517
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1320
1518
|
);
|
|
1321
1519
|
const newFeeRecipient = this.pickFeeRecipient();
|
|
1322
1520
|
const newFeeRecipientTokenAccount = getAssociatedTokenAddressSync(
|
|
@@ -1324,12 +1522,15 @@ export class PumpTrader {
|
|
|
1324
1522
|
newFeeRecipient,
|
|
1325
1523
|
true,
|
|
1326
1524
|
TOKEN_PROGRAM_ID,
|
|
1327
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1525
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1328
1526
|
);
|
|
1329
1527
|
|
|
1330
1528
|
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
1331
|
-
[
|
|
1332
|
-
|
|
1529
|
+
[
|
|
1530
|
+
Buffer.from("user_volume_accumulator"),
|
|
1531
|
+
this.wallet.publicKey.toBuffer(),
|
|
1532
|
+
],
|
|
1533
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1333
1534
|
);
|
|
1334
1535
|
|
|
1335
1536
|
const userVolumeAccumulatorWsolAta = getAssociatedTokenAddressSync(
|
|
@@ -1337,20 +1538,28 @@ export class PumpTrader {
|
|
|
1337
1538
|
userVolumeAccumulator,
|
|
1338
1539
|
true,
|
|
1339
1540
|
TOKEN_PROGRAM_ID,
|
|
1340
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1541
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1341
1542
|
);
|
|
1342
1543
|
|
|
1343
1544
|
const remainingKeys = [];
|
|
1344
1545
|
if (poolKeys.isCashbackCoin) {
|
|
1345
1546
|
remainingKeys.push(
|
|
1346
|
-
{
|
|
1347
|
-
|
|
1547
|
+
{
|
|
1548
|
+
pubkey: userVolumeAccumulatorWsolAta,
|
|
1549
|
+
isSigner: false,
|
|
1550
|
+
isWritable: true,
|
|
1551
|
+
},
|
|
1552
|
+
{ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
1348
1553
|
);
|
|
1349
1554
|
}
|
|
1350
1555
|
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
1351
1556
|
remainingKeys.push(
|
|
1352
1557
|
{ pubkey: newFeeRecipient, isSigner: false, isWritable: false },
|
|
1353
|
-
{
|
|
1558
|
+
{
|
|
1559
|
+
pubkey: newFeeRecipientTokenAccount,
|
|
1560
|
+
isSigner: false,
|
|
1561
|
+
isWritable: true,
|
|
1562
|
+
},
|
|
1354
1563
|
);
|
|
1355
1564
|
|
|
1356
1565
|
return new TransactionInstruction({
|
|
@@ -1363,65 +1572,93 @@ export class PumpTrader {
|
|
|
1363
1572
|
{ pubkey: poolKeys.quoteMint, isSigner: false, isWritable: false },
|
|
1364
1573
|
{ pubkey: userBaseAta, isSigner: false, isWritable: true },
|
|
1365
1574
|
{ pubkey: userQuoteAta, isSigner: false, isWritable: true },
|
|
1366
|
-
{
|
|
1367
|
-
|
|
1575
|
+
{
|
|
1576
|
+
pubkey: poolKeys.poolBaseTokenAccount,
|
|
1577
|
+
isSigner: false,
|
|
1578
|
+
isWritable: true,
|
|
1579
|
+
},
|
|
1580
|
+
{
|
|
1581
|
+
pubkey: poolKeys.poolQuoteTokenAccount,
|
|
1582
|
+
isSigner: false,
|
|
1583
|
+
isWritable: true,
|
|
1584
|
+
},
|
|
1368
1585
|
{ pubkey: protocolFeeRecipient, isSigner: false, isWritable: false },
|
|
1369
|
-
{
|
|
1586
|
+
{
|
|
1587
|
+
pubkey: protocolFeeRecipientTokenAccount,
|
|
1588
|
+
isSigner: false,
|
|
1589
|
+
isWritable: true,
|
|
1590
|
+
},
|
|
1370
1591
|
{ pubkey: tokenProgramId, isSigner: false, isWritable: false },
|
|
1371
1592
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1372
1593
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1373
|
-
{
|
|
1594
|
+
{
|
|
1595
|
+
pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1596
|
+
isSigner: false,
|
|
1597
|
+
isWritable: false,
|
|
1598
|
+
},
|
|
1374
1599
|
{ pubkey: eventAuthority, isSigner: false, isWritable: false },
|
|
1375
1600
|
{ pubkey: PROGRAM_IDS.PUMP_AMM, isSigner: false, isWritable: false },
|
|
1376
1601
|
{ pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true },
|
|
1377
|
-
{
|
|
1602
|
+
{
|
|
1603
|
+
pubkey: coinCreatorVaultAuthority,
|
|
1604
|
+
isSigner: false,
|
|
1605
|
+
isWritable: false,
|
|
1606
|
+
},
|
|
1378
1607
|
{ pubkey: feeConfig, isSigner: false, isWritable: false },
|
|
1379
1608
|
{ pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
|
|
1380
|
-
...remainingKeys
|
|
1609
|
+
...remainingKeys,
|
|
1381
1610
|
],
|
|
1382
1611
|
data: Buffer.concat([
|
|
1383
1612
|
DISCRIMINATORS.SELL,
|
|
1384
1613
|
u64(baseAmountIn),
|
|
1385
|
-
u64(minQuoteAmountOut > 0n ? minQuoteAmountOut : 1n)
|
|
1386
|
-
])
|
|
1614
|
+
u64(minQuoteAmountOut > 0n ? minQuoteAmountOut : 1n),
|
|
1615
|
+
]),
|
|
1387
1616
|
});
|
|
1388
1617
|
}
|
|
1389
1618
|
|
|
1390
1619
|
/* ---------- 交易确认 ---------- */
|
|
1391
1620
|
|
|
1392
|
-
async confirmTransactionWithPolling(
|
|
1393
|
-
|
|
1394
|
-
|
|
1621
|
+
async confirmTransactionWithPolling(
|
|
1622
|
+
signature,
|
|
1623
|
+
lastValidBlockHeight,
|
|
1624
|
+
maxAttempts = 5,
|
|
1625
|
+
delayMs = 2000,
|
|
1626
|
+
) {
|
|
1627
|
+
console.log("✅ 交易已发送:", signature);
|
|
1628
|
+
console.log("🔗 查看交易: https://solscan.io/tx/" + signature);
|
|
1395
1629
|
|
|
1396
1630
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1397
|
-
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
1631
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1398
1632
|
|
|
1399
1633
|
try {
|
|
1400
1634
|
console.log(`🔍 检查交易状态 (${attempt}/${maxAttempts})...`);
|
|
1401
1635
|
|
|
1402
1636
|
const txInfo = await this.connection.getTransaction(signature, {
|
|
1403
|
-
commitment:
|
|
1404
|
-
maxSupportedTransactionVersion: 0
|
|
1637
|
+
commitment: "confirmed",
|
|
1638
|
+
maxSupportedTransactionVersion: 0,
|
|
1405
1639
|
});
|
|
1406
1640
|
|
|
1407
1641
|
if (txInfo) {
|
|
1408
1642
|
if (txInfo.meta?.err) {
|
|
1409
|
-
console.error(
|
|
1410
|
-
throw new Error(
|
|
1643
|
+
console.error("❌ 交易失败:", txInfo.meta.err);
|
|
1644
|
+
throw new Error("交易失败: " + JSON.stringify(txInfo.meta.err));
|
|
1411
1645
|
}
|
|
1412
1646
|
|
|
1413
|
-
console.log(
|
|
1647
|
+
console.log("✅ 交易已确认!");
|
|
1414
1648
|
return signature;
|
|
1415
1649
|
}
|
|
1416
1650
|
|
|
1417
|
-
const currentBlockHeight =
|
|
1651
|
+
const currentBlockHeight =
|
|
1652
|
+
await this.connection.getBlockHeight("finalized");
|
|
1418
1653
|
if (currentBlockHeight > lastValidBlockHeight) {
|
|
1419
|
-
console.log(
|
|
1420
|
-
throw new Error(
|
|
1654
|
+
console.log("⚠️ 交易已过期(超过有效区块高度)");
|
|
1655
|
+
throw new Error("交易过期:未在有效区块高度内确认");
|
|
1421
1656
|
}
|
|
1422
|
-
|
|
1423
1657
|
} catch (error) {
|
|
1424
|
-
if (
|
|
1658
|
+
if (
|
|
1659
|
+
error.message?.includes("交易失败") ||
|
|
1660
|
+
error.message?.includes("交易过期")
|
|
1661
|
+
) {
|
|
1425
1662
|
throw error;
|
|
1426
1663
|
}
|
|
1427
1664
|
console.log(`⚠️ 查询出错,继续重试: ${error.message}`);
|
|
@@ -1429,7 +1666,7 @@ export class PumpTrader {
|
|
|
1429
1666
|
}
|
|
1430
1667
|
|
|
1431
1668
|
throw new Error(
|
|
1432
|
-
`交易确认超时(已尝试 ${maxAttempts} 次),签名: ${signature}
|
|
1669
|
+
`交易确认超时(已尝试 ${maxAttempts} 次),签名: ${signature}。请手动检查交易状态。`,
|
|
1433
1670
|
);
|
|
1434
1671
|
}
|
|
1435
1672
|
|
|
@@ -1442,7 +1679,10 @@ export class PumpTrader {
|
|
|
1442
1679
|
for (const logLine of log.logs) {
|
|
1443
1680
|
if (!logLine.startsWith("Program data: ")) continue;
|
|
1444
1681
|
|
|
1445
|
-
const buf = Buffer.from(
|
|
1682
|
+
const buf = Buffer.from(
|
|
1683
|
+
logLine.replace("Program data: ", ""),
|
|
1684
|
+
"base64",
|
|
1685
|
+
);
|
|
1446
1686
|
|
|
1447
1687
|
if (!buf.subarray(0, 8).equals(DISCRIMINATORS.TRADE_EVENT)) continue;
|
|
1448
1688
|
|
|
@@ -1468,11 +1708,11 @@ export class PumpTrader {
|
|
|
1468
1708
|
isBuy,
|
|
1469
1709
|
user: user.toBase58(),
|
|
1470
1710
|
timestamp,
|
|
1471
|
-
signature: log.signature
|
|
1711
|
+
signature: log.signature,
|
|
1472
1712
|
});
|
|
1473
1713
|
}
|
|
1474
1714
|
},
|
|
1475
|
-
"confirmed"
|
|
1715
|
+
"confirmed",
|
|
1476
1716
|
);
|
|
1477
1717
|
}
|
|
1478
1718
|
|
|
@@ -1485,18 +1725,22 @@ export class PumpTrader {
|
|
|
1485
1725
|
const metadata = await getTokenMetadata(
|
|
1486
1726
|
this.connection,
|
|
1487
1727
|
mint,
|
|
1488
|
-
TOKEN_2022_PROGRAM_ID
|
|
1728
|
+
TOKEN_2022_PROGRAM_ID,
|
|
1489
1729
|
);
|
|
1490
1730
|
|
|
1491
1731
|
return {
|
|
1492
1732
|
name: metadata?.name || "",
|
|
1493
1733
|
symbol: metadata?.symbol || "",
|
|
1494
|
-
uri: metadata?.uri || ""
|
|
1734
|
+
uri: metadata?.uri || "",
|
|
1495
1735
|
};
|
|
1496
1736
|
} catch (e) {
|
|
1497
1737
|
const metadataPda = PublicKey.findProgramAddressSync(
|
|
1498
|
-
[
|
|
1499
|
-
|
|
1738
|
+
[
|
|
1739
|
+
Buffer.from("metadata"),
|
|
1740
|
+
PROGRAM_IDS.METADATA.toBuffer(),
|
|
1741
|
+
mint.toBuffer(),
|
|
1742
|
+
],
|
|
1743
|
+
PROGRAM_IDS.METADATA,
|
|
1500
1744
|
)[0];
|
|
1501
1745
|
|
|
1502
1746
|
const acc = await this.connection.getAccountInfo(metadataPda);
|
|
@@ -1505,9 +1749,9 @@ export class PumpTrader {
|
|
|
1505
1749
|
const meta = parseMetadataAccount(acc.data);
|
|
1506
1750
|
|
|
1507
1751
|
return {
|
|
1508
|
-
name: meta?.name?.replace(/\u0000/g,
|
|
1509
|
-
symbol: meta?.symbol?.replace(/\u0000/g,
|
|
1510
|
-
uri: meta?.uri?.replace(/\u0000/g,
|
|
1752
|
+
name: meta?.name?.replace(/\u0000/g, "") || "",
|
|
1753
|
+
symbol: meta?.symbol?.replace(/\u0000/g, "") || "",
|
|
1754
|
+
uri: meta?.uri?.replace(/\u0000/g, "") || "",
|
|
1511
1755
|
};
|
|
1512
1756
|
}
|
|
1513
1757
|
}
|