pump-trader 1.1.3 → 1.1.4

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.d.ts CHANGED
@@ -104,6 +104,9 @@ export declare class PumpTrader {
104
104
  loadGlobal(): Promise<GlobalState>;
105
105
  getBondingPda(mint: PublicKey): PublicKey;
106
106
  deriveBondingCurveV2(mint: PublicKey): PublicKey;
107
+ private pickFeeRecipient;
108
+ private buildBondingBuyKeys;
109
+ private buildBondingSellKeys;
107
110
  loadBonding(mint: PublicKey): Promise<BondingInfo>;
108
111
  calcBuy(solIn: bigint, state: BondingCurveState): bigint;
109
112
  calcSell(tokenIn: bigint, state: BondingCurveState): bigint;
package/dist/index.js CHANGED
@@ -36,6 +36,16 @@ const DISCRIMINATORS = {
36
36
  };
37
37
  const AMM_FEE_BPS = 100n;
38
38
  const BPS_DENOMINATOR = 10000n;
39
+ const PUMP_NEW_FEE_RECIPIENTS = [
40
+ "5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD",
41
+ "9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7",
42
+ "GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL",
43
+ "3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR",
44
+ "5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6",
45
+ "EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL",
46
+ "5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD",
47
+ "A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"
48
+ ].map((value) => new web3_js_1.PublicKey(value));
39
49
  /* ================= 工具函数 ================= */
40
50
  const u64 = (v) => {
41
51
  const bn = typeof v === 'bigint' ? new bn_js_1.default(v.toString()) : new bn_js_1.default(v.toString());
@@ -227,6 +237,56 @@ class PumpTrader {
227
237
  deriveBondingCurveV2(mint) {
228
238
  return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("bonding-curve-v2"), mint.toBuffer()], PROGRAM_IDS.PUMP)[0];
229
239
  }
240
+ pickFeeRecipient(index = 0) {
241
+ return PUMP_NEW_FEE_RECIPIENTS[index % PUMP_NEW_FEE_RECIPIENTS.length];
242
+ }
243
+ buildBondingBuyKeys(args) {
244
+ const tokenProgramId = args.tokenProgramId ?? spl_token_1.TOKEN_PROGRAM_ID;
245
+ return [
246
+ { pubkey: args.global, isSigner: false, isWritable: false },
247
+ { pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
248
+ { pubkey: args.mint, isSigner: false, isWritable: false },
249
+ { pubkey: args.bonding, isSigner: false, isWritable: true },
250
+ { pubkey: args.associatedBondingCurve, isSigner: false, isWritable: true },
251
+ { pubkey: args.userAta, isSigner: false, isWritable: true },
252
+ { pubkey: args.wallet, isSigner: true, isWritable: true },
253
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
254
+ { pubkey: tokenProgramId, isSigner: false, isWritable: false },
255
+ { pubkey: args.creatorVault, isSigner: false, isWritable: true },
256
+ { pubkey: args.eventAuthority, isSigner: false, isWritable: false },
257
+ { pubkey: args.pumpProgram, isSigner: false, isWritable: false },
258
+ { pubkey: args.globalVolumeAccumulator, isSigner: false, isWritable: false },
259
+ { pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true },
260
+ { pubkey: args.feeConfig, isSigner: false, isWritable: false },
261
+ { pubkey: args.feeProgram, isSigner: false, isWritable: false },
262
+ { pubkey: args.bondingCurveV2, isSigner: false, isWritable: false },
263
+ { pubkey: args.feeRecipient, isSigner: false, isWritable: true }
264
+ ];
265
+ }
266
+ buildBondingSellKeys(args) {
267
+ const tokenProgramId = args.tokenProgramId ?? spl_token_1.TOKEN_PROGRAM_ID;
268
+ const keys = [
269
+ { pubkey: args.global, isSigner: false, isWritable: false },
270
+ { pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
271
+ { pubkey: args.mint, isSigner: false, isWritable: false },
272
+ { pubkey: args.bonding, isSigner: false, isWritable: true },
273
+ { pubkey: args.associatedBondingCurve, isSigner: false, isWritable: true },
274
+ { pubkey: args.userAta, isSigner: false, isWritable: true },
275
+ { pubkey: args.wallet, isSigner: true, isWritable: true },
276
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
277
+ { pubkey: args.creatorVault, isSigner: false, isWritable: true },
278
+ { pubkey: tokenProgramId, isSigner: false, isWritable: false },
279
+ { pubkey: args.eventAuthority, isSigner: false, isWritable: false },
280
+ { pubkey: args.pumpProgram, isSigner: false, isWritable: false },
281
+ { pubkey: args.feeConfig, isSigner: false, isWritable: false },
282
+ { pubkey: args.feeProgram, isSigner: false, isWritable: false }
283
+ ];
284
+ if (args.isCashbackCoin) {
285
+ keys.push({ pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true });
286
+ }
287
+ keys.push({ pubkey: args.bondingCurveV2, isSigner: false, isWritable: false }, { pubkey: args.feeRecipient, isSigner: false, isWritable: true });
288
+ return keys;
289
+ }
230
290
  async loadBonding(mint) {
231
291
  const bonding = this.getBondingPda(mint);
232
292
  const acc = await this.connection.getAccountInfo(bonding);
@@ -488,6 +548,7 @@ class PumpTrader {
488
548
  const [globalVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("global_volume_accumulator")], PROGRAM_IDS.PUMP);
489
549
  const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()], PROGRAM_IDS.PUMP);
490
550
  const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.FEE_CONFIG], PROGRAM_IDS.FEE);
551
+ const feeRecipient = this.pickFeeRecipient();
491
552
  for (let i = 0; i < solChunks.length; i++) {
492
553
  try {
493
554
  const solIn = solChunks[i];
@@ -503,25 +564,25 @@ class PumpTrader {
503
564
  const userAta = await this.ensureAta(tx, mint, tokenProgram.programId);
504
565
  tx.add(new web3_js_1.TransactionInstruction({
505
566
  programId: PROGRAM_IDS.PUMP,
506
- keys: [
507
- { pubkey: this.global, isSigner: false, isWritable: false },
508
- { pubkey: this.globalState.feeRecipient, isSigner: false, isWritable: true },
509
- { pubkey: mint, isSigner: false, isWritable: false },
510
- { pubkey: bonding, isSigner: false, isWritable: true },
511
- { pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
512
- { pubkey: userAta, isSigner: false, isWritable: true },
513
- { pubkey: this.wallet.publicKey, isSigner: true, isWritable: true },
514
- { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
515
- { pubkey: tokenProgram.programId, isSigner: false, isWritable: false },
516
- { pubkey: creatorVault, isSigner: false, isWritable: true },
517
- { pubkey: PROGRAM_IDS.EVENT_AUTHORITY, isSigner: false, isWritable: false },
518
- { pubkey: PROGRAM_IDS.PUMP, isSigner: false, isWritable: false },
519
- { pubkey: globalVolumeAccumulator, isSigner: false, isWritable: false },
520
- { pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
521
- { pubkey: feeConfig, isSigner: false, isWritable: false },
522
- { pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
523
- { pubkey: bondingCurveV2, isSigner: false, isWritable: false }
524
- ],
567
+ keys: this.buildBondingBuyKeys({
568
+ global: this.global,
569
+ globalFeeRecipient: this.globalState.feeRecipient,
570
+ mint,
571
+ bonding,
572
+ associatedBondingCurve,
573
+ userAta,
574
+ wallet: this.wallet.publicKey,
575
+ creatorVault,
576
+ eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
577
+ pumpProgram: PROGRAM_IDS.PUMP,
578
+ globalVolumeAccumulator,
579
+ userVolumeAccumulator,
580
+ feeConfig,
581
+ feeProgram: PROGRAM_IDS.FEE,
582
+ bondingCurveV2,
583
+ feeRecipient,
584
+ tokenProgramId: tokenProgram.programId
585
+ }),
525
586
  data: Buffer.concat([DISCRIMINATORS.BUY, u64(tokenOut), u64(maxSol)])
526
587
  }));
527
588
  const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash('finalized');
@@ -567,11 +628,7 @@ class PumpTrader {
567
628
  const [creatorVault] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("creator-vault"), creator.toBuffer()], PROGRAM_IDS.PUMP);
568
629
  const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.FEE_CONFIG], PROGRAM_IDS.FEE);
569
630
  const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()], PROGRAM_IDS.PUMP);
570
- const sellRemainingKeys = [];
571
- if (state.isCashbackCoin) {
572
- sellRemainingKeys.push({ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true });
573
- }
574
- sellRemainingKeys.push({ pubkey: bondingCurveV2, isSigner: false, isWritable: false });
631
+ const feeRecipient = this.pickFeeRecipient();
575
632
  for (let i = 0; i < tokenChunks.length; i++) {
576
633
  try {
577
634
  const tokenIn = tokenChunks[i];
@@ -586,23 +643,25 @@ class PumpTrader {
586
643
  const tx = new web3_js_1.Transaction().add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }));
587
644
  tx.add(new web3_js_1.TransactionInstruction({
588
645
  programId: PROGRAM_IDS.PUMP,
589
- keys: [
590
- { pubkey: this.global, isSigner: false, isWritable: false },
591
- { pubkey: this.globalState.feeRecipient, isSigner: false, isWritable: true },
592
- { pubkey: mint, isSigner: false, isWritable: false },
593
- { pubkey: bonding, isSigner: false, isWritable: true },
594
- { pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
595
- { pubkey: userAta, isSigner: false, isWritable: true },
596
- { pubkey: this.wallet.publicKey, isSigner: true, isWritable: true },
597
- { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
598
- { pubkey: creatorVault, isSigner: false, isWritable: true },
599
- { pubkey: tokenProgram.programId, isSigner: false, isWritable: false },
600
- { pubkey: PROGRAM_IDS.EVENT_AUTHORITY, isSigner: false, isWritable: false },
601
- { pubkey: PROGRAM_IDS.PUMP, isSigner: false, isWritable: false },
602
- { pubkey: feeConfig, isSigner: false, isWritable: false },
603
- { pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
604
- ...sellRemainingKeys
605
- ],
646
+ keys: this.buildBondingSellKeys({
647
+ global: this.global,
648
+ globalFeeRecipient: this.globalState.feeRecipient,
649
+ mint,
650
+ bonding,
651
+ associatedBondingCurve,
652
+ userAta,
653
+ wallet: this.wallet.publicKey,
654
+ creatorVault,
655
+ eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
656
+ pumpProgram: PROGRAM_IDS.PUMP,
657
+ feeConfig,
658
+ feeProgram: PROGRAM_IDS.FEE,
659
+ bondingCurveV2,
660
+ feeRecipient,
661
+ isCashbackCoin: !!state.isCashbackCoin,
662
+ userVolumeAccumulator,
663
+ tokenProgramId: tokenProgram.programId
664
+ }),
606
665
  data: Buffer.concat([
607
666
  DISCRIMINATORS.SELL,
608
667
  u64(tokenIn),
@@ -791,12 +850,15 @@ class PumpTrader {
791
850
  const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.AMM_FEE_CONFIG], PROGRAM_IDS.FEE);
792
851
  const protocolFeeRecipient = globalConfig.protocolFeeRecipients[0];
793
852
  const protocolFeeRecipientTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(SOL_MINT, protocolFeeRecipient, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
853
+ const newFeeRecipient = this.pickFeeRecipient();
854
+ const newFeeRecipientTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(poolKeys.quoteMint, newFeeRecipient, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
794
855
  const remainingKeys = [];
795
856
  if (poolKeys.isCashbackCoin) {
796
857
  const userVolumeAccumulatorWsolAta = (0, spl_token_1.getAssociatedTokenAddressSync)(SOL_MINT, userVolumeAccumulator, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
797
858
  remainingKeys.push({ pubkey: userVolumeAccumulatorWsolAta, isSigner: false, isWritable: true });
798
859
  }
799
860
  remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
861
+ remainingKeys.push({ pubkey: newFeeRecipient, isSigner: false, isWritable: false }, { pubkey: newFeeRecipientTokenAccount, isSigner: false, isWritable: true });
800
862
  return new web3_js_1.TransactionInstruction({
801
863
  programId: PROGRAM_IDS.PUMP_AMM,
802
864
  keys: [
@@ -842,6 +904,8 @@ class PumpTrader {
842
904
  const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.AMM_FEE_CONFIG], PROGRAM_IDS.FEE);
843
905
  const protocolFeeRecipient = globalConfig.protocolFeeRecipients[0];
844
906
  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
+ const newFeeRecipient = this.pickFeeRecipient();
908
+ const newFeeRecipientTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(poolKeys.quoteMint, newFeeRecipient, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
845
909
  const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()], PROGRAM_IDS.PUMP_AMM);
846
910
  const userVolumeAccumulatorWsolAta = (0, spl_token_1.getAssociatedTokenAddressSync)(SOL_MINT, userVolumeAccumulator, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
847
911
  const remainingKeys = [];
@@ -849,6 +913,7 @@ class PumpTrader {
849
913
  remainingKeys.push({ pubkey: userVolumeAccumulatorWsolAta, isSigner: false, isWritable: true }, { pubkey: userVolumeAccumulator, isSigner: false, isWritable: true });
850
914
  }
851
915
  remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
916
+ remainingKeys.push({ pubkey: newFeeRecipient, isSigner: false, isWritable: false }, { pubkey: newFeeRecipientTokenAccount, isSigner: false, isWritable: true });
852
917
  return new web3_js_1.TransactionInstruction({
853
918
  programId: PROGRAM_IDS.PUMP_AMM,
854
919
  keys: [
@@ -0,0 +1,34 @@
1
+ # Pump Fee Recipient Upgrade Design
2
+
3
+ **Context**
4
+
5
+ `pump-trader@1.1.3` currently hardcodes Pump bonding and AMM instruction account layouts that matched the pre-upgrade programs. Pump announced a breaking account layout change effective on April 28, 2026 at 16:00 UTC. After that upgrade, the existing instruction builders send the wrong account count and wrong trailing account order.
6
+
7
+ **Design**
8
+
9
+ Use a compatibility patch inside the existing handwritten instruction builders instead of migrating the project to the official SDKs. The patch adds the 8 published fee recipient addresses as constants, centralizes fee recipient selection behind a small helper, and updates the trailing account order for both bonding and AMM flows.
10
+
11
+ For bonding:
12
+ - append one mutable fee recipient after `bonding-curve-v2`
13
+ - keep all existing accounts before `bonding-curve-v2` unchanged
14
+
15
+ For AMM:
16
+ - keep existing accounts through `pool-v2`
17
+ - move the fee recipient pair to the end
18
+ - pass fee recipient as readonly
19
+ - pass quote mint ATA for that recipient as mutable
20
+
21
+ **Why This Approach**
22
+
23
+ This is the smallest change that restores runtime compatibility while preserving the library's current API and handwritten transaction flow. It also gives one future extension point for recipient selection without forcing an SDK dependency or wider refactor.
24
+
25
+ **Validation**
26
+
27
+ Add regression tests that assert instruction key count and tail ordering for:
28
+ - bonding buy
29
+ - bonding sell without cashback
30
+ - bonding sell with cashback
31
+ - AMM buy without cashback
32
+ - AMM buy with cashback
33
+ - AMM sell without cashback
34
+ - AMM sell with cashback
@@ -0,0 +1,143 @@
1
+ # Pump Fee Recipient Upgrade Implementation Plan
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
+
5
+ **Goal:** Update `pump-trader@1.1.3` so Pump bonding and AMM instructions remain compatible with the April 28, 2026 fee recipient account layout upgrade.
6
+
7
+ **Architecture:** Keep the existing handwritten instruction builders, add a centralized fee recipient selector, and rebuild the bonding and AMM account arrays so their tail order matches the upgraded programs. Use narrow regression tests that assert exact key ordering and account counts rather than broad network integration tests.
8
+
9
+ **Tech Stack:** TypeScript, Node.js built-in test runner, `tsx`, `@solana/web3.js`, `@solana/spl-token`
10
+
11
+ ---
12
+
13
+ ### Task 1: Add regression tests for upgraded account layouts
14
+
15
+ **Files:**
16
+ - Modify: `package.json`
17
+ - Create: `tests/instruction-accounts.test.ts`
18
+
19
+ **Step 1: Write the failing test**
20
+
21
+ Create tests that expect:
22
+ - bonding buy has 18 keys and the final two are `bondingCurveV2`, fee recipient
23
+ - bonding sell has 16 keys without cashback and 17 with cashback, with fee recipient last
24
+ - AMM buy has 26 keys without cashback and 27 with cashback, with `poolV2`, fee recipient, fee recipient quote ATA at the tail
25
+ - AMM sell has 24 keys without cashback and 26 with cashback, with the same tail order
26
+
27
+ **Step 2: Run test to verify it fails**
28
+
29
+ Run: `npm test`
30
+ Expected: FAIL because the new helper methods or expected key layout do not yet exist.
31
+
32
+ **Step 3: Write minimal implementation**
33
+
34
+ Add helper methods in `index.ts` that build the account arrays and are reused by the existing transaction builders.
35
+
36
+ **Step 4: Run test to verify it passes**
37
+
38
+ Run: `npm test`
39
+ Expected: PASS
40
+
41
+ **Step 5: Commit**
42
+
43
+ ```bash
44
+ git add package.json tests/instruction-accounts.test.ts index.ts index.js dist docs/plans
45
+ git commit -m "fix: support upgraded pump fee recipient accounts"
46
+ ```
47
+
48
+ ### Task 2: Update bonding instruction account assembly
49
+
50
+ **Files:**
51
+ - Modify: `index.ts`
52
+ - Modify: `index.js`
53
+ - Modify: `dist/index.js`
54
+ - Modify: `dist/index.d.ts`
55
+
56
+ **Step 1: Write the failing test**
57
+
58
+ Use the bonding tests from Task 1 as the failing coverage.
59
+
60
+ **Step 2: Run test to verify it fails**
61
+
62
+ Run: `npm test -- --test-name-pattern bonding`
63
+ Expected: FAIL because bonding key count and fee recipient tail order are still old.
64
+
65
+ **Step 3: Write minimal implementation**
66
+
67
+ Add the 8 fee recipient constants, selector helper, bonding key builders, and switch `buy()` / `sell()` to use them.
68
+
69
+ **Step 4: Run test to verify it passes**
70
+
71
+ Run: `npm test -- --test-name-pattern bonding`
72
+ Expected: PASS
73
+
74
+ **Step 5: Commit**
75
+
76
+ ```bash
77
+ git add index.ts index.js dist
78
+ git commit -m "fix: update bonding fee recipient accounts"
79
+ ```
80
+
81
+ ### Task 3: Update AMM instruction account assembly
82
+
83
+ **Files:**
84
+ - Modify: `index.ts`
85
+ - Modify: `index.js`
86
+ - Modify: `dist/index.js`
87
+ - Modify: `dist/index.d.ts`
88
+
89
+ **Step 1: Write the failing test**
90
+
91
+ Use the AMM tests from Task 1 as the failing coverage.
92
+
93
+ **Step 2: Run test to verify it fails**
94
+
95
+ Run: `npm test -- --test-name-pattern amm`
96
+ Expected: FAIL because the fee recipient pair is still placed before `pool-v2`.
97
+
98
+ **Step 3: Write minimal implementation**
99
+
100
+ Make AMM key assembly place `pool-v2` before the new trailing fee recipient pair for both buy and sell, preserving cashback-only extras.
101
+
102
+ **Step 4: Run test to verify it passes**
103
+
104
+ Run: `npm test -- --test-name-pattern amm`
105
+ Expected: PASS
106
+
107
+ **Step 5: Commit**
108
+
109
+ ```bash
110
+ git add index.ts index.js dist
111
+ git commit -m "fix: update amm fee recipient accounts"
112
+ ```
113
+
114
+ ### Task 4: Build and verify distributable output
115
+
116
+ **Files:**
117
+ - Modify: `dist/index.js`
118
+ - Modify: `dist/index.d.ts`
119
+
120
+ **Step 1: Write the failing test**
121
+
122
+ Use the build command as the verification target for generated output drift.
123
+
124
+ **Step 2: Run test to verify it fails**
125
+
126
+ Run: `npm run build`
127
+ Expected: PASS after source changes, with generated `dist` updated.
128
+
129
+ **Step 3: Write minimal implementation**
130
+
131
+ No extra implementation beyond generating `dist`.
132
+
133
+ **Step 4: Run test to verify it passes**
134
+
135
+ Run: `npm test && npm run build`
136
+ Expected: PASS
137
+
138
+ **Step 5: Commit**
139
+
140
+ ```bash
141
+ git add dist
142
+ git commit -m "build: refresh distribution output"
143
+ ```
package/index.js CHANGED
@@ -56,6 +56,16 @@ const DISCRIMINATORS = {
56
56
 
57
57
  const AMM_FEE_BPS = 100n; // 1%
58
58
  const BPS_DENOMINATOR = 10000n;
59
+ const PUMP_NEW_FEE_RECIPIENTS = [
60
+ "5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD",
61
+ "9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7",
62
+ "GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL",
63
+ "3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR",
64
+ "5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6",
65
+ "EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL",
66
+ "5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD",
67
+ "A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"
68
+ ].map((value) => new PublicKey(value));
59
69
 
60
70
  /* ================= 工具函数 ================= */
61
71
 
@@ -294,6 +304,66 @@ export class PumpTrader {
294
304
  )[0];
295
305
  }
296
306
 
307
+ pickFeeRecipient(index = 0) {
308
+ return PUMP_NEW_FEE_RECIPIENTS[index % PUMP_NEW_FEE_RECIPIENTS.length];
309
+ }
310
+
311
+ buildBondingBuyKeys(args) {
312
+ const tokenProgramId = args.tokenProgramId ?? TOKEN_PROGRAM_ID;
313
+
314
+ return [
315
+ { pubkey: args.global, isSigner: false, isWritable: false },
316
+ { pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
317
+ { pubkey: args.mint, isSigner: false, isWritable: false },
318
+ { pubkey: args.bonding, isSigner: false, isWritable: true },
319
+ { pubkey: args.associatedBondingCurve, isSigner: false, isWritable: true },
320
+ { pubkey: args.userAta, isSigner: false, isWritable: true },
321
+ { pubkey: args.wallet, isSigner: true, isWritable: true },
322
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
323
+ { pubkey: tokenProgramId, isSigner: false, isWritable: false },
324
+ { pubkey: args.creatorVault, isSigner: false, isWritable: true },
325
+ { pubkey: args.eventAuthority, isSigner: false, isWritable: false },
326
+ { pubkey: args.pumpProgram, isSigner: false, isWritable: false },
327
+ { pubkey: args.globalVolumeAccumulator, isSigner: false, isWritable: false },
328
+ { pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true },
329
+ { pubkey: args.feeConfig, isSigner: false, isWritable: false },
330
+ { pubkey: args.feeProgram, isSigner: false, isWritable: false },
331
+ { pubkey: args.bondingCurveV2, isSigner: false, isWritable: false },
332
+ { pubkey: args.feeRecipient, isSigner: false, isWritable: true }
333
+ ];
334
+ }
335
+
336
+ buildBondingSellKeys(args) {
337
+ const tokenProgramId = args.tokenProgramId ?? TOKEN_PROGRAM_ID;
338
+ const keys = [
339
+ { pubkey: args.global, isSigner: false, isWritable: false },
340
+ { pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
341
+ { pubkey: args.mint, isSigner: false, isWritable: false },
342
+ { pubkey: args.bonding, isSigner: false, isWritable: true },
343
+ { pubkey: args.associatedBondingCurve, isSigner: false, isWritable: true },
344
+ { pubkey: args.userAta, isSigner: false, isWritable: true },
345
+ { pubkey: args.wallet, isSigner: true, isWritable: true },
346
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
347
+ { pubkey: args.creatorVault, isSigner: false, isWritable: true },
348
+ { pubkey: tokenProgramId, isSigner: false, isWritable: false },
349
+ { pubkey: args.eventAuthority, isSigner: false, isWritable: false },
350
+ { pubkey: args.pumpProgram, isSigner: false, isWritable: false },
351
+ { pubkey: args.feeConfig, isSigner: false, isWritable: false },
352
+ { pubkey: args.feeProgram, isSigner: false, isWritable: false }
353
+ ];
354
+
355
+ if (args.isCashbackCoin) {
356
+ keys.push({ pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true });
357
+ }
358
+
359
+ keys.push(
360
+ { pubkey: args.bondingCurveV2, isSigner: false, isWritable: false },
361
+ { pubkey: args.feeRecipient, isSigner: false, isWritable: true }
362
+ );
363
+
364
+ return keys;
365
+ }
366
+
297
367
  async loadBonding(mint) {
298
368
  const bonding = this.getBondingPda(mint);
299
369
  const acc = await this.connection.getAccountInfo(bonding);
@@ -667,17 +737,7 @@ export class PumpTrader {
667
737
  [Buffer.from("fee_config"), SEEDS.FEE_CONFIG],
668
738
  PROGRAM_IDS.FEE
669
739
  );
670
-
671
- const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
672
- [Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()],
673
- PROGRAM_IDS.PUMP
674
- );
675
-
676
- const sellRemainingKeys = [];
677
- if (state.isCashbackCoin) {
678
- sellRemainingKeys.push({ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true });
679
- }
680
- sellRemainingKeys.push({ pubkey: bondingCurveV2, isSigner: false, isWritable: false });
740
+ const feeRecipient = this.pickFeeRecipient();
681
741
 
682
742
  for (let i = 0; i < solChunks.length; i++) {
683
743
  try {
@@ -701,25 +761,25 @@ export class PumpTrader {
701
761
  tx.add(
702
762
  new TransactionInstruction({
703
763
  programId: PROGRAM_IDS.PUMP,
704
- keys: [
705
- { pubkey: this.global, isSigner: false, isWritable: false },
706
- { pubkey: this.globalState.feeRecipient, isSigner: false, isWritable: true },
707
- { pubkey: mint, isSigner: false, isWritable: false },
708
- { pubkey: bonding, isSigner: false, isWritable: true },
709
- { pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
710
- { pubkey: userAta, isSigner: false, isWritable: true },
711
- { pubkey: this.wallet.publicKey, isSigner: true, isWritable: true },
712
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
713
- { pubkey: tokenProgram.programId, isSigner: false, isWritable: false },
714
- { pubkey: creatorVault, isSigner: false, isWritable: true },
715
- { pubkey: PROGRAM_IDS.EVENT_AUTHORITY, isSigner: false, isWritable: false },
716
- { pubkey: PROGRAM_IDS.PUMP, isSigner: false, isWritable: false },
717
- { pubkey: globalVolumeAccumulator, isSigner: false, isWritable: false },
718
- { pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
719
- { pubkey: feeConfig, isSigner: false, isWritable: false },
720
- { pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
721
- { pubkey: bondingCurveV2, isSigner: false, isWritable: false }
722
- ],
764
+ keys: this.buildBondingBuyKeys({
765
+ global: this.global,
766
+ globalFeeRecipient: this.globalState.feeRecipient,
767
+ mint,
768
+ bonding,
769
+ associatedBondingCurve,
770
+ userAta,
771
+ wallet: this.wallet.publicKey,
772
+ creatorVault,
773
+ eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
774
+ pumpProgram: PROGRAM_IDS.PUMP,
775
+ globalVolumeAccumulator,
776
+ userVolumeAccumulator,
777
+ feeConfig,
778
+ feeProgram: PROGRAM_IDS.FEE,
779
+ bondingCurveV2,
780
+ feeRecipient,
781
+ tokenProgramId: tokenProgram.programId
782
+ }),
723
783
  data: Buffer.concat([DISCRIMINATORS.BUY, u64(tokenOut), u64(maxSol)])
724
784
  })
725
785
  );
@@ -797,6 +857,11 @@ export class PumpTrader {
797
857
  [Buffer.from("fee_config"), SEEDS.FEE_CONFIG],
798
858
  PROGRAM_IDS.FEE
799
859
  );
860
+ const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
861
+ [Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()],
862
+ PROGRAM_IDS.PUMP
863
+ );
864
+ const feeRecipient = this.pickFeeRecipient();
800
865
 
801
866
  for (let i = 0; i < tokenChunks.length; i++) {
802
867
  try {
@@ -818,23 +883,25 @@ export class PumpTrader {
818
883
  tx.add(
819
884
  new TransactionInstruction({
820
885
  programId: PROGRAM_IDS.PUMP,
821
- keys: [
822
- { pubkey: this.global, isSigner: false, isWritable: false },
823
- { pubkey: this.globalState.feeRecipient, isSigner: false, isWritable: true },
824
- { pubkey: mint, isSigner: false, isWritable: false },
825
- { pubkey: bonding, isSigner: false, isWritable: true },
826
- { pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
827
- { pubkey: userAta, isSigner: false, isWritable: true },
828
- { pubkey: this.wallet.publicKey, isSigner: true, isWritable: true },
829
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
830
- { pubkey: creatorVault, isSigner: false, isWritable: true },
831
- { pubkey: tokenProgram.programId, isSigner: false, isWritable: false },
832
- { pubkey: PROGRAM_IDS.EVENT_AUTHORITY, isSigner: false, isWritable: false },
833
- { pubkey: PROGRAM_IDS.PUMP, isSigner: false, isWritable: false },
834
- { pubkey: feeConfig, isSigner: false, isWritable: false },
835
- { pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
836
- ...sellRemainingKeys
837
- ],
886
+ keys: this.buildBondingSellKeys({
887
+ global: this.global,
888
+ globalFeeRecipient: this.globalState.feeRecipient,
889
+ mint,
890
+ bonding,
891
+ associatedBondingCurve,
892
+ userAta,
893
+ wallet: this.wallet.publicKey,
894
+ creatorVault,
895
+ eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
896
+ pumpProgram: PROGRAM_IDS.PUMP,
897
+ feeConfig,
898
+ feeProgram: PROGRAM_IDS.FEE,
899
+ bondingCurveV2,
900
+ feeRecipient,
901
+ isCashbackCoin: !!state.isCashbackCoin,
902
+ userVolumeAccumulator,
903
+ tokenProgramId: tokenProgram.programId
904
+ }),
838
905
  data: Buffer.concat([
839
906
  DISCRIMINATORS.SELL,
840
907
  u64(tokenIn),
@@ -1153,6 +1220,14 @@ export class PumpTrader {
1153
1220
  TOKEN_PROGRAM_ID,
1154
1221
  ASSOCIATED_TOKEN_PROGRAM_ID
1155
1222
  );
1223
+ const newFeeRecipient = this.pickFeeRecipient();
1224
+ const newFeeRecipientTokenAccount = getAssociatedTokenAddressSync(
1225
+ poolKeys.quoteMint,
1226
+ newFeeRecipient,
1227
+ true,
1228
+ TOKEN_PROGRAM_ID,
1229
+ ASSOCIATED_TOKEN_PROGRAM_ID
1230
+ );
1156
1231
 
1157
1232
  const remainingKeys = [];
1158
1233
  if (poolKeys.isCashbackCoin) {
@@ -1166,6 +1241,10 @@ export class PumpTrader {
1166
1241
  remainingKeys.push({ pubkey: userVolumeAccumulatorWsolAta, isSigner: false, isWritable: true });
1167
1242
  }
1168
1243
  remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
1244
+ remainingKeys.push(
1245
+ { pubkey: newFeeRecipient, isSigner: false, isWritable: false },
1246
+ { pubkey: newFeeRecipientTokenAccount, isSigner: false, isWritable: true }
1247
+ );
1169
1248
 
1170
1249
  return new TransactionInstruction({
1171
1250
  programId: PROGRAM_IDS.PUMP_AMM,
@@ -1239,6 +1318,14 @@ export class PumpTrader {
1239
1318
  TOKEN_PROGRAM_ID,
1240
1319
  ASSOCIATED_TOKEN_PROGRAM_ID
1241
1320
  );
1321
+ const newFeeRecipient = this.pickFeeRecipient();
1322
+ const newFeeRecipientTokenAccount = getAssociatedTokenAddressSync(
1323
+ poolKeys.quoteMint,
1324
+ newFeeRecipient,
1325
+ true,
1326
+ TOKEN_PROGRAM_ID,
1327
+ ASSOCIATED_TOKEN_PROGRAM_ID
1328
+ );
1242
1329
 
1243
1330
  const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
1244
1331
  [Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()],
@@ -1261,6 +1348,10 @@ export class PumpTrader {
1261
1348
  );
1262
1349
  }
1263
1350
  remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
1351
+ remainingKeys.push(
1352
+ { pubkey: newFeeRecipient, isSigner: false, isWritable: false },
1353
+ { pubkey: newFeeRecipientTokenAccount, isSigner: false, isWritable: true }
1354
+ );
1264
1355
 
1265
1356
  return new TransactionInstruction({
1266
1357
  programId: PROGRAM_IDS.PUMP_AMM,
package/index.ts CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  PublicKey,
4
4
  Transaction,
5
5
  TransactionInstruction,
6
+ AccountMeta,
6
7
  SystemProgram,
7
8
  ComputeBudgetProgram,
8
9
  Keypair
@@ -153,6 +154,16 @@ const DISCRIMINATORS = {
153
154
 
154
155
  const AMM_FEE_BPS = 100n;
155
156
  const BPS_DENOMINATOR = 10000n;
157
+ const PUMP_NEW_FEE_RECIPIENTS = [
158
+ "5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD",
159
+ "9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7",
160
+ "GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL",
161
+ "3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR",
162
+ "5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6",
163
+ "EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL",
164
+ "5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD",
165
+ "A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"
166
+ ].map((value) => new PublicKey(value));
156
167
 
157
168
  /* ================= 工具函数 ================= */
158
169
 
@@ -394,6 +405,102 @@ export class PumpTrader {
394
405
  )[0];
395
406
  }
396
407
 
408
+ private pickFeeRecipient(index = 0): PublicKey {
409
+ return PUMP_NEW_FEE_RECIPIENTS[index % PUMP_NEW_FEE_RECIPIENTS.length];
410
+ }
411
+
412
+ private buildBondingBuyKeys(args: {
413
+ global: PublicKey;
414
+ globalFeeRecipient: PublicKey;
415
+ mint: PublicKey;
416
+ bonding: PublicKey;
417
+ associatedBondingCurve: PublicKey;
418
+ userAta: PublicKey;
419
+ wallet: PublicKey;
420
+ creatorVault: PublicKey;
421
+ eventAuthority: PublicKey;
422
+ pumpProgram: PublicKey;
423
+ globalVolumeAccumulator: PublicKey;
424
+ userVolumeAccumulator: PublicKey;
425
+ feeConfig: PublicKey;
426
+ feeProgram: PublicKey;
427
+ bondingCurveV2: PublicKey;
428
+ feeRecipient: PublicKey;
429
+ tokenProgramId?: PublicKey;
430
+ }): AccountMeta[] {
431
+ const tokenProgramId = args.tokenProgramId ?? TOKEN_PROGRAM_ID;
432
+
433
+ return [
434
+ { pubkey: args.global, isSigner: false, isWritable: false },
435
+ { pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
436
+ { pubkey: args.mint, isSigner: false, isWritable: false },
437
+ { pubkey: args.bonding, isSigner: false, isWritable: true },
438
+ { pubkey: args.associatedBondingCurve, isSigner: false, isWritable: true },
439
+ { pubkey: args.userAta, isSigner: false, isWritable: true },
440
+ { pubkey: args.wallet, isSigner: true, isWritable: true },
441
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
442
+ { pubkey: tokenProgramId, isSigner: false, isWritable: false },
443
+ { pubkey: args.creatorVault, isSigner: false, isWritable: true },
444
+ { pubkey: args.eventAuthority, isSigner: false, isWritable: false },
445
+ { pubkey: args.pumpProgram, isSigner: false, isWritable: false },
446
+ { pubkey: args.globalVolumeAccumulator, isSigner: false, isWritable: false },
447
+ { pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true },
448
+ { pubkey: args.feeConfig, isSigner: false, isWritable: false },
449
+ { pubkey: args.feeProgram, isSigner: false, isWritable: false },
450
+ { pubkey: args.bondingCurveV2, isSigner: false, isWritable: false },
451
+ { pubkey: args.feeRecipient, isSigner: false, isWritable: true }
452
+ ];
453
+ }
454
+
455
+ private buildBondingSellKeys(args: {
456
+ global: PublicKey;
457
+ globalFeeRecipient: PublicKey;
458
+ mint: PublicKey;
459
+ bonding: PublicKey;
460
+ associatedBondingCurve: PublicKey;
461
+ userAta: PublicKey;
462
+ wallet: PublicKey;
463
+ creatorVault: PublicKey;
464
+ eventAuthority: PublicKey;
465
+ pumpProgram: PublicKey;
466
+ feeConfig: PublicKey;
467
+ feeProgram: PublicKey;
468
+ bondingCurveV2: PublicKey;
469
+ feeRecipient: PublicKey;
470
+ isCashbackCoin: boolean;
471
+ userVolumeAccumulator: PublicKey;
472
+ tokenProgramId?: PublicKey;
473
+ }): AccountMeta[] {
474
+ const tokenProgramId = args.tokenProgramId ?? TOKEN_PROGRAM_ID;
475
+ const keys: AccountMeta[] = [
476
+ { pubkey: args.global, isSigner: false, isWritable: false },
477
+ { pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
478
+ { pubkey: args.mint, isSigner: false, isWritable: false },
479
+ { pubkey: args.bonding, isSigner: false, isWritable: true },
480
+ { pubkey: args.associatedBondingCurve, isSigner: false, isWritable: true },
481
+ { pubkey: args.userAta, isSigner: false, isWritable: true },
482
+ { pubkey: args.wallet, isSigner: true, isWritable: true },
483
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
484
+ { pubkey: args.creatorVault, isSigner: false, isWritable: true },
485
+ { pubkey: tokenProgramId, isSigner: false, isWritable: false },
486
+ { pubkey: args.eventAuthority, isSigner: false, isWritable: false },
487
+ { pubkey: args.pumpProgram, isSigner: false, isWritable: false },
488
+ { pubkey: args.feeConfig, isSigner: false, isWritable: false },
489
+ { pubkey: args.feeProgram, isSigner: false, isWritable: false }
490
+ ];
491
+
492
+ if (args.isCashbackCoin) {
493
+ keys.push({ pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true });
494
+ }
495
+
496
+ keys.push(
497
+ { pubkey: args.bondingCurveV2, isSigner: false, isWritable: false },
498
+ { pubkey: args.feeRecipient, isSigner: false, isWritable: true }
499
+ );
500
+
501
+ return keys;
502
+ }
503
+
397
504
  async loadBonding(mint: PublicKey): Promise<BondingInfo> {
398
505
  const bonding = this.getBondingPda(mint);
399
506
  const acc = await this.connection.getAccountInfo(bonding);
@@ -777,6 +884,7 @@ export class PumpTrader {
777
884
  [Buffer.from("fee_config"), SEEDS.FEE_CONFIG],
778
885
  PROGRAM_IDS.FEE
779
886
  );
887
+ const feeRecipient = this.pickFeeRecipient();
780
888
 
781
889
  for (let i = 0; i < solChunks.length; i++) {
782
890
  try {
@@ -800,25 +908,25 @@ export class PumpTrader {
800
908
  tx.add(
801
909
  new TransactionInstruction({
802
910
  programId: PROGRAM_IDS.PUMP,
803
- keys: [
804
- { pubkey: this.global, isSigner: false, isWritable: false },
805
- { pubkey: this.globalState!.feeRecipient, isSigner: false, isWritable: true },
806
- { pubkey: mint, isSigner: false, isWritable: false },
807
- { pubkey: bonding, isSigner: false, isWritable: true },
808
- { pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
809
- { pubkey: userAta, isSigner: false, isWritable: true },
810
- { pubkey: this.wallet.publicKey, isSigner: true, isWritable: true },
811
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
812
- { pubkey: tokenProgram.programId, isSigner: false, isWritable: false },
813
- { pubkey: creatorVault, isSigner: false, isWritable: true },
814
- { pubkey: PROGRAM_IDS.EVENT_AUTHORITY, isSigner: false, isWritable: false },
815
- { pubkey: PROGRAM_IDS.PUMP, isSigner: false, isWritable: false },
816
- { pubkey: globalVolumeAccumulator, isSigner: false, isWritable: false },
817
- { pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
818
- { pubkey: feeConfig, isSigner: false, isWritable: false },
819
- { pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
820
- { pubkey: bondingCurveV2, isSigner: false, isWritable: false }
821
- ],
911
+ keys: this.buildBondingBuyKeys({
912
+ global: this.global,
913
+ globalFeeRecipient: this.globalState!.feeRecipient,
914
+ mint,
915
+ bonding,
916
+ associatedBondingCurve,
917
+ userAta,
918
+ wallet: this.wallet.publicKey,
919
+ creatorVault,
920
+ eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
921
+ pumpProgram: PROGRAM_IDS.PUMP,
922
+ globalVolumeAccumulator,
923
+ userVolumeAccumulator,
924
+ feeConfig,
925
+ feeProgram: PROGRAM_IDS.FEE,
926
+ bondingCurveV2,
927
+ feeRecipient,
928
+ tokenProgramId: tokenProgram.programId
929
+ }),
822
930
  data: Buffer.concat([DISCRIMINATORS.BUY, u64(tokenOut), u64(maxSol)])
823
931
  })
824
932
  );
@@ -901,12 +1009,7 @@ export class PumpTrader {
901
1009
  [Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()],
902
1010
  PROGRAM_IDS.PUMP
903
1011
  );
904
-
905
- const sellRemainingKeys = [];
906
- if (state.isCashbackCoin) {
907
- sellRemainingKeys.push({ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true });
908
- }
909
- sellRemainingKeys.push({ pubkey: bondingCurveV2, isSigner: false, isWritable: false });
1012
+ const feeRecipient = this.pickFeeRecipient();
910
1013
 
911
1014
  for (let i = 0; i < tokenChunks.length; i++) {
912
1015
  try {
@@ -928,23 +1031,25 @@ export class PumpTrader {
928
1031
  tx.add(
929
1032
  new TransactionInstruction({
930
1033
  programId: PROGRAM_IDS.PUMP,
931
- keys: [
932
- { pubkey: this.global, isSigner: false, isWritable: false },
933
- { pubkey: this.globalState!.feeRecipient, isSigner: false, isWritable: true },
934
- { pubkey: mint, isSigner: false, isWritable: false },
935
- { pubkey: bonding, isSigner: false, isWritable: true },
936
- { pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
937
- { pubkey: userAta, isSigner: false, isWritable: true },
938
- { pubkey: this.wallet.publicKey, isSigner: true, isWritable: true },
939
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
940
- { pubkey: creatorVault, isSigner: false, isWritable: true },
941
- { pubkey: tokenProgram.programId, isSigner: false, isWritable: false },
942
- { pubkey: PROGRAM_IDS.EVENT_AUTHORITY, isSigner: false, isWritable: false },
943
- { pubkey: PROGRAM_IDS.PUMP, isSigner: false, isWritable: false },
944
- { pubkey: feeConfig, isSigner: false, isWritable: false },
945
- { pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
946
- ...sellRemainingKeys
947
- ],
1034
+ keys: this.buildBondingSellKeys({
1035
+ global: this.global,
1036
+ globalFeeRecipient: this.globalState!.feeRecipient,
1037
+ mint,
1038
+ bonding,
1039
+ associatedBondingCurve,
1040
+ userAta,
1041
+ wallet: this.wallet.publicKey,
1042
+ creatorVault,
1043
+ eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
1044
+ pumpProgram: PROGRAM_IDS.PUMP,
1045
+ feeConfig,
1046
+ feeProgram: PROGRAM_IDS.FEE,
1047
+ bondingCurveV2,
1048
+ feeRecipient,
1049
+ isCashbackCoin: !!state.isCashbackCoin,
1050
+ userVolumeAccumulator,
1051
+ tokenProgramId: tokenProgram.programId
1052
+ }),
948
1053
  data: Buffer.concat([
949
1054
  DISCRIMINATORS.SELL,
950
1055
  u64(tokenIn),
@@ -1269,6 +1374,14 @@ export class PumpTrader {
1269
1374
  TOKEN_PROGRAM_ID,
1270
1375
  ASSOCIATED_TOKEN_PROGRAM_ID
1271
1376
  );
1377
+ const newFeeRecipient = this.pickFeeRecipient();
1378
+ const newFeeRecipientTokenAccount = getAssociatedTokenAddressSync(
1379
+ poolKeys.quoteMint,
1380
+ newFeeRecipient,
1381
+ true,
1382
+ TOKEN_PROGRAM_ID,
1383
+ ASSOCIATED_TOKEN_PROGRAM_ID
1384
+ );
1272
1385
 
1273
1386
  const remainingKeys = [];
1274
1387
  if (poolKeys.isCashbackCoin) {
@@ -1282,6 +1395,10 @@ export class PumpTrader {
1282
1395
  remainingKeys.push({ pubkey: userVolumeAccumulatorWsolAta, isSigner: false, isWritable: true });
1283
1396
  }
1284
1397
  remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
1398
+ remainingKeys.push(
1399
+ { pubkey: newFeeRecipient, isSigner: false, isWritable: false },
1400
+ { pubkey: newFeeRecipientTokenAccount, isSigner: false, isWritable: true }
1401
+ );
1285
1402
 
1286
1403
  return new TransactionInstruction({
1287
1404
  programId: PROGRAM_IDS.PUMP_AMM,
@@ -1362,6 +1479,14 @@ export class PumpTrader {
1362
1479
  TOKEN_PROGRAM_ID,
1363
1480
  ASSOCIATED_TOKEN_PROGRAM_ID
1364
1481
  );
1482
+ const newFeeRecipient = this.pickFeeRecipient();
1483
+ const newFeeRecipientTokenAccount = getAssociatedTokenAddressSync(
1484
+ poolKeys.quoteMint,
1485
+ newFeeRecipient,
1486
+ true,
1487
+ TOKEN_PROGRAM_ID,
1488
+ ASSOCIATED_TOKEN_PROGRAM_ID
1489
+ );
1365
1490
 
1366
1491
  const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
1367
1492
  [Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()],
@@ -1384,6 +1509,10 @@ export class PumpTrader {
1384
1509
  );
1385
1510
  }
1386
1511
  remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
1512
+ remainingKeys.push(
1513
+ { pubkey: newFeeRecipient, isSigner: false, isWritable: false },
1514
+ { pubkey: newFeeRecipientTokenAccount, isSigner: false, isWritable: true }
1515
+ );
1387
1516
 
1388
1517
  return new TransactionInstruction({
1389
1518
  programId: PROGRAM_IDS.PUMP_AMM,
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "pump-trader",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "PumpFun 交易库 - 自动判断 Token Program 和内盘/外盘",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc",
9
+ "test": "node --test --import tsx tests/**/*.test.ts",
9
10
  "format": "prettier --write ."
10
11
  },
11
12
  "keywords": [
@@ -0,0 +1,209 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ import bs58 from "bs58";
5
+ import { Keypair, PublicKey } from "@solana/web3.js";
6
+ import { ASSOCIATED_TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from "@solana/spl-token";
7
+
8
+ import { PumpTrader } from "../index";
9
+
10
+ const SOL_MINT = new PublicKey("So11111111111111111111111111111111111111112");
11
+ const FEE_RECIPIENTS = [
12
+ "5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD",
13
+ "9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7",
14
+ "GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL",
15
+ "3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR",
16
+ "5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6",
17
+ "EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL",
18
+ "5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD",
19
+ "A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"
20
+ ].map((value) => new PublicKey(value));
21
+
22
+ function createTrader() {
23
+ return new PumpTrader("http://127.0.0.1:8899", bs58.encode(Keypair.generate().secretKey));
24
+ }
25
+
26
+ function createPoolInfo(isCashbackCoin: boolean) {
27
+ return {
28
+ pool: Keypair.generate().publicKey,
29
+ poolAuthority: Keypair.generate().publicKey,
30
+ poolKeys: {
31
+ baseMint: Keypair.generate().publicKey,
32
+ quoteMint: SOL_MINT,
33
+ poolBaseTokenAccount: Keypair.generate().publicKey,
34
+ poolQuoteTokenAccount: Keypair.generate().publicKey,
35
+ coinCreator: Keypair.generate().publicKey,
36
+ isCashbackCoin
37
+ },
38
+ globalConfig: {
39
+ address: Keypair.generate().publicKey,
40
+ protocolFeeRecipients: FEE_RECIPIENTS
41
+ }
42
+ };
43
+ }
44
+
45
+ test("bonding buy appends mutable fee recipient after bondingCurveV2", () => {
46
+ const trader = createTrader() as any;
47
+ const bondingCurveV2 = Keypair.generate().publicKey;
48
+ const feeRecipient = FEE_RECIPIENTS[0];
49
+
50
+ const keys = trader.buildBondingBuyKeys({
51
+ global: Keypair.generate().publicKey,
52
+ globalFeeRecipient: Keypair.generate().publicKey,
53
+ mint: Keypair.generate().publicKey,
54
+ bonding: Keypair.generate().publicKey,
55
+ associatedBondingCurve: Keypair.generate().publicKey,
56
+ userAta: Keypair.generate().publicKey,
57
+ wallet: Keypair.generate().publicKey,
58
+ creatorVault: Keypair.generate().publicKey,
59
+ eventAuthority: Keypair.generate().publicKey,
60
+ pumpProgram: Keypair.generate().publicKey,
61
+ globalVolumeAccumulator: Keypair.generate().publicKey,
62
+ userVolumeAccumulator: Keypair.generate().publicKey,
63
+ feeConfig: Keypair.generate().publicKey,
64
+ feeProgram: Keypair.generate().publicKey,
65
+ bondingCurveV2,
66
+ feeRecipient
67
+ });
68
+
69
+ assert.equal(keys.length, 18);
70
+ assert.equal(keys.at(-2).pubkey.toBase58(), bondingCurveV2.toBase58());
71
+ assert.equal(keys.at(-2).isWritable, false);
72
+ assert.equal(keys.at(-1).pubkey.toBase58(), feeRecipient.toBase58());
73
+ assert.equal(keys.at(-1).isWritable, true);
74
+ });
75
+
76
+ test("bonding sell appends mutable fee recipient after optional cashback and bondingCurveV2", () => {
77
+ const trader = createTrader() as any;
78
+ const baseArgs = {
79
+ global: Keypair.generate().publicKey,
80
+ globalFeeRecipient: Keypair.generate().publicKey,
81
+ mint: Keypair.generate().publicKey,
82
+ bonding: Keypair.generate().publicKey,
83
+ associatedBondingCurve: Keypair.generate().publicKey,
84
+ userAta: Keypair.generate().publicKey,
85
+ wallet: Keypair.generate().publicKey,
86
+ creatorVault: Keypair.generate().publicKey,
87
+ eventAuthority: Keypair.generate().publicKey,
88
+ pumpProgram: Keypair.generate().publicKey,
89
+ feeConfig: Keypair.generate().publicKey,
90
+ feeProgram: Keypair.generate().publicKey,
91
+ bondingCurveV2: Keypair.generate().publicKey,
92
+ feeRecipient: FEE_RECIPIENTS[0],
93
+ userVolumeAccumulator: Keypair.generate().publicKey
94
+ };
95
+
96
+ const nonCashback = trader.buildBondingSellKeys({
97
+ ...baseArgs,
98
+ isCashbackCoin: false
99
+ });
100
+ assert.equal(nonCashback.length, 16);
101
+ assert.equal(nonCashback.at(-2).pubkey.toBase58(), baseArgs.bondingCurveV2.toBase58());
102
+ assert.equal(nonCashback.at(-1).pubkey.toBase58(), baseArgs.feeRecipient.toBase58());
103
+ assert.equal(nonCashback.at(-1).isWritable, true);
104
+
105
+ const cashback = trader.buildBondingSellKeys({
106
+ ...baseArgs,
107
+ isCashbackCoin: true
108
+ });
109
+ assert.equal(cashback.length, 17);
110
+ assert.equal(cashback.at(-3).pubkey.toBase58(), baseArgs.userVolumeAccumulator.toBase58());
111
+ assert.equal(cashback.at(-2).pubkey.toBase58(), baseArgs.bondingCurveV2.toBase58());
112
+ assert.equal(cashback.at(-1).pubkey.toBase58(), baseArgs.feeRecipient.toBase58());
113
+ });
114
+
115
+ test("amm buy places poolV2 before fee recipient pair for non-cashback coins", () => {
116
+ const trader = createTrader();
117
+ const poolInfo = createPoolInfo(false);
118
+ const userBaseAta = Keypair.generate().publicKey;
119
+ const userQuoteAta = Keypair.generate().publicKey;
120
+
121
+ const instruction = trader.createAmmBuyInstruction(
122
+ poolInfo as any,
123
+ userBaseAta,
124
+ userQuoteAta,
125
+ 1n,
126
+ 2n,
127
+ TOKEN_PROGRAM_ID
128
+ );
129
+
130
+ const feeRecipient = poolInfo.globalConfig.protocolFeeRecipients[0];
131
+ const feeRecipientAta = getAssociatedTokenAddressSync(
132
+ SOL_MINT,
133
+ feeRecipient,
134
+ true,
135
+ TOKEN_PROGRAM_ID,
136
+ ASSOCIATED_TOKEN_PROGRAM_ID
137
+ );
138
+
139
+ assert.equal(instruction.keys.length, 26);
140
+ assert.equal(instruction.keys.at(-3)?.pubkey.toBase58(), trader.deriveAmmPoolV2(poolInfo.poolKeys.baseMint).toBase58());
141
+ assert.equal(instruction.keys.at(-2)?.pubkey.toBase58(), feeRecipient.toBase58());
142
+ assert.equal(instruction.keys.at(-2)?.isWritable, false);
143
+ assert.equal(instruction.keys.at(-1)?.pubkey.toBase58(), feeRecipientAta.toBase58());
144
+ assert.equal(instruction.keys.at(-1)?.isWritable, true);
145
+ });
146
+
147
+ test("amm buy places cashback account before poolV2 and fee recipient tail", () => {
148
+ const trader = createTrader();
149
+ const poolInfo = createPoolInfo(true);
150
+
151
+ const instruction = trader.createAmmBuyInstruction(
152
+ poolInfo as any,
153
+ Keypair.generate().publicKey,
154
+ Keypair.generate().publicKey,
155
+ 1n,
156
+ 2n,
157
+ TOKEN_PROGRAM_ID
158
+ );
159
+
160
+ assert.equal(instruction.keys.length, 27);
161
+ assert.equal(instruction.keys.at(-3)?.isWritable, false);
162
+ assert.equal(instruction.keys.at(-2)?.pubkey.toBase58(), poolInfo.globalConfig.protocolFeeRecipients[0].toBase58());
163
+ assert.equal(instruction.keys.at(-1)?.isWritable, true);
164
+ });
165
+
166
+ test("amm sell places poolV2 before fee recipient pair for non-cashback coins", () => {
167
+ const trader = createTrader();
168
+ const poolInfo = createPoolInfo(false);
169
+
170
+ const instruction = trader.createAmmSellInstruction(
171
+ poolInfo as any,
172
+ Keypair.generate().publicKey,
173
+ Keypair.generate().publicKey,
174
+ 1n,
175
+ 2n,
176
+ TOKEN_PROGRAM_ID
177
+ );
178
+
179
+ assert.equal(instruction.keys.length, 24);
180
+ assert.equal(instruction.keys.at(-3)?.pubkey.toBase58(), trader.deriveAmmPoolV2(poolInfo.poolKeys.baseMint).toBase58());
181
+ assert.equal(instruction.keys.at(-2)?.pubkey.toBase58(), poolInfo.globalConfig.protocolFeeRecipients[0].toBase58());
182
+ assert.equal(instruction.keys.at(-1)?.isWritable, true);
183
+ });
184
+
185
+ test("amm sell places cashback accounts before poolV2 and fee recipient tail", () => {
186
+ const trader = createTrader();
187
+ const poolInfo = createPoolInfo(true);
188
+
189
+ const instruction = trader.createAmmSellInstruction(
190
+ poolInfo as any,
191
+ Keypair.generate().publicKey,
192
+ Keypair.generate().publicKey,
193
+ 1n,
194
+ 2n,
195
+ TOKEN_PROGRAM_ID
196
+ );
197
+
198
+ assert.equal(instruction.keys.length, 26);
199
+ assert.equal(instruction.keys.at(-3)?.pubkey.toBase58(), trader.deriveAmmPoolV2(poolInfo.poolKeys.baseMint).toBase58());
200
+ assert.equal(instruction.keys.at(-2)?.pubkey.toBase58(), poolInfo.globalConfig.protocolFeeRecipients[0].toBase58());
201
+ assert.equal(instruction.keys.at(-1)?.pubkey.toBase58(), getAssociatedTokenAddressSync(
202
+ SOL_MINT,
203
+ poolInfo.globalConfig.protocolFeeRecipients[0],
204
+ true,
205
+ TOKEN_PROGRAM_ID,
206
+ ASSOCIATED_TOKEN_PROGRAM_ID
207
+ ).toBase58());
208
+ assert.equal(instruction.keys.at(-1)?.isWritable, true);
209
+ });