pump-trader 1.1.5 → 1.1.9

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