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