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 +3 -0
- package/dist/index.js +106 -41
- 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 +138 -47
- package/index.ts +171 -42
- package/package.json +2 -1
- package/tests/instruction-accounts.test.ts +209 -0
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
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|
|
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
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
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
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
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
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
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
|
+
"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
|
+
});
|