pump-trader 1.1.8 → 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/dist/index.js CHANGED
@@ -7,36 +7,60 @@ exports.PumpTrader = void 0;
7
7
  const web3_js_1 = require("@solana/web3.js");
8
8
  const spl_token_1 = require("@solana/spl-token");
9
9
  const bn_js_1 = __importDefault(require("bn.js"));
10
- const bs58_1 = __importDefault(require("bs58"));
11
10
  /* ================= 常量定义 ================= */
12
11
  const PROGRAM_IDS = {
13
12
  PUMP: new web3_js_1.PublicKey("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"),
14
13
  PUMP_AMM: new web3_js_1.PublicKey("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"),
15
14
  METADATA: new web3_js_1.PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"),
16
15
  FEE: new web3_js_1.PublicKey("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"),
17
- EVENT_AUTHORITY: new web3_js_1.PublicKey("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1")
16
+ EVENT_AUTHORITY: new web3_js_1.PublicKey("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1"),
18
17
  };
19
18
  const SOL_MINT = new web3_js_1.PublicKey("So11111111111111111111111111111111111111112");
20
19
  const SEEDS = {
21
20
  FEE_CONFIG: new Uint8Array([
22
- 1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, 91, 170,
23
- 81, 137, 203, 151, 245, 210, 255, 59, 101, 93, 43, 182, 253, 109, 24, 176
21
+ 1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, 91, 170, 81,
22
+ 137, 203, 151, 245, 210, 255, 59, 101, 93, 43, 182, 253, 109, 24, 176,
24
23
  ]),
25
24
  AMM_FEE_CONFIG: Buffer.from([
26
- 12, 20, 222, 252, 130, 94, 198, 118, 148, 37, 8, 24, 187, 101, 64, 101,
27
- 244, 41, 141, 49, 86, 213, 113, 180, 212, 248, 9, 12, 24, 233, 168, 99
25
+ 12, 20, 222, 252, 130, 94, 198, 118, 148, 37, 8, 24, 187, 101, 64, 101, 244,
26
+ 41, 141, 49, 86, 213, 113, 180, 212, 248, 9, 12, 24, 233, 168, 99,
28
27
  ]),
29
28
  GLOBAL: Buffer.from("global"),
30
- BONDING: Buffer.from("bonding-curve")
29
+ BONDING: Buffer.from("bonding-curve"),
31
30
  };
32
31
  const DISCRIMINATORS = {
33
32
  BUY: Buffer.from([102, 6, 61, 18, 1, 218, 235, 234]),
34
33
  SELL: Buffer.from([51, 230, 133, 164, 1, 127, 131, 173]),
35
- TRADE_EVENT: Buffer.from([189, 219, 127, 211, 78, 230, 97, 238])
34
+ TRADE_EVENT: Buffer.from([189, 219, 127, 211, 78, 230, 97, 238]),
35
+ // V2 instructions
36
+ BUY_V2: Buffer.from([184, 23, 238, 97, 103, 197, 211, 61]),
37
+ SELL_V2: Buffer.from([93, 246, 130, 60, 231, 233, 64, 178]),
38
+ BUY_EXACT_QUOTE_IN_V2: Buffer.from([194, 171, 28, 70, 104, 77, 91, 47]),
39
+ COLLECT_CREATOR_FEE_V2: Buffer.from([207, 17, 138, 242, 4, 34, 19, 56]),
36
40
  };
37
41
  const AMM_FEE_BPS = 100n;
38
42
  const BPS_DENOMINATOR = 10000n;
39
43
  const PUMP_NEW_FEE_RECIPIENTS = [
44
+ "62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV",
45
+ "7VtfL8fvgNfhz17qKRMjzQEXgbdpnHHHQRh54R9jP2RJ",
46
+ "7hTckgnGnLQR6sdH7YkqFTAA7VwTfYFaZ6EhEsU3saCX",
47
+ "9rPYyANsfQZw3DnDmKE3YCQF5E8oD89UXoHn9JFEhJUz",
48
+ "AVmoTthdrX6tKt4nDjco2D775W2YK3sDhxPcMmzUAmTY",
49
+ "CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM",
50
+ "FWsW1xNtWscwNmKv6wVsU1iTzRN6wmmk3MjxRP5tT7hz",
51
+ "G5UZAVbAf46s7cKWoyKu8kYTip9DGTpbLZ2qa9Aq69dP",
52
+ ].map((value) => new web3_js_1.PublicKey(value));
53
+ const PUMP_RESERVED_FEE_RECIPIENTS = [
54
+ "GesfTA3X2arioaHp8bbKdjG9vJtskViWACZoYvxp4twS",
55
+ "4budycTjhs9fD6xw62VBducVTNgMgJJ5BgtKq7mAZwn6",
56
+ "8SBKzEQU4nLSzcwF4a74F2iaUDQyTfjGndn6qUWBnrpR",
57
+ "4UQeTP1T39KZ9Sfxzo3WR5skgsaP6NZa87BAkuazLEKH",
58
+ "8sNeir4QsLsJdYpc9RZacohhK1Y5FLU3nC5LXgYB4aa6",
59
+ "Fh9HmeLNUMVCvejxCtCL2DbYaRyBFVJ5xrWkLnMH6fdk",
60
+ "463MEnMeGyJekNZFQSTUABBEbLnvMTALbT6ZmsxAbAdq",
61
+ "6AUH3WEHucYZyC61hqpqYUWVto5qA5hjHuNQ32GNnNxA",
62
+ ].map((value) => new web3_js_1.PublicKey(value));
63
+ const PUMP_BUYBACK_FEE_RECIPIENTS = [
40
64
  "5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD",
41
65
  "9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7",
42
66
  "GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL",
@@ -44,11 +68,11 @@ const PUMP_NEW_FEE_RECIPIENTS = [
44
68
  "5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6",
45
69
  "EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL",
46
70
  "5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD",
47
- "A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"
71
+ "A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW",
48
72
  ].map((value) => new web3_js_1.PublicKey(value));
49
73
  /* ================= 工具函数 ================= */
50
74
  const u64 = (v) => {
51
- const bn = typeof v === 'bigint' ? new bn_js_1.default(v.toString()) : new bn_js_1.default(v.toString());
75
+ const bn = typeof v === "bigint" ? new bn_js_1.default(v.toString()) : new bn_js_1.default(v.toString());
52
76
  return bn.toArrayLike(Buffer, "le", 8);
53
77
  };
54
78
  const readU64 = (buf, offset) => {
@@ -62,7 +86,9 @@ const readU32 = (buf, offsetObj) => {
62
86
  };
63
87
  const readString = (buf, offsetObj) => {
64
88
  const len = readU32(buf, offsetObj);
65
- const str = buf.slice(offsetObj.offset, offsetObj.offset + len).toString("utf8");
89
+ const str = buf
90
+ .slice(offsetObj.offset, offsetObj.offset + len)
91
+ .toString("utf8");
66
92
  offsetObj.offset += len;
67
93
  return str;
68
94
  };
@@ -81,12 +107,12 @@ function parseMetadataAccount(data) {
81
107
  mint: mint.toBase58(),
82
108
  name,
83
109
  symbol,
84
- uri
110
+ uri,
85
111
  };
86
112
  }
87
113
  function parsePoolKeys(data) {
88
114
  if (!data || data.length < 280) {
89
- throw new Error('Invalid pool account data');
115
+ throw new Error("Invalid pool account data");
90
116
  }
91
117
  let offset = 8;
92
118
  const poolBump = data.readUInt8(offset);
@@ -121,18 +147,32 @@ function parsePoolKeys(data) {
121
147
  poolQuoteTokenAccount,
122
148
  coinCreator,
123
149
  isMayhemMode,
124
- isCashbackCoin
150
+ isCashbackCoin,
125
151
  };
126
152
  }
127
153
  /* ================= PumpTrader 类 ================= */
128
154
  class PumpTrader {
129
- constructor(rpc, privateKey) {
155
+ constructor(rpc, wallet) {
130
156
  this.connection = new web3_js_1.Connection(rpc, "confirmed");
131
- this.wallet = web3_js_1.Keypair.fromSecretKey(bs58_1.default.decode(privateKey));
157
+ if (wallet instanceof web3_js_1.Keypair) {
158
+ this._wallet = wallet;
159
+ this.publicKey = wallet.publicKey;
160
+ }
161
+ else {
162
+ this._wallet = wallet;
163
+ this.publicKey = wallet.publicKey;
164
+ }
132
165
  this.global = web3_js_1.PublicKey.findProgramAddressSync([SEEDS.GLOBAL], PROGRAM_IDS.PUMP)[0];
133
166
  this.globalState = null;
134
167
  this.tokenProgramCache = new Map();
135
168
  }
169
+ async signTx(tx) {
170
+ if (this._wallet instanceof web3_js_1.Keypair) {
171
+ tx.sign(this._wallet);
172
+ return tx;
173
+ }
174
+ return this._wallet.signTransaction(tx);
175
+ }
136
176
  /* ---------- Token Program 检测 ---------- */
137
177
  /**
138
178
  * 自动检测代币使用的 token program
@@ -148,7 +188,7 @@ class PumpTrader {
148
188
  const mintData = await (0, spl_token_1.getMint)(this.connection, mint, "confirmed", spl_token_1.TOKEN_2022_PROGRAM_ID);
149
189
  const result = {
150
190
  type: "TOKEN_2022_PROGRAM_ID",
151
- programId: spl_token_1.TOKEN_2022_PROGRAM_ID
191
+ programId: spl_token_1.TOKEN_2022_PROGRAM_ID,
152
192
  };
153
193
  this.tokenProgramCache.set(tokenAddr, result);
154
194
  return result;
@@ -159,7 +199,7 @@ class PumpTrader {
159
199
  const mintData = await (0, spl_token_1.getMint)(this.connection, mint, "confirmed", spl_token_1.TOKEN_PROGRAM_ID);
160
200
  const result = {
161
201
  type: "TOKEN_PROGRAM_ID",
162
- programId: spl_token_1.TOKEN_PROGRAM_ID
202
+ programId: spl_token_1.TOKEN_PROGRAM_ID,
163
203
  };
164
204
  this.tokenProgramCache.set(tokenAddr, result);
165
205
  return result;
@@ -169,6 +209,19 @@ class PumpTrader {
169
209
  }
170
210
  }
171
211
  }
212
+ async detectQuoteTokenProgram(quoteMint) {
213
+ const quoteAddr = quoteMint.toBase58();
214
+ if (this.tokenProgramCache.has(quoteAddr)) {
215
+ return this.tokenProgramCache.get(quoteAddr).programId;
216
+ }
217
+ try {
218
+ await (0, spl_token_1.getMint)(this.connection, quoteMint, "confirmed", spl_token_1.TOKEN_2022_PROGRAM_ID);
219
+ return spl_token_1.TOKEN_2022_PROGRAM_ID;
220
+ }
221
+ catch {
222
+ return spl_token_1.TOKEN_PROGRAM_ID;
223
+ }
224
+ }
172
225
  /* ---------- 内盘/外盘检测 ---------- */
173
226
  /**
174
227
  * 检测代币是否在外盘 (AMM)
@@ -226,7 +279,7 @@ class PumpTrader {
226
279
  initialVirtualSolReserves: readU64(),
227
280
  initialRealTokenReserves: readU64(),
228
281
  tokenTotalSupply: readU64(),
229
- feeBasisPoints: readU64()
282
+ feeBasisPoints: readU64(),
230
283
  };
231
284
  return this.globalState;
232
285
  }
@@ -240,6 +293,15 @@ class PumpTrader {
240
293
  pickFeeRecipient(index = 0) {
241
294
  return PUMP_NEW_FEE_RECIPIENTS[index % PUMP_NEW_FEE_RECIPIENTS.length];
242
295
  }
296
+ pickBuybackFeeRecipient(index = 0) {
297
+ return PUMP_BUYBACK_FEE_RECIPIENTS[index % PUMP_BUYBACK_FEE_RECIPIENTS.length];
298
+ }
299
+ pickReservedFeeRecipient(index = 0) {
300
+ return PUMP_RESERVED_FEE_RECIPIENTS[index % PUMP_RESERVED_FEE_RECIPIENTS.length];
301
+ }
302
+ getSharingConfigPda(mint) {
303
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("sharing-config"), mint.toBuffer()], PROGRAM_IDS.FEE)[0];
304
+ }
243
305
  buildBondingBuyKeys(args) {
244
306
  const tokenProgramId = args.tokenProgramId ?? spl_token_1.TOKEN_PROGRAM_ID;
245
307
  return [
@@ -247,7 +309,11 @@ class PumpTrader {
247
309
  { pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
248
310
  { pubkey: args.mint, isSigner: false, isWritable: false },
249
311
  { pubkey: args.bonding, isSigner: false, isWritable: true },
250
- { pubkey: args.associatedBondingCurve, isSigner: false, isWritable: true },
312
+ {
313
+ pubkey: args.associatedBondingCurve,
314
+ isSigner: false,
315
+ isWritable: true,
316
+ },
251
317
  { pubkey: args.userAta, isSigner: false, isWritable: true },
252
318
  { pubkey: args.wallet, isSigner: true, isWritable: true },
253
319
  { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
@@ -255,12 +321,16 @@ class PumpTrader {
255
321
  { pubkey: args.creatorVault, isSigner: false, isWritable: true },
256
322
  { pubkey: args.eventAuthority, isSigner: false, isWritable: false },
257
323
  { pubkey: args.pumpProgram, isSigner: false, isWritable: false },
258
- { pubkey: args.globalVolumeAccumulator, isSigner: false, isWritable: false },
324
+ {
325
+ pubkey: args.globalVolumeAccumulator,
326
+ isSigner: false,
327
+ isWritable: false,
328
+ },
259
329
  { pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true },
260
330
  { pubkey: args.feeConfig, isSigner: false, isWritable: false },
261
331
  { pubkey: args.feeProgram, isSigner: false, isWritable: false },
262
332
  { pubkey: args.bondingCurveV2, isSigner: false, isWritable: false },
263
- { pubkey: args.feeRecipient, isSigner: false, isWritable: true }
333
+ { pubkey: args.feeRecipient, isSigner: false, isWritable: true },
264
334
  ];
265
335
  }
266
336
  buildBondingSellKeys(args) {
@@ -270,7 +340,11 @@ class PumpTrader {
270
340
  { pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
271
341
  { pubkey: args.mint, isSigner: false, isWritable: false },
272
342
  { pubkey: args.bonding, isSigner: false, isWritable: true },
273
- { pubkey: args.associatedBondingCurve, isSigner: false, isWritable: true },
343
+ {
344
+ pubkey: args.associatedBondingCurve,
345
+ isSigner: false,
346
+ isWritable: true,
347
+ },
274
348
  { pubkey: args.userAta, isSigner: false, isWritable: true },
275
349
  { pubkey: args.wallet, isSigner: true, isWritable: true },
276
350
  { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
@@ -279,10 +353,14 @@ class PumpTrader {
279
353
  { pubkey: args.eventAuthority, isSigner: false, isWritable: false },
280
354
  { pubkey: args.pumpProgram, isSigner: false, isWritable: false },
281
355
  { pubkey: args.feeConfig, isSigner: false, isWritable: false },
282
- { pubkey: args.feeProgram, isSigner: false, isWritable: false }
356
+ { pubkey: args.feeProgram, isSigner: false, isWritable: false },
283
357
  ];
284
358
  if (args.isCashbackCoin) {
285
- keys.push({ pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true });
359
+ keys.push({
360
+ pubkey: args.userVolumeAccumulator,
361
+ isSigner: false,
362
+ isWritable: true,
363
+ });
286
364
  }
287
365
  keys.push({ pubkey: args.bondingCurveV2, isSigner: false, isWritable: false }, { pubkey: args.feeRecipient, isSigner: false, isWritable: true });
288
366
  return keys;
@@ -304,6 +382,18 @@ class PumpTrader {
304
382
  offset += 1;
305
383
  const creator = new web3_js_1.PublicKey(data.slice(offset, offset + 32));
306
384
  offset += 32;
385
+ // V2 fields (115-byte bonding curve layout): quote_mint, virtual_quote_reserves, real_quote_reserves
386
+ // For legacy coins these may not be present, check remaining data length
387
+ if (offset + 32 <= data.length) {
388
+ state.quoteMint = new web3_js_1.PublicKey(data.slice(offset, offset + 32));
389
+ offset += 32;
390
+ }
391
+ if (offset + 8 <= data.length) {
392
+ [state.virtualQuoteReserves, offset] = readU64(data, offset);
393
+ }
394
+ if (offset + 8 <= data.length) {
395
+ [state.realQuoteReserves, offset] = readU64(data, offset);
396
+ }
307
397
  state.isMayhemMode = offset < data.length ? data[offset] === 1 : false;
308
398
  offset += 1;
309
399
  state.isCashbackCoin = offset < data.length ? data[offset] === 1 : false;
@@ -348,14 +438,20 @@ class PumpTrader {
348
438
  async getAmmPrice(mint) {
349
439
  const [poolCreator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("pool-authority"), mint.toBuffer()], PROGRAM_IDS.PUMP);
350
440
  const indexBuffer = new bn_js_1.default(0).toArrayLike(Buffer, "le", 2);
351
- const [pool] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("pool"), indexBuffer, poolCreator.toBuffer(), mint.toBuffer(), SOL_MINT.toBuffer()], PROGRAM_IDS.PUMP_AMM);
441
+ const [pool] = web3_js_1.PublicKey.findProgramAddressSync([
442
+ Buffer.from("pool"),
443
+ indexBuffer,
444
+ poolCreator.toBuffer(),
445
+ mint.toBuffer(),
446
+ SOL_MINT.toBuffer(),
447
+ ], PROGRAM_IDS.PUMP_AMM);
352
448
  const acc = await this.connection.getAccountInfo(pool);
353
449
  if (!acc)
354
450
  throw new Error("Pool not found");
355
451
  const poolKeys = parsePoolKeys(acc.data);
356
452
  const [baseInfo, quoteInfo] = await Promise.all([
357
453
  this.connection.getTokenAccountBalance(poolKeys.poolBaseTokenAccount),
358
- this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount)
454
+ this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
359
455
  ]);
360
456
  return quoteInfo.value.uiAmount / baseInfo.value.uiAmount;
361
457
  }
@@ -369,8 +465,9 @@ class PumpTrader {
369
465
  if (tokenAddr) {
370
466
  // 查询单个代币
371
467
  const mint = new web3_js_1.PublicKey(tokenAddr);
372
- const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner(this.wallet.publicKey, { mint });
373
- return tokenAccounts.value[0]?.account.data.parsed.info.tokenAmount.uiAmount || 0;
468
+ const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner(this.publicKey, { mint });
469
+ return (tokenAccounts.value[0]?.account.data.parsed.info.tokenAmount.uiAmount ||
470
+ 0);
374
471
  }
375
472
  else {
376
473
  // 查询所有代币
@@ -382,11 +479,11 @@ class PumpTrader {
382
479
  * @returns 代币信息数组,包含mint地址、余额等信息
383
480
  */
384
481
  async getAllTokenBalances() {
385
- const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner(this.wallet.publicKey, { programId: spl_token_1.TOKEN_PROGRAM_ID });
482
+ const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner(this.publicKey, { programId: spl_token_1.TOKEN_PROGRAM_ID });
386
483
  const balances = tokenAccounts.value
387
484
  .map((account) => {
388
485
  const parsed = account.account.data.parsed;
389
- if (parsed.type !== 'account')
486
+ if (parsed.type !== "account")
390
487
  return null;
391
488
  const tokenAmount = parsed.info.tokenAmount;
392
489
  if (Number(tokenAmount.amount) === 0)
@@ -395,16 +492,16 @@ class PumpTrader {
395
492
  mint: parsed.info.mint,
396
493
  amount: BigInt(tokenAmount.amount),
397
494
  decimals: tokenAmount.decimals,
398
- uiAmount: tokenAmount.uiAmount || 0
495
+ uiAmount: tokenAmount.uiAmount || 0,
399
496
  };
400
497
  })
401
498
  .filter((item) => item !== null);
402
499
  // 同时查询 TOKEN_2022_PROGRAM_ID
403
- const token2022Accounts = await this.connection.getParsedTokenAccountsByOwner(this.wallet.publicKey, { programId: spl_token_1.TOKEN_2022_PROGRAM_ID });
500
+ const token2022Accounts = await this.connection.getParsedTokenAccountsByOwner(this.publicKey, { programId: spl_token_1.TOKEN_2022_PROGRAM_ID });
404
501
  const token2022Balances = token2022Accounts.value
405
502
  .map((account) => {
406
503
  const parsed = account.account.data.parsed;
407
- if (parsed.type !== 'account')
504
+ if (parsed.type !== "account")
408
505
  return null;
409
506
  const tokenAmount = parsed.info.tokenAmount;
410
507
  if (Number(tokenAmount.amount) === 0)
@@ -413,7 +510,7 @@ class PumpTrader {
413
510
  mint: parsed.info.mint,
414
511
  amount: BigInt(tokenAmount.amount),
415
512
  decimals: tokenAmount.decimals,
416
- uiAmount: tokenAmount.uiAmount || 0
513
+ uiAmount: tokenAmount.uiAmount || 0,
417
514
  };
418
515
  })
419
516
  .filter((item) => item !== null);
@@ -431,21 +528,21 @@ class PumpTrader {
431
528
  mint: b.mint,
432
529
  amount: Number(b.amount),
433
530
  decimals: b.decimals,
434
- uiAmount: b.uiAmount
531
+ uiAmount: b.uiAmount,
435
532
  }));
436
533
  return uniqueBalances;
437
534
  }
438
535
  async solBalance() {
439
- const balance = await this.connection.getBalance(this.wallet.publicKey);
536
+ const balance = await this.connection.getBalance(this.publicKey);
440
537
  return balance / 1e9;
441
538
  }
442
539
  /* ---------- ATA 管理 ---------- */
443
540
  async ensureAta(tx, mint, tokenProgram) {
444
541
  const program = tokenProgram || spl_token_1.TOKEN_2022_PROGRAM_ID;
445
- const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, this.wallet.publicKey, false, program, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
542
+ const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, this.publicKey, false, program, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
446
543
  const acc = await this.connection.getAccountInfo(ata);
447
544
  if (!acc) {
448
- tx.add((0, spl_token_1.createAssociatedTokenAccountInstruction)(this.wallet.publicKey, ata, this.wallet.publicKey, mint, program, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
545
+ tx.add((0, spl_token_1.createAssociatedTokenAccountInstruction)(this.publicKey, ata, this.publicKey, mint, program, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
449
546
  }
450
547
  return ata;
451
548
  }
@@ -455,11 +552,11 @@ class PumpTrader {
455
552
  if (!acc) {
456
553
  tx.add((0, spl_token_1.createAssociatedTokenAccountInstruction)(owner, wsolAta, owner, SOL_MINT, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
457
554
  }
458
- if (mode === 'buy' && lamports) {
555
+ if (mode === "buy" && lamports) {
459
556
  tx.add(web3_js_1.SystemProgram.transfer({
460
557
  fromPubkey: owner,
461
558
  toPubkey: wsolAta,
462
- lamports: Number(lamports)
559
+ lamports: Number(lamports),
463
560
  }));
464
561
  tx.add((0, spl_token_1.createSyncNativeInstruction)(wsolAta));
465
562
  }
@@ -470,7 +567,7 @@ class PumpTrader {
470
567
  if (!priorityOpt?.enableRandom || !priorityOpt.randomRange) {
471
568
  return priorityOpt.base;
472
569
  }
473
- return priorityOpt.base + Math.floor(Math.random() * priorityOpt.randomRange);
570
+ return (priorityOpt.base + Math.floor(Math.random() * priorityOpt.randomRange));
474
571
  }
475
572
  calcSlippage({ tradeSize, reserve, slippageOpt }) {
476
573
  const impact = Number(tradeSize) / Math.max(Number(reserve), 1);
@@ -508,26 +605,36 @@ class PumpTrader {
508
605
  /* ---------- 统一交易接口 ---------- */
509
606
  /**
510
607
  * 自动判断内盘/外盘并执行买入
608
+ * @param useV2 - use buy_v2 instruction (supports USDC quote) instead of legacy buy
609
+ * @param quoteMint - quote mint for V2 (SOL_MINT for SOL-paired, or USDC mint for USDC-paired)
511
610
  */
512
- async autoBuy(tokenAddr, totalSolIn, tradeOpt) {
611
+ async autoBuy(tokenAddr, totalSolIn, tradeOpt, useV2 = false, quoteMint = SOL_MINT) {
513
612
  const mode = await this.getTradeMode(tokenAddr);
514
613
  if (mode === "bonding") {
614
+ if (useV2) {
615
+ return this.buyV2(tokenAddr, totalSolIn, tradeOpt, quoteMint);
616
+ }
515
617
  return this.buy(tokenAddr, totalSolIn, tradeOpt);
516
618
  }
517
619
  else {
518
- return this.ammBuy(tokenAddr, totalSolIn, tradeOpt);
620
+ return this.ammBuy(tokenAddr, totalSolIn, tradeOpt, quoteMint);
519
621
  }
520
622
  }
521
623
  /**
522
624
  * 自动判断内盘/外盘并执行卖出
625
+ * @param useV2 - use sell_v2 instruction (supports USDC quote) instead of legacy sell
626
+ * @param quoteMint - quote mint for V2 (SOL_MINT for SOL-paired, or USDC mint for USDC-paired)
523
627
  */
524
- async autoSell(tokenAddr, totalTokenIn, tradeOpt) {
628
+ async autoSell(tokenAddr, totalTokenIn, tradeOpt, useV2 = false, quoteMint = SOL_MINT) {
525
629
  const mode = await this.getTradeMode(tokenAddr);
526
630
  if (mode === "bonding") {
631
+ if (useV2) {
632
+ return this.sellV2(tokenAddr, totalTokenIn, tradeOpt, quoteMint);
633
+ }
527
634
  return this.sell(tokenAddr, totalTokenIn, tradeOpt);
528
635
  }
529
636
  else {
530
- return this.ammSell(tokenAddr, totalTokenIn, tradeOpt);
637
+ return this.ammSell(tokenAddr, totalTokenIn, tradeOpt, quoteMint);
531
638
  }
532
639
  }
533
640
  /* ---------- 内盘交易 ---------- */
@@ -546,7 +653,10 @@ class PumpTrader {
546
653
  const associatedBondingCurve = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, bonding, true, tokenProgram.programId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
547
654
  const [creatorVault] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("creator-vault"), creator.toBuffer()], PROGRAM_IDS.PUMP);
548
655
  const [globalVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("global_volume_accumulator")], PROGRAM_IDS.PUMP);
549
- const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()], PROGRAM_IDS.PUMP);
656
+ const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([
657
+ Buffer.from("user_volume_accumulator"),
658
+ this.publicKey.toBuffer(),
659
+ ], PROGRAM_IDS.PUMP);
550
660
  const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.FEE_CONFIG], PROGRAM_IDS.FEE);
551
661
  const feeRecipient = this.pickFeeRecipient();
552
662
  for (let i = 0; i < solChunks.length; i++) {
@@ -556,7 +666,7 @@ class PumpTrader {
556
666
  const slippageBps = this.calcSlippage({
557
667
  tradeSize: solIn,
558
668
  reserve: state.virtualSolReserves,
559
- slippageOpt: tradeOpt.slippage
669
+ slippageOpt: tradeOpt.slippage,
560
670
  });
561
671
  const maxSol = (solIn * BigInt(10_000 + slippageBps)) / 10000n;
562
672
  const priority = this.genPriority(tradeOpt.priority);
@@ -571,7 +681,7 @@ class PumpTrader {
571
681
  bonding,
572
682
  associatedBondingCurve,
573
683
  userAta,
574
- wallet: this.wallet.publicKey,
684
+ wallet: this.publicKey,
575
685
  creatorVault,
576
686
  eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
577
687
  pumpProgram: PROGRAM_IDS.PUMP,
@@ -581,28 +691,32 @@ class PumpTrader {
581
691
  feeProgram: PROGRAM_IDS.FEE,
582
692
  bondingCurveV2,
583
693
  feeRecipient,
584
- tokenProgramId: tokenProgram.programId
694
+ tokenProgramId: tokenProgram.programId,
585
695
  }),
586
- data: Buffer.concat([DISCRIMINATORS.BUY, u64(tokenOut), u64(maxSol)])
696
+ data: Buffer.concat([
697
+ DISCRIMINATORS.BUY,
698
+ u64(tokenOut),
699
+ u64(maxSol),
700
+ ]),
587
701
  }));
588
- const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash('finalized');
702
+ const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash("finalized");
589
703
  tx.recentBlockhash = blockhash;
590
- tx.feePayer = this.wallet.publicKey;
591
- tx.sign(this.wallet);
704
+ tx.feePayer = this.publicKey;
705
+ await this.signTx(tx);
592
706
  const signature = await this.connection.sendRawTransaction(tx.serialize(), {
593
707
  skipPreflight: false,
594
- maxRetries: 2
708
+ maxRetries: 2,
595
709
  });
596
710
  pendingTransactions.push({
597
711
  signature,
598
712
  lastValidBlockHeight,
599
- index: i
713
+ index: i,
600
714
  });
601
715
  }
602
716
  catch (e) {
603
717
  failedTransactions.push({
604
718
  index: i,
605
- error: e.message
719
+ error: e.message,
606
720
  });
607
721
  }
608
722
  }
@@ -624,10 +738,13 @@ class PumpTrader {
624
738
  const pendingTransactions = [];
625
739
  const failedTransactions = [];
626
740
  const associatedBondingCurve = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, bonding, true, tokenProgram.programId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
627
- const userAta = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, this.wallet.publicKey, false, tokenProgram.programId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
741
+ const userAta = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, this.publicKey, false, tokenProgram.programId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
628
742
  const [creatorVault] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("creator-vault"), creator.toBuffer()], PROGRAM_IDS.PUMP);
629
743
  const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.FEE_CONFIG], PROGRAM_IDS.FEE);
630
- const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()], PROGRAM_IDS.PUMP);
744
+ const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([
745
+ Buffer.from("user_volume_accumulator"),
746
+ this.publicKey.toBuffer(),
747
+ ], PROGRAM_IDS.PUMP);
631
748
  const feeRecipient = this.pickFeeRecipient();
632
749
  for (let i = 0; i < tokenChunks.length; i++) {
633
750
  try {
@@ -636,7 +753,7 @@ class PumpTrader {
636
753
  const slippageBps = this.calcSlippage({
637
754
  tradeSize: tokenIn,
638
755
  reserve: state.virtualTokenReserves,
639
- slippageOpt: tradeOpt.slippage
756
+ slippageOpt: tradeOpt.slippage,
640
757
  });
641
758
  const minSol = (solOut * BigInt(10_000 - slippageBps)) / 10000n;
642
759
  const priority = this.genPriority(tradeOpt.priority);
@@ -650,7 +767,7 @@ class PumpTrader {
650
767
  bonding,
651
768
  associatedBondingCurve,
652
769
  userAta,
653
- wallet: this.wallet.publicKey,
770
+ wallet: this.publicKey,
654
771
  creatorVault,
655
772
  eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
656
773
  pumpProgram: PROGRAM_IDS.PUMP,
@@ -660,41 +777,45 @@ class PumpTrader {
660
777
  feeRecipient,
661
778
  isCashbackCoin: !!state.isCashbackCoin,
662
779
  userVolumeAccumulator,
663
- tokenProgramId: tokenProgram.programId
780
+ tokenProgramId: tokenProgram.programId,
664
781
  }),
665
782
  data: Buffer.concat([
666
783
  DISCRIMINATORS.SELL,
667
784
  u64(tokenIn),
668
- u64(minSol > 0n ? minSol : 1n)
669
- ])
785
+ u64(minSol > 0n ? minSol : 1n),
786
+ ]),
670
787
  }));
671
788
  const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash("finalized");
672
789
  tx.recentBlockhash = blockhash;
673
- tx.feePayer = this.wallet.publicKey;
674
- tx.sign(this.wallet);
790
+ tx.feePayer = this.publicKey;
791
+ await this.signTx(tx);
675
792
  const signature = await this.connection.sendRawTransaction(tx.serialize());
676
793
  pendingTransactions.push({
677
794
  signature,
678
795
  lastValidBlockHeight,
679
- index: i
796
+ index: i,
680
797
  });
681
798
  }
682
799
  catch (e) {
683
800
  failedTransactions.push({
684
801
  index: i,
685
- error: e.message
802
+ error: e.message,
686
803
  });
687
804
  }
688
805
  }
689
806
  return { pendingTransactions, failedTransactions };
690
807
  }
691
808
  /* ---------- 外盘交易 ---------- */
692
- async ammBuy(tokenAddr, totalSolIn, tradeOpt) {
809
+ async ammBuy(tokenAddr, totalSolIn, tradeOpt, quoteMint = SOL_MINT) {
693
810
  const mint = new web3_js_1.PublicKey(tokenAddr);
694
- const poolInfo = await this.getAmmPoolInfo(mint);
811
+ const poolInfo = await this.getAmmPoolInfo(mint, quoteMint);
695
812
  const reserves = await this.getAmmPoolReserves(poolInfo.poolKeys);
696
813
  const solChunks = this.splitByMax(totalSolIn, tradeOpt.maxSolPerTx);
697
814
  const tokenProgram = await this.detectTokenProgram(tokenAddr);
815
+ const isSolQuote = quoteMint.equals(SOL_MINT);
816
+ const quoteTokenProgramId = isSolQuote
817
+ ? spl_token_1.TOKEN_PROGRAM_ID
818
+ : await this.detectQuoteTokenProgram(quoteMint);
698
819
  const pendingTransactions = [];
699
820
  const failedTransactions = [];
700
821
  for (let i = 0; i < solChunks.length; i++) {
@@ -704,45 +825,53 @@ class PumpTrader {
704
825
  const slippageBps = this.calcSlippage({
705
826
  tradeSize: solIn,
706
827
  reserve: reserves.quoteAmount,
707
- slippageOpt: tradeOpt.slippage
828
+ slippageOpt: tradeOpt.slippage,
708
829
  });
709
830
  const maxQuoteIn = (solIn * BigInt(10_000 + slippageBps)) / 10000n;
710
831
  const priority = this.genPriority(tradeOpt.priority);
711
832
  const tx = new web3_js_1.Transaction().add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }));
712
833
  const userBaseAta = await this.ensureAta(tx, poolInfo.poolKeys.baseMint, tokenProgram.programId);
713
- const userQuoteAta = await this.ensureWSOLAta(tx, this.wallet.publicKey, "buy", maxQuoteIn);
834
+ const userQuoteAta = isSolQuote
835
+ ? await this.ensureWSOLAta(tx, this.publicKey, "buy", maxQuoteIn)
836
+ : await this.ensureAta(tx, quoteMint, quoteTokenProgramId);
714
837
  const buyIx = this.createAmmBuyInstruction(poolInfo, userBaseAta, userQuoteAta, baseAmountOut, maxQuoteIn, tokenProgram.programId);
715
838
  tx.add(buyIx);
716
- tx.add((0, spl_token_1.createCloseAccountInstruction)(userQuoteAta, this.wallet.publicKey, this.wallet.publicKey));
717
- const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash('finalized');
839
+ if (isSolQuote) {
840
+ tx.add((0, spl_token_1.createCloseAccountInstruction)(userQuoteAta, this.publicKey, this.publicKey));
841
+ }
842
+ const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash("finalized");
718
843
  tx.recentBlockhash = blockhash;
719
- tx.feePayer = this.wallet.publicKey;
720
- tx.sign(this.wallet);
844
+ tx.feePayer = this.publicKey;
845
+ await this.signTx(tx);
721
846
  const signature = await this.connection.sendRawTransaction(tx.serialize(), {
722
847
  skipPreflight: false,
723
- maxRetries: 2
848
+ maxRetries: 2,
724
849
  });
725
850
  pendingTransactions.push({
726
851
  signature,
727
852
  lastValidBlockHeight,
728
- index: i
853
+ index: i,
729
854
  });
730
855
  }
731
856
  catch (e) {
732
857
  failedTransactions.push({
733
858
  index: i,
734
- error: e.message
859
+ error: e.message,
735
860
  });
736
861
  }
737
862
  }
738
863
  return { pendingTransactions, failedTransactions };
739
864
  }
740
- async ammSell(tokenAddr, totalTokenIn, tradeOpt) {
865
+ async ammSell(tokenAddr, totalTokenIn, tradeOpt, quoteMint = SOL_MINT) {
741
866
  const mint = new web3_js_1.PublicKey(tokenAddr);
742
- const poolInfo = await this.getAmmPoolInfo(mint);
867
+ const poolInfo = await this.getAmmPoolInfo(mint, quoteMint);
743
868
  const reserves = await this.getAmmPoolReserves(poolInfo.poolKeys);
744
869
  const totalSolOut = this.calculateAmmSellOutput(totalTokenIn, reserves);
745
870
  const tokenProgram = await this.detectTokenProgram(tokenAddr);
871
+ const isSolQuote = quoteMint.equals(SOL_MINT);
872
+ const quoteTokenProgramId = isSolQuote
873
+ ? spl_token_1.TOKEN_PROGRAM_ID
874
+ : await this.detectQuoteTokenProgram(quoteMint);
746
875
  const tokenChunks = totalSolOut <= tradeOpt.maxSolPerTx
747
876
  ? [totalTokenIn]
748
877
  : this.splitIntoN(totalTokenIn, Number((totalSolOut + tradeOpt.maxSolPerTx - 1n) / tradeOpt.maxSolPerTx));
@@ -755,48 +884,52 @@ class PumpTrader {
755
884
  const slippageBps = this.calcSlippage({
756
885
  tradeSize: tokenIn,
757
886
  reserve: reserves.baseAmount,
758
- slippageOpt: tradeOpt.slippage
887
+ slippageOpt: tradeOpt.slippage,
759
888
  });
760
889
  const minQuoteOut = (solOut * BigInt(10_000 - slippageBps)) / 10000n;
761
890
  const priority = this.genPriority(tradeOpt.priority);
762
891
  const tx = new web3_js_1.Transaction().add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }));
763
892
  const userBaseAta = await this.ensureAta(tx, poolInfo.poolKeys.baseMint, tokenProgram.programId);
764
- const userQuoteAta = await this.ensureWSOLAta(tx, this.wallet.publicKey, "sell");
893
+ const userQuoteAta = isSolQuote
894
+ ? await this.ensureWSOLAta(tx, this.publicKey, "sell")
895
+ : await this.ensureAta(tx, quoteMint, quoteTokenProgramId);
765
896
  const sellIx = this.createAmmSellInstruction(poolInfo, userBaseAta, userQuoteAta, tokenIn, minQuoteOut, tokenProgram.programId);
766
897
  tx.add(sellIx);
767
- tx.add((0, spl_token_1.createCloseAccountInstruction)(userQuoteAta, this.wallet.publicKey, this.wallet.publicKey));
768
- const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash('finalized');
898
+ if (isSolQuote) {
899
+ tx.add((0, spl_token_1.createCloseAccountInstruction)(userQuoteAta, this.publicKey, this.publicKey));
900
+ }
901
+ const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash("finalized");
769
902
  tx.recentBlockhash = blockhash;
770
- tx.feePayer = this.wallet.publicKey;
771
- tx.sign(this.wallet);
903
+ tx.feePayer = this.publicKey;
904
+ await this.signTx(tx);
772
905
  const signature = await this.connection.sendRawTransaction(tx.serialize(), {
773
906
  skipPreflight: false,
774
- maxRetries: 2
907
+ maxRetries: 2,
775
908
  });
776
909
  pendingTransactions.push({
777
910
  signature,
778
911
  lastValidBlockHeight,
779
- index: i
912
+ index: i,
780
913
  });
781
914
  }
782
915
  catch (e) {
783
916
  failedTransactions.push({
784
917
  index: i,
785
- error: e.message
918
+ error: e.message,
786
919
  });
787
920
  }
788
921
  }
789
922
  return { pendingTransactions, failedTransactions };
790
923
  }
791
924
  /* ---------- AMM 池信息 ---------- */
792
- async getAmmPoolInfo(mint) {
925
+ async getAmmPoolInfo(mint, quoteMint = SOL_MINT) {
793
926
  const [poolAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("pool-authority"), mint.toBuffer()], PROGRAM_IDS.PUMP);
794
927
  const [pool] = web3_js_1.PublicKey.findProgramAddressSync([
795
928
  Buffer.from("pool"),
796
929
  new bn_js_1.default(0).toArrayLike(Buffer, "le", 2),
797
930
  poolAuthority.toBuffer(),
798
931
  mint.toBuffer(),
799
- SOL_MINT.toBuffer()
932
+ quoteMint.toBuffer(),
800
933
  ], PROGRAM_IDS.PUMP_AMM);
801
934
  const acc = await this.connection.getAccountInfo(pool);
802
935
  if (!acc)
@@ -826,13 +959,13 @@ class PumpTrader {
826
959
  async getAmmPoolReserves(poolKeys) {
827
960
  const [baseInfo, quoteInfo] = await Promise.all([
828
961
  this.connection.getTokenAccountBalance(poolKeys.poolBaseTokenAccount),
829
- this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount)
962
+ this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
830
963
  ]);
831
964
  return {
832
965
  baseAmount: BigInt(baseInfo.value.amount),
833
966
  quoteAmount: BigInt(quoteInfo.value.amount),
834
967
  baseDecimals: baseInfo.value.decimals,
835
- quoteDecimals: quoteInfo.value.decimals
968
+ quoteDecimals: quoteInfo.value.decimals,
836
969
  };
837
970
  }
838
971
  deriveAmmPoolV2(baseMint) {
@@ -846,7 +979,10 @@ class PumpTrader {
846
979
  const [coinCreatorVaultAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("creator_vault"), poolKeys.coinCreator.toBuffer()], PROGRAM_IDS.PUMP_AMM);
847
980
  const coinCreatorVaultAta = (0, spl_token_1.getAssociatedTokenAddressSync)(SOL_MINT, coinCreatorVaultAuthority, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
848
981
  const [globalVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("global_volume_accumulator")], PROGRAM_IDS.PUMP_AMM);
849
- const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()], PROGRAM_IDS.PUMP_AMM);
982
+ const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([
983
+ Buffer.from("user_volume_accumulator"),
984
+ this.publicKey.toBuffer(),
985
+ ], PROGRAM_IDS.PUMP_AMM);
850
986
  const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.AMM_FEE_CONFIG], PROGRAM_IDS.FEE);
851
987
  const protocolFeeRecipient = globalConfig.protocolFeeRecipients[0];
852
988
  const protocolFeeRecipientTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(SOL_MINT, protocolFeeRecipient, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
@@ -855,44 +991,72 @@ class PumpTrader {
855
991
  const remainingKeys = [];
856
992
  if (poolKeys.isCashbackCoin) {
857
993
  const userVolumeAccumulatorWsolAta = (0, spl_token_1.getAssociatedTokenAddressSync)(SOL_MINT, userVolumeAccumulator, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
858
- remainingKeys.push({ pubkey: userVolumeAccumulatorWsolAta, isSigner: false, isWritable: true });
994
+ remainingKeys.push({
995
+ pubkey: userVolumeAccumulatorWsolAta,
996
+ isSigner: false,
997
+ isWritable: true,
998
+ });
859
999
  }
860
1000
  remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
861
- remainingKeys.push({ pubkey: newFeeRecipient, isSigner: false, isWritable: false }, { pubkey: newFeeRecipientTokenAccount, isSigner: false, isWritable: true });
1001
+ remainingKeys.push({ pubkey: newFeeRecipient, isSigner: false, isWritable: false }, {
1002
+ pubkey: newFeeRecipientTokenAccount,
1003
+ isSigner: false,
1004
+ isWritable: true,
1005
+ });
862
1006
  return new web3_js_1.TransactionInstruction({
863
1007
  programId: PROGRAM_IDS.PUMP_AMM,
864
1008
  keys: [
865
1009
  { pubkey: pool, isSigner: false, isWritable: true },
866
- { pubkey: this.wallet.publicKey, isSigner: true, isWritable: true },
1010
+ { pubkey: this.publicKey, isSigner: true, isWritable: true },
867
1011
  { pubkey: globalConfig.address, isSigner: false, isWritable: false },
868
1012
  { pubkey: poolKeys.baseMint, isSigner: false, isWritable: false },
869
1013
  { pubkey: poolKeys.quoteMint, isSigner: false, isWritable: false },
870
1014
  { pubkey: userBaseAta, isSigner: false, isWritable: true },
871
1015
  { pubkey: userQuoteAta, isSigner: false, isWritable: true },
872
- { pubkey: poolKeys.poolBaseTokenAccount, isSigner: false, isWritable: true },
873
- { pubkey: poolKeys.poolQuoteTokenAccount, isSigner: false, isWritable: true },
1016
+ {
1017
+ pubkey: poolKeys.poolBaseTokenAccount,
1018
+ isSigner: false,
1019
+ isWritable: true,
1020
+ },
1021
+ {
1022
+ pubkey: poolKeys.poolQuoteTokenAccount,
1023
+ isSigner: false,
1024
+ isWritable: true,
1025
+ },
874
1026
  { pubkey: protocolFeeRecipient, isSigner: false, isWritable: false },
875
- { pubkey: protocolFeeRecipientTokenAccount, isSigner: false, isWritable: true },
1027
+ {
1028
+ pubkey: protocolFeeRecipientTokenAccount,
1029
+ isSigner: false,
1030
+ isWritable: true,
1031
+ },
876
1032
  { pubkey: tokenProgramId, isSigner: false, isWritable: false },
877
1033
  { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
878
1034
  { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
879
- { pubkey: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
1035
+ {
1036
+ pubkey: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID,
1037
+ isSigner: false,
1038
+ isWritable: false,
1039
+ },
880
1040
  { pubkey: eventAuthority, isSigner: false, isWritable: false },
881
1041
  { pubkey: PROGRAM_IDS.PUMP_AMM, isSigner: false, isWritable: false },
882
1042
  { pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true },
883
- { pubkey: coinCreatorVaultAuthority, isSigner: false, isWritable: false },
1043
+ {
1044
+ pubkey: coinCreatorVaultAuthority,
1045
+ isSigner: false,
1046
+ isWritable: false,
1047
+ },
884
1048
  { pubkey: globalVolumeAccumulator, isSigner: false, isWritable: false },
885
1049
  { pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
886
1050
  { pubkey: feeConfig, isSigner: false, isWritable: false },
887
1051
  { pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
888
- ...remainingKeys
1052
+ ...remainingKeys,
889
1053
  ],
890
1054
  data: Buffer.concat([
891
1055
  DISCRIMINATORS.BUY,
892
1056
  u64(baseAmountOut),
893
1057
  u64(maxQuoteAmountIn),
894
- Buffer.from([1, 1])
895
- ])
1058
+ Buffer.from([1, 1]),
1059
+ ]),
896
1060
  });
897
1061
  }
898
1062
  createAmmSellInstruction(poolInfo, userBaseAta, userQuoteAta, baseAmountIn, minQuoteAmountOut, tokenProgramId) {
@@ -906,76 +1070,515 @@ class PumpTrader {
906
1070
  const protocolFeeRecipientTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(SOL_MINT, protocolFeeRecipient, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
907
1071
  const newFeeRecipient = this.pickFeeRecipient();
908
1072
  const newFeeRecipientTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(poolKeys.quoteMint, newFeeRecipient, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
909
- const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()], PROGRAM_IDS.PUMP_AMM);
1073
+ const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([
1074
+ Buffer.from("user_volume_accumulator"),
1075
+ this.publicKey.toBuffer(),
1076
+ ], PROGRAM_IDS.PUMP_AMM);
910
1077
  const userVolumeAccumulatorWsolAta = (0, spl_token_1.getAssociatedTokenAddressSync)(SOL_MINT, userVolumeAccumulator, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
911
1078
  const remainingKeys = [];
912
1079
  if (poolKeys.isCashbackCoin) {
913
- remainingKeys.push({ pubkey: userVolumeAccumulatorWsolAta, isSigner: false, isWritable: true }, { pubkey: userVolumeAccumulator, isSigner: false, isWritable: true });
1080
+ remainingKeys.push({
1081
+ pubkey: userVolumeAccumulatorWsolAta,
1082
+ isSigner: false,
1083
+ isWritable: true,
1084
+ }, { pubkey: userVolumeAccumulator, isSigner: false, isWritable: true });
914
1085
  }
915
1086
  remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
916
- remainingKeys.push({ pubkey: newFeeRecipient, isSigner: false, isWritable: false }, { pubkey: newFeeRecipientTokenAccount, isSigner: false, isWritable: true });
1087
+ remainingKeys.push({ pubkey: newFeeRecipient, isSigner: false, isWritable: false }, {
1088
+ pubkey: newFeeRecipientTokenAccount,
1089
+ isSigner: false,
1090
+ isWritable: true,
1091
+ });
917
1092
  return new web3_js_1.TransactionInstruction({
918
1093
  programId: PROGRAM_IDS.PUMP_AMM,
919
1094
  keys: [
920
1095
  { pubkey: pool, isSigner: false, isWritable: true },
921
- { pubkey: this.wallet.publicKey, isSigner: true, isWritable: true },
1096
+ { pubkey: this.publicKey, isSigner: true, isWritable: true },
922
1097
  { pubkey: globalConfig.address, isSigner: false, isWritable: false },
923
1098
  { pubkey: poolKeys.baseMint, isSigner: false, isWritable: false },
924
1099
  { pubkey: poolKeys.quoteMint, isSigner: false, isWritable: false },
925
1100
  { pubkey: userBaseAta, isSigner: false, isWritable: true },
926
1101
  { pubkey: userQuoteAta, isSigner: false, isWritable: true },
927
- { pubkey: poolKeys.poolBaseTokenAccount, isSigner: false, isWritable: true },
928
- { pubkey: poolKeys.poolQuoteTokenAccount, isSigner: false, isWritable: true },
1102
+ {
1103
+ pubkey: poolKeys.poolBaseTokenAccount,
1104
+ isSigner: false,
1105
+ isWritable: true,
1106
+ },
1107
+ {
1108
+ pubkey: poolKeys.poolQuoteTokenAccount,
1109
+ isSigner: false,
1110
+ isWritable: true,
1111
+ },
929
1112
  { pubkey: protocolFeeRecipient, isSigner: false, isWritable: false },
930
- { pubkey: protocolFeeRecipientTokenAccount, isSigner: false, isWritable: true },
1113
+ {
1114
+ pubkey: protocolFeeRecipientTokenAccount,
1115
+ isSigner: false,
1116
+ isWritable: true,
1117
+ },
931
1118
  { pubkey: tokenProgramId, isSigner: false, isWritable: false },
932
1119
  { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
933
1120
  { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
934
- { pubkey: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
1121
+ {
1122
+ pubkey: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID,
1123
+ isSigner: false,
1124
+ isWritable: false,
1125
+ },
935
1126
  { pubkey: eventAuthority, isSigner: false, isWritable: false },
936
1127
  { pubkey: PROGRAM_IDS.PUMP_AMM, isSigner: false, isWritable: false },
937
1128
  { pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true },
938
- { pubkey: coinCreatorVaultAuthority, isSigner: false, isWritable: false },
1129
+ {
1130
+ pubkey: coinCreatorVaultAuthority,
1131
+ isSigner: false,
1132
+ isWritable: false,
1133
+ },
939
1134
  { pubkey: feeConfig, isSigner: false, isWritable: false },
940
1135
  { pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
941
- ...remainingKeys
1136
+ ...remainingKeys,
942
1137
  ],
943
1138
  data: Buffer.concat([
944
1139
  DISCRIMINATORS.SELL,
945
1140
  u64(baseAmountIn),
946
- u64(minQuoteAmountOut > 0n ? minQuoteAmountOut : 1n)
947
- ])
1141
+ u64(minQuoteAmountOut > 0n ? minQuoteAmountOut : 1n),
1142
+ ]),
948
1143
  });
949
1144
  }
1145
+ /* ---------- V2 指令账户构建 ---------- */
1146
+ /**
1147
+ * Build accounts for buy_v2 instruction (27 accounts)
1148
+ * Ref: https://github.com/pump-fun/pump-public-docs/blob/main/docs/instructions/BUY.md
1149
+ */
1150
+ buildBondingBuyV2Keys(args) {
1151
+ return [
1152
+ { pubkey: args.global, isSigner: false, isWritable: false },
1153
+ { pubkey: args.baseMint, isSigner: false, isWritable: false },
1154
+ { pubkey: args.quoteMint, isSigner: false, isWritable: false },
1155
+ { pubkey: args.baseTokenProgram, isSigner: false, isWritable: false },
1156
+ { pubkey: args.quoteTokenProgram, isSigner: false, isWritable: false },
1157
+ {
1158
+ pubkey: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID,
1159
+ isSigner: false,
1160
+ isWritable: false,
1161
+ },
1162
+ { pubkey: args.feeRecipient, isSigner: false, isWritable: true },
1163
+ {
1164
+ pubkey: args.associatedQuoteFeeRecipient,
1165
+ isSigner: false,
1166
+ isWritable: true,
1167
+ },
1168
+ { pubkey: args.buybackFeeRecipient, isSigner: false, isWritable: true },
1169
+ {
1170
+ pubkey: args.associatedQuoteBuybackFeeRecipient,
1171
+ isSigner: false,
1172
+ isWritable: true,
1173
+ },
1174
+ { pubkey: args.bondingCurve, isSigner: false, isWritable: true },
1175
+ {
1176
+ pubkey: args.associatedBaseBondingCurve,
1177
+ isSigner: false,
1178
+ isWritable: true,
1179
+ },
1180
+ {
1181
+ pubkey: args.associatedQuoteBondingCurve,
1182
+ isSigner: false,
1183
+ isWritable: true,
1184
+ },
1185
+ { pubkey: args.user, isSigner: true, isWritable: true },
1186
+ { pubkey: args.associatedBaseUser, isSigner: false, isWritable: true },
1187
+ { pubkey: args.associatedQuoteUser, isSigner: false, isWritable: true },
1188
+ { pubkey: args.creatorVault, isSigner: false, isWritable: true },
1189
+ {
1190
+ pubkey: args.associatedCreatorVault,
1191
+ isSigner: false,
1192
+ isWritable: true,
1193
+ },
1194
+ { pubkey: args.sharingConfig, isSigner: false, isWritable: false },
1195
+ {
1196
+ pubkey: args.globalVolumeAccumulator,
1197
+ isSigner: false,
1198
+ isWritable: false,
1199
+ },
1200
+ { pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true },
1201
+ {
1202
+ pubkey: args.associatedUserVolumeAccumulator,
1203
+ isSigner: false,
1204
+ isWritable: true,
1205
+ },
1206
+ { pubkey: args.feeConfig, isSigner: false, isWritable: false },
1207
+ { pubkey: args.feeProgram, isSigner: false, isWritable: false },
1208
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
1209
+ { pubkey: args.eventAuthority, isSigner: false, isWritable: false },
1210
+ { pubkey: args.pumpProgram, isSigner: false, isWritable: false },
1211
+ ];
1212
+ }
1213
+ /**
1214
+ * Build accounts for sell_v2 instruction (26 accounts)
1215
+ * Ref: https://github.com/pump-fun/pump-public-docs/blob/main/docs/instructions/SELL.md
1216
+ */
1217
+ buildBondingSellV2Keys(args) {
1218
+ return [
1219
+ { pubkey: args.global, isSigner: false, isWritable: false },
1220
+ { pubkey: args.baseMint, isSigner: false, isWritable: false },
1221
+ { pubkey: args.quoteMint, isSigner: false, isWritable: false },
1222
+ { pubkey: args.baseTokenProgram, isSigner: false, isWritable: false },
1223
+ { pubkey: args.quoteTokenProgram, isSigner: false, isWritable: false },
1224
+ {
1225
+ pubkey: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID,
1226
+ isSigner: false,
1227
+ isWritable: false,
1228
+ },
1229
+ { pubkey: args.feeRecipient, isSigner: false, isWritable: true },
1230
+ {
1231
+ pubkey: args.associatedQuoteFeeRecipient,
1232
+ isSigner: false,
1233
+ isWritable: true,
1234
+ },
1235
+ { pubkey: args.buybackFeeRecipient, isSigner: false, isWritable: true },
1236
+ {
1237
+ pubkey: args.associatedQuoteBuybackFeeRecipient,
1238
+ isSigner: false,
1239
+ isWritable: true,
1240
+ },
1241
+ { pubkey: args.bondingCurve, isSigner: false, isWritable: true },
1242
+ {
1243
+ pubkey: args.associatedBaseBondingCurve,
1244
+ isSigner: false,
1245
+ isWritable: true,
1246
+ },
1247
+ {
1248
+ pubkey: args.associatedQuoteBondingCurve,
1249
+ isSigner: false,
1250
+ isWritable: true,
1251
+ },
1252
+ { pubkey: args.user, isSigner: true, isWritable: true },
1253
+ { pubkey: args.associatedBaseUser, isSigner: false, isWritable: true },
1254
+ { pubkey: args.associatedQuoteUser, isSigner: false, isWritable: true },
1255
+ { pubkey: args.creatorVault, isSigner: false, isWritable: true },
1256
+ {
1257
+ pubkey: args.associatedCreatorVault,
1258
+ isSigner: false,
1259
+ isWritable: true,
1260
+ },
1261
+ { pubkey: args.sharingConfig, isSigner: false, isWritable: false },
1262
+ { pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true },
1263
+ {
1264
+ pubkey: args.associatedUserVolumeAccumulator,
1265
+ isSigner: false,
1266
+ isWritable: true,
1267
+ },
1268
+ { pubkey: args.feeConfig, isSigner: false, isWritable: false },
1269
+ { pubkey: args.feeProgram, isSigner: false, isWritable: false },
1270
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
1271
+ { pubkey: args.eventAuthority, isSigner: false, isWritable: false },
1272
+ { pubkey: args.pumpProgram, isSigner: false, isWritable: false },
1273
+ ];
1274
+ }
1275
+ /* ---------- V2 交易 ---------- */
1276
+ /**
1277
+ * Buy using buy_v2 instruction (supports both SOL-paired and USDC-paired coins)
1278
+ * For SOL-paired coins: quoteMint = SOL_MINT, quoteTokenProgram = TOKEN_PROGRAM_ID
1279
+ * For USDC-paired coins: quoteMint = USDC mint, quoteTokenProgram = TOKEN_PROGRAM_ID
1280
+ */
1281
+ async buyV2(tokenAddr, totalQuoteIn, tradeOpt, quoteMint = SOL_MINT) {
1282
+ const baseMint = new web3_js_1.PublicKey(tokenAddr);
1283
+ const baseTokenProgram = await this.detectTokenProgram(tokenAddr);
1284
+ const quoteTokenProgramId = quoteMint.equals(SOL_MINT)
1285
+ ? spl_token_1.TOKEN_PROGRAM_ID
1286
+ : spl_token_1.TOKEN_PROGRAM_ID;
1287
+ if (!this.globalState)
1288
+ await this.loadGlobal();
1289
+ const { bonding, state, creator } = await this.loadBonding(baseMint);
1290
+ if (state.complete)
1291
+ throw new Error("Bonding curve already completed");
1292
+ const solEquivalent = quoteMint.equals(SOL_MINT) ? totalQuoteIn : 0n;
1293
+ const quoteChunks = solEquivalent > 0n
1294
+ ? this.splitByMax(solEquivalent, tradeOpt.maxSolPerTx)
1295
+ : this.splitByMax(totalQuoteIn, tradeOpt.maxSolPerTx);
1296
+ const pendingTransactions = [];
1297
+ const failedTransactions = [];
1298
+ // Pre-compute PDAs
1299
+ const associatedBaseBondingCurve = (0, spl_token_1.getAssociatedTokenAddressSync)(baseMint, bonding, true, baseTokenProgram.programId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1300
+ const associatedQuoteBondingCurve = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, bonding, true, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1301
+ const [creatorVault] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("creator-vault"), creator.toBuffer()], PROGRAM_IDS.PUMP);
1302
+ const associatedCreatorVault = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, creatorVault, true, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1303
+ const [globalVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("global_volume_accumulator")], PROGRAM_IDS.PUMP);
1304
+ const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([
1305
+ Buffer.from("user_volume_accumulator"),
1306
+ this.publicKey.toBuffer(),
1307
+ ], PROGRAM_IDS.PUMP);
1308
+ const associatedUserVolumeAccumulator = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, userVolumeAccumulator, true, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1309
+ const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.FEE_CONFIG], PROGRAM_IDS.FEE);
1310
+ const sharingConfig = this.getSharingConfigPda(baseMint);
1311
+ const feeRecipient = this.pickFeeRecipient();
1312
+ const associatedQuoteFeeRecipient = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, feeRecipient, true, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1313
+ const buybackFeeRecipient = this.pickBuybackFeeRecipient();
1314
+ const associatedQuoteBuybackFeeRecipient = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, buybackFeeRecipient, true, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1315
+ const associatedQuoteUser = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, this.publicKey, false, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1316
+ for (let i = 0; i < quoteChunks.length; i++) {
1317
+ try {
1318
+ const quoteIn = quoteChunks[i];
1319
+ const tokenOut = this.calcBuy(quoteIn, state);
1320
+ const slippageBps = this.calcSlippage({
1321
+ tradeSize: quoteIn,
1322
+ reserve: state.virtualSolReserves,
1323
+ slippageOpt: tradeOpt.slippage,
1324
+ });
1325
+ const maxQuoteCost = (quoteIn * BigInt(10_000 + slippageBps)) / 10000n;
1326
+ const priority = this.genPriority(tradeOpt.priority);
1327
+ const tx = new web3_js_1.Transaction().add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }));
1328
+ // For SOL-paired coins, need wSOL ATA for the user's quote account
1329
+ if (quoteMint.equals(SOL_MINT)) {
1330
+ await this.ensureWSOLAta(tx, this.publicKey, "buy", maxQuoteCost);
1331
+ }
1332
+ else {
1333
+ // For non-SOL quote (e.g. USDC), ensure user has the quote token ATA
1334
+ const userQuoteAta = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, this.publicKey, false, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1335
+ const acc = await this.connection.getAccountInfo(userQuoteAta);
1336
+ if (!acc) {
1337
+ tx.add((0, spl_token_1.createAssociatedTokenAccountInstruction)(this.publicKey, userQuoteAta, this.publicKey, quoteMint, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
1338
+ }
1339
+ }
1340
+ const userBaseAta = await this.ensureAta(tx, baseMint, baseTokenProgram.programId);
1341
+ tx.add(new web3_js_1.TransactionInstruction({
1342
+ programId: PROGRAM_IDS.PUMP,
1343
+ keys: this.buildBondingBuyV2Keys({
1344
+ global: this.global,
1345
+ baseMint,
1346
+ quoteMint,
1347
+ baseTokenProgram: baseTokenProgram.programId,
1348
+ quoteTokenProgram: quoteTokenProgramId,
1349
+ feeRecipient,
1350
+ associatedQuoteFeeRecipient,
1351
+ buybackFeeRecipient,
1352
+ associatedQuoteBuybackFeeRecipient,
1353
+ bondingCurve: bonding,
1354
+ associatedBaseBondingCurve,
1355
+ associatedQuoteBondingCurve,
1356
+ user: this.publicKey,
1357
+ associatedBaseUser: userBaseAta,
1358
+ associatedQuoteUser,
1359
+ creatorVault,
1360
+ associatedCreatorVault,
1361
+ sharingConfig,
1362
+ globalVolumeAccumulator,
1363
+ userVolumeAccumulator,
1364
+ associatedUserVolumeAccumulator,
1365
+ feeConfig,
1366
+ feeProgram: PROGRAM_IDS.FEE,
1367
+ eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
1368
+ pumpProgram: PROGRAM_IDS.PUMP,
1369
+ }),
1370
+ data: Buffer.concat([
1371
+ DISCRIMINATORS.BUY_V2,
1372
+ u64(tokenOut),
1373
+ u64(maxQuoteCost),
1374
+ ]),
1375
+ }));
1376
+ // Close wSOL ATA after buy for SOL-paired coins
1377
+ if (quoteMint.equals(SOL_MINT)) {
1378
+ const wsolAta = (0, spl_token_1.getAssociatedTokenAddressSync)(SOL_MINT, this.publicKey, false, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1379
+ tx.add((0, spl_token_1.createCloseAccountInstruction)(wsolAta, this.publicKey, this.publicKey));
1380
+ }
1381
+ const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash("finalized");
1382
+ tx.recentBlockhash = blockhash;
1383
+ tx.feePayer = this.publicKey;
1384
+ await this.signTx(tx);
1385
+ const signature = await this.connection.sendRawTransaction(tx.serialize(), {
1386
+ skipPreflight: false,
1387
+ maxRetries: 2,
1388
+ });
1389
+ pendingTransactions.push({ signature, lastValidBlockHeight, index: i });
1390
+ }
1391
+ catch (e) {
1392
+ failedTransactions.push({ index: i, error: e.message });
1393
+ }
1394
+ }
1395
+ return { pendingTransactions, failedTransactions };
1396
+ }
1397
+ /**
1398
+ * Sell using sell_v2 instruction (supports both SOL-paired and USDC-paired coins)
1399
+ */
1400
+ async sellV2(tokenAddr, totalTokenIn, tradeOpt, quoteMint = SOL_MINT) {
1401
+ const baseMint = new web3_js_1.PublicKey(tokenAddr);
1402
+ const baseTokenProgram = await this.detectTokenProgram(tokenAddr);
1403
+ const quoteTokenProgramId = quoteMint.equals(SOL_MINT)
1404
+ ? spl_token_1.TOKEN_PROGRAM_ID
1405
+ : spl_token_1.TOKEN_PROGRAM_ID;
1406
+ if (!this.globalState)
1407
+ await this.loadGlobal();
1408
+ const { bonding, state, creator } = await this.loadBonding(baseMint);
1409
+ if (state.complete)
1410
+ throw new Error("Bonding curve already completed");
1411
+ const totalQuoteOut = this.calcSell(totalTokenIn, state);
1412
+ const tokenChunks = totalQuoteOut <= tradeOpt.maxSolPerTx
1413
+ ? [totalTokenIn]
1414
+ : this.splitIntoN(totalTokenIn, Number((totalQuoteOut + tradeOpt.maxSolPerTx - 1n) /
1415
+ tradeOpt.maxSolPerTx));
1416
+ const pendingTransactions = [];
1417
+ const failedTransactions = [];
1418
+ // Pre-compute PDAs
1419
+ const associatedBaseBondingCurve = (0, spl_token_1.getAssociatedTokenAddressSync)(baseMint, bonding, true, baseTokenProgram.programId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1420
+ const associatedQuoteBondingCurve = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, bonding, true, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1421
+ const [creatorVault] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("creator-vault"), creator.toBuffer()], PROGRAM_IDS.PUMP);
1422
+ const associatedCreatorVault = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, creatorVault, true, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1423
+ const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([
1424
+ Buffer.from("user_volume_accumulator"),
1425
+ this.publicKey.toBuffer(),
1426
+ ], PROGRAM_IDS.PUMP);
1427
+ const associatedUserVolumeAccumulator = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, userVolumeAccumulator, true, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1428
+ const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.FEE_CONFIG], PROGRAM_IDS.FEE);
1429
+ const sharingConfig = this.getSharingConfigPda(baseMint);
1430
+ const feeRecipient = this.pickFeeRecipient();
1431
+ const associatedQuoteFeeRecipient = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, feeRecipient, true, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1432
+ const buybackFeeRecipient = this.pickBuybackFeeRecipient();
1433
+ const associatedQuoteBuybackFeeRecipient = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, buybackFeeRecipient, true, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1434
+ const associatedQuoteUser = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, this.publicKey, false, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1435
+ const userBaseAta = (0, spl_token_1.getAssociatedTokenAddressSync)(baseMint, this.publicKey, false, baseTokenProgram.programId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1436
+ for (let i = 0; i < tokenChunks.length; i++) {
1437
+ try {
1438
+ const tokenIn = tokenChunks[i];
1439
+ const quoteOut = this.calcSell(tokenIn, state);
1440
+ const slippageBps = this.calcSlippage({
1441
+ tradeSize: tokenIn,
1442
+ reserve: state.virtualTokenReserves,
1443
+ slippageOpt: tradeOpt.slippage,
1444
+ });
1445
+ const minQuoteOut = (quoteOut * BigInt(10_000 - slippageBps)) / 10000n;
1446
+ const priority = this.genPriority(tradeOpt.priority);
1447
+ const tx = new web3_js_1.Transaction().add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }));
1448
+ // For non-SOL quotes, ensure user has the quote token ATA (to receive proceeds)
1449
+ if (!quoteMint.equals(SOL_MINT)) {
1450
+ const userQuoteAta = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, this.publicKey, false, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1451
+ const acc = await this.connection.getAccountInfo(userQuoteAta);
1452
+ if (!acc) {
1453
+ tx.add((0, spl_token_1.createAssociatedTokenAccountInstruction)(this.publicKey, userQuoteAta, this.publicKey, quoteMint, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
1454
+ }
1455
+ }
1456
+ tx.add(new web3_js_1.TransactionInstruction({
1457
+ programId: PROGRAM_IDS.PUMP,
1458
+ keys: this.buildBondingSellV2Keys({
1459
+ global: this.global,
1460
+ baseMint,
1461
+ quoteMint,
1462
+ baseTokenProgram: baseTokenProgram.programId,
1463
+ quoteTokenProgram: quoteTokenProgramId,
1464
+ feeRecipient,
1465
+ associatedQuoteFeeRecipient,
1466
+ buybackFeeRecipient,
1467
+ associatedQuoteBuybackFeeRecipient,
1468
+ bondingCurve: bonding,
1469
+ associatedBaseBondingCurve,
1470
+ associatedQuoteBondingCurve,
1471
+ user: this.publicKey,
1472
+ associatedBaseUser: userBaseAta,
1473
+ associatedQuoteUser,
1474
+ creatorVault,
1475
+ associatedCreatorVault,
1476
+ sharingConfig,
1477
+ userVolumeAccumulator,
1478
+ associatedUserVolumeAccumulator,
1479
+ feeConfig,
1480
+ feeProgram: PROGRAM_IDS.FEE,
1481
+ eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
1482
+ pumpProgram: PROGRAM_IDS.PUMP,
1483
+ }),
1484
+ data: Buffer.concat([
1485
+ DISCRIMINATORS.SELL_V2,
1486
+ u64(tokenIn),
1487
+ u64(minQuoteOut > 0n ? minQuoteOut : 1n),
1488
+ ]),
1489
+ }));
1490
+ const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash("finalized");
1491
+ tx.recentBlockhash = blockhash;
1492
+ tx.feePayer = this.publicKey;
1493
+ await this.signTx(tx);
1494
+ const signature = await this.connection.sendRawTransaction(tx.serialize());
1495
+ pendingTransactions.push({ signature, lastValidBlockHeight, index: i });
1496
+ }
1497
+ catch (e) {
1498
+ failedTransactions.push({ index: i, error: e.message });
1499
+ }
1500
+ }
1501
+ return { pendingTransactions, failedTransactions };
1502
+ }
1503
+ /* ---------- Collect Creator Fee V2 ---------- */
1504
+ /**
1505
+ * Collect creator fees from bonding curve creator vault (collect_creator_fee_v2)
1506
+ * Ref: https://github.com/pump-fun/pump-public-docs/blob/main/docs/instructions/COLLECT_CREATOR_FEE.md
1507
+ */
1508
+ async collectCreatorFeeV2(creator, quoteMint = SOL_MINT) {
1509
+ const quoteTokenProgramId = quoteMint.equals(SOL_MINT)
1510
+ ? spl_token_1.TOKEN_PROGRAM_ID
1511
+ : spl_token_1.TOKEN_PROGRAM_ID;
1512
+ const [creatorVault] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("creator-vault"), creator.toBuffer()], PROGRAM_IDS.PUMP);
1513
+ const creatorTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, creator, false, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1514
+ const creatorVaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, creatorVault, true, quoteTokenProgramId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
1515
+ const [eventAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], PROGRAM_IDS.PUMP);
1516
+ const tx = new web3_js_1.Transaction().add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), new web3_js_1.TransactionInstruction({
1517
+ programId: PROGRAM_IDS.PUMP,
1518
+ keys: [
1519
+ { pubkey: creator, isSigner: false, isWritable: false },
1520
+ { pubkey: creatorTokenAccount, isSigner: false, isWritable: true },
1521
+ { pubkey: creatorVault, isSigner: false, isWritable: true },
1522
+ {
1523
+ pubkey: creatorVaultTokenAccount,
1524
+ isSigner: false,
1525
+ isWritable: true,
1526
+ },
1527
+ { pubkey: quoteMint, isSigner: false, isWritable: false },
1528
+ { pubkey: quoteTokenProgramId, isSigner: false, isWritable: false },
1529
+ {
1530
+ pubkey: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID,
1531
+ isSigner: false,
1532
+ isWritable: false,
1533
+ },
1534
+ {
1535
+ pubkey: web3_js_1.SystemProgram.programId,
1536
+ isSigner: false,
1537
+ isWritable: false,
1538
+ },
1539
+ { pubkey: eventAuthority, isSigner: false, isWritable: false },
1540
+ { pubkey: PROGRAM_IDS.PUMP, isSigner: false, isWritable: false },
1541
+ ],
1542
+ data: DISCRIMINATORS.COLLECT_CREATOR_FEE_V2,
1543
+ }));
1544
+ const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash("finalized");
1545
+ tx.recentBlockhash = blockhash;
1546
+ tx.feePayer = this.publicKey;
1547
+ await this.signTx(tx);
1548
+ const signature = await this.connection.sendRawTransaction(tx.serialize());
1549
+ await this.confirmTransactionWithPolling(signature, lastValidBlockHeight);
1550
+ return signature;
1551
+ }
950
1552
  /* ---------- 交易确认 ---------- */
951
1553
  async confirmTransactionWithPolling(signature, lastValidBlockHeight, maxAttempts = 5, delayMs = 2000) {
952
- console.log('✅ 交易已发送:', signature);
953
- console.log('🔗 查看交易: https://solscan.io/tx/' + signature);
1554
+ console.log("✅ 交易已发送:", signature);
1555
+ console.log("🔗 查看交易: https://solscan.io/tx/" + signature);
954
1556
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
955
- await new Promise(resolve => setTimeout(resolve, delayMs));
1557
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
956
1558
  try {
957
1559
  console.log(`🔍 检查交易状态 (${attempt}/${maxAttempts})...`);
958
1560
  const txInfo = await this.connection.getTransaction(signature, {
959
- commitment: 'confirmed',
960
- maxSupportedTransactionVersion: 0
1561
+ commitment: "confirmed",
1562
+ maxSupportedTransactionVersion: 0,
961
1563
  });
962
1564
  if (txInfo) {
963
1565
  if (txInfo.meta?.err) {
964
- console.error('❌ 交易失败:', txInfo.meta.err);
965
- throw new Error('交易失败: ' + JSON.stringify(txInfo.meta.err));
1566
+ console.error("❌ 交易失败:", txInfo.meta.err);
1567
+ throw new Error("交易失败: " + JSON.stringify(txInfo.meta.err));
966
1568
  }
967
- console.log('✅ 交易已确认!');
1569
+ console.log("✅ 交易已确认!");
968
1570
  return signature;
969
1571
  }
970
- const currentBlockHeight = await this.connection.getBlockHeight('finalized');
1572
+ const currentBlockHeight = await this.connection.getBlockHeight("finalized");
971
1573
  if (currentBlockHeight > lastValidBlockHeight) {
972
- console.log('⚠️ 交易已过期(超过有效区块高度)');
973
- throw new Error('交易过期:未在有效区块高度内确认');
1574
+ console.log("⚠️ 交易已过期(超过有效区块高度)");
1575
+ throw new Error("交易过期:未在有效区块高度内确认");
974
1576
  }
975
1577
  }
976
1578
  catch (error) {
977
1579
  const err = error;
978
- if (err.message?.includes('交易失败') || err.message?.includes('交易过期')) {
1580
+ if (err.message?.includes("交易失败") ||
1581
+ err.message?.includes("交易过期")) {
979
1582
  throw error;
980
1583
  }
981
1584
  console.log(`⚠️ 查询出错,继续重试: ${err.message}`);
@@ -1012,7 +1615,7 @@ class PumpTrader {
1012
1615
  isBuy,
1013
1616
  user: user.toBase58(),
1014
1617
  timestamp,
1015
- signature: log.signature
1618
+ signature: log.signature,
1016
1619
  });
1017
1620
  }
1018
1621
  }, "confirmed");
@@ -1025,25 +1628,34 @@ class PumpTrader {
1025
1628
  return {
1026
1629
  name: metadata?.name || "",
1027
1630
  symbol: metadata?.symbol || "",
1028
- uri: metadata?.uri || ""
1631
+ uri: metadata?.uri || "",
1029
1632
  };
1030
1633
  }
1031
1634
  catch (e) {
1032
- const metadataPda = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("metadata"), PROGRAM_IDS.METADATA.toBuffer(), mint.toBuffer()], PROGRAM_IDS.METADATA)[0];
1635
+ const metadataPda = web3_js_1.PublicKey.findProgramAddressSync([
1636
+ Buffer.from("metadata"),
1637
+ PROGRAM_IDS.METADATA.toBuffer(),
1638
+ mint.toBuffer(),
1639
+ ], PROGRAM_IDS.METADATA)[0];
1033
1640
  const acc = await this.connection.getAccountInfo(metadataPda);
1034
1641
  if (!acc)
1035
1642
  return null;
1036
1643
  const meta = parseMetadataAccount(acc.data);
1037
1644
  return {
1038
- name: meta?.name?.replace(/\u0000/g, '') || "",
1039
- symbol: meta?.symbol?.replace(/\u0000/g, '') || "",
1040
- uri: meta?.uri?.replace(/\u0000/g, '') || ""
1645
+ name: meta?.name?.replace(/\u0000/g, "") || "",
1646
+ symbol: meta?.symbol?.replace(/\u0000/g, "") || "",
1647
+ uri: meta?.uri?.replace(/\u0000/g, "") || "",
1041
1648
  };
1042
1649
  }
1043
1650
  }
1044
- // 公开wallet方法
1651
+ /**
1652
+ * 获取原始 wallet 对象(Keypair 或前端 WalletAdapter)
1653
+ */
1045
1654
  getWallet() {
1046
- return this.wallet;
1655
+ return this._wallet;
1656
+ }
1657
+ getPublicKey() {
1658
+ return this.publicKey;
1047
1659
  }
1048
1660
  getConnection() {
1049
1661
  return this.connection;