pump-trader 1.1.8 → 1.2.1

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