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