pump-trader 1.1.3 → 1.1.5
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 +7 -1
- package/dist/index.js +134 -46
- package/docs/plans/2026-04-29-pump-fee-recipient-design.md +34 -0
- package/docs/plans/2026-04-29-pump-fee-recipient.md +143 -0
- package/index.js +173 -52
- package/index.ts +206 -47
- package/package.json +2 -1
- package/tests/instruction-accounts.test.ts +301 -0
package/dist/index.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ interface BondingCurveState {
|
|
|
33
33
|
realSolReserves: bigint;
|
|
34
34
|
tokenTotalSupply: bigint;
|
|
35
35
|
complete: boolean;
|
|
36
|
+
quoteMint?: PublicKey;
|
|
36
37
|
isMayhemMode?: boolean;
|
|
37
38
|
isCashbackCoin?: boolean;
|
|
38
39
|
}
|
|
@@ -104,6 +105,9 @@ export declare class PumpTrader {
|
|
|
104
105
|
loadGlobal(): Promise<GlobalState>;
|
|
105
106
|
getBondingPda(mint: PublicKey): PublicKey;
|
|
106
107
|
deriveBondingCurveV2(mint: PublicKey): PublicKey;
|
|
108
|
+
private pickFeeRecipient;
|
|
109
|
+
private buildBondingBuyKeys;
|
|
110
|
+
private buildBondingSellKeys;
|
|
107
111
|
loadBonding(mint: PublicKey): Promise<BondingInfo>;
|
|
108
112
|
calcBuy(solIn: bigint, state: BondingCurveState): bigint;
|
|
109
113
|
calcSell(tokenIn: bigint, state: BondingCurveState): bigint;
|
|
@@ -113,7 +117,9 @@ export declare class PumpTrader {
|
|
|
113
117
|
price: number;
|
|
114
118
|
completed: boolean;
|
|
115
119
|
}>;
|
|
116
|
-
getAmmPrice(mint: PublicKey): Promise<number>;
|
|
120
|
+
getAmmPrice(mint: PublicKey, quoteMint?: PublicKey): Promise<number>;
|
|
121
|
+
private getEffectiveQuoteMint;
|
|
122
|
+
private getMintDecimals;
|
|
117
123
|
/**
|
|
118
124
|
* 查询代币余额
|
|
119
125
|
* @param tokenAddr - 代币地址(可选),如果不传则返回所有代币
|
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);
|
|
@@ -244,6 +304,10 @@ class PumpTrader {
|
|
|
244
304
|
offset += 1;
|
|
245
305
|
const creator = new web3_js_1.PublicKey(data.slice(offset, offset + 32));
|
|
246
306
|
offset += 32;
|
|
307
|
+
if (offset + 32 <= data.length - 2) {
|
|
308
|
+
state.quoteMint = new web3_js_1.PublicKey(data.slice(offset, offset + 32));
|
|
309
|
+
offset += 32;
|
|
310
|
+
}
|
|
247
311
|
state.isMayhemMode = offset < data.length ? data[offset] === 1 : false;
|
|
248
312
|
offset += 1;
|
|
249
313
|
state.isCashbackCoin = offset < data.length ? data[offset] === 1 : false;
|
|
@@ -276,19 +340,21 @@ class PumpTrader {
|
|
|
276
340
|
async getPriceAndStatus(tokenAddr) {
|
|
277
341
|
const mint = new web3_js_1.PublicKey(tokenAddr);
|
|
278
342
|
const { state } = await this.loadBonding(mint);
|
|
343
|
+
const quoteMint = this.getEffectiveQuoteMint(state.quoteMint);
|
|
279
344
|
if (state.complete) {
|
|
280
|
-
const price = await this.getAmmPrice(mint);
|
|
345
|
+
const price = await this.getAmmPrice(mint, quoteMint);
|
|
281
346
|
return { price, completed: true };
|
|
282
347
|
}
|
|
283
348
|
const oneToken = BigInt(1_000_000);
|
|
284
|
-
const
|
|
285
|
-
const
|
|
349
|
+
const quoteOut = this.calcSell(oneToken, state);
|
|
350
|
+
const quoteDecimals = await this.getMintDecimals(quoteMint);
|
|
351
|
+
const price = Number(quoteOut) / 10 ** quoteDecimals;
|
|
286
352
|
return { price, completed: false };
|
|
287
353
|
}
|
|
288
|
-
async getAmmPrice(mint) {
|
|
354
|
+
async getAmmPrice(mint, quoteMint = SOL_MINT) {
|
|
289
355
|
const [poolCreator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("pool-authority"), mint.toBuffer()], PROGRAM_IDS.PUMP);
|
|
290
356
|
const indexBuffer = new bn_js_1.default(0).toArrayLike(Buffer, "le", 2);
|
|
291
|
-
const [pool] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("pool"), indexBuffer, poolCreator.toBuffer(), mint.toBuffer(),
|
|
357
|
+
const [pool] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("pool"), indexBuffer, poolCreator.toBuffer(), mint.toBuffer(), quoteMint.toBuffer()], PROGRAM_IDS.PUMP_AMM);
|
|
292
358
|
const acc = await this.connection.getAccountInfo(pool);
|
|
293
359
|
if (!acc)
|
|
294
360
|
throw new Error("Pool not found");
|
|
@@ -299,6 +365,23 @@ class PumpTrader {
|
|
|
299
365
|
]);
|
|
300
366
|
return quoteInfo.value.uiAmount / baseInfo.value.uiAmount;
|
|
301
367
|
}
|
|
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
|
+
}
|
|
302
385
|
/* ---------- 余额查询 ---------- */
|
|
303
386
|
/**
|
|
304
387
|
* 查询代币余额
|
|
@@ -488,6 +571,7 @@ class PumpTrader {
|
|
|
488
571
|
const [globalVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("global_volume_accumulator")], PROGRAM_IDS.PUMP);
|
|
489
572
|
const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()], PROGRAM_IDS.PUMP);
|
|
490
573
|
const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.FEE_CONFIG], PROGRAM_IDS.FEE);
|
|
574
|
+
const feeRecipient = this.pickFeeRecipient();
|
|
491
575
|
for (let i = 0; i < solChunks.length; i++) {
|
|
492
576
|
try {
|
|
493
577
|
const solIn = solChunks[i];
|
|
@@ -503,25 +587,25 @@ class PumpTrader {
|
|
|
503
587
|
const userAta = await this.ensureAta(tx, mint, tokenProgram.programId);
|
|
504
588
|
tx.add(new web3_js_1.TransactionInstruction({
|
|
505
589
|
programId: PROGRAM_IDS.PUMP,
|
|
506
|
-
keys:
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
590
|
+
keys: this.buildBondingBuyKeys({
|
|
591
|
+
global: this.global,
|
|
592
|
+
globalFeeRecipient: this.globalState.feeRecipient,
|
|
593
|
+
mint,
|
|
594
|
+
bonding,
|
|
595
|
+
associatedBondingCurve,
|
|
596
|
+
userAta,
|
|
597
|
+
wallet: this.wallet.publicKey,
|
|
598
|
+
creatorVault,
|
|
599
|
+
eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
|
|
600
|
+
pumpProgram: PROGRAM_IDS.PUMP,
|
|
601
|
+
globalVolumeAccumulator,
|
|
602
|
+
userVolumeAccumulator,
|
|
603
|
+
feeConfig,
|
|
604
|
+
feeProgram: PROGRAM_IDS.FEE,
|
|
605
|
+
bondingCurveV2,
|
|
606
|
+
feeRecipient,
|
|
607
|
+
tokenProgramId: tokenProgram.programId
|
|
608
|
+
}),
|
|
525
609
|
data: Buffer.concat([DISCRIMINATORS.BUY, u64(tokenOut), u64(maxSol)])
|
|
526
610
|
}));
|
|
527
611
|
const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash('finalized');
|
|
@@ -567,11 +651,7 @@ class PumpTrader {
|
|
|
567
651
|
const [creatorVault] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("creator-vault"), creator.toBuffer()], PROGRAM_IDS.PUMP);
|
|
568
652
|
const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.FEE_CONFIG], PROGRAM_IDS.FEE);
|
|
569
653
|
const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()], PROGRAM_IDS.PUMP);
|
|
570
|
-
const
|
|
571
|
-
if (state.isCashbackCoin) {
|
|
572
|
-
sellRemainingKeys.push({ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true });
|
|
573
|
-
}
|
|
574
|
-
sellRemainingKeys.push({ pubkey: bondingCurveV2, isSigner: false, isWritable: false });
|
|
654
|
+
const feeRecipient = this.pickFeeRecipient();
|
|
575
655
|
for (let i = 0; i < tokenChunks.length; i++) {
|
|
576
656
|
try {
|
|
577
657
|
const tokenIn = tokenChunks[i];
|
|
@@ -586,23 +666,25 @@ class PumpTrader {
|
|
|
586
666
|
const tx = new web3_js_1.Transaction().add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }));
|
|
587
667
|
tx.add(new web3_js_1.TransactionInstruction({
|
|
588
668
|
programId: PROGRAM_IDS.PUMP,
|
|
589
|
-
keys:
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
669
|
+
keys: this.buildBondingSellKeys({
|
|
670
|
+
global: this.global,
|
|
671
|
+
globalFeeRecipient: this.globalState.feeRecipient,
|
|
672
|
+
mint,
|
|
673
|
+
bonding,
|
|
674
|
+
associatedBondingCurve,
|
|
675
|
+
userAta,
|
|
676
|
+
wallet: this.wallet.publicKey,
|
|
677
|
+
creatorVault,
|
|
678
|
+
eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
|
|
679
|
+
pumpProgram: PROGRAM_IDS.PUMP,
|
|
680
|
+
feeConfig,
|
|
681
|
+
feeProgram: PROGRAM_IDS.FEE,
|
|
682
|
+
bondingCurveV2,
|
|
683
|
+
feeRecipient,
|
|
684
|
+
isCashbackCoin: !!state.isCashbackCoin,
|
|
685
|
+
userVolumeAccumulator,
|
|
686
|
+
tokenProgramId: tokenProgram.programId
|
|
687
|
+
}),
|
|
606
688
|
data: Buffer.concat([
|
|
607
689
|
DISCRIMINATORS.SELL,
|
|
608
690
|
u64(tokenIn),
|
|
@@ -791,12 +873,15 @@ class PumpTrader {
|
|
|
791
873
|
const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.AMM_FEE_CONFIG], PROGRAM_IDS.FEE);
|
|
792
874
|
const protocolFeeRecipient = globalConfig.protocolFeeRecipients[0];
|
|
793
875
|
const protocolFeeRecipientTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(SOL_MINT, protocolFeeRecipient, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
876
|
+
const newFeeRecipient = this.pickFeeRecipient();
|
|
877
|
+
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
878
|
const remainingKeys = [];
|
|
795
879
|
if (poolKeys.isCashbackCoin) {
|
|
796
880
|
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
881
|
remainingKeys.push({ pubkey: userVolumeAccumulatorWsolAta, isSigner: false, isWritable: true });
|
|
798
882
|
}
|
|
799
883
|
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
884
|
+
remainingKeys.push({ pubkey: newFeeRecipient, isSigner: false, isWritable: false }, { pubkey: newFeeRecipientTokenAccount, isSigner: false, isWritable: true });
|
|
800
885
|
return new web3_js_1.TransactionInstruction({
|
|
801
886
|
programId: PROGRAM_IDS.PUMP_AMM,
|
|
802
887
|
keys: [
|
|
@@ -842,6 +927,8 @@ class PumpTrader {
|
|
|
842
927
|
const [feeConfig] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), SEEDS.AMM_FEE_CONFIG], PROGRAM_IDS.FEE);
|
|
843
928
|
const protocolFeeRecipient = globalConfig.protocolFeeRecipients[0];
|
|
844
929
|
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
|
+
const newFeeRecipient = this.pickFeeRecipient();
|
|
931
|
+
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
932
|
const [userVolumeAccumulator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("user_volume_accumulator"), this.wallet.publicKey.toBuffer()], PROGRAM_IDS.PUMP_AMM);
|
|
846
933
|
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
934
|
const remainingKeys = [];
|
|
@@ -849,6 +936,7 @@ class PumpTrader {
|
|
|
849
936
|
remainingKeys.push({ pubkey: userVolumeAccumulatorWsolAta, isSigner: false, isWritable: true }, { pubkey: userVolumeAccumulator, isSigner: false, isWritable: true });
|
|
850
937
|
}
|
|
851
938
|
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
939
|
+
remainingKeys.push({ pubkey: newFeeRecipient, isSigner: false, isWritable: false }, { pubkey: newFeeRecipientTokenAccount, isSigner: false, isWritable: true });
|
|
852
940
|
return new web3_js_1.TransactionInstruction({
|
|
853
941
|
programId: PROGRAM_IDS.PUMP_AMM,
|
|
854
942
|
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
|
+
```
|