pump-trader 1.1.5 → 1.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,10 +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;
307
- if (offset + 32 <= data.length - 2) {
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) {
308
388
  state.quoteMint = new web3_js_1.PublicKey(data.slice(offset, offset + 32));
309
389
  offset += 32;
310
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
+ }
311
397
  state.isMayhemMode = offset < data.length ? data[offset] === 1 : false;
312
398
  offset += 1;
313
399
  state.isCashbackCoin = offset < data.length ? data[offset] === 1 : false;
@@ -340,48 +426,35 @@ class PumpTrader {
340
426
  async getPriceAndStatus(tokenAddr) {
341
427
  const mint = new web3_js_1.PublicKey(tokenAddr);
342
428
  const { state } = await this.loadBonding(mint);
343
- const quoteMint = this.getEffectiveQuoteMint(state.quoteMint);
344
429
  if (state.complete) {
345
- const price = await this.getAmmPrice(mint, quoteMint);
430
+ const price = await this.getAmmPrice(mint);
346
431
  return { price, completed: true };
347
432
  }
348
433
  const oneToken = BigInt(1_000_000);
349
- const quoteOut = this.calcSell(oneToken, state);
350
- const quoteDecimals = await this.getMintDecimals(quoteMint);
351
- const price = Number(quoteOut) / 10 ** quoteDecimals;
434
+ const solOut = this.calcSell(oneToken, state);
435
+ const price = Number(solOut) / 1e9;
352
436
  return { price, completed: false };
353
437
  }
354
- async getAmmPrice(mint, quoteMint = SOL_MINT) {
438
+ async getAmmPrice(mint) {
355
439
  const [poolCreator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("pool-authority"), mint.toBuffer()], PROGRAM_IDS.PUMP);
356
440
  const indexBuffer = new bn_js_1.default(0).toArrayLike(Buffer, "le", 2);
357
- const [pool] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("pool"), indexBuffer, poolCreator.toBuffer(), mint.toBuffer(), quoteMint.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);
358
448
  const acc = await this.connection.getAccountInfo(pool);
359
449
  if (!acc)
360
450
  throw new Error("Pool not found");
361
451
  const poolKeys = parsePoolKeys(acc.data);
362
452
  const [baseInfo, quoteInfo] = await Promise.all([
363
453
  this.connection.getTokenAccountBalance(poolKeys.poolBaseTokenAccount),
364
- this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount)
454
+ this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
365
455
  ]);
366
456
  return quoteInfo.value.uiAmount / baseInfo.value.uiAmount;
367
457
  }
368
- getEffectiveQuoteMint(quoteMint) {
369
- if (!quoteMint || quoteMint.equals(web3_js_1.PublicKey.default)) {
370
- return SOL_MINT;
371
- }
372
- return quoteMint;
373
- }
374
- async getMintDecimals(mint) {
375
- if (mint.equals(SOL_MINT)) {
376
- return 9;
377
- }
378
- const mintInfo = await this.connection.getParsedAccountInfo(mint);
379
- const parsedData = mintInfo.value?.data;
380
- if (parsedData && "parsed" in parsedData) {
381
- return parsedData.parsed.info.decimals;
382
- }
383
- throw new Error(`Unable to determine mint decimals for ${mint.toBase58()}`);
384
- }
385
458
  /* ---------- 余额查询 ---------- */
386
459
  /**
387
460
  * 查询代币余额
@@ -392,8 +465,9 @@ class PumpTrader {
392
465
  if (tokenAddr) {
393
466
  // 查询单个代币
394
467
  const mint = new web3_js_1.PublicKey(tokenAddr);
395
- const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner(this.wallet.publicKey, { mint });
396
- 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);
397
471
  }
398
472
  else {
399
473
  // 查询所有代币
@@ -405,11 +479,11 @@ class PumpTrader {
405
479
  * @returns 代币信息数组,包含mint地址、余额等信息
406
480
  */
407
481
  async getAllTokenBalances() {
408
- 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 });
409
483
  const balances = tokenAccounts.value
410
484
  .map((account) => {
411
485
  const parsed = account.account.data.parsed;
412
- if (parsed.type !== 'account')
486
+ if (parsed.type !== "account")
413
487
  return null;
414
488
  const tokenAmount = parsed.info.tokenAmount;
415
489
  if (Number(tokenAmount.amount) === 0)
@@ -418,16 +492,16 @@ class PumpTrader {
418
492
  mint: parsed.info.mint,
419
493
  amount: BigInt(tokenAmount.amount),
420
494
  decimals: tokenAmount.decimals,
421
- uiAmount: tokenAmount.uiAmount || 0
495
+ uiAmount: tokenAmount.uiAmount || 0,
422
496
  };
423
497
  })
424
498
  .filter((item) => item !== null);
425
499
  // 同时查询 TOKEN_2022_PROGRAM_ID
426
- 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 });
427
501
  const token2022Balances = token2022Accounts.value
428
502
  .map((account) => {
429
503
  const parsed = account.account.data.parsed;
430
- if (parsed.type !== 'account')
504
+ if (parsed.type !== "account")
431
505
  return null;
432
506
  const tokenAmount = parsed.info.tokenAmount;
433
507
  if (Number(tokenAmount.amount) === 0)
@@ -436,7 +510,7 @@ class PumpTrader {
436
510
  mint: parsed.info.mint,
437
511
  amount: BigInt(tokenAmount.amount),
438
512
  decimals: tokenAmount.decimals,
439
- uiAmount: tokenAmount.uiAmount || 0
513
+ uiAmount: tokenAmount.uiAmount || 0,
440
514
  };
441
515
  })
442
516
  .filter((item) => item !== null);
@@ -454,21 +528,21 @@ class PumpTrader {
454
528
  mint: b.mint,
455
529
  amount: Number(b.amount),
456
530
  decimals: b.decimals,
457
- uiAmount: b.uiAmount
531
+ uiAmount: b.uiAmount,
458
532
  }));
459
533
  return uniqueBalances;
460
534
  }
461
535
  async solBalance() {
462
- const balance = await this.connection.getBalance(this.wallet.publicKey);
536
+ const balance = await this.connection.getBalance(this.publicKey);
463
537
  return balance / 1e9;
464
538
  }
465
539
  /* ---------- ATA 管理 ---------- */
466
540
  async ensureAta(tx, mint, tokenProgram) {
467
541
  const program = tokenProgram || spl_token_1.TOKEN_2022_PROGRAM_ID;
468
- 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);
469
543
  const acc = await this.connection.getAccountInfo(ata);
470
544
  if (!acc) {
471
- 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));
472
546
  }
473
547
  return ata;
474
548
  }
@@ -478,11 +552,11 @@ class PumpTrader {
478
552
  if (!acc) {
479
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));
480
554
  }
481
- if (mode === 'buy' && lamports) {
555
+ if (mode === "buy" && lamports) {
482
556
  tx.add(web3_js_1.SystemProgram.transfer({
483
557
  fromPubkey: owner,
484
558
  toPubkey: wsolAta,
485
- lamports: Number(lamports)
559
+ lamports: Number(lamports),
486
560
  }));
487
561
  tx.add((0, spl_token_1.createSyncNativeInstruction)(wsolAta));
488
562
  }
@@ -493,7 +567,7 @@ class PumpTrader {
493
567
  if (!priorityOpt?.enableRandom || !priorityOpt.randomRange) {
494
568
  return priorityOpt.base;
495
569
  }
496
- return priorityOpt.base + Math.floor(Math.random() * priorityOpt.randomRange);
570
+ return (priorityOpt.base + Math.floor(Math.random() * priorityOpt.randomRange));
497
571
  }
498
572
  calcSlippage({ tradeSize, reserve, slippageOpt }) {
499
573
  const impact = Number(tradeSize) / Math.max(Number(reserve), 1);
@@ -531,26 +605,36 @@ class PumpTrader {
531
605
  /* ---------- 统一交易接口 ---------- */
532
606
  /**
533
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)
534
610
  */
535
- async autoBuy(tokenAddr, totalSolIn, tradeOpt) {
611
+ async autoBuy(tokenAddr, totalSolIn, tradeOpt, useV2 = false, quoteMint = SOL_MINT) {
536
612
  const mode = await this.getTradeMode(tokenAddr);
537
613
  if (mode === "bonding") {
614
+ if (useV2) {
615
+ return this.buyV2(tokenAddr, totalSolIn, tradeOpt, quoteMint);
616
+ }
538
617
  return this.buy(tokenAddr, totalSolIn, tradeOpt);
539
618
  }
540
619
  else {
541
- return this.ammBuy(tokenAddr, totalSolIn, tradeOpt);
620
+ return this.ammBuy(tokenAddr, totalSolIn, tradeOpt, quoteMint);
542
621
  }
543
622
  }
544
623
  /**
545
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)
546
627
  */
547
- async autoSell(tokenAddr, totalTokenIn, tradeOpt) {
628
+ async autoSell(tokenAddr, totalTokenIn, tradeOpt, useV2 = false, quoteMint = SOL_MINT) {
548
629
  const mode = await this.getTradeMode(tokenAddr);
549
630
  if (mode === "bonding") {
631
+ if (useV2) {
632
+ return this.sellV2(tokenAddr, totalTokenIn, tradeOpt, quoteMint);
633
+ }
550
634
  return this.sell(tokenAddr, totalTokenIn, tradeOpt);
551
635
  }
552
636
  else {
553
- return this.ammSell(tokenAddr, totalTokenIn, tradeOpt);
637
+ return this.ammSell(tokenAddr, totalTokenIn, tradeOpt, quoteMint);
554
638
  }
555
639
  }
556
640
  /* ---------- 内盘交易 ---------- */
@@ -569,7 +653,10 @@ class PumpTrader {
569
653
  const associatedBondingCurve = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, bonding, true, tokenProgram.programId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
570
654
  const [creatorVault] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("creator-vault"), creator.toBuffer()], PROGRAM_IDS.PUMP);
571
655
  const [globalVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("global_volume_accumulator")], PROGRAM_IDS.PUMP);
572
- 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);
573
660
  const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.FEE_CONFIG], PROGRAM_IDS.FEE);
574
661
  const feeRecipient = this.pickFeeRecipient();
575
662
  for (let i = 0; i < solChunks.length; i++) {
@@ -579,7 +666,7 @@ class PumpTrader {
579
666
  const slippageBps = this.calcSlippage({
580
667
  tradeSize: solIn,
581
668
  reserve: state.virtualSolReserves,
582
- slippageOpt: tradeOpt.slippage
669
+ slippageOpt: tradeOpt.slippage,
583
670
  });
584
671
  const maxSol = (solIn * BigInt(10_000 + slippageBps)) / 10000n;
585
672
  const priority = this.genPriority(tradeOpt.priority);
@@ -594,7 +681,7 @@ class PumpTrader {
594
681
  bonding,
595
682
  associatedBondingCurve,
596
683
  userAta,
597
- wallet: this.wallet.publicKey,
684
+ wallet: this.publicKey,
598
685
  creatorVault,
599
686
  eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
600
687
  pumpProgram: PROGRAM_IDS.PUMP,
@@ -604,28 +691,32 @@ class PumpTrader {
604
691
  feeProgram: PROGRAM_IDS.FEE,
605
692
  bondingCurveV2,
606
693
  feeRecipient,
607
- tokenProgramId: tokenProgram.programId
694
+ tokenProgramId: tokenProgram.programId,
608
695
  }),
609
- data: Buffer.concat([DISCRIMINATORS.BUY, u64(tokenOut), u64(maxSol)])
696
+ data: Buffer.concat([
697
+ DISCRIMINATORS.BUY,
698
+ u64(tokenOut),
699
+ u64(maxSol),
700
+ ]),
610
701
  }));
611
- const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash('finalized');
702
+ const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash("finalized");
612
703
  tx.recentBlockhash = blockhash;
613
- tx.feePayer = this.wallet.publicKey;
614
- tx.sign(this.wallet);
704
+ tx.feePayer = this.publicKey;
705
+ await this.signTx(tx);
615
706
  const signature = await this.connection.sendRawTransaction(tx.serialize(), {
616
707
  skipPreflight: false,
617
- maxRetries: 2
708
+ maxRetries: 2,
618
709
  });
619
710
  pendingTransactions.push({
620
711
  signature,
621
712
  lastValidBlockHeight,
622
- index: i
713
+ index: i,
623
714
  });
624
715
  }
625
716
  catch (e) {
626
717
  failedTransactions.push({
627
718
  index: i,
628
- error: e.message
719
+ error: e.message,
629
720
  });
630
721
  }
631
722
  }
@@ -647,10 +738,13 @@ class PumpTrader {
647
738
  const pendingTransactions = [];
648
739
  const failedTransactions = [];
649
740
  const associatedBondingCurve = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, bonding, true, tokenProgram.programId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
650
- 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);
651
742
  const [creatorVault] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("creator-vault"), creator.toBuffer()], PROGRAM_IDS.PUMP);
652
743
  const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.FEE_CONFIG], PROGRAM_IDS.FEE);
653
- 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);
654
748
  const feeRecipient = this.pickFeeRecipient();
655
749
  for (let i = 0; i < tokenChunks.length; i++) {
656
750
  try {
@@ -659,7 +753,7 @@ class PumpTrader {
659
753
  const slippageBps = this.calcSlippage({
660
754
  tradeSize: tokenIn,
661
755
  reserve: state.virtualTokenReserves,
662
- slippageOpt: tradeOpt.slippage
756
+ slippageOpt: tradeOpt.slippage,
663
757
  });
664
758
  const minSol = (solOut * BigInt(10_000 - slippageBps)) / 10000n;
665
759
  const priority = this.genPriority(tradeOpt.priority);
@@ -673,7 +767,7 @@ class PumpTrader {
673
767
  bonding,
674
768
  associatedBondingCurve,
675
769
  userAta,
676
- wallet: this.wallet.publicKey,
770
+ wallet: this.publicKey,
677
771
  creatorVault,
678
772
  eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
679
773
  pumpProgram: PROGRAM_IDS.PUMP,
@@ -683,41 +777,45 @@ class PumpTrader {
683
777
  feeRecipient,
684
778
  isCashbackCoin: !!state.isCashbackCoin,
685
779
  userVolumeAccumulator,
686
- tokenProgramId: tokenProgram.programId
780
+ tokenProgramId: tokenProgram.programId,
687
781
  }),
688
782
  data: Buffer.concat([
689
783
  DISCRIMINATORS.SELL,
690
784
  u64(tokenIn),
691
- u64(minSol > 0n ? minSol : 1n)
692
- ])
785
+ u64(minSol > 0n ? minSol : 1n),
786
+ ]),
693
787
  }));
694
788
  const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash("finalized");
695
789
  tx.recentBlockhash = blockhash;
696
- tx.feePayer = this.wallet.publicKey;
697
- tx.sign(this.wallet);
790
+ tx.feePayer = this.publicKey;
791
+ await this.signTx(tx);
698
792
  const signature = await this.connection.sendRawTransaction(tx.serialize());
699
793
  pendingTransactions.push({
700
794
  signature,
701
795
  lastValidBlockHeight,
702
- index: i
796
+ index: i,
703
797
  });
704
798
  }
705
799
  catch (e) {
706
800
  failedTransactions.push({
707
801
  index: i,
708
- error: e.message
802
+ error: e.message,
709
803
  });
710
804
  }
711
805
  }
712
806
  return { pendingTransactions, failedTransactions };
713
807
  }
714
808
  /* ---------- 外盘交易 ---------- */
715
- async ammBuy(tokenAddr, totalSolIn, tradeOpt) {
809
+ async ammBuy(tokenAddr, totalSolIn, tradeOpt, quoteMint = SOL_MINT) {
716
810
  const mint = new web3_js_1.PublicKey(tokenAddr);
717
- const poolInfo = await this.getAmmPoolInfo(mint);
811
+ const poolInfo = await this.getAmmPoolInfo(mint, quoteMint);
718
812
  const reserves = await this.getAmmPoolReserves(poolInfo.poolKeys);
719
813
  const solChunks = this.splitByMax(totalSolIn, tradeOpt.maxSolPerTx);
720
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);
721
819
  const pendingTransactions = [];
722
820
  const failedTransactions = [];
723
821
  for (let i = 0; i < solChunks.length; i++) {
@@ -727,45 +825,53 @@ class PumpTrader {
727
825
  const slippageBps = this.calcSlippage({
728
826
  tradeSize: solIn,
729
827
  reserve: reserves.quoteAmount,
730
- slippageOpt: tradeOpt.slippage
828
+ slippageOpt: tradeOpt.slippage,
731
829
  });
732
830
  const maxQuoteIn = (solIn * BigInt(10_000 + slippageBps)) / 10000n;
733
831
  const priority = this.genPriority(tradeOpt.priority);
734
832
  const tx = new web3_js_1.Transaction().add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }));
735
833
  const userBaseAta = await this.ensureAta(tx, poolInfo.poolKeys.baseMint, tokenProgram.programId);
736
- 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);
737
837
  const buyIx = this.createAmmBuyInstruction(poolInfo, userBaseAta, userQuoteAta, baseAmountOut, maxQuoteIn, tokenProgram.programId);
738
838
  tx.add(buyIx);
739
- tx.add((0, spl_token_1.createCloseAccountInstruction)(userQuoteAta, this.wallet.publicKey, this.wallet.publicKey));
740
- 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");
741
843
  tx.recentBlockhash = blockhash;
742
- tx.feePayer = this.wallet.publicKey;
743
- tx.sign(this.wallet);
844
+ tx.feePayer = this.publicKey;
845
+ await this.signTx(tx);
744
846
  const signature = await this.connection.sendRawTransaction(tx.serialize(), {
745
847
  skipPreflight: false,
746
- maxRetries: 2
848
+ maxRetries: 2,
747
849
  });
748
850
  pendingTransactions.push({
749
851
  signature,
750
852
  lastValidBlockHeight,
751
- index: i
853
+ index: i,
752
854
  });
753
855
  }
754
856
  catch (e) {
755
857
  failedTransactions.push({
756
858
  index: i,
757
- error: e.message
859
+ error: e.message,
758
860
  });
759
861
  }
760
862
  }
761
863
  return { pendingTransactions, failedTransactions };
762
864
  }
763
- async ammSell(tokenAddr, totalTokenIn, tradeOpt) {
865
+ async ammSell(tokenAddr, totalTokenIn, tradeOpt, quoteMint = SOL_MINT) {
764
866
  const mint = new web3_js_1.PublicKey(tokenAddr);
765
- const poolInfo = await this.getAmmPoolInfo(mint);
867
+ const poolInfo = await this.getAmmPoolInfo(mint, quoteMint);
766
868
  const reserves = await this.getAmmPoolReserves(poolInfo.poolKeys);
767
869
  const totalSolOut = this.calculateAmmSellOutput(totalTokenIn, reserves);
768
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);
769
875
  const tokenChunks = totalSolOut <= tradeOpt.maxSolPerTx
770
876
  ? [totalTokenIn]
771
877
  : this.splitIntoN(totalTokenIn, Number((totalSolOut + tradeOpt.maxSolPerTx - 1n) / tradeOpt.maxSolPerTx));
@@ -778,48 +884,52 @@ class PumpTrader {
778
884
  const slippageBps = this.calcSlippage({
779
885
  tradeSize: tokenIn,
780
886
  reserve: reserves.baseAmount,
781
- slippageOpt: tradeOpt.slippage
887
+ slippageOpt: tradeOpt.slippage,
782
888
  });
783
889
  const minQuoteOut = (solOut * BigInt(10_000 - slippageBps)) / 10000n;
784
890
  const priority = this.genPriority(tradeOpt.priority);
785
891
  const tx = new web3_js_1.Transaction().add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }));
786
892
  const userBaseAta = await this.ensureAta(tx, poolInfo.poolKeys.baseMint, tokenProgram.programId);
787
- 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);
788
896
  const sellIx = this.createAmmSellInstruction(poolInfo, userBaseAta, userQuoteAta, tokenIn, minQuoteOut, tokenProgram.programId);
789
897
  tx.add(sellIx);
790
- tx.add((0, spl_token_1.createCloseAccountInstruction)(userQuoteAta, this.wallet.publicKey, this.wallet.publicKey));
791
- 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");
792
902
  tx.recentBlockhash = blockhash;
793
- tx.feePayer = this.wallet.publicKey;
794
- tx.sign(this.wallet);
903
+ tx.feePayer = this.publicKey;
904
+ await this.signTx(tx);
795
905
  const signature = await this.connection.sendRawTransaction(tx.serialize(), {
796
906
  skipPreflight: false,
797
- maxRetries: 2
907
+ maxRetries: 2,
798
908
  });
799
909
  pendingTransactions.push({
800
910
  signature,
801
911
  lastValidBlockHeight,
802
- index: i
912
+ index: i,
803
913
  });
804
914
  }
805
915
  catch (e) {
806
916
  failedTransactions.push({
807
917
  index: i,
808
- error: e.message
918
+ error: e.message,
809
919
  });
810
920
  }
811
921
  }
812
922
  return { pendingTransactions, failedTransactions };
813
923
  }
814
924
  /* ---------- AMM 池信息 ---------- */
815
- async getAmmPoolInfo(mint) {
925
+ async getAmmPoolInfo(mint, quoteMint = SOL_MINT) {
816
926
  const [poolAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("pool-authority"), mint.toBuffer()], PROGRAM_IDS.PUMP);
817
927
  const [pool] = web3_js_1.PublicKey.findProgramAddressSync([
818
928
  Buffer.from("pool"),
819
929
  new bn_js_1.default(0).toArrayLike(Buffer, "le", 2),
820
930
  poolAuthority.toBuffer(),
821
931
  mint.toBuffer(),
822
- SOL_MINT.toBuffer()
932
+ quoteMint.toBuffer(),
823
933
  ], PROGRAM_IDS.PUMP_AMM);
824
934
  const acc = await this.connection.getAccountInfo(pool);
825
935
  if (!acc)
@@ -849,13 +959,13 @@ class PumpTrader {
849
959
  async getAmmPoolReserves(poolKeys) {
850
960
  const [baseInfo, quoteInfo] = await Promise.all([
851
961
  this.connection.getTokenAccountBalance(poolKeys.poolBaseTokenAccount),
852
- this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount)
962
+ this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
853
963
  ]);
854
964
  return {
855
965
  baseAmount: BigInt(baseInfo.value.amount),
856
966
  quoteAmount: BigInt(quoteInfo.value.amount),
857
967
  baseDecimals: baseInfo.value.decimals,
858
- quoteDecimals: quoteInfo.value.decimals
968
+ quoteDecimals: quoteInfo.value.decimals,
859
969
  };
860
970
  }
861
971
  deriveAmmPoolV2(baseMint) {
@@ -869,7 +979,10 @@ class PumpTrader {
869
979
  const [coinCreatorVaultAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("creator_vault"), poolKeys.coinCreator.toBuffer()], PROGRAM_IDS.PUMP_AMM);
870
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);
871
981
  const [globalVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("global_volume_accumulator")], PROGRAM_IDS.PUMP_AMM);
872
- 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);
873
986
  const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.AMM_FEE_CONFIG], PROGRAM_IDS.FEE);
874
987
  const protocolFeeRecipient = globalConfig.protocolFeeRecipients[0];
875
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);
@@ -878,44 +991,72 @@ class PumpTrader {
878
991
  const remainingKeys = [];
879
992
  if (poolKeys.isCashbackCoin) {
880
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);
881
- remainingKeys.push({ pubkey: userVolumeAccumulatorWsolAta, isSigner: false, isWritable: true });
994
+ remainingKeys.push({
995
+ pubkey: userVolumeAccumulatorWsolAta,
996
+ isSigner: false,
997
+ isWritable: true,
998
+ });
882
999
  }
883
1000
  remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
884
- 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
+ });
885
1006
  return new web3_js_1.TransactionInstruction({
886
1007
  programId: PROGRAM_IDS.PUMP_AMM,
887
1008
  keys: [
888
1009
  { pubkey: pool, isSigner: false, isWritable: true },
889
- { pubkey: this.wallet.publicKey, isSigner: true, isWritable: true },
1010
+ { pubkey: this.publicKey, isSigner: true, isWritable: true },
890
1011
  { pubkey: globalConfig.address, isSigner: false, isWritable: false },
891
1012
  { pubkey: poolKeys.baseMint, isSigner: false, isWritable: false },
892
1013
  { pubkey: poolKeys.quoteMint, isSigner: false, isWritable: false },
893
1014
  { pubkey: userBaseAta, isSigner: false, isWritable: true },
894
1015
  { pubkey: userQuoteAta, isSigner: false, isWritable: true },
895
- { pubkey: poolKeys.poolBaseTokenAccount, isSigner: false, isWritable: true },
896
- { 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
+ },
897
1026
  { pubkey: protocolFeeRecipient, isSigner: false, isWritable: false },
898
- { pubkey: protocolFeeRecipientTokenAccount, isSigner: false, isWritable: true },
1027
+ {
1028
+ pubkey: protocolFeeRecipientTokenAccount,
1029
+ isSigner: false,
1030
+ isWritable: true,
1031
+ },
899
1032
  { pubkey: tokenProgramId, isSigner: false, isWritable: false },
900
1033
  { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
901
1034
  { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
902
- { 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
+ },
903
1040
  { pubkey: eventAuthority, isSigner: false, isWritable: false },
904
1041
  { pubkey: PROGRAM_IDS.PUMP_AMM, isSigner: false, isWritable: false },
905
1042
  { pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true },
906
- { pubkey: coinCreatorVaultAuthority, isSigner: false, isWritable: false },
1043
+ {
1044
+ pubkey: coinCreatorVaultAuthority,
1045
+ isSigner: false,
1046
+ isWritable: false,
1047
+ },
907
1048
  { pubkey: globalVolumeAccumulator, isSigner: false, isWritable: false },
908
1049
  { pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
909
1050
  { pubkey: feeConfig, isSigner: false, isWritable: false },
910
1051
  { pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
911
- ...remainingKeys
1052
+ ...remainingKeys,
912
1053
  ],
913
1054
  data: Buffer.concat([
914
1055
  DISCRIMINATORS.BUY,
915
1056
  u64(baseAmountOut),
916
1057
  u64(maxQuoteAmountIn),
917
- Buffer.from([1, 1])
918
- ])
1058
+ Buffer.from([1, 1]),
1059
+ ]),
919
1060
  });
920
1061
  }
921
1062
  createAmmSellInstruction(poolInfo, userBaseAta, userQuoteAta, baseAmountIn, minQuoteAmountOut, tokenProgramId) {
@@ -929,76 +1070,515 @@ class PumpTrader {
929
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);
930
1071
  const newFeeRecipient = this.pickFeeRecipient();
931
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);
932
- 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);
933
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);
934
1078
  const remainingKeys = [];
935
1079
  if (poolKeys.isCashbackCoin) {
936
- 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 });
937
1085
  }
938
1086
  remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
939
- 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
+ });
940
1092
  return new web3_js_1.TransactionInstruction({
941
1093
  programId: PROGRAM_IDS.PUMP_AMM,
942
1094
  keys: [
943
1095
  { pubkey: pool, isSigner: false, isWritable: true },
944
- { pubkey: this.wallet.publicKey, isSigner: true, isWritable: true },
1096
+ { pubkey: this.publicKey, isSigner: true, isWritable: true },
945
1097
  { pubkey: globalConfig.address, isSigner: false, isWritable: false },
946
1098
  { pubkey: poolKeys.baseMint, isSigner: false, isWritable: false },
947
1099
  { pubkey: poolKeys.quoteMint, isSigner: false, isWritable: false },
948
1100
  { pubkey: userBaseAta, isSigner: false, isWritable: true },
949
1101
  { pubkey: userQuoteAta, isSigner: false, isWritable: true },
950
- { pubkey: poolKeys.poolBaseTokenAccount, isSigner: false, isWritable: true },
951
- { 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
+ },
952
1112
  { pubkey: protocolFeeRecipient, isSigner: false, isWritable: false },
953
- { pubkey: protocolFeeRecipientTokenAccount, isSigner: false, isWritable: true },
1113
+ {
1114
+ pubkey: protocolFeeRecipientTokenAccount,
1115
+ isSigner: false,
1116
+ isWritable: true,
1117
+ },
954
1118
  { pubkey: tokenProgramId, isSigner: false, isWritable: false },
955
1119
  { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
956
1120
  { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
957
- { 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
+ },
958
1126
  { pubkey: eventAuthority, isSigner: false, isWritable: false },
959
1127
  { pubkey: PROGRAM_IDS.PUMP_AMM, isSigner: false, isWritable: false },
960
1128
  { pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true },
961
- { pubkey: coinCreatorVaultAuthority, isSigner: false, isWritable: false },
1129
+ {
1130
+ pubkey: coinCreatorVaultAuthority,
1131
+ isSigner: false,
1132
+ isWritable: false,
1133
+ },
962
1134
  { pubkey: feeConfig, isSigner: false, isWritable: false },
963
1135
  { pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
964
- ...remainingKeys
1136
+ ...remainingKeys,
965
1137
  ],
966
1138
  data: Buffer.concat([
967
1139
  DISCRIMINATORS.SELL,
968
1140
  u64(baseAmountIn),
969
- u64(minQuoteAmountOut > 0n ? minQuoteAmountOut : 1n)
970
- ])
1141
+ u64(minQuoteAmountOut > 0n ? minQuoteAmountOut : 1n),
1142
+ ]),
971
1143
  });
972
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
+ }
973
1552
  /* ---------- 交易确认 ---------- */
974
1553
  async confirmTransactionWithPolling(signature, lastValidBlockHeight, maxAttempts = 5, delayMs = 2000) {
975
- console.log('✅ 交易已发送:', signature);
976
- console.log('🔗 查看交易: https://solscan.io/tx/' + signature);
1554
+ console.log("✅ 交易已发送:", signature);
1555
+ console.log("🔗 查看交易: https://solscan.io/tx/" + signature);
977
1556
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
978
- await new Promise(resolve => setTimeout(resolve, delayMs));
1557
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
979
1558
  try {
980
1559
  console.log(`🔍 检查交易状态 (${attempt}/${maxAttempts})...`);
981
1560
  const txInfo = await this.connection.getTransaction(signature, {
982
- commitment: 'confirmed',
983
- maxSupportedTransactionVersion: 0
1561
+ commitment: "confirmed",
1562
+ maxSupportedTransactionVersion: 0,
984
1563
  });
985
1564
  if (txInfo) {
986
1565
  if (txInfo.meta?.err) {
987
- console.error('❌ 交易失败:', txInfo.meta.err);
988
- throw new Error('交易失败: ' + JSON.stringify(txInfo.meta.err));
1566
+ console.error("❌ 交易失败:", txInfo.meta.err);
1567
+ throw new Error("交易失败: " + JSON.stringify(txInfo.meta.err));
989
1568
  }
990
- console.log('✅ 交易已确认!');
1569
+ console.log("✅ 交易已确认!");
991
1570
  return signature;
992
1571
  }
993
- const currentBlockHeight = await this.connection.getBlockHeight('finalized');
1572
+ const currentBlockHeight = await this.connection.getBlockHeight("finalized");
994
1573
  if (currentBlockHeight > lastValidBlockHeight) {
995
- console.log('⚠️ 交易已过期(超过有效区块高度)');
996
- throw new Error('交易过期:未在有效区块高度内确认');
1574
+ console.log("⚠️ 交易已过期(超过有效区块高度)");
1575
+ throw new Error("交易过期:未在有效区块高度内确认");
997
1576
  }
998
1577
  }
999
1578
  catch (error) {
1000
1579
  const err = error;
1001
- if (err.message?.includes('交易失败') || err.message?.includes('交易过期')) {
1580
+ if (err.message?.includes("交易失败") ||
1581
+ err.message?.includes("交易过期")) {
1002
1582
  throw error;
1003
1583
  }
1004
1584
  console.log(`⚠️ 查询出错,继续重试: ${err.message}`);
@@ -1035,7 +1615,7 @@ class PumpTrader {
1035
1615
  isBuy,
1036
1616
  user: user.toBase58(),
1037
1617
  timestamp,
1038
- signature: log.signature
1618
+ signature: log.signature,
1039
1619
  });
1040
1620
  }
1041
1621
  }, "confirmed");
@@ -1048,25 +1628,34 @@ class PumpTrader {
1048
1628
  return {
1049
1629
  name: metadata?.name || "",
1050
1630
  symbol: metadata?.symbol || "",
1051
- uri: metadata?.uri || ""
1631
+ uri: metadata?.uri || "",
1052
1632
  };
1053
1633
  }
1054
1634
  catch (e) {
1055
- 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];
1056
1640
  const acc = await this.connection.getAccountInfo(metadataPda);
1057
1641
  if (!acc)
1058
1642
  return null;
1059
1643
  const meta = parseMetadataAccount(acc.data);
1060
1644
  return {
1061
- name: meta?.name?.replace(/\u0000/g, '') || "",
1062
- symbol: meta?.symbol?.replace(/\u0000/g, '') || "",
1063
- 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, "") || "",
1064
1648
  };
1065
1649
  }
1066
1650
  }
1067
- // 公开wallet方法
1651
+ /**
1652
+ * 获取原始 wallet 对象(Keypair 或前端 WalletAdapter)
1653
+ */
1068
1654
  getWallet() {
1069
- return this.wallet;
1655
+ return this._wallet;
1656
+ }
1657
+ getPublicKey() {
1658
+ return this.publicKey;
1070
1659
  }
1071
1660
  getConnection() {
1072
1661
  return this.connection;