pump-trader 1.1.5 → 1.1.8
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 +1 -4
- package/dist/index.js +5 -28
- package/index.js +484 -270
- package/index.ts +1311 -277
- package/package.json +1 -1
- package/tests/instruction-accounts.test.ts +0 -92
package/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
AccountMeta,
|
|
7
7
|
SystemProgram,
|
|
8
8
|
ComputeBudgetProgram,
|
|
9
|
-
Keypair
|
|
9
|
+
Keypair,
|
|
10
10
|
} from "@solana/web3.js";
|
|
11
11
|
|
|
12
12
|
import {
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
getTokenMetadata,
|
|
19
19
|
TOKEN_2022_PROGRAM_ID,
|
|
20
20
|
TOKEN_PROGRAM_ID,
|
|
21
|
-
getMint
|
|
21
|
+
getMint,
|
|
22
22
|
} from "@solana/spl-token";
|
|
23
23
|
|
|
24
24
|
import BN from "bn.js";
|
|
@@ -64,9 +64,12 @@ interface BondingCurveState {
|
|
|
64
64
|
realSolReserves: bigint;
|
|
65
65
|
tokenTotalSupply: bigint;
|
|
66
66
|
complete: boolean;
|
|
67
|
-
quoteMint?: PublicKey;
|
|
68
67
|
isMayhemMode?: boolean;
|
|
69
68
|
isCashbackCoin?: boolean;
|
|
69
|
+
// V2 fields
|
|
70
|
+
quoteMint?: PublicKey;
|
|
71
|
+
virtualQuoteReserves?: bigint;
|
|
72
|
+
realQuoteReserves?: bigint;
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
interface BondingInfo {
|
|
@@ -129,33 +132,62 @@ const PROGRAM_IDS = {
|
|
|
129
132
|
PUMP_AMM: new PublicKey("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"),
|
|
130
133
|
METADATA: new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"),
|
|
131
134
|
FEE: new PublicKey("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"),
|
|
132
|
-
EVENT_AUTHORITY: new PublicKey(
|
|
135
|
+
EVENT_AUTHORITY: new PublicKey(
|
|
136
|
+
"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1",
|
|
137
|
+
),
|
|
133
138
|
};
|
|
134
139
|
|
|
135
140
|
const SOL_MINT = new PublicKey("So11111111111111111111111111111111111111112");
|
|
136
141
|
|
|
137
142
|
const SEEDS = {
|
|
138
143
|
FEE_CONFIG: new Uint8Array([
|
|
139
|
-
1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, 91, 170,
|
|
140
|
-
|
|
144
|
+
1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, 91, 170, 81,
|
|
145
|
+
137, 203, 151, 245, 210, 255, 59, 101, 93, 43, 182, 253, 109, 24, 176,
|
|
141
146
|
]),
|
|
142
147
|
AMM_FEE_CONFIG: Buffer.from([
|
|
143
|
-
12, 20, 222, 252, 130, 94, 198, 118, 148, 37, 8, 24, 187, 101, 64, 101,
|
|
144
|
-
|
|
148
|
+
12, 20, 222, 252, 130, 94, 198, 118, 148, 37, 8, 24, 187, 101, 64, 101, 244,
|
|
149
|
+
41, 141, 49, 86, 213, 113, 180, 212, 248, 9, 12, 24, 233, 168, 99,
|
|
145
150
|
]),
|
|
146
151
|
GLOBAL: Buffer.from("global"),
|
|
147
|
-
BONDING: Buffer.from("bonding-curve")
|
|
152
|
+
BONDING: Buffer.from("bonding-curve"),
|
|
148
153
|
};
|
|
149
154
|
|
|
150
155
|
const DISCRIMINATORS = {
|
|
151
156
|
BUY: Buffer.from([102, 6, 61, 18, 1, 218, 235, 234]),
|
|
152
157
|
SELL: Buffer.from([51, 230, 133, 164, 1, 127, 131, 173]),
|
|
153
|
-
TRADE_EVENT: Buffer.from([189, 219, 127, 211, 78, 230, 97, 238])
|
|
158
|
+
TRADE_EVENT: Buffer.from([189, 219, 127, 211, 78, 230, 97, 238]),
|
|
159
|
+
// V2 instructions
|
|
160
|
+
BUY_V2: Buffer.from([184, 23, 238, 97, 103, 197, 211, 61]),
|
|
161
|
+
SELL_V2: Buffer.from([93, 246, 130, 60, 231, 233, 64, 178]),
|
|
162
|
+
BUY_EXACT_QUOTE_IN_V2: Buffer.from([194, 171, 28, 70, 104, 77, 91, 47]),
|
|
163
|
+
COLLECT_CREATOR_FEE_V2: Buffer.from([207, 17, 138, 242, 4, 34, 19, 56]),
|
|
154
164
|
};
|
|
155
165
|
|
|
156
166
|
const AMM_FEE_BPS = 100n;
|
|
157
167
|
const BPS_DENOMINATOR = 10000n;
|
|
158
168
|
const PUMP_NEW_FEE_RECIPIENTS = [
|
|
169
|
+
"62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV",
|
|
170
|
+
"7VtfL8fvgNfhz17qKRMjzQEXgbdpnHHHQRh54R9jP2RJ",
|
|
171
|
+
"7hTckgnGnLQR6sdH7YkqFTAA7VwTfYFaZ6EhEsU3saCX",
|
|
172
|
+
"9rPYyANsfQZw3DnDmKE3YCQF5E8oD89UXoHn9JFEhJUz",
|
|
173
|
+
"AVmoTthdrX6tKt4nDjco2D775W2YK3sDhxPcMmzUAmTY",
|
|
174
|
+
"CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM",
|
|
175
|
+
"FWsW1xNtWscwNmKv6wVsU1iTzRN6wmmk3MjxRP5tT7hz",
|
|
176
|
+
"G5UZAVbAf46s7cKWoyKu8kYTip9DGTpbLZ2qa9Aq69dP",
|
|
177
|
+
].map((value) => new PublicKey(value));
|
|
178
|
+
|
|
179
|
+
const PUMP_RESERVED_FEE_RECIPIENTS = [
|
|
180
|
+
"GesfTA3X2arioaHp8bbKdjG9vJtskViWACZoYvxp4twS",
|
|
181
|
+
"4budycTjhs9fD6xw62VBducVTNgMgJJ5BgtKq7mAZwn6",
|
|
182
|
+
"8SBKzEQU4nLSzcwF4a74F2iaUDQyTfjGndn6qUWBnrpR",
|
|
183
|
+
"4UQeTP1T39KZ9Sfxzo3WR5skgsaP6NZa87BAkuazLEKH",
|
|
184
|
+
"8sNeir4QsLsJdYpc9RZacohhK1Y5FLU3nC5LXgYB4aa6",
|
|
185
|
+
"Fh9HmeLNUMVCvejxCtCL2DbYaRyBFVJ5xrWkLnMH6fdk",
|
|
186
|
+
"463MEnMeGyJekNZFQSTUABBEbLnvMTALbT6ZmsxAbAdq",
|
|
187
|
+
"6AUH3WEHucYZyC61hqpqYUWVto5qA5hjHuNQ32GNnNxA",
|
|
188
|
+
].map((value) => new PublicKey(value));
|
|
189
|
+
|
|
190
|
+
const PUMP_BUYBACK_FEE_RECIPIENTS = [
|
|
159
191
|
"5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD",
|
|
160
192
|
"9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7",
|
|
161
193
|
"GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL",
|
|
@@ -163,13 +195,14 @@ const PUMP_NEW_FEE_RECIPIENTS = [
|
|
|
163
195
|
"5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6",
|
|
164
196
|
"EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL",
|
|
165
197
|
"5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD",
|
|
166
|
-
"A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"
|
|
198
|
+
"A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW",
|
|
167
199
|
].map((value) => new PublicKey(value));
|
|
168
200
|
|
|
169
201
|
/* ================= 工具函数 ================= */
|
|
170
202
|
|
|
171
203
|
const u64 = (v: bigint | BN | number): Buffer => {
|
|
172
|
-
const bn =
|
|
204
|
+
const bn =
|
|
205
|
+
typeof v === "bigint" ? new BN(v.toString()) : new BN(v.toString());
|
|
173
206
|
return bn.toArrayLike(Buffer, "le", 8);
|
|
174
207
|
};
|
|
175
208
|
|
|
@@ -186,7 +219,9 @@ const readU32 = (buf: Buffer, offsetObj: { offset: number }): number => {
|
|
|
186
219
|
|
|
187
220
|
const readString = (buf: Buffer, offsetObj: { offset: number }): string => {
|
|
188
221
|
const len = readU32(buf, offsetObj);
|
|
189
|
-
const str = buf
|
|
222
|
+
const str = buf
|
|
223
|
+
.slice(offsetObj.offset, offsetObj.offset + len)
|
|
224
|
+
.toString("utf8");
|
|
190
225
|
offsetObj.offset += len;
|
|
191
226
|
return str;
|
|
192
227
|
};
|
|
@@ -196,10 +231,14 @@ const readString = (buf: Buffer, offsetObj: { offset: number }): string => {
|
|
|
196
231
|
function parseMetadataAccount(data: Buffer) {
|
|
197
232
|
const offsetObj = { offset: 1 };
|
|
198
233
|
|
|
199
|
-
const updateAuthority = new PublicKey(
|
|
234
|
+
const updateAuthority = new PublicKey(
|
|
235
|
+
data.slice(offsetObj.offset, offsetObj.offset + 32),
|
|
236
|
+
);
|
|
200
237
|
offsetObj.offset += 32;
|
|
201
238
|
|
|
202
|
-
const mint = new PublicKey(
|
|
239
|
+
const mint = new PublicKey(
|
|
240
|
+
data.slice(offsetObj.offset, offsetObj.offset + 32),
|
|
241
|
+
);
|
|
203
242
|
offsetObj.offset += 32;
|
|
204
243
|
|
|
205
244
|
const name = readString(data, offsetObj);
|
|
@@ -211,13 +250,13 @@ function parseMetadataAccount(data: Buffer) {
|
|
|
211
250
|
mint: mint.toBase58(),
|
|
212
251
|
name,
|
|
213
252
|
symbol,
|
|
214
|
-
uri
|
|
253
|
+
uri,
|
|
215
254
|
};
|
|
216
255
|
}
|
|
217
256
|
|
|
218
257
|
function parsePoolKeys(data: Buffer) {
|
|
219
258
|
if (!data || data.length < 280) {
|
|
220
|
-
throw new Error(
|
|
259
|
+
throw new Error("Invalid pool account data");
|
|
221
260
|
}
|
|
222
261
|
|
|
223
262
|
let offset = 8;
|
|
@@ -254,7 +293,8 @@ function parsePoolKeys(data: Buffer) {
|
|
|
254
293
|
|
|
255
294
|
const isMayhemMode = data.readUInt8(offset) === 1;
|
|
256
295
|
offset += 1;
|
|
257
|
-
const isCashbackCoin =
|
|
296
|
+
const isCashbackCoin =
|
|
297
|
+
offset < data.length ? data.readUInt8(offset) === 1 : false;
|
|
258
298
|
|
|
259
299
|
return {
|
|
260
300
|
creator,
|
|
@@ -265,7 +305,7 @@ function parsePoolKeys(data: Buffer) {
|
|
|
265
305
|
poolQuoteTokenAccount,
|
|
266
306
|
coinCreator,
|
|
267
307
|
isMayhemMode,
|
|
268
|
-
isCashbackCoin
|
|
308
|
+
isCashbackCoin,
|
|
269
309
|
};
|
|
270
310
|
}
|
|
271
311
|
|
|
@@ -281,7 +321,10 @@ export class PumpTrader {
|
|
|
281
321
|
constructor(rpc: string, privateKey: string) {
|
|
282
322
|
this.connection = new Connection(rpc, "confirmed");
|
|
283
323
|
this.wallet = Keypair.fromSecretKey(bs58.decode(privateKey));
|
|
284
|
-
this.global = PublicKey.findProgramAddressSync(
|
|
324
|
+
this.global = PublicKey.findProgramAddressSync(
|
|
325
|
+
[SEEDS.GLOBAL],
|
|
326
|
+
PROGRAM_IDS.PUMP,
|
|
327
|
+
)[0];
|
|
285
328
|
this.globalState = null;
|
|
286
329
|
this.tokenProgramCache = new Map();
|
|
287
330
|
}
|
|
@@ -301,29 +344,59 @@ export class PumpTrader {
|
|
|
301
344
|
|
|
302
345
|
try {
|
|
303
346
|
// 首先尝试获取 TOKEN_2022 的代币信息
|
|
304
|
-
const mintData = await getMint(
|
|
347
|
+
const mintData = await getMint(
|
|
348
|
+
this.connection,
|
|
349
|
+
mint,
|
|
350
|
+
"confirmed",
|
|
351
|
+
TOKEN_2022_PROGRAM_ID,
|
|
352
|
+
);
|
|
305
353
|
const result: TokenProgramType = {
|
|
306
354
|
type: "TOKEN_2022_PROGRAM_ID",
|
|
307
|
-
programId: TOKEN_2022_PROGRAM_ID
|
|
355
|
+
programId: TOKEN_2022_PROGRAM_ID,
|
|
308
356
|
};
|
|
309
357
|
this.tokenProgramCache.set(tokenAddr, result);
|
|
310
358
|
return result;
|
|
311
359
|
} catch (e) {
|
|
312
360
|
try {
|
|
313
361
|
// 如果失败,尝试标准 TOKEN_PROGRAM_ID
|
|
314
|
-
const mintData = await getMint(
|
|
362
|
+
const mintData = await getMint(
|
|
363
|
+
this.connection,
|
|
364
|
+
mint,
|
|
365
|
+
"confirmed",
|
|
366
|
+
TOKEN_PROGRAM_ID,
|
|
367
|
+
);
|
|
315
368
|
const result: TokenProgramType = {
|
|
316
369
|
type: "TOKEN_PROGRAM_ID",
|
|
317
|
-
programId: TOKEN_PROGRAM_ID
|
|
370
|
+
programId: TOKEN_PROGRAM_ID,
|
|
318
371
|
};
|
|
319
372
|
this.tokenProgramCache.set(tokenAddr, result);
|
|
320
373
|
return result;
|
|
321
374
|
} catch (error) {
|
|
322
|
-
throw new Error(
|
|
375
|
+
throw new Error(
|
|
376
|
+
`Failed to detect token program for ${tokenAddr}: ${error}`,
|
|
377
|
+
);
|
|
323
378
|
}
|
|
324
379
|
}
|
|
325
380
|
}
|
|
326
381
|
|
|
382
|
+
async detectQuoteTokenProgram(quoteMint: PublicKey): Promise<PublicKey> {
|
|
383
|
+
const quoteAddr = quoteMint.toBase58();
|
|
384
|
+
if (this.tokenProgramCache.has(quoteAddr)) {
|
|
385
|
+
return this.tokenProgramCache.get(quoteAddr)!.programId;
|
|
386
|
+
}
|
|
387
|
+
try {
|
|
388
|
+
await getMint(
|
|
389
|
+
this.connection,
|
|
390
|
+
quoteMint,
|
|
391
|
+
"confirmed",
|
|
392
|
+
TOKEN_2022_PROGRAM_ID,
|
|
393
|
+
);
|
|
394
|
+
return TOKEN_2022_PROGRAM_ID;
|
|
395
|
+
} catch {
|
|
396
|
+
return TOKEN_PROGRAM_ID;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
327
400
|
/* ---------- 内盘/外盘检测 ---------- */
|
|
328
401
|
|
|
329
402
|
/**
|
|
@@ -384,7 +457,7 @@ export class PumpTrader {
|
|
|
384
457
|
initialVirtualSolReserves: readU64(),
|
|
385
458
|
initialRealTokenReserves: readU64(),
|
|
386
459
|
tokenTotalSupply: readU64(),
|
|
387
|
-
feeBasisPoints: readU64()
|
|
460
|
+
feeBasisPoints: readU64(),
|
|
388
461
|
};
|
|
389
462
|
|
|
390
463
|
return this.globalState;
|
|
@@ -395,14 +468,14 @@ export class PumpTrader {
|
|
|
395
468
|
getBondingPda(mint: PublicKey): PublicKey {
|
|
396
469
|
return PublicKey.findProgramAddressSync(
|
|
397
470
|
[SEEDS.BONDING, mint.toBuffer()],
|
|
398
|
-
PROGRAM_IDS.PUMP
|
|
471
|
+
PROGRAM_IDS.PUMP,
|
|
399
472
|
)[0];
|
|
400
473
|
}
|
|
401
474
|
|
|
402
475
|
deriveBondingCurveV2(mint: PublicKey): PublicKey {
|
|
403
476
|
return PublicKey.findProgramAddressSync(
|
|
404
477
|
[Buffer.from("bonding-curve-v2"), mint.toBuffer()],
|
|
405
|
-
PROGRAM_IDS.PUMP
|
|
478
|
+
PROGRAM_IDS.PUMP,
|
|
406
479
|
)[0];
|
|
407
480
|
}
|
|
408
481
|
|
|
@@ -410,6 +483,25 @@ export class PumpTrader {
|
|
|
410
483
|
return PUMP_NEW_FEE_RECIPIENTS[index % PUMP_NEW_FEE_RECIPIENTS.length];
|
|
411
484
|
}
|
|
412
485
|
|
|
486
|
+
private pickBuybackFeeRecipient(index = 0): PublicKey {
|
|
487
|
+
return PUMP_BUYBACK_FEE_RECIPIENTS[
|
|
488
|
+
index % PUMP_BUYBACK_FEE_RECIPIENTS.length
|
|
489
|
+
];
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
private pickReservedFeeRecipient(index = 0): PublicKey {
|
|
493
|
+
return PUMP_RESERVED_FEE_RECIPIENTS[
|
|
494
|
+
index % PUMP_RESERVED_FEE_RECIPIENTS.length
|
|
495
|
+
];
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
getSharingConfigPda(mint: PublicKey): PublicKey {
|
|
499
|
+
return PublicKey.findProgramAddressSync(
|
|
500
|
+
[Buffer.from("sharing-config"), mint.toBuffer()],
|
|
501
|
+
PROGRAM_IDS.FEE,
|
|
502
|
+
)[0];
|
|
503
|
+
}
|
|
504
|
+
|
|
413
505
|
private buildBondingBuyKeys(args: {
|
|
414
506
|
global: PublicKey;
|
|
415
507
|
globalFeeRecipient: PublicKey;
|
|
@@ -436,7 +528,11 @@ export class PumpTrader {
|
|
|
436
528
|
{ pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
|
|
437
529
|
{ pubkey: args.mint, isSigner: false, isWritable: false },
|
|
438
530
|
{ pubkey: args.bonding, isSigner: false, isWritable: true },
|
|
439
|
-
{
|
|
531
|
+
{
|
|
532
|
+
pubkey: args.associatedBondingCurve,
|
|
533
|
+
isSigner: false,
|
|
534
|
+
isWritable: true,
|
|
535
|
+
},
|
|
440
536
|
{ pubkey: args.userAta, isSigner: false, isWritable: true },
|
|
441
537
|
{ pubkey: args.wallet, isSigner: true, isWritable: true },
|
|
442
538
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
@@ -444,12 +540,16 @@ export class PumpTrader {
|
|
|
444
540
|
{ pubkey: args.creatorVault, isSigner: false, isWritable: true },
|
|
445
541
|
{ pubkey: args.eventAuthority, isSigner: false, isWritable: false },
|
|
446
542
|
{ pubkey: args.pumpProgram, isSigner: false, isWritable: false },
|
|
447
|
-
{
|
|
543
|
+
{
|
|
544
|
+
pubkey: args.globalVolumeAccumulator,
|
|
545
|
+
isSigner: false,
|
|
546
|
+
isWritable: false,
|
|
547
|
+
},
|
|
448
548
|
{ pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
449
549
|
{ pubkey: args.feeConfig, isSigner: false, isWritable: false },
|
|
450
550
|
{ pubkey: args.feeProgram, isSigner: false, isWritable: false },
|
|
451
551
|
{ pubkey: args.bondingCurveV2, isSigner: false, isWritable: false },
|
|
452
|
-
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true }
|
|
552
|
+
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true },
|
|
453
553
|
];
|
|
454
554
|
}
|
|
455
555
|
|
|
@@ -478,7 +578,11 @@ export class PumpTrader {
|
|
|
478
578
|
{ pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
|
|
479
579
|
{ pubkey: args.mint, isSigner: false, isWritable: false },
|
|
480
580
|
{ pubkey: args.bonding, isSigner: false, isWritable: true },
|
|
481
|
-
{
|
|
581
|
+
{
|
|
582
|
+
pubkey: args.associatedBondingCurve,
|
|
583
|
+
isSigner: false,
|
|
584
|
+
isWritable: true,
|
|
585
|
+
},
|
|
482
586
|
{ pubkey: args.userAta, isSigner: false, isWritable: true },
|
|
483
587
|
{ pubkey: args.wallet, isSigner: true, isWritable: true },
|
|
484
588
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
@@ -487,16 +591,20 @@ export class PumpTrader {
|
|
|
487
591
|
{ pubkey: args.eventAuthority, isSigner: false, isWritable: false },
|
|
488
592
|
{ pubkey: args.pumpProgram, isSigner: false, isWritable: false },
|
|
489
593
|
{ pubkey: args.feeConfig, isSigner: false, isWritable: false },
|
|
490
|
-
{ pubkey: args.feeProgram, isSigner: false, isWritable: false }
|
|
594
|
+
{ pubkey: args.feeProgram, isSigner: false, isWritable: false },
|
|
491
595
|
];
|
|
492
596
|
|
|
493
597
|
if (args.isCashbackCoin) {
|
|
494
|
-
keys.push({
|
|
598
|
+
keys.push({
|
|
599
|
+
pubkey: args.userVolumeAccumulator,
|
|
600
|
+
isSigner: false,
|
|
601
|
+
isWritable: true,
|
|
602
|
+
});
|
|
495
603
|
}
|
|
496
604
|
|
|
497
605
|
keys.push(
|
|
498
606
|
{ pubkey: args.bondingCurveV2, isSigner: false, isWritable: false },
|
|
499
|
-
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true }
|
|
607
|
+
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true },
|
|
500
608
|
);
|
|
501
609
|
|
|
502
610
|
return keys;
|
|
@@ -522,10 +630,18 @@ export class PumpTrader {
|
|
|
522
630
|
const creator = new PublicKey(data.slice(offset, offset + 32));
|
|
523
631
|
offset += 32;
|
|
524
632
|
|
|
525
|
-
|
|
633
|
+
// V2 fields (115-byte bonding curve layout): quote_mint, virtual_quote_reserves, real_quote_reserves
|
|
634
|
+
// For legacy coins these may not be present, check remaining data length
|
|
635
|
+
if (offset + 32 <= data.length) {
|
|
526
636
|
state.quoteMint = new PublicKey(data.slice(offset, offset + 32));
|
|
527
637
|
offset += 32;
|
|
528
638
|
}
|
|
639
|
+
if (offset + 8 <= data.length) {
|
|
640
|
+
[state.virtualQuoteReserves, offset] = readU64(data, offset);
|
|
641
|
+
}
|
|
642
|
+
if (offset + 8 <= data.length) {
|
|
643
|
+
[state.realQuoteReserves, offset] = readU64(data, offset);
|
|
644
|
+
}
|
|
529
645
|
|
|
530
646
|
state.isMayhemMode = offset < data.length ? data[offset] === 1 : false;
|
|
531
647
|
offset += 1;
|
|
@@ -538,25 +654,29 @@ export class PumpTrader {
|
|
|
538
654
|
|
|
539
655
|
calcBuy(solIn: bigint, state: BondingCurveState): bigint {
|
|
540
656
|
const newVirtualSol = state.virtualSolReserves + solIn;
|
|
541
|
-
const newVirtualToken =
|
|
657
|
+
const newVirtualToken =
|
|
658
|
+
(state.virtualSolReserves * state.virtualTokenReserves) / newVirtualSol;
|
|
542
659
|
return state.virtualTokenReserves - newVirtualToken;
|
|
543
660
|
}
|
|
544
661
|
|
|
545
662
|
calcSell(tokenIn: bigint, state: BondingCurveState): bigint {
|
|
546
663
|
const newVirtualToken = state.virtualTokenReserves + tokenIn;
|
|
547
|
-
const newVirtualSol =
|
|
664
|
+
const newVirtualSol =
|
|
665
|
+
(state.virtualSolReserves * state.virtualTokenReserves) / newVirtualToken;
|
|
548
666
|
return state.virtualSolReserves - newVirtualSol;
|
|
549
667
|
}
|
|
550
668
|
|
|
551
669
|
calculateAmmBuyOutput(quoteIn: bigint, reserves: PoolReserves): bigint {
|
|
552
|
-
const quoteInAfterFee =
|
|
670
|
+
const quoteInAfterFee =
|
|
671
|
+
(quoteIn * (BPS_DENOMINATOR - AMM_FEE_BPS)) / BPS_DENOMINATOR;
|
|
553
672
|
const numerator = reserves.baseAmount * quoteInAfterFee;
|
|
554
673
|
const denominator = reserves.quoteAmount + quoteInAfterFee;
|
|
555
674
|
return numerator / denominator;
|
|
556
675
|
}
|
|
557
676
|
|
|
558
677
|
calculateAmmSellOutput(baseIn: bigint, reserves: PoolReserves): bigint {
|
|
559
|
-
const baseInAfterFee =
|
|
678
|
+
const baseInAfterFee =
|
|
679
|
+
(baseIn * (BPS_DENOMINATOR - AMM_FEE_BPS)) / BPS_DENOMINATOR;
|
|
560
680
|
const numerator = reserves.quoteAmount * baseInAfterFee;
|
|
561
681
|
const denominator = reserves.baseAmount + baseInAfterFee;
|
|
562
682
|
return numerator / denominator;
|
|
@@ -564,33 +684,39 @@ export class PumpTrader {
|
|
|
564
684
|
|
|
565
685
|
/* ---------- 价格查询 ---------- */
|
|
566
686
|
|
|
567
|
-
async getPriceAndStatus(
|
|
687
|
+
async getPriceAndStatus(
|
|
688
|
+
tokenAddr: string,
|
|
689
|
+
): Promise<{ price: number; completed: boolean }> {
|
|
568
690
|
const mint = new PublicKey(tokenAddr);
|
|
569
691
|
const { state } = await this.loadBonding(mint);
|
|
570
|
-
const quoteMint = this.getEffectiveQuoteMint(state.quoteMint);
|
|
571
692
|
|
|
572
693
|
if (state.complete) {
|
|
573
|
-
const price = await this.getAmmPrice(mint
|
|
694
|
+
const price = await this.getAmmPrice(mint);
|
|
574
695
|
return { price, completed: true };
|
|
575
696
|
}
|
|
576
697
|
|
|
577
698
|
const oneToken = BigInt(1_000_000);
|
|
578
|
-
const
|
|
579
|
-
const
|
|
580
|
-
const price = Number(quoteOut) / 10 ** quoteDecimals;
|
|
699
|
+
const solOut = this.calcSell(oneToken, state);
|
|
700
|
+
const price = Number(solOut) / 1e9;
|
|
581
701
|
return { price, completed: false };
|
|
582
702
|
}
|
|
583
703
|
|
|
584
|
-
async getAmmPrice(mint: PublicKey
|
|
704
|
+
async getAmmPrice(mint: PublicKey): Promise<number> {
|
|
585
705
|
const [poolCreator] = PublicKey.findProgramAddressSync(
|
|
586
706
|
[Buffer.from("pool-authority"), mint.toBuffer()],
|
|
587
|
-
PROGRAM_IDS.PUMP
|
|
707
|
+
PROGRAM_IDS.PUMP,
|
|
588
708
|
);
|
|
589
709
|
|
|
590
710
|
const indexBuffer = new BN(0).toArrayLike(Buffer, "le", 2);
|
|
591
711
|
const [pool] = PublicKey.findProgramAddressSync(
|
|
592
|
-
[
|
|
593
|
-
|
|
712
|
+
[
|
|
713
|
+
Buffer.from("pool"),
|
|
714
|
+
indexBuffer,
|
|
715
|
+
poolCreator.toBuffer(),
|
|
716
|
+
mint.toBuffer(),
|
|
717
|
+
SOL_MINT.toBuffer(),
|
|
718
|
+
],
|
|
719
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
594
720
|
);
|
|
595
721
|
|
|
596
722
|
const acc = await this.connection.getAccountInfo(pool);
|
|
@@ -599,34 +725,12 @@ export class PumpTrader {
|
|
|
599
725
|
const poolKeys = parsePoolKeys(acc.data);
|
|
600
726
|
const [baseInfo, quoteInfo] = await Promise.all([
|
|
601
727
|
this.connection.getTokenAccountBalance(poolKeys.poolBaseTokenAccount),
|
|
602
|
-
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount)
|
|
728
|
+
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
|
|
603
729
|
]);
|
|
604
730
|
|
|
605
731
|
return quoteInfo.value.uiAmount! / baseInfo.value.uiAmount!;
|
|
606
732
|
}
|
|
607
733
|
|
|
608
|
-
private getEffectiveQuoteMint(quoteMint?: PublicKey): PublicKey {
|
|
609
|
-
if (!quoteMint || quoteMint.equals(PublicKey.default)) {
|
|
610
|
-
return SOL_MINT;
|
|
611
|
-
}
|
|
612
|
-
return quoteMint;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
private async getMintDecimals(mint: PublicKey): Promise<number> {
|
|
616
|
-
if (mint.equals(SOL_MINT)) {
|
|
617
|
-
return 9;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
const mintInfo = await this.connection.getParsedAccountInfo(mint);
|
|
621
|
-
const parsedData = mintInfo.value?.data;
|
|
622
|
-
|
|
623
|
-
if (parsedData && "parsed" in parsedData) {
|
|
624
|
-
return parsedData.parsed.info.decimals;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
throw new Error(`Unable to determine mint decimals for ${mint.toBase58()}`);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
734
|
/* ---------- 余额查询 ---------- */
|
|
631
735
|
|
|
632
736
|
/**
|
|
@@ -634,15 +738,26 @@ export class PumpTrader {
|
|
|
634
738
|
* @param tokenAddr - 代币地址(可选),如果不传则返回所有代币
|
|
635
739
|
* @returns 如果传入地址则返回该代币的余额数字,否则返回所有代币的详细信息
|
|
636
740
|
*/
|
|
637
|
-
async tokenBalance(tokenAddr?: string): Promise<
|
|
741
|
+
async tokenBalance(tokenAddr?: string): Promise<
|
|
742
|
+
| number
|
|
743
|
+
| Array<{
|
|
744
|
+
mint: string;
|
|
745
|
+
amount: number;
|
|
746
|
+
decimals: number;
|
|
747
|
+
uiAmount: number;
|
|
748
|
+
}>
|
|
749
|
+
> {
|
|
638
750
|
if (tokenAddr) {
|
|
639
751
|
// 查询单个代币
|
|
640
752
|
const mint = new PublicKey(tokenAddr);
|
|
641
753
|
const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner(
|
|
642
754
|
this.wallet.publicKey,
|
|
643
|
-
{ mint }
|
|
755
|
+
{ mint },
|
|
756
|
+
);
|
|
757
|
+
return (
|
|
758
|
+
tokenAccounts.value[0]?.account.data.parsed.info.tokenAmount.uiAmount ||
|
|
759
|
+
0
|
|
644
760
|
);
|
|
645
|
-
return tokenAccounts.value[0]?.account.data.parsed.info.tokenAmount.uiAmount || 0;
|
|
646
761
|
} else {
|
|
647
762
|
// 查询所有代币
|
|
648
763
|
return this.getAllTokenBalances();
|
|
@@ -653,16 +768,18 @@ export class PumpTrader {
|
|
|
653
768
|
* 获取账户所有代币余额(仅显示余额 > 0 的)
|
|
654
769
|
* @returns 代币信息数组,包含mint地址、余额等信息
|
|
655
770
|
*/
|
|
656
|
-
async getAllTokenBalances(): Promise<
|
|
771
|
+
async getAllTokenBalances(): Promise<
|
|
772
|
+
Array<{ mint: string; amount: number; decimals: number; uiAmount: number }>
|
|
773
|
+
> {
|
|
657
774
|
const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner(
|
|
658
775
|
this.wallet.publicKey,
|
|
659
|
-
{ programId: TOKEN_PROGRAM_ID }
|
|
776
|
+
{ programId: TOKEN_PROGRAM_ID },
|
|
660
777
|
);
|
|
661
778
|
|
|
662
779
|
const balances = tokenAccounts.value
|
|
663
780
|
.map((account) => {
|
|
664
781
|
const parsed = account.account.data.parsed;
|
|
665
|
-
if (parsed.type !==
|
|
782
|
+
if (parsed.type !== "account") return null;
|
|
666
783
|
|
|
667
784
|
const tokenAmount = parsed.info.tokenAmount;
|
|
668
785
|
if (Number(tokenAmount.amount) === 0) return null; // 跳过余额为0的
|
|
@@ -671,21 +788,27 @@ export class PumpTrader {
|
|
|
671
788
|
mint: parsed.info.mint,
|
|
672
789
|
amount: BigInt(tokenAmount.amount),
|
|
673
790
|
decimals: tokenAmount.decimals,
|
|
674
|
-
uiAmount: tokenAmount.uiAmount || 0
|
|
791
|
+
uiAmount: tokenAmount.uiAmount || 0,
|
|
675
792
|
};
|
|
676
793
|
})
|
|
677
|
-
.filter((item) => item !== null) as Array<{
|
|
794
|
+
.filter((item) => item !== null) as Array<{
|
|
795
|
+
mint: string;
|
|
796
|
+
amount: bigint;
|
|
797
|
+
decimals: number;
|
|
798
|
+
uiAmount: number;
|
|
799
|
+
}>;
|
|
678
800
|
|
|
679
801
|
// 同时查询 TOKEN_2022_PROGRAM_ID
|
|
680
|
-
const token2022Accounts =
|
|
681
|
-
this.
|
|
682
|
-
|
|
683
|
-
|
|
802
|
+
const token2022Accounts =
|
|
803
|
+
await this.connection.getParsedTokenAccountsByOwner(
|
|
804
|
+
this.wallet.publicKey,
|
|
805
|
+
{ programId: TOKEN_2022_PROGRAM_ID },
|
|
806
|
+
);
|
|
684
807
|
|
|
685
808
|
const token2022Balances = token2022Accounts.value
|
|
686
809
|
.map((account) => {
|
|
687
810
|
const parsed = account.account.data.parsed;
|
|
688
|
-
if (parsed.type !==
|
|
811
|
+
if (parsed.type !== "account") return null;
|
|
689
812
|
|
|
690
813
|
const tokenAmount = parsed.info.tokenAmount;
|
|
691
814
|
if (Number(tokenAmount.amount) === 0) return null; // 跳过余额为0的
|
|
@@ -694,10 +817,15 @@ export class PumpTrader {
|
|
|
694
817
|
mint: parsed.info.mint,
|
|
695
818
|
amount: BigInt(tokenAmount.amount),
|
|
696
819
|
decimals: tokenAmount.decimals,
|
|
697
|
-
uiAmount: tokenAmount.uiAmount || 0
|
|
820
|
+
uiAmount: tokenAmount.uiAmount || 0,
|
|
698
821
|
};
|
|
699
822
|
})
|
|
700
|
-
.filter((item) => item !== null) as Array<{
|
|
823
|
+
.filter((item) => item !== null) as Array<{
|
|
824
|
+
mint: string;
|
|
825
|
+
amount: bigint;
|
|
826
|
+
decimals: number;
|
|
827
|
+
uiAmount: number;
|
|
828
|
+
}>;
|
|
701
829
|
|
|
702
830
|
// 合并并去重
|
|
703
831
|
const allBalances = [...balances, ...token2022Balances];
|
|
@@ -712,7 +840,7 @@ export class PumpTrader {
|
|
|
712
840
|
mint: b.mint,
|
|
713
841
|
amount: Number(b.amount),
|
|
714
842
|
decimals: b.decimals,
|
|
715
|
-
uiAmount: b.uiAmount
|
|
843
|
+
uiAmount: b.uiAmount,
|
|
716
844
|
}));
|
|
717
845
|
|
|
718
846
|
return uniqueBalances;
|
|
@@ -725,14 +853,18 @@ export class PumpTrader {
|
|
|
725
853
|
|
|
726
854
|
/* ---------- ATA 管理 ---------- */
|
|
727
855
|
|
|
728
|
-
async ensureAta(
|
|
856
|
+
async ensureAta(
|
|
857
|
+
tx: Transaction,
|
|
858
|
+
mint: PublicKey,
|
|
859
|
+
tokenProgram?: PublicKey,
|
|
860
|
+
): Promise<PublicKey> {
|
|
729
861
|
const program = tokenProgram || TOKEN_2022_PROGRAM_ID;
|
|
730
862
|
const ata = getAssociatedTokenAddressSync(
|
|
731
863
|
mint,
|
|
732
864
|
this.wallet.publicKey,
|
|
733
865
|
false,
|
|
734
866
|
program,
|
|
735
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
867
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
736
868
|
);
|
|
737
869
|
|
|
738
870
|
const acc = await this.connection.getAccountInfo(ata);
|
|
@@ -744,8 +876,8 @@ export class PumpTrader {
|
|
|
744
876
|
this.wallet.publicKey,
|
|
745
877
|
mint,
|
|
746
878
|
program,
|
|
747
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
748
|
-
)
|
|
879
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
880
|
+
),
|
|
749
881
|
);
|
|
750
882
|
}
|
|
751
883
|
|
|
@@ -756,14 +888,14 @@ export class PumpTrader {
|
|
|
756
888
|
tx: Transaction,
|
|
757
889
|
owner: PublicKey,
|
|
758
890
|
mode: "buy" | "sell",
|
|
759
|
-
lamports?: bigint
|
|
891
|
+
lamports?: bigint,
|
|
760
892
|
): Promise<PublicKey> {
|
|
761
893
|
const wsolAta = getAssociatedTokenAddressSync(
|
|
762
894
|
SOL_MINT,
|
|
763
895
|
owner,
|
|
764
896
|
false,
|
|
765
897
|
TOKEN_PROGRAM_ID,
|
|
766
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
898
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
767
899
|
);
|
|
768
900
|
|
|
769
901
|
const acc = await this.connection.getAccountInfo(wsolAta);
|
|
@@ -776,18 +908,18 @@ export class PumpTrader {
|
|
|
776
908
|
owner,
|
|
777
909
|
SOL_MINT,
|
|
778
910
|
TOKEN_PROGRAM_ID,
|
|
779
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
780
|
-
)
|
|
911
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
912
|
+
),
|
|
781
913
|
);
|
|
782
914
|
}
|
|
783
915
|
|
|
784
|
-
if (mode ===
|
|
916
|
+
if (mode === "buy" && lamports) {
|
|
785
917
|
tx.add(
|
|
786
918
|
SystemProgram.transfer({
|
|
787
919
|
fromPubkey: owner,
|
|
788
920
|
toPubkey: wsolAta,
|
|
789
|
-
lamports: Number(lamports)
|
|
790
|
-
})
|
|
921
|
+
lamports: Number(lamports),
|
|
922
|
+
}),
|
|
791
923
|
);
|
|
792
924
|
tx.add(createSyncNativeInstruction(wsolAta));
|
|
793
925
|
}
|
|
@@ -801,7 +933,9 @@ export class PumpTrader {
|
|
|
801
933
|
if (!priorityOpt?.enableRandom || !priorityOpt.randomRange) {
|
|
802
934
|
return priorityOpt.base;
|
|
803
935
|
}
|
|
804
|
-
return
|
|
936
|
+
return (
|
|
937
|
+
priorityOpt.base + Math.floor(Math.random() * priorityOpt.randomRange)
|
|
938
|
+
);
|
|
805
939
|
}
|
|
806
940
|
|
|
807
941
|
calcSlippage({ tradeSize, reserve, slippageOpt }: any): number {
|
|
@@ -849,31 +983,57 @@ export class PumpTrader {
|
|
|
849
983
|
|
|
850
984
|
/**
|
|
851
985
|
* 自动判断内盘/外盘并执行买入
|
|
986
|
+
* @param useV2 - use buy_v2 instruction (supports USDC quote) instead of legacy buy
|
|
987
|
+
* @param quoteMint - quote mint for V2 (SOL_MINT for SOL-paired, or USDC mint for USDC-paired)
|
|
852
988
|
*/
|
|
853
|
-
async autoBuy(
|
|
989
|
+
async autoBuy(
|
|
990
|
+
tokenAddr: string,
|
|
991
|
+
totalSolIn: bigint,
|
|
992
|
+
tradeOpt: TradeOptions,
|
|
993
|
+
useV2: boolean = false,
|
|
994
|
+
quoteMint: PublicKey = SOL_MINT,
|
|
995
|
+
): Promise<TradeResult> {
|
|
854
996
|
const mode = await this.getTradeMode(tokenAddr);
|
|
855
997
|
if (mode === "bonding") {
|
|
998
|
+
if (useV2) {
|
|
999
|
+
return this.buyV2(tokenAddr, totalSolIn, tradeOpt, quoteMint);
|
|
1000
|
+
}
|
|
856
1001
|
return this.buy(tokenAddr, totalSolIn, tradeOpt);
|
|
857
1002
|
} else {
|
|
858
|
-
return this.ammBuy(tokenAddr, totalSolIn, tradeOpt);
|
|
1003
|
+
return this.ammBuy(tokenAddr, totalSolIn, tradeOpt, quoteMint);
|
|
859
1004
|
}
|
|
860
1005
|
}
|
|
861
1006
|
|
|
862
1007
|
/**
|
|
863
1008
|
* 自动判断内盘/外盘并执行卖出
|
|
1009
|
+
* @param useV2 - use sell_v2 instruction (supports USDC quote) instead of legacy sell
|
|
1010
|
+
* @param quoteMint - quote mint for V2 (SOL_MINT for SOL-paired, or USDC mint for USDC-paired)
|
|
864
1011
|
*/
|
|
865
|
-
async autoSell(
|
|
1012
|
+
async autoSell(
|
|
1013
|
+
tokenAddr: string,
|
|
1014
|
+
totalTokenIn: bigint,
|
|
1015
|
+
tradeOpt: TradeOptions,
|
|
1016
|
+
useV2: boolean = false,
|
|
1017
|
+
quoteMint: PublicKey = SOL_MINT,
|
|
1018
|
+
): Promise<TradeResult> {
|
|
866
1019
|
const mode = await this.getTradeMode(tokenAddr);
|
|
867
1020
|
if (mode === "bonding") {
|
|
1021
|
+
if (useV2) {
|
|
1022
|
+
return this.sellV2(tokenAddr, totalTokenIn, tradeOpt, quoteMint);
|
|
1023
|
+
}
|
|
868
1024
|
return this.sell(tokenAddr, totalTokenIn, tradeOpt);
|
|
869
1025
|
} else {
|
|
870
|
-
return this.ammSell(tokenAddr, totalTokenIn, tradeOpt);
|
|
1026
|
+
return this.ammSell(tokenAddr, totalTokenIn, tradeOpt, quoteMint);
|
|
871
1027
|
}
|
|
872
1028
|
}
|
|
873
1029
|
|
|
874
1030
|
/* ---------- 内盘交易 ---------- */
|
|
875
1031
|
|
|
876
|
-
async buy(
|
|
1032
|
+
async buy(
|
|
1033
|
+
tokenAddr: string,
|
|
1034
|
+
totalSolIn: bigint,
|
|
1035
|
+
tradeOpt: TradeOptions,
|
|
1036
|
+
): Promise<TradeResult> {
|
|
877
1037
|
const mint = new PublicKey(tokenAddr);
|
|
878
1038
|
const tokenProgram = await this.detectTokenProgram(tokenAddr);
|
|
879
1039
|
const bondingCurveV2 = this.deriveBondingCurveV2(mint);
|
|
@@ -892,27 +1052,30 @@ export class PumpTrader {
|
|
|
892
1052
|
bonding,
|
|
893
1053
|
true,
|
|
894
1054
|
tokenProgram.programId,
|
|
895
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1055
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
896
1056
|
);
|
|
897
1057
|
|
|
898
1058
|
const [creatorVault] = PublicKey.findProgramAddressSync(
|
|
899
1059
|
[Buffer.from("creator-vault"), creator.toBuffer()],
|
|
900
|
-
PROGRAM_IDS.PUMP
|
|
1060
|
+
PROGRAM_IDS.PUMP,
|
|
901
1061
|
);
|
|
902
1062
|
|
|
903
1063
|
const [globalVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
904
1064
|
[Buffer.from("global_volume_accumulator")],
|
|
905
|
-
PROGRAM_IDS.PUMP
|
|
1065
|
+
PROGRAM_IDS.PUMP,
|
|
906
1066
|
);
|
|
907
1067
|
|
|
908
1068
|
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
909
|
-
[
|
|
910
|
-
|
|
1069
|
+
[
|
|
1070
|
+
Buffer.from("user_volume_accumulator"),
|
|
1071
|
+
this.wallet.publicKey.toBuffer(),
|
|
1072
|
+
],
|
|
1073
|
+
PROGRAM_IDS.PUMP,
|
|
911
1074
|
);
|
|
912
1075
|
|
|
913
1076
|
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
914
1077
|
[Buffer.from("fee_config"), SEEDS.FEE_CONFIG],
|
|
915
|
-
PROGRAM_IDS.FEE
|
|
1078
|
+
PROGRAM_IDS.FEE,
|
|
916
1079
|
);
|
|
917
1080
|
const feeRecipient = this.pickFeeRecipient();
|
|
918
1081
|
|
|
@@ -923,14 +1086,14 @@ export class PumpTrader {
|
|
|
923
1086
|
const slippageBps = this.calcSlippage({
|
|
924
1087
|
tradeSize: solIn,
|
|
925
1088
|
reserve: state.virtualSolReserves,
|
|
926
|
-
slippageOpt: tradeOpt.slippage
|
|
1089
|
+
slippageOpt: tradeOpt.slippage,
|
|
927
1090
|
});
|
|
928
1091
|
const maxSol = (solIn * BigInt(10_000 + slippageBps)) / 10_000n;
|
|
929
1092
|
const priority = this.genPriority(tradeOpt.priority);
|
|
930
1093
|
|
|
931
1094
|
const tx = new Transaction().add(
|
|
932
1095
|
ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
|
|
933
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority })
|
|
1096
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
934
1097
|
);
|
|
935
1098
|
|
|
936
1099
|
const userAta = await this.ensureAta(tx, mint, tokenProgram.programId);
|
|
@@ -955,32 +1118,39 @@ export class PumpTrader {
|
|
|
955
1118
|
feeProgram: PROGRAM_IDS.FEE,
|
|
956
1119
|
bondingCurveV2,
|
|
957
1120
|
feeRecipient,
|
|
958
|
-
tokenProgramId: tokenProgram.programId
|
|
1121
|
+
tokenProgramId: tokenProgram.programId,
|
|
959
1122
|
}),
|
|
960
|
-
data: Buffer.concat([
|
|
961
|
-
|
|
1123
|
+
data: Buffer.concat([
|
|
1124
|
+
DISCRIMINATORS.BUY,
|
|
1125
|
+
u64(tokenOut),
|
|
1126
|
+
u64(maxSol),
|
|
1127
|
+
]),
|
|
1128
|
+
}),
|
|
962
1129
|
);
|
|
963
1130
|
|
|
964
1131
|
const { blockhash, lastValidBlockHeight } =
|
|
965
|
-
await this.connection.getLatestBlockhash(
|
|
1132
|
+
await this.connection.getLatestBlockhash("finalized");
|
|
966
1133
|
tx.recentBlockhash = blockhash;
|
|
967
1134
|
tx.feePayer = this.wallet.publicKey;
|
|
968
1135
|
tx.sign(this.wallet);
|
|
969
1136
|
|
|
970
|
-
const signature = await this.connection.sendRawTransaction(
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1137
|
+
const signature = await this.connection.sendRawTransaction(
|
|
1138
|
+
tx.serialize(),
|
|
1139
|
+
{
|
|
1140
|
+
skipPreflight: false,
|
|
1141
|
+
maxRetries: 2,
|
|
1142
|
+
},
|
|
1143
|
+
);
|
|
974
1144
|
|
|
975
1145
|
pendingTransactions.push({
|
|
976
1146
|
signature,
|
|
977
1147
|
lastValidBlockHeight,
|
|
978
|
-
index: i
|
|
1148
|
+
index: i,
|
|
979
1149
|
});
|
|
980
1150
|
} catch (e) {
|
|
981
1151
|
failedTransactions.push({
|
|
982
1152
|
index: i,
|
|
983
|
-
error: (e as Error).message
|
|
1153
|
+
error: (e as Error).message,
|
|
984
1154
|
});
|
|
985
1155
|
}
|
|
986
1156
|
}
|
|
@@ -988,7 +1158,11 @@ export class PumpTrader {
|
|
|
988
1158
|
return { pendingTransactions, failedTransactions };
|
|
989
1159
|
}
|
|
990
1160
|
|
|
991
|
-
async sell(
|
|
1161
|
+
async sell(
|
|
1162
|
+
tokenAddr: string,
|
|
1163
|
+
totalTokenIn: bigint,
|
|
1164
|
+
tradeOpt: TradeOptions,
|
|
1165
|
+
): Promise<TradeResult> {
|
|
992
1166
|
const mint = new PublicKey(tokenAddr);
|
|
993
1167
|
const tokenProgram = await this.detectTokenProgram(tokenAddr);
|
|
994
1168
|
const bondingCurveV2 = this.deriveBondingCurveV2(mint);
|
|
@@ -999,12 +1173,15 @@ export class PumpTrader {
|
|
|
999
1173
|
if (state.complete) throw new Error("Bonding curve already completed");
|
|
1000
1174
|
|
|
1001
1175
|
const totalSolOut = this.calcSell(totalTokenIn, state);
|
|
1002
|
-
const tokenChunks =
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1176
|
+
const tokenChunks =
|
|
1177
|
+
totalSolOut <= tradeOpt.maxSolPerTx
|
|
1178
|
+
? [totalTokenIn]
|
|
1179
|
+
: this.splitIntoN(
|
|
1180
|
+
totalTokenIn,
|
|
1181
|
+
Number(
|
|
1182
|
+
(totalSolOut + tradeOpt.maxSolPerTx - 1n) / tradeOpt.maxSolPerTx,
|
|
1183
|
+
),
|
|
1184
|
+
);
|
|
1008
1185
|
|
|
1009
1186
|
const pendingTransactions: PendingTransaction[] = [];
|
|
1010
1187
|
const failedTransactions: FailedTransaction[] = [];
|
|
@@ -1014,7 +1191,7 @@ export class PumpTrader {
|
|
|
1014
1191
|
bonding,
|
|
1015
1192
|
true,
|
|
1016
1193
|
tokenProgram.programId,
|
|
1017
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1194
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1018
1195
|
);
|
|
1019
1196
|
|
|
1020
1197
|
const userAta = getAssociatedTokenAddressSync(
|
|
@@ -1022,22 +1199,25 @@ export class PumpTrader {
|
|
|
1022
1199
|
this.wallet.publicKey,
|
|
1023
1200
|
false,
|
|
1024
1201
|
tokenProgram.programId,
|
|
1025
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1202
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1026
1203
|
);
|
|
1027
1204
|
|
|
1028
1205
|
const [creatorVault] = PublicKey.findProgramAddressSync(
|
|
1029
1206
|
[Buffer.from("creator-vault"), creator.toBuffer()],
|
|
1030
|
-
PROGRAM_IDS.PUMP
|
|
1207
|
+
PROGRAM_IDS.PUMP,
|
|
1031
1208
|
);
|
|
1032
1209
|
|
|
1033
1210
|
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
1034
1211
|
[Buffer.from("fee_config"), SEEDS.FEE_CONFIG],
|
|
1035
|
-
PROGRAM_IDS.FEE
|
|
1212
|
+
PROGRAM_IDS.FEE,
|
|
1036
1213
|
);
|
|
1037
1214
|
|
|
1038
1215
|
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
1039
|
-
[
|
|
1040
|
-
|
|
1216
|
+
[
|
|
1217
|
+
Buffer.from("user_volume_accumulator"),
|
|
1218
|
+
this.wallet.publicKey.toBuffer(),
|
|
1219
|
+
],
|
|
1220
|
+
PROGRAM_IDS.PUMP,
|
|
1041
1221
|
);
|
|
1042
1222
|
const feeRecipient = this.pickFeeRecipient();
|
|
1043
1223
|
|
|
@@ -1048,14 +1228,14 @@ export class PumpTrader {
|
|
|
1048
1228
|
const slippageBps = this.calcSlippage({
|
|
1049
1229
|
tradeSize: tokenIn,
|
|
1050
1230
|
reserve: state.virtualTokenReserves,
|
|
1051
|
-
slippageOpt: tradeOpt.slippage
|
|
1231
|
+
slippageOpt: tradeOpt.slippage,
|
|
1052
1232
|
});
|
|
1053
1233
|
const minSol = (solOut * BigInt(10_000 - slippageBps)) / 10_000n;
|
|
1054
1234
|
const priority = this.genPriority(tradeOpt.priority);
|
|
1055
1235
|
|
|
1056
1236
|
const tx = new Transaction().add(
|
|
1057
1237
|
ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
|
|
1058
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority })
|
|
1238
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
1059
1239
|
);
|
|
1060
1240
|
|
|
1061
1241
|
tx.add(
|
|
@@ -1078,14 +1258,14 @@ export class PumpTrader {
|
|
|
1078
1258
|
feeRecipient,
|
|
1079
1259
|
isCashbackCoin: !!state.isCashbackCoin,
|
|
1080
1260
|
userVolumeAccumulator,
|
|
1081
|
-
tokenProgramId: tokenProgram.programId
|
|
1261
|
+
tokenProgramId: tokenProgram.programId,
|
|
1082
1262
|
}),
|
|
1083
1263
|
data: Buffer.concat([
|
|
1084
1264
|
DISCRIMINATORS.SELL,
|
|
1085
1265
|
u64(tokenIn),
|
|
1086
|
-
u64(minSol > 0n ? minSol : 1n)
|
|
1087
|
-
])
|
|
1088
|
-
})
|
|
1266
|
+
u64(minSol > 0n ? minSol : 1n),
|
|
1267
|
+
]),
|
|
1268
|
+
}),
|
|
1089
1269
|
);
|
|
1090
1270
|
|
|
1091
1271
|
const { blockhash, lastValidBlockHeight } =
|
|
@@ -1094,16 +1274,18 @@ export class PumpTrader {
|
|
|
1094
1274
|
tx.feePayer = this.wallet.publicKey;
|
|
1095
1275
|
tx.sign(this.wallet);
|
|
1096
1276
|
|
|
1097
|
-
const signature = await this.connection.sendRawTransaction(
|
|
1277
|
+
const signature = await this.connection.sendRawTransaction(
|
|
1278
|
+
tx.serialize(),
|
|
1279
|
+
);
|
|
1098
1280
|
pendingTransactions.push({
|
|
1099
1281
|
signature,
|
|
1100
1282
|
lastValidBlockHeight,
|
|
1101
|
-
index: i
|
|
1283
|
+
index: i,
|
|
1102
1284
|
});
|
|
1103
1285
|
} catch (e) {
|
|
1104
1286
|
failedTransactions.push({
|
|
1105
1287
|
index: i,
|
|
1106
|
-
error: (e as Error).message
|
|
1288
|
+
error: (e as Error).message,
|
|
1107
1289
|
});
|
|
1108
1290
|
}
|
|
1109
1291
|
}
|
|
@@ -1113,12 +1295,21 @@ export class PumpTrader {
|
|
|
1113
1295
|
|
|
1114
1296
|
/* ---------- 外盘交易 ---------- */
|
|
1115
1297
|
|
|
1116
|
-
async ammBuy(
|
|
1298
|
+
async ammBuy(
|
|
1299
|
+
tokenAddr: string,
|
|
1300
|
+
totalSolIn: bigint,
|
|
1301
|
+
tradeOpt: TradeOptions,
|
|
1302
|
+
quoteMint: PublicKey = SOL_MINT,
|
|
1303
|
+
): Promise<TradeResult> {
|
|
1117
1304
|
const mint = new PublicKey(tokenAddr);
|
|
1118
|
-
const poolInfo = await this.getAmmPoolInfo(mint);
|
|
1305
|
+
const poolInfo = await this.getAmmPoolInfo(mint, quoteMint);
|
|
1119
1306
|
const reserves = await this.getAmmPoolReserves(poolInfo.poolKeys);
|
|
1120
1307
|
const solChunks = this.splitByMax(totalSolIn, tradeOpt.maxSolPerTx);
|
|
1121
1308
|
const tokenProgram = await this.detectTokenProgram(tokenAddr);
|
|
1309
|
+
const isSolQuote = quoteMint.equals(SOL_MINT);
|
|
1310
|
+
const quoteTokenProgramId = isSolQuote
|
|
1311
|
+
? TOKEN_PROGRAM_ID
|
|
1312
|
+
: await this.detectQuoteTokenProgram(quoteMint);
|
|
1122
1313
|
const pendingTransactions: PendingTransaction[] = [];
|
|
1123
1314
|
const failedTransactions: FailedTransaction[] = [];
|
|
1124
1315
|
|
|
@@ -1129,23 +1320,29 @@ export class PumpTrader {
|
|
|
1129
1320
|
const slippageBps = this.calcSlippage({
|
|
1130
1321
|
tradeSize: solIn,
|
|
1131
1322
|
reserve: reserves.quoteAmount,
|
|
1132
|
-
slippageOpt: tradeOpt.slippage
|
|
1323
|
+
slippageOpt: tradeOpt.slippage,
|
|
1133
1324
|
});
|
|
1134
1325
|
const maxQuoteIn = (solIn * BigInt(10_000 + slippageBps)) / 10_000n;
|
|
1135
1326
|
const priority = this.genPriority(tradeOpt.priority);
|
|
1136
1327
|
|
|
1137
1328
|
const tx = new Transaction().add(
|
|
1138
1329
|
ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }),
|
|
1139
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority })
|
|
1330
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
1140
1331
|
);
|
|
1141
1332
|
|
|
1142
|
-
const userBaseAta = await this.ensureAta(
|
|
1143
|
-
const userQuoteAta = await this.ensureWSOLAta(
|
|
1333
|
+
const userBaseAta = await this.ensureAta(
|
|
1144
1334
|
tx,
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
maxQuoteIn
|
|
1335
|
+
poolInfo.poolKeys.baseMint,
|
|
1336
|
+
tokenProgram.programId,
|
|
1148
1337
|
);
|
|
1338
|
+
const userQuoteAta = isSolQuote
|
|
1339
|
+
? await this.ensureWSOLAta(
|
|
1340
|
+
tx,
|
|
1341
|
+
this.wallet.publicKey,
|
|
1342
|
+
"buy",
|
|
1343
|
+
maxQuoteIn,
|
|
1344
|
+
)
|
|
1345
|
+
: await this.ensureAta(tx, quoteMint, quoteTokenProgramId);
|
|
1149
1346
|
|
|
1150
1347
|
const buyIx = this.createAmmBuyInstruction(
|
|
1151
1348
|
poolInfo,
|
|
@@ -1153,38 +1350,43 @@ export class PumpTrader {
|
|
|
1153
1350
|
userQuoteAta,
|
|
1154
1351
|
baseAmountOut,
|
|
1155
1352
|
maxQuoteIn,
|
|
1156
|
-
tokenProgram.programId
|
|
1353
|
+
tokenProgram.programId,
|
|
1157
1354
|
);
|
|
1158
1355
|
|
|
1159
1356
|
tx.add(buyIx);
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1357
|
+
if (isSolQuote) {
|
|
1358
|
+
tx.add(
|
|
1359
|
+
createCloseAccountInstruction(
|
|
1360
|
+
userQuoteAta,
|
|
1361
|
+
this.wallet.publicKey,
|
|
1362
|
+
this.wallet.publicKey,
|
|
1363
|
+
),
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1167
1366
|
|
|
1168
1367
|
const { blockhash, lastValidBlockHeight } =
|
|
1169
|
-
await this.connection.getLatestBlockhash(
|
|
1368
|
+
await this.connection.getLatestBlockhash("finalized");
|
|
1170
1369
|
tx.recentBlockhash = blockhash;
|
|
1171
1370
|
tx.feePayer = this.wallet.publicKey;
|
|
1172
1371
|
tx.sign(this.wallet);
|
|
1173
1372
|
|
|
1174
|
-
const signature = await this.connection.sendRawTransaction(
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1373
|
+
const signature = await this.connection.sendRawTransaction(
|
|
1374
|
+
tx.serialize(),
|
|
1375
|
+
{
|
|
1376
|
+
skipPreflight: false,
|
|
1377
|
+
maxRetries: 2,
|
|
1378
|
+
},
|
|
1379
|
+
);
|
|
1178
1380
|
|
|
1179
1381
|
pendingTransactions.push({
|
|
1180
1382
|
signature,
|
|
1181
1383
|
lastValidBlockHeight,
|
|
1182
|
-
index: i
|
|
1384
|
+
index: i,
|
|
1183
1385
|
});
|
|
1184
1386
|
} catch (e) {
|
|
1185
1387
|
failedTransactions.push({
|
|
1186
1388
|
index: i,
|
|
1187
|
-
error: (e as Error).message
|
|
1389
|
+
error: (e as Error).message,
|
|
1188
1390
|
});
|
|
1189
1391
|
}
|
|
1190
1392
|
}
|
|
@@ -1192,18 +1394,30 @@ export class PumpTrader {
|
|
|
1192
1394
|
return { pendingTransactions, failedTransactions };
|
|
1193
1395
|
}
|
|
1194
1396
|
|
|
1195
|
-
async ammSell(
|
|
1397
|
+
async ammSell(
|
|
1398
|
+
tokenAddr: string,
|
|
1399
|
+
totalTokenIn: bigint,
|
|
1400
|
+
tradeOpt: TradeOptions,
|
|
1401
|
+
quoteMint: PublicKey = SOL_MINT,
|
|
1402
|
+
): Promise<TradeResult> {
|
|
1196
1403
|
const mint = new PublicKey(tokenAddr);
|
|
1197
|
-
const poolInfo = await this.getAmmPoolInfo(mint);
|
|
1404
|
+
const poolInfo = await this.getAmmPoolInfo(mint, quoteMint);
|
|
1198
1405
|
const reserves = await this.getAmmPoolReserves(poolInfo.poolKeys);
|
|
1199
1406
|
const totalSolOut = this.calculateAmmSellOutput(totalTokenIn, reserves);
|
|
1200
1407
|
const tokenProgram = await this.detectTokenProgram(tokenAddr);
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1408
|
+
const isSolQuote = quoteMint.equals(SOL_MINT);
|
|
1409
|
+
const quoteTokenProgramId = isSolQuote
|
|
1410
|
+
? TOKEN_PROGRAM_ID
|
|
1411
|
+
: await this.detectQuoteTokenProgram(quoteMint);
|
|
1412
|
+
const tokenChunks =
|
|
1413
|
+
totalSolOut <= tradeOpt.maxSolPerTx
|
|
1414
|
+
? [totalTokenIn]
|
|
1415
|
+
: this.splitIntoN(
|
|
1416
|
+
totalTokenIn,
|
|
1417
|
+
Number(
|
|
1418
|
+
(totalSolOut + tradeOpt.maxSolPerTx - 1n) / tradeOpt.maxSolPerTx,
|
|
1419
|
+
),
|
|
1420
|
+
);
|
|
1207
1421
|
|
|
1208
1422
|
const pendingTransactions: PendingTransaction[] = [];
|
|
1209
1423
|
const failedTransactions: FailedTransaction[] = [];
|
|
@@ -1215,18 +1429,24 @@ export class PumpTrader {
|
|
|
1215
1429
|
const slippageBps = this.calcSlippage({
|
|
1216
1430
|
tradeSize: tokenIn,
|
|
1217
1431
|
reserve: reserves.baseAmount,
|
|
1218
|
-
slippageOpt: tradeOpt.slippage
|
|
1432
|
+
slippageOpt: tradeOpt.slippage,
|
|
1219
1433
|
});
|
|
1220
1434
|
const minQuoteOut = (solOut * BigInt(10_000 - slippageBps)) / 10_000n;
|
|
1221
1435
|
const priority = this.genPriority(tradeOpt.priority);
|
|
1222
1436
|
|
|
1223
1437
|
const tx = new Transaction().add(
|
|
1224
1438
|
ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }),
|
|
1225
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority })
|
|
1439
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
1226
1440
|
);
|
|
1227
1441
|
|
|
1228
|
-
const userBaseAta = await this.ensureAta(
|
|
1229
|
-
|
|
1442
|
+
const userBaseAta = await this.ensureAta(
|
|
1443
|
+
tx,
|
|
1444
|
+
poolInfo.poolKeys.baseMint,
|
|
1445
|
+
tokenProgram.programId,
|
|
1446
|
+
);
|
|
1447
|
+
const userQuoteAta = isSolQuote
|
|
1448
|
+
? await this.ensureWSOLAta(tx, this.wallet.publicKey, "sell")
|
|
1449
|
+
: await this.ensureAta(tx, quoteMint, quoteTokenProgramId);
|
|
1230
1450
|
|
|
1231
1451
|
const sellIx = this.createAmmSellInstruction(
|
|
1232
1452
|
poolInfo,
|
|
@@ -1234,38 +1454,43 @@ export class PumpTrader {
|
|
|
1234
1454
|
userQuoteAta,
|
|
1235
1455
|
tokenIn,
|
|
1236
1456
|
minQuoteOut,
|
|
1237
|
-
tokenProgram.programId
|
|
1457
|
+
tokenProgram.programId,
|
|
1238
1458
|
);
|
|
1239
1459
|
|
|
1240
1460
|
tx.add(sellIx);
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1461
|
+
if (isSolQuote) {
|
|
1462
|
+
tx.add(
|
|
1463
|
+
createCloseAccountInstruction(
|
|
1464
|
+
userQuoteAta,
|
|
1465
|
+
this.wallet.publicKey,
|
|
1466
|
+
this.wallet.publicKey,
|
|
1467
|
+
),
|
|
1468
|
+
);
|
|
1469
|
+
}
|
|
1248
1470
|
|
|
1249
1471
|
const { blockhash, lastValidBlockHeight } =
|
|
1250
|
-
await this.connection.getLatestBlockhash(
|
|
1472
|
+
await this.connection.getLatestBlockhash("finalized");
|
|
1251
1473
|
tx.recentBlockhash = blockhash;
|
|
1252
1474
|
tx.feePayer = this.wallet.publicKey;
|
|
1253
1475
|
tx.sign(this.wallet);
|
|
1254
1476
|
|
|
1255
|
-
const signature = await this.connection.sendRawTransaction(
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1477
|
+
const signature = await this.connection.sendRawTransaction(
|
|
1478
|
+
tx.serialize(),
|
|
1479
|
+
{
|
|
1480
|
+
skipPreflight: false,
|
|
1481
|
+
maxRetries: 2,
|
|
1482
|
+
},
|
|
1483
|
+
);
|
|
1259
1484
|
|
|
1260
1485
|
pendingTransactions.push({
|
|
1261
1486
|
signature,
|
|
1262
1487
|
lastValidBlockHeight,
|
|
1263
|
-
index: i
|
|
1488
|
+
index: i,
|
|
1264
1489
|
});
|
|
1265
1490
|
} catch (e) {
|
|
1266
1491
|
failedTransactions.push({
|
|
1267
1492
|
index: i,
|
|
1268
|
-
error: (e as Error).message
|
|
1493
|
+
error: (e as Error).message,
|
|
1269
1494
|
});
|
|
1270
1495
|
}
|
|
1271
1496
|
}
|
|
@@ -1275,10 +1500,13 @@ export class PumpTrader {
|
|
|
1275
1500
|
|
|
1276
1501
|
/* ---------- AMM 池信息 ---------- */
|
|
1277
1502
|
|
|
1278
|
-
async getAmmPoolInfo(
|
|
1503
|
+
async getAmmPoolInfo(
|
|
1504
|
+
mint: PublicKey,
|
|
1505
|
+
quoteMint: PublicKey = SOL_MINT,
|
|
1506
|
+
): Promise<PoolInfo> {
|
|
1279
1507
|
const [poolAuthority] = PublicKey.findProgramAddressSync(
|
|
1280
1508
|
[Buffer.from("pool-authority"), mint.toBuffer()],
|
|
1281
|
-
PROGRAM_IDS.PUMP
|
|
1509
|
+
PROGRAM_IDS.PUMP,
|
|
1282
1510
|
);
|
|
1283
1511
|
|
|
1284
1512
|
const [pool] = PublicKey.findProgramAddressSync(
|
|
@@ -1287,9 +1515,9 @@ export class PumpTrader {
|
|
|
1287
1515
|
new BN(0).toArrayLike(Buffer, "le", 2),
|
|
1288
1516
|
poolAuthority.toBuffer(),
|
|
1289
1517
|
mint.toBuffer(),
|
|
1290
|
-
|
|
1518
|
+
quoteMint.toBuffer(),
|
|
1291
1519
|
],
|
|
1292
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1520
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1293
1521
|
);
|
|
1294
1522
|
|
|
1295
1523
|
const acc = await this.connection.getAccountInfo(pool);
|
|
@@ -1299,13 +1527,17 @@ export class PumpTrader {
|
|
|
1299
1527
|
|
|
1300
1528
|
const [globalConfigPda] = PublicKey.findProgramAddressSync(
|
|
1301
1529
|
[Buffer.from("global_config")],
|
|
1302
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1530
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1303
1531
|
);
|
|
1304
1532
|
|
|
1305
|
-
const globalConfigAcc =
|
|
1533
|
+
const globalConfigAcc =
|
|
1534
|
+
await this.connection.getAccountInfo(globalConfigPda);
|
|
1306
1535
|
if (!globalConfigAcc) throw new Error("Global config not found");
|
|
1307
1536
|
|
|
1308
|
-
const globalConfig = this.parseAmmGlobalConfig(
|
|
1537
|
+
const globalConfig = this.parseAmmGlobalConfig(
|
|
1538
|
+
globalConfigAcc.data,
|
|
1539
|
+
globalConfigPda,
|
|
1540
|
+
);
|
|
1309
1541
|
|
|
1310
1542
|
return { pool, poolAuthority, poolKeys, globalConfig };
|
|
1311
1543
|
}
|
|
@@ -1322,7 +1554,9 @@ export class PumpTrader {
|
|
|
1322
1554
|
|
|
1323
1555
|
const protocolFeeRecipients: PublicKey[] = [];
|
|
1324
1556
|
for (let i = 0; i < 8; i++) {
|
|
1325
|
-
protocolFeeRecipients.push(
|
|
1557
|
+
protocolFeeRecipients.push(
|
|
1558
|
+
new PublicKey(data.slice(offset, offset + 32)),
|
|
1559
|
+
);
|
|
1326
1560
|
offset += 32;
|
|
1327
1561
|
}
|
|
1328
1562
|
|
|
@@ -1332,21 +1566,21 @@ export class PumpTrader {
|
|
|
1332
1566
|
async getAmmPoolReserves(poolKeys: any): Promise<PoolReserves> {
|
|
1333
1567
|
const [baseInfo, quoteInfo] = await Promise.all([
|
|
1334
1568
|
this.connection.getTokenAccountBalance(poolKeys.poolBaseTokenAccount),
|
|
1335
|
-
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount)
|
|
1569
|
+
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
|
|
1336
1570
|
]);
|
|
1337
1571
|
|
|
1338
1572
|
return {
|
|
1339
1573
|
baseAmount: BigInt(baseInfo.value.amount),
|
|
1340
1574
|
quoteAmount: BigInt(quoteInfo.value.amount),
|
|
1341
1575
|
baseDecimals: baseInfo.value.decimals,
|
|
1342
|
-
quoteDecimals: quoteInfo.value.decimals
|
|
1576
|
+
quoteDecimals: quoteInfo.value.decimals,
|
|
1343
1577
|
};
|
|
1344
1578
|
}
|
|
1345
1579
|
|
|
1346
1580
|
deriveAmmPoolV2(baseMint: PublicKey): PublicKey {
|
|
1347
1581
|
return PublicKey.findProgramAddressSync(
|
|
1348
1582
|
[Buffer.from("pool-v2"), baseMint.toBuffer()],
|
|
1349
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1583
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1350
1584
|
)[0];
|
|
1351
1585
|
}
|
|
1352
1586
|
|
|
@@ -1358,19 +1592,19 @@ export class PumpTrader {
|
|
|
1358
1592
|
userQuoteAta: PublicKey,
|
|
1359
1593
|
baseAmountOut: bigint,
|
|
1360
1594
|
maxQuoteAmountIn: bigint,
|
|
1361
|
-
tokenProgramId: PublicKey
|
|
1595
|
+
tokenProgramId: PublicKey,
|
|
1362
1596
|
): TransactionInstruction {
|
|
1363
1597
|
const { pool, poolKeys, globalConfig } = poolInfo;
|
|
1364
1598
|
const poolV2 = this.deriveAmmPoolV2(poolKeys.baseMint);
|
|
1365
1599
|
|
|
1366
1600
|
const [eventAuthority] = PublicKey.findProgramAddressSync(
|
|
1367
1601
|
[Buffer.from("__event_authority")],
|
|
1368
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1602
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1369
1603
|
);
|
|
1370
1604
|
|
|
1371
1605
|
const [coinCreatorVaultAuthority] = PublicKey.findProgramAddressSync(
|
|
1372
1606
|
[Buffer.from("creator_vault"), poolKeys.coinCreator.toBuffer()],
|
|
1373
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1607
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1374
1608
|
);
|
|
1375
1609
|
|
|
1376
1610
|
const coinCreatorVaultAta = getAssociatedTokenAddressSync(
|
|
@@ -1378,22 +1612,25 @@ export class PumpTrader {
|
|
|
1378
1612
|
coinCreatorVaultAuthority,
|
|
1379
1613
|
true,
|
|
1380
1614
|
TOKEN_PROGRAM_ID,
|
|
1381
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1615
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1382
1616
|
);
|
|
1383
1617
|
|
|
1384
1618
|
const [globalVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
1385
1619
|
[Buffer.from("global_volume_accumulator")],
|
|
1386
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1620
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1387
1621
|
);
|
|
1388
1622
|
|
|
1389
1623
|
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
1390
|
-
[
|
|
1391
|
-
|
|
1624
|
+
[
|
|
1625
|
+
Buffer.from("user_volume_accumulator"),
|
|
1626
|
+
this.wallet.publicKey.toBuffer(),
|
|
1627
|
+
],
|
|
1628
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1392
1629
|
);
|
|
1393
1630
|
|
|
1394
1631
|
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
1395
1632
|
[Buffer.from("fee_config"), SEEDS.AMM_FEE_CONFIG],
|
|
1396
|
-
PROGRAM_IDS.FEE
|
|
1633
|
+
PROGRAM_IDS.FEE,
|
|
1397
1634
|
);
|
|
1398
1635
|
|
|
1399
1636
|
const protocolFeeRecipient = globalConfig.protocolFeeRecipients[0];
|
|
@@ -1402,7 +1639,7 @@ export class PumpTrader {
|
|
|
1402
1639
|
protocolFeeRecipient,
|
|
1403
1640
|
true,
|
|
1404
1641
|
TOKEN_PROGRAM_ID,
|
|
1405
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1642
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1406
1643
|
);
|
|
1407
1644
|
const newFeeRecipient = this.pickFeeRecipient();
|
|
1408
1645
|
const newFeeRecipientTokenAccount = getAssociatedTokenAddressSync(
|
|
@@ -1410,7 +1647,7 @@ export class PumpTrader {
|
|
|
1410
1647
|
newFeeRecipient,
|
|
1411
1648
|
true,
|
|
1412
1649
|
TOKEN_PROGRAM_ID,
|
|
1413
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1650
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1414
1651
|
);
|
|
1415
1652
|
|
|
1416
1653
|
const remainingKeys = [];
|
|
@@ -1420,14 +1657,22 @@ export class PumpTrader {
|
|
|
1420
1657
|
userVolumeAccumulator,
|
|
1421
1658
|
true,
|
|
1422
1659
|
TOKEN_PROGRAM_ID,
|
|
1423
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1660
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1424
1661
|
);
|
|
1425
|
-
remainingKeys.push({
|
|
1662
|
+
remainingKeys.push({
|
|
1663
|
+
pubkey: userVolumeAccumulatorWsolAta,
|
|
1664
|
+
isSigner: false,
|
|
1665
|
+
isWritable: true,
|
|
1666
|
+
});
|
|
1426
1667
|
}
|
|
1427
1668
|
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
1428
1669
|
remainingKeys.push(
|
|
1429
1670
|
{ pubkey: newFeeRecipient, isSigner: false, isWritable: false },
|
|
1430
|
-
{
|
|
1671
|
+
{
|
|
1672
|
+
pubkey: newFeeRecipientTokenAccount,
|
|
1673
|
+
isSigner: false,
|
|
1674
|
+
isWritable: true,
|
|
1675
|
+
},
|
|
1431
1676
|
);
|
|
1432
1677
|
|
|
1433
1678
|
return new TransactionInstruction({
|
|
@@ -1440,30 +1685,50 @@ export class PumpTrader {
|
|
|
1440
1685
|
{ pubkey: poolKeys.quoteMint, isSigner: false, isWritable: false },
|
|
1441
1686
|
{ pubkey: userBaseAta, isSigner: false, isWritable: true },
|
|
1442
1687
|
{ pubkey: userQuoteAta, isSigner: false, isWritable: true },
|
|
1443
|
-
{
|
|
1444
|
-
|
|
1688
|
+
{
|
|
1689
|
+
pubkey: poolKeys.poolBaseTokenAccount,
|
|
1690
|
+
isSigner: false,
|
|
1691
|
+
isWritable: true,
|
|
1692
|
+
},
|
|
1693
|
+
{
|
|
1694
|
+
pubkey: poolKeys.poolQuoteTokenAccount,
|
|
1695
|
+
isSigner: false,
|
|
1696
|
+
isWritable: true,
|
|
1697
|
+
},
|
|
1445
1698
|
{ pubkey: protocolFeeRecipient, isSigner: false, isWritable: false },
|
|
1446
|
-
{
|
|
1699
|
+
{
|
|
1700
|
+
pubkey: protocolFeeRecipientTokenAccount,
|
|
1701
|
+
isSigner: false,
|
|
1702
|
+
isWritable: true,
|
|
1703
|
+
},
|
|
1447
1704
|
{ pubkey: tokenProgramId, isSigner: false, isWritable: false },
|
|
1448
1705
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1449
1706
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1450
|
-
{
|
|
1707
|
+
{
|
|
1708
|
+
pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1709
|
+
isSigner: false,
|
|
1710
|
+
isWritable: false,
|
|
1711
|
+
},
|
|
1451
1712
|
{ pubkey: eventAuthority, isSigner: false, isWritable: false },
|
|
1452
1713
|
{ pubkey: PROGRAM_IDS.PUMP_AMM, isSigner: false, isWritable: false },
|
|
1453
1714
|
{ pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true },
|
|
1454
|
-
{
|
|
1715
|
+
{
|
|
1716
|
+
pubkey: coinCreatorVaultAuthority,
|
|
1717
|
+
isSigner: false,
|
|
1718
|
+
isWritable: false,
|
|
1719
|
+
},
|
|
1455
1720
|
{ pubkey: globalVolumeAccumulator, isSigner: false, isWritable: false },
|
|
1456
1721
|
{ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
1457
1722
|
{ pubkey: feeConfig, isSigner: false, isWritable: false },
|
|
1458
1723
|
{ pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
|
|
1459
|
-
...remainingKeys
|
|
1724
|
+
...remainingKeys,
|
|
1460
1725
|
],
|
|
1461
1726
|
data: Buffer.concat([
|
|
1462
1727
|
DISCRIMINATORS.BUY,
|
|
1463
1728
|
u64(baseAmountOut),
|
|
1464
1729
|
u64(maxQuoteAmountIn),
|
|
1465
|
-
Buffer.from([1, 1])
|
|
1466
|
-
])
|
|
1730
|
+
Buffer.from([1, 1]),
|
|
1731
|
+
]),
|
|
1467
1732
|
});
|
|
1468
1733
|
}
|
|
1469
1734
|
|
|
@@ -1473,19 +1738,19 @@ export class PumpTrader {
|
|
|
1473
1738
|
userQuoteAta: PublicKey,
|
|
1474
1739
|
baseAmountIn: bigint,
|
|
1475
1740
|
minQuoteAmountOut: bigint,
|
|
1476
|
-
tokenProgramId: PublicKey
|
|
1741
|
+
tokenProgramId: PublicKey,
|
|
1477
1742
|
): TransactionInstruction {
|
|
1478
1743
|
const { pool, poolKeys, globalConfig } = poolInfo;
|
|
1479
1744
|
const poolV2 = this.deriveAmmPoolV2(poolKeys.baseMint);
|
|
1480
1745
|
|
|
1481
1746
|
const [eventAuthority] = PublicKey.findProgramAddressSync(
|
|
1482
1747
|
[Buffer.from("__event_authority")],
|
|
1483
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1748
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1484
1749
|
);
|
|
1485
1750
|
|
|
1486
1751
|
const [coinCreatorVaultAuthority] = PublicKey.findProgramAddressSync(
|
|
1487
1752
|
[Buffer.from("creator_vault"), poolKeys.coinCreator.toBuffer()],
|
|
1488
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1753
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1489
1754
|
);
|
|
1490
1755
|
|
|
1491
1756
|
const coinCreatorVaultAta = getAssociatedTokenAddressSync(
|
|
@@ -1493,12 +1758,12 @@ export class PumpTrader {
|
|
|
1493
1758
|
coinCreatorVaultAuthority,
|
|
1494
1759
|
true,
|
|
1495
1760
|
TOKEN_PROGRAM_ID,
|
|
1496
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1761
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1497
1762
|
);
|
|
1498
1763
|
|
|
1499
1764
|
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
1500
1765
|
[Buffer.from("fee_config"), SEEDS.AMM_FEE_CONFIG],
|
|
1501
|
-
PROGRAM_IDS.FEE
|
|
1766
|
+
PROGRAM_IDS.FEE,
|
|
1502
1767
|
);
|
|
1503
1768
|
|
|
1504
1769
|
const protocolFeeRecipient = globalConfig.protocolFeeRecipients[0];
|
|
@@ -1507,7 +1772,7 @@ export class PumpTrader {
|
|
|
1507
1772
|
protocolFeeRecipient,
|
|
1508
1773
|
true,
|
|
1509
1774
|
TOKEN_PROGRAM_ID,
|
|
1510
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1775
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1511
1776
|
);
|
|
1512
1777
|
const newFeeRecipient = this.pickFeeRecipient();
|
|
1513
1778
|
const newFeeRecipientTokenAccount = getAssociatedTokenAddressSync(
|
|
@@ -1515,12 +1780,15 @@ export class PumpTrader {
|
|
|
1515
1780
|
newFeeRecipient,
|
|
1516
1781
|
true,
|
|
1517
1782
|
TOKEN_PROGRAM_ID,
|
|
1518
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1783
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1519
1784
|
);
|
|
1520
1785
|
|
|
1521
1786
|
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
1522
|
-
[
|
|
1523
|
-
|
|
1787
|
+
[
|
|
1788
|
+
Buffer.from("user_volume_accumulator"),
|
|
1789
|
+
this.wallet.publicKey.toBuffer(),
|
|
1790
|
+
],
|
|
1791
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1524
1792
|
);
|
|
1525
1793
|
|
|
1526
1794
|
const userVolumeAccumulatorWsolAta = getAssociatedTokenAddressSync(
|
|
@@ -1528,20 +1796,28 @@ export class PumpTrader {
|
|
|
1528
1796
|
userVolumeAccumulator,
|
|
1529
1797
|
true,
|
|
1530
1798
|
TOKEN_PROGRAM_ID,
|
|
1531
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1799
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1532
1800
|
);
|
|
1533
1801
|
|
|
1534
1802
|
const remainingKeys = [];
|
|
1535
1803
|
if (poolKeys.isCashbackCoin) {
|
|
1536
1804
|
remainingKeys.push(
|
|
1537
|
-
{
|
|
1538
|
-
|
|
1805
|
+
{
|
|
1806
|
+
pubkey: userVolumeAccumulatorWsolAta,
|
|
1807
|
+
isSigner: false,
|
|
1808
|
+
isWritable: true,
|
|
1809
|
+
},
|
|
1810
|
+
{ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
1539
1811
|
);
|
|
1540
1812
|
}
|
|
1541
1813
|
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
1542
1814
|
remainingKeys.push(
|
|
1543
1815
|
{ pubkey: newFeeRecipient, isSigner: false, isWritable: false },
|
|
1544
|
-
{
|
|
1816
|
+
{
|
|
1817
|
+
pubkey: newFeeRecipientTokenAccount,
|
|
1818
|
+
isSigner: false,
|
|
1819
|
+
isWritable: true,
|
|
1820
|
+
},
|
|
1545
1821
|
);
|
|
1546
1822
|
|
|
1547
1823
|
return new TransactionInstruction({
|
|
@@ -1554,71 +1830,819 @@ export class PumpTrader {
|
|
|
1554
1830
|
{ pubkey: poolKeys.quoteMint, isSigner: false, isWritable: false },
|
|
1555
1831
|
{ pubkey: userBaseAta, isSigner: false, isWritable: true },
|
|
1556
1832
|
{ pubkey: userQuoteAta, isSigner: false, isWritable: true },
|
|
1557
|
-
{
|
|
1558
|
-
|
|
1833
|
+
{
|
|
1834
|
+
pubkey: poolKeys.poolBaseTokenAccount,
|
|
1835
|
+
isSigner: false,
|
|
1836
|
+
isWritable: true,
|
|
1837
|
+
},
|
|
1838
|
+
{
|
|
1839
|
+
pubkey: poolKeys.poolQuoteTokenAccount,
|
|
1840
|
+
isSigner: false,
|
|
1841
|
+
isWritable: true,
|
|
1842
|
+
},
|
|
1559
1843
|
{ pubkey: protocolFeeRecipient, isSigner: false, isWritable: false },
|
|
1560
|
-
{
|
|
1844
|
+
{
|
|
1845
|
+
pubkey: protocolFeeRecipientTokenAccount,
|
|
1846
|
+
isSigner: false,
|
|
1847
|
+
isWritable: true,
|
|
1848
|
+
},
|
|
1561
1849
|
{ pubkey: tokenProgramId, isSigner: false, isWritable: false },
|
|
1562
1850
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1563
1851
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1564
|
-
{
|
|
1852
|
+
{
|
|
1853
|
+
pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1854
|
+
isSigner: false,
|
|
1855
|
+
isWritable: false,
|
|
1856
|
+
},
|
|
1565
1857
|
{ pubkey: eventAuthority, isSigner: false, isWritable: false },
|
|
1566
1858
|
{ pubkey: PROGRAM_IDS.PUMP_AMM, isSigner: false, isWritable: false },
|
|
1567
1859
|
{ pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true },
|
|
1568
|
-
{
|
|
1860
|
+
{
|
|
1861
|
+
pubkey: coinCreatorVaultAuthority,
|
|
1862
|
+
isSigner: false,
|
|
1863
|
+
isWritable: false,
|
|
1864
|
+
},
|
|
1569
1865
|
{ pubkey: feeConfig, isSigner: false, isWritable: false },
|
|
1570
1866
|
{ pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
|
|
1571
|
-
...remainingKeys
|
|
1867
|
+
...remainingKeys,
|
|
1572
1868
|
],
|
|
1573
1869
|
data: Buffer.concat([
|
|
1574
1870
|
DISCRIMINATORS.SELL,
|
|
1575
1871
|
u64(baseAmountIn),
|
|
1576
|
-
u64(minQuoteAmountOut > 0n ? minQuoteAmountOut : 1n)
|
|
1577
|
-
])
|
|
1872
|
+
u64(minQuoteAmountOut > 0n ? minQuoteAmountOut : 1n),
|
|
1873
|
+
]),
|
|
1578
1874
|
});
|
|
1579
1875
|
}
|
|
1580
1876
|
|
|
1877
|
+
/* ---------- V2 指令账户构建 ---------- */
|
|
1878
|
+
|
|
1879
|
+
/**
|
|
1880
|
+
* Build accounts for buy_v2 instruction (27 accounts)
|
|
1881
|
+
* Ref: https://github.com/pump-fun/pump-public-docs/blob/main/docs/instructions/BUY.md
|
|
1882
|
+
*/
|
|
1883
|
+
private buildBondingBuyV2Keys(args: {
|
|
1884
|
+
global: PublicKey;
|
|
1885
|
+
baseMint: PublicKey;
|
|
1886
|
+
quoteMint: PublicKey;
|
|
1887
|
+
baseTokenProgram: PublicKey;
|
|
1888
|
+
quoteTokenProgram: PublicKey;
|
|
1889
|
+
feeRecipient: PublicKey;
|
|
1890
|
+
associatedQuoteFeeRecipient: PublicKey;
|
|
1891
|
+
buybackFeeRecipient: PublicKey;
|
|
1892
|
+
associatedQuoteBuybackFeeRecipient: PublicKey;
|
|
1893
|
+
bondingCurve: PublicKey;
|
|
1894
|
+
associatedBaseBondingCurve: PublicKey;
|
|
1895
|
+
associatedQuoteBondingCurve: PublicKey;
|
|
1896
|
+
user: PublicKey;
|
|
1897
|
+
associatedBaseUser: PublicKey;
|
|
1898
|
+
associatedQuoteUser: PublicKey;
|
|
1899
|
+
creatorVault: PublicKey;
|
|
1900
|
+
associatedCreatorVault: PublicKey;
|
|
1901
|
+
sharingConfig: PublicKey;
|
|
1902
|
+
globalVolumeAccumulator: PublicKey;
|
|
1903
|
+
userVolumeAccumulator: PublicKey;
|
|
1904
|
+
associatedUserVolumeAccumulator: PublicKey;
|
|
1905
|
+
feeConfig: PublicKey;
|
|
1906
|
+
feeProgram: PublicKey;
|
|
1907
|
+
eventAuthority: PublicKey;
|
|
1908
|
+
pumpProgram: PublicKey;
|
|
1909
|
+
}): AccountMeta[] {
|
|
1910
|
+
return [
|
|
1911
|
+
{ pubkey: args.global, isSigner: false, isWritable: false },
|
|
1912
|
+
{ pubkey: args.baseMint, isSigner: false, isWritable: false },
|
|
1913
|
+
{ pubkey: args.quoteMint, isSigner: false, isWritable: false },
|
|
1914
|
+
{ pubkey: args.baseTokenProgram, isSigner: false, isWritable: false },
|
|
1915
|
+
{ pubkey: args.quoteTokenProgram, isSigner: false, isWritable: false },
|
|
1916
|
+
{
|
|
1917
|
+
pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1918
|
+
isSigner: false,
|
|
1919
|
+
isWritable: false,
|
|
1920
|
+
},
|
|
1921
|
+
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true },
|
|
1922
|
+
{
|
|
1923
|
+
pubkey: args.associatedQuoteFeeRecipient,
|
|
1924
|
+
isSigner: false,
|
|
1925
|
+
isWritable: true,
|
|
1926
|
+
},
|
|
1927
|
+
{ pubkey: args.buybackFeeRecipient, isSigner: false, isWritable: true },
|
|
1928
|
+
{
|
|
1929
|
+
pubkey: args.associatedQuoteBuybackFeeRecipient,
|
|
1930
|
+
isSigner: false,
|
|
1931
|
+
isWritable: true,
|
|
1932
|
+
},
|
|
1933
|
+
{ pubkey: args.bondingCurve, isSigner: false, isWritable: true },
|
|
1934
|
+
{
|
|
1935
|
+
pubkey: args.associatedBaseBondingCurve,
|
|
1936
|
+
isSigner: false,
|
|
1937
|
+
isWritable: true,
|
|
1938
|
+
},
|
|
1939
|
+
{
|
|
1940
|
+
pubkey: args.associatedQuoteBondingCurve,
|
|
1941
|
+
isSigner: false,
|
|
1942
|
+
isWritable: true,
|
|
1943
|
+
},
|
|
1944
|
+
{ pubkey: args.user, isSigner: true, isWritable: true },
|
|
1945
|
+
{ pubkey: args.associatedBaseUser, isSigner: false, isWritable: true },
|
|
1946
|
+
{ pubkey: args.associatedQuoteUser, isSigner: false, isWritable: true },
|
|
1947
|
+
{ pubkey: args.creatorVault, isSigner: false, isWritable: true },
|
|
1948
|
+
{
|
|
1949
|
+
pubkey: args.associatedCreatorVault,
|
|
1950
|
+
isSigner: false,
|
|
1951
|
+
isWritable: true,
|
|
1952
|
+
},
|
|
1953
|
+
{ pubkey: args.sharingConfig, isSigner: false, isWritable: false },
|
|
1954
|
+
{
|
|
1955
|
+
pubkey: args.globalVolumeAccumulator,
|
|
1956
|
+
isSigner: false,
|
|
1957
|
+
isWritable: false,
|
|
1958
|
+
},
|
|
1959
|
+
{ pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
1960
|
+
{
|
|
1961
|
+
pubkey: args.associatedUserVolumeAccumulator,
|
|
1962
|
+
isSigner: false,
|
|
1963
|
+
isWritable: true,
|
|
1964
|
+
},
|
|
1965
|
+
{ pubkey: args.feeConfig, isSigner: false, isWritable: false },
|
|
1966
|
+
{ pubkey: args.feeProgram, isSigner: false, isWritable: false },
|
|
1967
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1968
|
+
{ pubkey: args.eventAuthority, isSigner: false, isWritable: false },
|
|
1969
|
+
{ pubkey: args.pumpProgram, isSigner: false, isWritable: false },
|
|
1970
|
+
];
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
/**
|
|
1974
|
+
* Build accounts for sell_v2 instruction (26 accounts)
|
|
1975
|
+
* Ref: https://github.com/pump-fun/pump-public-docs/blob/main/docs/instructions/SELL.md
|
|
1976
|
+
*/
|
|
1977
|
+
private buildBondingSellV2Keys(args: {
|
|
1978
|
+
global: PublicKey;
|
|
1979
|
+
baseMint: PublicKey;
|
|
1980
|
+
quoteMint: PublicKey;
|
|
1981
|
+
baseTokenProgram: PublicKey;
|
|
1982
|
+
quoteTokenProgram: PublicKey;
|
|
1983
|
+
feeRecipient: PublicKey;
|
|
1984
|
+
associatedQuoteFeeRecipient: PublicKey;
|
|
1985
|
+
buybackFeeRecipient: PublicKey;
|
|
1986
|
+
associatedQuoteBuybackFeeRecipient: PublicKey;
|
|
1987
|
+
bondingCurve: PublicKey;
|
|
1988
|
+
associatedBaseBondingCurve: PublicKey;
|
|
1989
|
+
associatedQuoteBondingCurve: PublicKey;
|
|
1990
|
+
user: PublicKey;
|
|
1991
|
+
associatedBaseUser: PublicKey;
|
|
1992
|
+
associatedQuoteUser: PublicKey;
|
|
1993
|
+
creatorVault: PublicKey;
|
|
1994
|
+
associatedCreatorVault: PublicKey;
|
|
1995
|
+
sharingConfig: PublicKey;
|
|
1996
|
+
userVolumeAccumulator: PublicKey;
|
|
1997
|
+
associatedUserVolumeAccumulator: PublicKey;
|
|
1998
|
+
feeConfig: PublicKey;
|
|
1999
|
+
feeProgram: PublicKey;
|
|
2000
|
+
eventAuthority: PublicKey;
|
|
2001
|
+
pumpProgram: PublicKey;
|
|
2002
|
+
}): AccountMeta[] {
|
|
2003
|
+
return [
|
|
2004
|
+
{ pubkey: args.global, isSigner: false, isWritable: false },
|
|
2005
|
+
{ pubkey: args.baseMint, isSigner: false, isWritable: false },
|
|
2006
|
+
{ pubkey: args.quoteMint, isSigner: false, isWritable: false },
|
|
2007
|
+
{ pubkey: args.baseTokenProgram, isSigner: false, isWritable: false },
|
|
2008
|
+
{ pubkey: args.quoteTokenProgram, isSigner: false, isWritable: false },
|
|
2009
|
+
{
|
|
2010
|
+
pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2011
|
+
isSigner: false,
|
|
2012
|
+
isWritable: false,
|
|
2013
|
+
},
|
|
2014
|
+
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true },
|
|
2015
|
+
{
|
|
2016
|
+
pubkey: args.associatedQuoteFeeRecipient,
|
|
2017
|
+
isSigner: false,
|
|
2018
|
+
isWritable: true,
|
|
2019
|
+
},
|
|
2020
|
+
{ pubkey: args.buybackFeeRecipient, isSigner: false, isWritable: true },
|
|
2021
|
+
{
|
|
2022
|
+
pubkey: args.associatedQuoteBuybackFeeRecipient,
|
|
2023
|
+
isSigner: false,
|
|
2024
|
+
isWritable: true,
|
|
2025
|
+
},
|
|
2026
|
+
{ pubkey: args.bondingCurve, isSigner: false, isWritable: true },
|
|
2027
|
+
{
|
|
2028
|
+
pubkey: args.associatedBaseBondingCurve,
|
|
2029
|
+
isSigner: false,
|
|
2030
|
+
isWritable: true,
|
|
2031
|
+
},
|
|
2032
|
+
{
|
|
2033
|
+
pubkey: args.associatedQuoteBondingCurve,
|
|
2034
|
+
isSigner: false,
|
|
2035
|
+
isWritable: true,
|
|
2036
|
+
},
|
|
2037
|
+
{ pubkey: args.user, isSigner: true, isWritable: true },
|
|
2038
|
+
{ pubkey: args.associatedBaseUser, isSigner: false, isWritable: true },
|
|
2039
|
+
{ pubkey: args.associatedQuoteUser, isSigner: false, isWritable: true },
|
|
2040
|
+
{ pubkey: args.creatorVault, isSigner: false, isWritable: true },
|
|
2041
|
+
{
|
|
2042
|
+
pubkey: args.associatedCreatorVault,
|
|
2043
|
+
isSigner: false,
|
|
2044
|
+
isWritable: true,
|
|
2045
|
+
},
|
|
2046
|
+
{ pubkey: args.sharingConfig, isSigner: false, isWritable: false },
|
|
2047
|
+
{ pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
2048
|
+
{
|
|
2049
|
+
pubkey: args.associatedUserVolumeAccumulator,
|
|
2050
|
+
isSigner: false,
|
|
2051
|
+
isWritable: true,
|
|
2052
|
+
},
|
|
2053
|
+
{ pubkey: args.feeConfig, isSigner: false, isWritable: false },
|
|
2054
|
+
{ pubkey: args.feeProgram, isSigner: false, isWritable: false },
|
|
2055
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
2056
|
+
{ pubkey: args.eventAuthority, isSigner: false, isWritable: false },
|
|
2057
|
+
{ pubkey: args.pumpProgram, isSigner: false, isWritable: false },
|
|
2058
|
+
];
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
/* ---------- V2 交易 ---------- */
|
|
2062
|
+
|
|
2063
|
+
/**
|
|
2064
|
+
* Buy using buy_v2 instruction (supports both SOL-paired and USDC-paired coins)
|
|
2065
|
+
* For SOL-paired coins: quoteMint = SOL_MINT, quoteTokenProgram = TOKEN_PROGRAM_ID
|
|
2066
|
+
* For USDC-paired coins: quoteMint = USDC mint, quoteTokenProgram = TOKEN_PROGRAM_ID
|
|
2067
|
+
*/
|
|
2068
|
+
async buyV2(
|
|
2069
|
+
tokenAddr: string,
|
|
2070
|
+
totalQuoteIn: bigint,
|
|
2071
|
+
tradeOpt: TradeOptions,
|
|
2072
|
+
quoteMint: PublicKey = SOL_MINT,
|
|
2073
|
+
): Promise<TradeResult> {
|
|
2074
|
+
const baseMint = new PublicKey(tokenAddr);
|
|
2075
|
+
const baseTokenProgram = await this.detectTokenProgram(tokenAddr);
|
|
2076
|
+
const quoteTokenProgramId = quoteMint.equals(SOL_MINT)
|
|
2077
|
+
? TOKEN_PROGRAM_ID
|
|
2078
|
+
: TOKEN_PROGRAM_ID;
|
|
2079
|
+
|
|
2080
|
+
if (!this.globalState) await this.loadGlobal();
|
|
2081
|
+
|
|
2082
|
+
const { bonding, state, creator } = await this.loadBonding(baseMint);
|
|
2083
|
+
if (state.complete) throw new Error("Bonding curve already completed");
|
|
2084
|
+
|
|
2085
|
+
const solEquivalent = quoteMint.equals(SOL_MINT) ? totalQuoteIn : 0n;
|
|
2086
|
+
const quoteChunks =
|
|
2087
|
+
solEquivalent > 0n
|
|
2088
|
+
? this.splitByMax(solEquivalent, tradeOpt.maxSolPerTx)
|
|
2089
|
+
: this.splitByMax(totalQuoteIn, tradeOpt.maxSolPerTx);
|
|
2090
|
+
|
|
2091
|
+
const pendingTransactions: PendingTransaction[] = [];
|
|
2092
|
+
const failedTransactions: FailedTransaction[] = [];
|
|
2093
|
+
|
|
2094
|
+
// Pre-compute PDAs
|
|
2095
|
+
const associatedBaseBondingCurve = getAssociatedTokenAddressSync(
|
|
2096
|
+
baseMint,
|
|
2097
|
+
bonding,
|
|
2098
|
+
true,
|
|
2099
|
+
baseTokenProgram.programId,
|
|
2100
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2101
|
+
);
|
|
2102
|
+
const associatedQuoteBondingCurve = getAssociatedTokenAddressSync(
|
|
2103
|
+
quoteMint,
|
|
2104
|
+
bonding,
|
|
2105
|
+
true,
|
|
2106
|
+
quoteTokenProgramId,
|
|
2107
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2108
|
+
);
|
|
2109
|
+
|
|
2110
|
+
const [creatorVault] = PublicKey.findProgramAddressSync(
|
|
2111
|
+
[Buffer.from("creator-vault"), creator.toBuffer()],
|
|
2112
|
+
PROGRAM_IDS.PUMP,
|
|
2113
|
+
);
|
|
2114
|
+
const associatedCreatorVault = getAssociatedTokenAddressSync(
|
|
2115
|
+
quoteMint,
|
|
2116
|
+
creatorVault,
|
|
2117
|
+
true,
|
|
2118
|
+
quoteTokenProgramId,
|
|
2119
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2120
|
+
);
|
|
2121
|
+
|
|
2122
|
+
const [globalVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
2123
|
+
[Buffer.from("global_volume_accumulator")],
|
|
2124
|
+
PROGRAM_IDS.PUMP,
|
|
2125
|
+
);
|
|
2126
|
+
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
2127
|
+
[
|
|
2128
|
+
Buffer.from("user_volume_accumulator"),
|
|
2129
|
+
this.wallet.publicKey.toBuffer(),
|
|
2130
|
+
],
|
|
2131
|
+
PROGRAM_IDS.PUMP,
|
|
2132
|
+
);
|
|
2133
|
+
const associatedUserVolumeAccumulator = getAssociatedTokenAddressSync(
|
|
2134
|
+
quoteMint,
|
|
2135
|
+
userVolumeAccumulator,
|
|
2136
|
+
true,
|
|
2137
|
+
quoteTokenProgramId,
|
|
2138
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2139
|
+
);
|
|
2140
|
+
|
|
2141
|
+
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
2142
|
+
[Buffer.from("fee_config"), SEEDS.FEE_CONFIG],
|
|
2143
|
+
PROGRAM_IDS.FEE,
|
|
2144
|
+
);
|
|
2145
|
+
|
|
2146
|
+
const sharingConfig = this.getSharingConfigPda(baseMint);
|
|
2147
|
+
const feeRecipient = this.pickFeeRecipient();
|
|
2148
|
+
const associatedQuoteFeeRecipient = getAssociatedTokenAddressSync(
|
|
2149
|
+
quoteMint,
|
|
2150
|
+
feeRecipient,
|
|
2151
|
+
true,
|
|
2152
|
+
quoteTokenProgramId,
|
|
2153
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2154
|
+
);
|
|
2155
|
+
const buybackFeeRecipient = this.pickBuybackFeeRecipient();
|
|
2156
|
+
const associatedQuoteBuybackFeeRecipient = getAssociatedTokenAddressSync(
|
|
2157
|
+
quoteMint,
|
|
2158
|
+
buybackFeeRecipient,
|
|
2159
|
+
true,
|
|
2160
|
+
quoteTokenProgramId,
|
|
2161
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2162
|
+
);
|
|
2163
|
+
|
|
2164
|
+
const associatedQuoteUser = getAssociatedTokenAddressSync(
|
|
2165
|
+
quoteMint,
|
|
2166
|
+
this.wallet.publicKey,
|
|
2167
|
+
false,
|
|
2168
|
+
quoteTokenProgramId,
|
|
2169
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2170
|
+
);
|
|
2171
|
+
|
|
2172
|
+
for (let i = 0; i < quoteChunks.length; i++) {
|
|
2173
|
+
try {
|
|
2174
|
+
const quoteIn = quoteChunks[i];
|
|
2175
|
+
const tokenOut = this.calcBuy(quoteIn, state);
|
|
2176
|
+
const slippageBps = this.calcSlippage({
|
|
2177
|
+
tradeSize: quoteIn,
|
|
2178
|
+
reserve: state.virtualSolReserves,
|
|
2179
|
+
slippageOpt: tradeOpt.slippage,
|
|
2180
|
+
});
|
|
2181
|
+
const maxQuoteCost = (quoteIn * BigInt(10_000 + slippageBps)) / 10_000n;
|
|
2182
|
+
const priority = this.genPriority(tradeOpt.priority);
|
|
2183
|
+
|
|
2184
|
+
const tx = new Transaction().add(
|
|
2185
|
+
ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }),
|
|
2186
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
2187
|
+
);
|
|
2188
|
+
|
|
2189
|
+
// For SOL-paired coins, need wSOL ATA for the user's quote account
|
|
2190
|
+
if (quoteMint.equals(SOL_MINT)) {
|
|
2191
|
+
await this.ensureWSOLAta(
|
|
2192
|
+
tx,
|
|
2193
|
+
this.wallet.publicKey,
|
|
2194
|
+
"buy",
|
|
2195
|
+
maxQuoteCost,
|
|
2196
|
+
);
|
|
2197
|
+
} else {
|
|
2198
|
+
// For non-SOL quote (e.g. USDC), ensure user has the quote token ATA
|
|
2199
|
+
const userQuoteAta = getAssociatedTokenAddressSync(
|
|
2200
|
+
quoteMint,
|
|
2201
|
+
this.wallet.publicKey,
|
|
2202
|
+
false,
|
|
2203
|
+
quoteTokenProgramId,
|
|
2204
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2205
|
+
);
|
|
2206
|
+
const acc = await this.connection.getAccountInfo(userQuoteAta);
|
|
2207
|
+
if (!acc) {
|
|
2208
|
+
tx.add(
|
|
2209
|
+
createAssociatedTokenAccountInstruction(
|
|
2210
|
+
this.wallet.publicKey,
|
|
2211
|
+
userQuoteAta,
|
|
2212
|
+
this.wallet.publicKey,
|
|
2213
|
+
quoteMint,
|
|
2214
|
+
quoteTokenProgramId,
|
|
2215
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2216
|
+
),
|
|
2217
|
+
);
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
const userBaseAta = await this.ensureAta(
|
|
2222
|
+
tx,
|
|
2223
|
+
baseMint,
|
|
2224
|
+
baseTokenProgram.programId,
|
|
2225
|
+
);
|
|
2226
|
+
|
|
2227
|
+
tx.add(
|
|
2228
|
+
new TransactionInstruction({
|
|
2229
|
+
programId: PROGRAM_IDS.PUMP,
|
|
2230
|
+
keys: this.buildBondingBuyV2Keys({
|
|
2231
|
+
global: this.global,
|
|
2232
|
+
baseMint,
|
|
2233
|
+
quoteMint,
|
|
2234
|
+
baseTokenProgram: baseTokenProgram.programId,
|
|
2235
|
+
quoteTokenProgram: quoteTokenProgramId,
|
|
2236
|
+
feeRecipient,
|
|
2237
|
+
associatedQuoteFeeRecipient,
|
|
2238
|
+
buybackFeeRecipient,
|
|
2239
|
+
associatedQuoteBuybackFeeRecipient,
|
|
2240
|
+
bondingCurve: bonding,
|
|
2241
|
+
associatedBaseBondingCurve,
|
|
2242
|
+
associatedQuoteBondingCurve,
|
|
2243
|
+
user: this.wallet.publicKey,
|
|
2244
|
+
associatedBaseUser: userBaseAta,
|
|
2245
|
+
associatedQuoteUser,
|
|
2246
|
+
creatorVault,
|
|
2247
|
+
associatedCreatorVault,
|
|
2248
|
+
sharingConfig,
|
|
2249
|
+
globalVolumeAccumulator,
|
|
2250
|
+
userVolumeAccumulator,
|
|
2251
|
+
associatedUserVolumeAccumulator,
|
|
2252
|
+
feeConfig,
|
|
2253
|
+
feeProgram: PROGRAM_IDS.FEE,
|
|
2254
|
+
eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
|
|
2255
|
+
pumpProgram: PROGRAM_IDS.PUMP,
|
|
2256
|
+
}),
|
|
2257
|
+
data: Buffer.concat([
|
|
2258
|
+
DISCRIMINATORS.BUY_V2,
|
|
2259
|
+
u64(tokenOut),
|
|
2260
|
+
u64(maxQuoteCost),
|
|
2261
|
+
]),
|
|
2262
|
+
}),
|
|
2263
|
+
);
|
|
2264
|
+
|
|
2265
|
+
// Close wSOL ATA after buy for SOL-paired coins
|
|
2266
|
+
if (quoteMint.equals(SOL_MINT)) {
|
|
2267
|
+
const wsolAta = getAssociatedTokenAddressSync(
|
|
2268
|
+
SOL_MINT,
|
|
2269
|
+
this.wallet.publicKey,
|
|
2270
|
+
false,
|
|
2271
|
+
TOKEN_PROGRAM_ID,
|
|
2272
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2273
|
+
);
|
|
2274
|
+
tx.add(
|
|
2275
|
+
createCloseAccountInstruction(
|
|
2276
|
+
wsolAta,
|
|
2277
|
+
this.wallet.publicKey,
|
|
2278
|
+
this.wallet.publicKey,
|
|
2279
|
+
),
|
|
2280
|
+
);
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
const { blockhash, lastValidBlockHeight } =
|
|
2284
|
+
await this.connection.getLatestBlockhash("finalized");
|
|
2285
|
+
tx.recentBlockhash = blockhash;
|
|
2286
|
+
tx.feePayer = this.wallet.publicKey;
|
|
2287
|
+
tx.sign(this.wallet);
|
|
2288
|
+
|
|
2289
|
+
const signature = await this.connection.sendRawTransaction(
|
|
2290
|
+
tx.serialize(),
|
|
2291
|
+
{
|
|
2292
|
+
skipPreflight: false,
|
|
2293
|
+
maxRetries: 2,
|
|
2294
|
+
},
|
|
2295
|
+
);
|
|
2296
|
+
|
|
2297
|
+
pendingTransactions.push({ signature, lastValidBlockHeight, index: i });
|
|
2298
|
+
} catch (e) {
|
|
2299
|
+
failedTransactions.push({ index: i, error: (e as Error).message });
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
return { pendingTransactions, failedTransactions };
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
/**
|
|
2307
|
+
* Sell using sell_v2 instruction (supports both SOL-paired and USDC-paired coins)
|
|
2308
|
+
*/
|
|
2309
|
+
async sellV2(
|
|
2310
|
+
tokenAddr: string,
|
|
2311
|
+
totalTokenIn: bigint,
|
|
2312
|
+
tradeOpt: TradeOptions,
|
|
2313
|
+
quoteMint: PublicKey = SOL_MINT,
|
|
2314
|
+
): Promise<TradeResult> {
|
|
2315
|
+
const baseMint = new PublicKey(tokenAddr);
|
|
2316
|
+
const baseTokenProgram = await this.detectTokenProgram(tokenAddr);
|
|
2317
|
+
const quoteTokenProgramId = quoteMint.equals(SOL_MINT)
|
|
2318
|
+
? TOKEN_PROGRAM_ID
|
|
2319
|
+
: TOKEN_PROGRAM_ID;
|
|
2320
|
+
|
|
2321
|
+
if (!this.globalState) await this.loadGlobal();
|
|
2322
|
+
|
|
2323
|
+
const { bonding, state, creator } = await this.loadBonding(baseMint);
|
|
2324
|
+
if (state.complete) throw new Error("Bonding curve already completed");
|
|
2325
|
+
|
|
2326
|
+
const totalQuoteOut = this.calcSell(totalTokenIn, state);
|
|
2327
|
+
const tokenChunks =
|
|
2328
|
+
totalQuoteOut <= tradeOpt.maxSolPerTx
|
|
2329
|
+
? [totalTokenIn]
|
|
2330
|
+
: this.splitIntoN(
|
|
2331
|
+
totalTokenIn,
|
|
2332
|
+
Number(
|
|
2333
|
+
(totalQuoteOut + tradeOpt.maxSolPerTx - 1n) /
|
|
2334
|
+
tradeOpt.maxSolPerTx,
|
|
2335
|
+
),
|
|
2336
|
+
);
|
|
2337
|
+
|
|
2338
|
+
const pendingTransactions: PendingTransaction[] = [];
|
|
2339
|
+
const failedTransactions: FailedTransaction[] = [];
|
|
2340
|
+
|
|
2341
|
+
// Pre-compute PDAs
|
|
2342
|
+
const associatedBaseBondingCurve = getAssociatedTokenAddressSync(
|
|
2343
|
+
baseMint,
|
|
2344
|
+
bonding,
|
|
2345
|
+
true,
|
|
2346
|
+
baseTokenProgram.programId,
|
|
2347
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2348
|
+
);
|
|
2349
|
+
const associatedQuoteBondingCurve = getAssociatedTokenAddressSync(
|
|
2350
|
+
quoteMint,
|
|
2351
|
+
bonding,
|
|
2352
|
+
true,
|
|
2353
|
+
quoteTokenProgramId,
|
|
2354
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2355
|
+
);
|
|
2356
|
+
|
|
2357
|
+
const [creatorVault] = PublicKey.findProgramAddressSync(
|
|
2358
|
+
[Buffer.from("creator-vault"), creator.toBuffer()],
|
|
2359
|
+
PROGRAM_IDS.PUMP,
|
|
2360
|
+
);
|
|
2361
|
+
const associatedCreatorVault = getAssociatedTokenAddressSync(
|
|
2362
|
+
quoteMint,
|
|
2363
|
+
creatorVault,
|
|
2364
|
+
true,
|
|
2365
|
+
quoteTokenProgramId,
|
|
2366
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2367
|
+
);
|
|
2368
|
+
|
|
2369
|
+
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
2370
|
+
[
|
|
2371
|
+
Buffer.from("user_volume_accumulator"),
|
|
2372
|
+
this.wallet.publicKey.toBuffer(),
|
|
2373
|
+
],
|
|
2374
|
+
PROGRAM_IDS.PUMP,
|
|
2375
|
+
);
|
|
2376
|
+
const associatedUserVolumeAccumulator = getAssociatedTokenAddressSync(
|
|
2377
|
+
quoteMint,
|
|
2378
|
+
userVolumeAccumulator,
|
|
2379
|
+
true,
|
|
2380
|
+
quoteTokenProgramId,
|
|
2381
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2382
|
+
);
|
|
2383
|
+
|
|
2384
|
+
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
2385
|
+
[Buffer.from("fee_config"), SEEDS.FEE_CONFIG],
|
|
2386
|
+
PROGRAM_IDS.FEE,
|
|
2387
|
+
);
|
|
2388
|
+
|
|
2389
|
+
const sharingConfig = this.getSharingConfigPda(baseMint);
|
|
2390
|
+
const feeRecipient = this.pickFeeRecipient();
|
|
2391
|
+
const associatedQuoteFeeRecipient = getAssociatedTokenAddressSync(
|
|
2392
|
+
quoteMint,
|
|
2393
|
+
feeRecipient,
|
|
2394
|
+
true,
|
|
2395
|
+
quoteTokenProgramId,
|
|
2396
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2397
|
+
);
|
|
2398
|
+
const buybackFeeRecipient = this.pickBuybackFeeRecipient();
|
|
2399
|
+
const associatedQuoteBuybackFeeRecipient = getAssociatedTokenAddressSync(
|
|
2400
|
+
quoteMint,
|
|
2401
|
+
buybackFeeRecipient,
|
|
2402
|
+
true,
|
|
2403
|
+
quoteTokenProgramId,
|
|
2404
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2405
|
+
);
|
|
2406
|
+
|
|
2407
|
+
const associatedQuoteUser = getAssociatedTokenAddressSync(
|
|
2408
|
+
quoteMint,
|
|
2409
|
+
this.wallet.publicKey,
|
|
2410
|
+
false,
|
|
2411
|
+
quoteTokenProgramId,
|
|
2412
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2413
|
+
);
|
|
2414
|
+
|
|
2415
|
+
const userBaseAta = getAssociatedTokenAddressSync(
|
|
2416
|
+
baseMint,
|
|
2417
|
+
this.wallet.publicKey,
|
|
2418
|
+
false,
|
|
2419
|
+
baseTokenProgram.programId,
|
|
2420
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2421
|
+
);
|
|
2422
|
+
|
|
2423
|
+
for (let i = 0; i < tokenChunks.length; i++) {
|
|
2424
|
+
try {
|
|
2425
|
+
const tokenIn = tokenChunks[i];
|
|
2426
|
+
const quoteOut = this.calcSell(tokenIn, state);
|
|
2427
|
+
const slippageBps = this.calcSlippage({
|
|
2428
|
+
tradeSize: tokenIn,
|
|
2429
|
+
reserve: state.virtualTokenReserves,
|
|
2430
|
+
slippageOpt: tradeOpt.slippage,
|
|
2431
|
+
});
|
|
2432
|
+
const minQuoteOut = (quoteOut * BigInt(10_000 - slippageBps)) / 10_000n;
|
|
2433
|
+
const priority = this.genPriority(tradeOpt.priority);
|
|
2434
|
+
|
|
2435
|
+
const tx = new Transaction().add(
|
|
2436
|
+
ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }),
|
|
2437
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
2438
|
+
);
|
|
2439
|
+
|
|
2440
|
+
// For non-SOL quotes, ensure user has the quote token ATA (to receive proceeds)
|
|
2441
|
+
if (!quoteMint.equals(SOL_MINT)) {
|
|
2442
|
+
const userQuoteAta = getAssociatedTokenAddressSync(
|
|
2443
|
+
quoteMint,
|
|
2444
|
+
this.wallet.publicKey,
|
|
2445
|
+
false,
|
|
2446
|
+
quoteTokenProgramId,
|
|
2447
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2448
|
+
);
|
|
2449
|
+
const acc = await this.connection.getAccountInfo(userQuoteAta);
|
|
2450
|
+
if (!acc) {
|
|
2451
|
+
tx.add(
|
|
2452
|
+
createAssociatedTokenAccountInstruction(
|
|
2453
|
+
this.wallet.publicKey,
|
|
2454
|
+
userQuoteAta,
|
|
2455
|
+
this.wallet.publicKey,
|
|
2456
|
+
quoteMint,
|
|
2457
|
+
quoteTokenProgramId,
|
|
2458
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2459
|
+
),
|
|
2460
|
+
);
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
tx.add(
|
|
2465
|
+
new TransactionInstruction({
|
|
2466
|
+
programId: PROGRAM_IDS.PUMP,
|
|
2467
|
+
keys: this.buildBondingSellV2Keys({
|
|
2468
|
+
global: this.global,
|
|
2469
|
+
baseMint,
|
|
2470
|
+
quoteMint,
|
|
2471
|
+
baseTokenProgram: baseTokenProgram.programId,
|
|
2472
|
+
quoteTokenProgram: quoteTokenProgramId,
|
|
2473
|
+
feeRecipient,
|
|
2474
|
+
associatedQuoteFeeRecipient,
|
|
2475
|
+
buybackFeeRecipient,
|
|
2476
|
+
associatedQuoteBuybackFeeRecipient,
|
|
2477
|
+
bondingCurve: bonding,
|
|
2478
|
+
associatedBaseBondingCurve,
|
|
2479
|
+
associatedQuoteBondingCurve,
|
|
2480
|
+
user: this.wallet.publicKey,
|
|
2481
|
+
associatedBaseUser: userBaseAta,
|
|
2482
|
+
associatedQuoteUser,
|
|
2483
|
+
creatorVault,
|
|
2484
|
+
associatedCreatorVault,
|
|
2485
|
+
sharingConfig,
|
|
2486
|
+
userVolumeAccumulator,
|
|
2487
|
+
associatedUserVolumeAccumulator,
|
|
2488
|
+
feeConfig,
|
|
2489
|
+
feeProgram: PROGRAM_IDS.FEE,
|
|
2490
|
+
eventAuthority: PROGRAM_IDS.EVENT_AUTHORITY,
|
|
2491
|
+
pumpProgram: PROGRAM_IDS.PUMP,
|
|
2492
|
+
}),
|
|
2493
|
+
data: Buffer.concat([
|
|
2494
|
+
DISCRIMINATORS.SELL_V2,
|
|
2495
|
+
u64(tokenIn),
|
|
2496
|
+
u64(minQuoteOut > 0n ? minQuoteOut : 1n),
|
|
2497
|
+
]),
|
|
2498
|
+
}),
|
|
2499
|
+
);
|
|
2500
|
+
|
|
2501
|
+
const { blockhash, lastValidBlockHeight } =
|
|
2502
|
+
await this.connection.getLatestBlockhash("finalized");
|
|
2503
|
+
tx.recentBlockhash = blockhash;
|
|
2504
|
+
tx.feePayer = this.wallet.publicKey;
|
|
2505
|
+
tx.sign(this.wallet);
|
|
2506
|
+
|
|
2507
|
+
const signature = await this.connection.sendRawTransaction(
|
|
2508
|
+
tx.serialize(),
|
|
2509
|
+
);
|
|
2510
|
+
pendingTransactions.push({ signature, lastValidBlockHeight, index: i });
|
|
2511
|
+
} catch (e) {
|
|
2512
|
+
failedTransactions.push({ index: i, error: (e as Error).message });
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
return { pendingTransactions, failedTransactions };
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
/* ---------- Collect Creator Fee V2 ---------- */
|
|
2520
|
+
|
|
2521
|
+
/**
|
|
2522
|
+
* Collect creator fees from bonding curve creator vault (collect_creator_fee_v2)
|
|
2523
|
+
* Ref: https://github.com/pump-fun/pump-public-docs/blob/main/docs/instructions/COLLECT_CREATOR_FEE.md
|
|
2524
|
+
*/
|
|
2525
|
+
async collectCreatorFeeV2(
|
|
2526
|
+
creator: PublicKey,
|
|
2527
|
+
quoteMint: PublicKey = SOL_MINT,
|
|
2528
|
+
): Promise<string> {
|
|
2529
|
+
const quoteTokenProgramId = quoteMint.equals(SOL_MINT)
|
|
2530
|
+
? TOKEN_PROGRAM_ID
|
|
2531
|
+
: TOKEN_PROGRAM_ID;
|
|
2532
|
+
|
|
2533
|
+
const [creatorVault] = PublicKey.findProgramAddressSync(
|
|
2534
|
+
[Buffer.from("creator-vault"), creator.toBuffer()],
|
|
2535
|
+
PROGRAM_IDS.PUMP,
|
|
2536
|
+
);
|
|
2537
|
+
|
|
2538
|
+
const creatorTokenAccount = getAssociatedTokenAddressSync(
|
|
2539
|
+
quoteMint,
|
|
2540
|
+
creator,
|
|
2541
|
+
false,
|
|
2542
|
+
quoteTokenProgramId,
|
|
2543
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2544
|
+
);
|
|
2545
|
+
|
|
2546
|
+
const creatorVaultTokenAccount = getAssociatedTokenAddressSync(
|
|
2547
|
+
quoteMint,
|
|
2548
|
+
creatorVault,
|
|
2549
|
+
true,
|
|
2550
|
+
quoteTokenProgramId,
|
|
2551
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2552
|
+
);
|
|
2553
|
+
|
|
2554
|
+
const [eventAuthority] = PublicKey.findProgramAddressSync(
|
|
2555
|
+
[Buffer.from("__event_authority")],
|
|
2556
|
+
PROGRAM_IDS.PUMP,
|
|
2557
|
+
);
|
|
2558
|
+
|
|
2559
|
+
const tx = new Transaction().add(
|
|
2560
|
+
ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
|
|
2561
|
+
new TransactionInstruction({
|
|
2562
|
+
programId: PROGRAM_IDS.PUMP,
|
|
2563
|
+
keys: [
|
|
2564
|
+
{ pubkey: creator, isSigner: false, isWritable: false },
|
|
2565
|
+
{ pubkey: creatorTokenAccount, isSigner: false, isWritable: true },
|
|
2566
|
+
{ pubkey: creatorVault, isSigner: false, isWritable: true },
|
|
2567
|
+
{
|
|
2568
|
+
pubkey: creatorVaultTokenAccount,
|
|
2569
|
+
isSigner: false,
|
|
2570
|
+
isWritable: true,
|
|
2571
|
+
},
|
|
2572
|
+
{ pubkey: quoteMint, isSigner: false, isWritable: false },
|
|
2573
|
+
{ pubkey: quoteTokenProgramId, isSigner: false, isWritable: false },
|
|
2574
|
+
{
|
|
2575
|
+
pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
2576
|
+
isSigner: false,
|
|
2577
|
+
isWritable: false,
|
|
2578
|
+
},
|
|
2579
|
+
{
|
|
2580
|
+
pubkey: SystemProgram.programId,
|
|
2581
|
+
isSigner: false,
|
|
2582
|
+
isWritable: false,
|
|
2583
|
+
},
|
|
2584
|
+
{ pubkey: eventAuthority, isSigner: false, isWritable: false },
|
|
2585
|
+
{ pubkey: PROGRAM_IDS.PUMP, isSigner: false, isWritable: false },
|
|
2586
|
+
],
|
|
2587
|
+
data: DISCRIMINATORS.COLLECT_CREATOR_FEE_V2,
|
|
2588
|
+
}),
|
|
2589
|
+
);
|
|
2590
|
+
|
|
2591
|
+
const { blockhash, lastValidBlockHeight } =
|
|
2592
|
+
await this.connection.getLatestBlockhash("finalized");
|
|
2593
|
+
tx.recentBlockhash = blockhash;
|
|
2594
|
+
tx.feePayer = this.wallet.publicKey;
|
|
2595
|
+
tx.sign(this.wallet);
|
|
2596
|
+
|
|
2597
|
+
const signature = await this.connection.sendRawTransaction(tx.serialize());
|
|
2598
|
+
await this.confirmTransactionWithPolling(signature, lastValidBlockHeight);
|
|
2599
|
+
return signature;
|
|
2600
|
+
}
|
|
2601
|
+
|
|
1581
2602
|
/* ---------- 交易确认 ---------- */
|
|
1582
2603
|
|
|
1583
2604
|
async confirmTransactionWithPolling(
|
|
1584
2605
|
signature: string,
|
|
1585
2606
|
lastValidBlockHeight: number,
|
|
1586
2607
|
maxAttempts: number = 5,
|
|
1587
|
-
delayMs: number = 2000
|
|
2608
|
+
delayMs: number = 2000,
|
|
1588
2609
|
): Promise<string> {
|
|
1589
|
-
console.log(
|
|
1590
|
-
console.log(
|
|
2610
|
+
console.log("✅ 交易已发送:", signature);
|
|
2611
|
+
console.log("🔗 查看交易: https://solscan.io/tx/" + signature);
|
|
1591
2612
|
|
|
1592
2613
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1593
|
-
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
2614
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1594
2615
|
|
|
1595
2616
|
try {
|
|
1596
2617
|
console.log(`🔍 检查交易状态 (${attempt}/${maxAttempts})...`);
|
|
1597
2618
|
|
|
1598
2619
|
const txInfo = await this.connection.getTransaction(signature, {
|
|
1599
|
-
commitment:
|
|
1600
|
-
maxSupportedTransactionVersion: 0
|
|
2620
|
+
commitment: "confirmed",
|
|
2621
|
+
maxSupportedTransactionVersion: 0,
|
|
1601
2622
|
});
|
|
1602
2623
|
|
|
1603
2624
|
if (txInfo) {
|
|
1604
2625
|
if (txInfo.meta?.err) {
|
|
1605
|
-
console.error(
|
|
1606
|
-
throw new Error(
|
|
2626
|
+
console.error("❌ 交易失败:", txInfo.meta.err);
|
|
2627
|
+
throw new Error("交易失败: " + JSON.stringify(txInfo.meta.err));
|
|
1607
2628
|
}
|
|
1608
2629
|
|
|
1609
|
-
console.log(
|
|
2630
|
+
console.log("✅ 交易已确认!");
|
|
1610
2631
|
return signature;
|
|
1611
2632
|
}
|
|
1612
2633
|
|
|
1613
|
-
const currentBlockHeight =
|
|
2634
|
+
const currentBlockHeight =
|
|
2635
|
+
await this.connection.getBlockHeight("finalized");
|
|
1614
2636
|
if (currentBlockHeight > lastValidBlockHeight) {
|
|
1615
|
-
console.log(
|
|
1616
|
-
throw new Error(
|
|
2637
|
+
console.log("⚠️ 交易已过期(超过有效区块高度)");
|
|
2638
|
+
throw new Error("交易过期:未在有效区块高度内确认");
|
|
1617
2639
|
}
|
|
1618
|
-
|
|
1619
2640
|
} catch (error) {
|
|
1620
2641
|
const err = error as Error;
|
|
1621
|
-
if (
|
|
2642
|
+
if (
|
|
2643
|
+
err.message?.includes("交易失败") ||
|
|
2644
|
+
err.message?.includes("交易过期")
|
|
2645
|
+
) {
|
|
1622
2646
|
throw error;
|
|
1623
2647
|
}
|
|
1624
2648
|
console.log(`⚠️ 查询出错,继续重试: ${err.message}`);
|
|
@@ -1626,20 +2650,26 @@ export class PumpTrader {
|
|
|
1626
2650
|
}
|
|
1627
2651
|
|
|
1628
2652
|
throw new Error(
|
|
1629
|
-
`交易确认超时(已尝试 ${maxAttempts} 次),签名: ${signature}
|
|
2653
|
+
`交易确认超时(已尝试 ${maxAttempts} 次),签名: ${signature}。请手动检查交易状态。`,
|
|
1630
2654
|
);
|
|
1631
2655
|
}
|
|
1632
2656
|
|
|
1633
2657
|
/* ---------- 事件监听 ---------- */
|
|
1634
2658
|
|
|
1635
|
-
listenTrades(
|
|
2659
|
+
listenTrades(
|
|
2660
|
+
callback: (event: TradeEvent) => void,
|
|
2661
|
+
mintFilter?: PublicKey | null,
|
|
2662
|
+
) {
|
|
1636
2663
|
return this.connection.onLogs(
|
|
1637
2664
|
PROGRAM_IDS.PUMP,
|
|
1638
2665
|
(log) => {
|
|
1639
2666
|
for (const logLine of log.logs) {
|
|
1640
2667
|
if (!logLine.startsWith("Program data: ")) continue;
|
|
1641
2668
|
|
|
1642
|
-
const buf = Buffer.from(
|
|
2669
|
+
const buf = Buffer.from(
|
|
2670
|
+
logLine.replace("Program data: ", ""),
|
|
2671
|
+
"base64",
|
|
2672
|
+
);
|
|
1643
2673
|
|
|
1644
2674
|
if (!buf.subarray(0, 8).equals(DISCRIMINATORS.TRADE_EVENT)) continue;
|
|
1645
2675
|
|
|
@@ -1665,11 +2695,11 @@ export class PumpTrader {
|
|
|
1665
2695
|
isBuy,
|
|
1666
2696
|
user: user.toBase58(),
|
|
1667
2697
|
timestamp,
|
|
1668
|
-
signature: log.signature
|
|
2698
|
+
signature: log.signature,
|
|
1669
2699
|
});
|
|
1670
2700
|
}
|
|
1671
2701
|
},
|
|
1672
|
-
"confirmed"
|
|
2702
|
+
"confirmed",
|
|
1673
2703
|
);
|
|
1674
2704
|
}
|
|
1675
2705
|
|
|
@@ -1683,18 +2713,22 @@ export class PumpTrader {
|
|
|
1683
2713
|
this.connection,
|
|
1684
2714
|
mint,
|
|
1685
2715
|
"confirmed",
|
|
1686
|
-
TOKEN_2022_PROGRAM_ID
|
|
2716
|
+
TOKEN_2022_PROGRAM_ID,
|
|
1687
2717
|
);
|
|
1688
2718
|
|
|
1689
2719
|
return {
|
|
1690
2720
|
name: metadata?.name || "",
|
|
1691
2721
|
symbol: metadata?.symbol || "",
|
|
1692
|
-
uri: metadata?.uri || ""
|
|
2722
|
+
uri: metadata?.uri || "",
|
|
1693
2723
|
};
|
|
1694
2724
|
} catch (e) {
|
|
1695
2725
|
const metadataPda = PublicKey.findProgramAddressSync(
|
|
1696
|
-
[
|
|
1697
|
-
|
|
2726
|
+
[
|
|
2727
|
+
Buffer.from("metadata"),
|
|
2728
|
+
PROGRAM_IDS.METADATA.toBuffer(),
|
|
2729
|
+
mint.toBuffer(),
|
|
2730
|
+
],
|
|
2731
|
+
PROGRAM_IDS.METADATA,
|
|
1698
2732
|
)[0];
|
|
1699
2733
|
|
|
1700
2734
|
const acc = await this.connection.getAccountInfo(metadataPda);
|
|
@@ -1703,9 +2737,9 @@ export class PumpTrader {
|
|
|
1703
2737
|
const meta = parseMetadataAccount(acc.data);
|
|
1704
2738
|
|
|
1705
2739
|
return {
|
|
1706
|
-
name: meta?.name?.replace(/\u0000/g,
|
|
1707
|
-
symbol: meta?.symbol?.replace(/\u0000/g,
|
|
1708
|
-
uri: meta?.uri?.replace(/\u0000/g,
|
|
2740
|
+
name: meta?.name?.replace(/\u0000/g, "") || "",
|
|
2741
|
+
symbol: meta?.symbol?.replace(/\u0000/g, "") || "",
|
|
2742
|
+
uri: meta?.uri?.replace(/\u0000/g, "") || "",
|
|
1709
2743
|
};
|
|
1710
2744
|
}
|
|
1711
2745
|
}
|
|
@@ -1751,5 +2785,5 @@ export type {
|
|
|
1751
2785
|
GlobalState,
|
|
1752
2786
|
TokenProgramType,
|
|
1753
2787
|
PoolInfo,
|
|
1754
|
-
MetadataInfo
|
|
2788
|
+
MetadataInfo,
|
|
1755
2789
|
};
|