pump-trader 1.1.4 → 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/index.js +480 -236
- package/index.ts +1311 -247
- package/package.json +1 -1
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";
|
|
@@ -66,6 +66,10 @@ interface BondingCurveState {
|
|
|
66
66
|
complete: boolean;
|
|
67
67
|
isMayhemMode?: boolean;
|
|
68
68
|
isCashbackCoin?: boolean;
|
|
69
|
+
// V2 fields
|
|
70
|
+
quoteMint?: PublicKey;
|
|
71
|
+
virtualQuoteReserves?: bigint;
|
|
72
|
+
realQuoteReserves?: bigint;
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
interface BondingInfo {
|
|
@@ -128,33 +132,62 @@ const PROGRAM_IDS = {
|
|
|
128
132
|
PUMP_AMM: new PublicKey("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"),
|
|
129
133
|
METADATA: new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"),
|
|
130
134
|
FEE: new PublicKey("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"),
|
|
131
|
-
EVENT_AUTHORITY: new PublicKey(
|
|
135
|
+
EVENT_AUTHORITY: new PublicKey(
|
|
136
|
+
"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1",
|
|
137
|
+
),
|
|
132
138
|
};
|
|
133
139
|
|
|
134
140
|
const SOL_MINT = new PublicKey("So11111111111111111111111111111111111111112");
|
|
135
141
|
|
|
136
142
|
const SEEDS = {
|
|
137
143
|
FEE_CONFIG: new Uint8Array([
|
|
138
|
-
1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, 91, 170,
|
|
139
|
-
|
|
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,
|
|
140
146
|
]),
|
|
141
147
|
AMM_FEE_CONFIG: Buffer.from([
|
|
142
|
-
12, 20, 222, 252, 130, 94, 198, 118, 148, 37, 8, 24, 187, 101, 64, 101,
|
|
143
|
-
|
|
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,
|
|
144
150
|
]),
|
|
145
151
|
GLOBAL: Buffer.from("global"),
|
|
146
|
-
BONDING: Buffer.from("bonding-curve")
|
|
152
|
+
BONDING: Buffer.from("bonding-curve"),
|
|
147
153
|
};
|
|
148
154
|
|
|
149
155
|
const DISCRIMINATORS = {
|
|
150
156
|
BUY: Buffer.from([102, 6, 61, 18, 1, 218, 235, 234]),
|
|
151
157
|
SELL: Buffer.from([51, 230, 133, 164, 1, 127, 131, 173]),
|
|
152
|
-
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]),
|
|
153
164
|
};
|
|
154
165
|
|
|
155
166
|
const AMM_FEE_BPS = 100n;
|
|
156
167
|
const BPS_DENOMINATOR = 10000n;
|
|
157
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 = [
|
|
158
191
|
"5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD",
|
|
159
192
|
"9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7",
|
|
160
193
|
"GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL",
|
|
@@ -162,13 +195,14 @@ const PUMP_NEW_FEE_RECIPIENTS = [
|
|
|
162
195
|
"5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6",
|
|
163
196
|
"EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL",
|
|
164
197
|
"5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD",
|
|
165
|
-
"A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"
|
|
198
|
+
"A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW",
|
|
166
199
|
].map((value) => new PublicKey(value));
|
|
167
200
|
|
|
168
201
|
/* ================= 工具函数 ================= */
|
|
169
202
|
|
|
170
203
|
const u64 = (v: bigint | BN | number): Buffer => {
|
|
171
|
-
const bn =
|
|
204
|
+
const bn =
|
|
205
|
+
typeof v === "bigint" ? new BN(v.toString()) : new BN(v.toString());
|
|
172
206
|
return bn.toArrayLike(Buffer, "le", 8);
|
|
173
207
|
};
|
|
174
208
|
|
|
@@ -185,7 +219,9 @@ const readU32 = (buf: Buffer, offsetObj: { offset: number }): number => {
|
|
|
185
219
|
|
|
186
220
|
const readString = (buf: Buffer, offsetObj: { offset: number }): string => {
|
|
187
221
|
const len = readU32(buf, offsetObj);
|
|
188
|
-
const str = buf
|
|
222
|
+
const str = buf
|
|
223
|
+
.slice(offsetObj.offset, offsetObj.offset + len)
|
|
224
|
+
.toString("utf8");
|
|
189
225
|
offsetObj.offset += len;
|
|
190
226
|
return str;
|
|
191
227
|
};
|
|
@@ -195,10 +231,14 @@ const readString = (buf: Buffer, offsetObj: { offset: number }): string => {
|
|
|
195
231
|
function parseMetadataAccount(data: Buffer) {
|
|
196
232
|
const offsetObj = { offset: 1 };
|
|
197
233
|
|
|
198
|
-
const updateAuthority = new PublicKey(
|
|
234
|
+
const updateAuthority = new PublicKey(
|
|
235
|
+
data.slice(offsetObj.offset, offsetObj.offset + 32),
|
|
236
|
+
);
|
|
199
237
|
offsetObj.offset += 32;
|
|
200
238
|
|
|
201
|
-
const mint = new PublicKey(
|
|
239
|
+
const mint = new PublicKey(
|
|
240
|
+
data.slice(offsetObj.offset, offsetObj.offset + 32),
|
|
241
|
+
);
|
|
202
242
|
offsetObj.offset += 32;
|
|
203
243
|
|
|
204
244
|
const name = readString(data, offsetObj);
|
|
@@ -210,13 +250,13 @@ function parseMetadataAccount(data: Buffer) {
|
|
|
210
250
|
mint: mint.toBase58(),
|
|
211
251
|
name,
|
|
212
252
|
symbol,
|
|
213
|
-
uri
|
|
253
|
+
uri,
|
|
214
254
|
};
|
|
215
255
|
}
|
|
216
256
|
|
|
217
257
|
function parsePoolKeys(data: Buffer) {
|
|
218
258
|
if (!data || data.length < 280) {
|
|
219
|
-
throw new Error(
|
|
259
|
+
throw new Error("Invalid pool account data");
|
|
220
260
|
}
|
|
221
261
|
|
|
222
262
|
let offset = 8;
|
|
@@ -253,7 +293,8 @@ function parsePoolKeys(data: Buffer) {
|
|
|
253
293
|
|
|
254
294
|
const isMayhemMode = data.readUInt8(offset) === 1;
|
|
255
295
|
offset += 1;
|
|
256
|
-
const isCashbackCoin =
|
|
296
|
+
const isCashbackCoin =
|
|
297
|
+
offset < data.length ? data.readUInt8(offset) === 1 : false;
|
|
257
298
|
|
|
258
299
|
return {
|
|
259
300
|
creator,
|
|
@@ -264,7 +305,7 @@ function parsePoolKeys(data: Buffer) {
|
|
|
264
305
|
poolQuoteTokenAccount,
|
|
265
306
|
coinCreator,
|
|
266
307
|
isMayhemMode,
|
|
267
|
-
isCashbackCoin
|
|
308
|
+
isCashbackCoin,
|
|
268
309
|
};
|
|
269
310
|
}
|
|
270
311
|
|
|
@@ -280,7 +321,10 @@ export class PumpTrader {
|
|
|
280
321
|
constructor(rpc: string, privateKey: string) {
|
|
281
322
|
this.connection = new Connection(rpc, "confirmed");
|
|
282
323
|
this.wallet = Keypair.fromSecretKey(bs58.decode(privateKey));
|
|
283
|
-
this.global = PublicKey.findProgramAddressSync(
|
|
324
|
+
this.global = PublicKey.findProgramAddressSync(
|
|
325
|
+
[SEEDS.GLOBAL],
|
|
326
|
+
PROGRAM_IDS.PUMP,
|
|
327
|
+
)[0];
|
|
284
328
|
this.globalState = null;
|
|
285
329
|
this.tokenProgramCache = new Map();
|
|
286
330
|
}
|
|
@@ -300,29 +344,59 @@ export class PumpTrader {
|
|
|
300
344
|
|
|
301
345
|
try {
|
|
302
346
|
// 首先尝试获取 TOKEN_2022 的代币信息
|
|
303
|
-
const mintData = await getMint(
|
|
347
|
+
const mintData = await getMint(
|
|
348
|
+
this.connection,
|
|
349
|
+
mint,
|
|
350
|
+
"confirmed",
|
|
351
|
+
TOKEN_2022_PROGRAM_ID,
|
|
352
|
+
);
|
|
304
353
|
const result: TokenProgramType = {
|
|
305
354
|
type: "TOKEN_2022_PROGRAM_ID",
|
|
306
|
-
programId: TOKEN_2022_PROGRAM_ID
|
|
355
|
+
programId: TOKEN_2022_PROGRAM_ID,
|
|
307
356
|
};
|
|
308
357
|
this.tokenProgramCache.set(tokenAddr, result);
|
|
309
358
|
return result;
|
|
310
359
|
} catch (e) {
|
|
311
360
|
try {
|
|
312
361
|
// 如果失败,尝试标准 TOKEN_PROGRAM_ID
|
|
313
|
-
const mintData = await getMint(
|
|
362
|
+
const mintData = await getMint(
|
|
363
|
+
this.connection,
|
|
364
|
+
mint,
|
|
365
|
+
"confirmed",
|
|
366
|
+
TOKEN_PROGRAM_ID,
|
|
367
|
+
);
|
|
314
368
|
const result: TokenProgramType = {
|
|
315
369
|
type: "TOKEN_PROGRAM_ID",
|
|
316
|
-
programId: TOKEN_PROGRAM_ID
|
|
370
|
+
programId: TOKEN_PROGRAM_ID,
|
|
317
371
|
};
|
|
318
372
|
this.tokenProgramCache.set(tokenAddr, result);
|
|
319
373
|
return result;
|
|
320
374
|
} catch (error) {
|
|
321
|
-
throw new Error(
|
|
375
|
+
throw new Error(
|
|
376
|
+
`Failed to detect token program for ${tokenAddr}: ${error}`,
|
|
377
|
+
);
|
|
322
378
|
}
|
|
323
379
|
}
|
|
324
380
|
}
|
|
325
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
|
+
|
|
326
400
|
/* ---------- 内盘/外盘检测 ---------- */
|
|
327
401
|
|
|
328
402
|
/**
|
|
@@ -383,7 +457,7 @@ export class PumpTrader {
|
|
|
383
457
|
initialVirtualSolReserves: readU64(),
|
|
384
458
|
initialRealTokenReserves: readU64(),
|
|
385
459
|
tokenTotalSupply: readU64(),
|
|
386
|
-
feeBasisPoints: readU64()
|
|
460
|
+
feeBasisPoints: readU64(),
|
|
387
461
|
};
|
|
388
462
|
|
|
389
463
|
return this.globalState;
|
|
@@ -394,14 +468,14 @@ export class PumpTrader {
|
|
|
394
468
|
getBondingPda(mint: PublicKey): PublicKey {
|
|
395
469
|
return PublicKey.findProgramAddressSync(
|
|
396
470
|
[SEEDS.BONDING, mint.toBuffer()],
|
|
397
|
-
PROGRAM_IDS.PUMP
|
|
471
|
+
PROGRAM_IDS.PUMP,
|
|
398
472
|
)[0];
|
|
399
473
|
}
|
|
400
474
|
|
|
401
475
|
deriveBondingCurveV2(mint: PublicKey): PublicKey {
|
|
402
476
|
return PublicKey.findProgramAddressSync(
|
|
403
477
|
[Buffer.from("bonding-curve-v2"), mint.toBuffer()],
|
|
404
|
-
PROGRAM_IDS.PUMP
|
|
478
|
+
PROGRAM_IDS.PUMP,
|
|
405
479
|
)[0];
|
|
406
480
|
}
|
|
407
481
|
|
|
@@ -409,6 +483,25 @@ export class PumpTrader {
|
|
|
409
483
|
return PUMP_NEW_FEE_RECIPIENTS[index % PUMP_NEW_FEE_RECIPIENTS.length];
|
|
410
484
|
}
|
|
411
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
|
+
|
|
412
505
|
private buildBondingBuyKeys(args: {
|
|
413
506
|
global: PublicKey;
|
|
414
507
|
globalFeeRecipient: PublicKey;
|
|
@@ -435,7 +528,11 @@ export class PumpTrader {
|
|
|
435
528
|
{ pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
|
|
436
529
|
{ pubkey: args.mint, isSigner: false, isWritable: false },
|
|
437
530
|
{ pubkey: args.bonding, isSigner: false, isWritable: true },
|
|
438
|
-
{
|
|
531
|
+
{
|
|
532
|
+
pubkey: args.associatedBondingCurve,
|
|
533
|
+
isSigner: false,
|
|
534
|
+
isWritable: true,
|
|
535
|
+
},
|
|
439
536
|
{ pubkey: args.userAta, isSigner: false, isWritable: true },
|
|
440
537
|
{ pubkey: args.wallet, isSigner: true, isWritable: true },
|
|
441
538
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
@@ -443,12 +540,16 @@ export class PumpTrader {
|
|
|
443
540
|
{ pubkey: args.creatorVault, isSigner: false, isWritable: true },
|
|
444
541
|
{ pubkey: args.eventAuthority, isSigner: false, isWritable: false },
|
|
445
542
|
{ pubkey: args.pumpProgram, isSigner: false, isWritable: false },
|
|
446
|
-
{
|
|
543
|
+
{
|
|
544
|
+
pubkey: args.globalVolumeAccumulator,
|
|
545
|
+
isSigner: false,
|
|
546
|
+
isWritable: false,
|
|
547
|
+
},
|
|
447
548
|
{ pubkey: args.userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
448
549
|
{ pubkey: args.feeConfig, isSigner: false, isWritable: false },
|
|
449
550
|
{ pubkey: args.feeProgram, isSigner: false, isWritable: false },
|
|
450
551
|
{ pubkey: args.bondingCurveV2, isSigner: false, isWritable: false },
|
|
451
|
-
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true }
|
|
552
|
+
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true },
|
|
452
553
|
];
|
|
453
554
|
}
|
|
454
555
|
|
|
@@ -477,7 +578,11 @@ export class PumpTrader {
|
|
|
477
578
|
{ pubkey: args.globalFeeRecipient, isSigner: false, isWritable: true },
|
|
478
579
|
{ pubkey: args.mint, isSigner: false, isWritable: false },
|
|
479
580
|
{ pubkey: args.bonding, isSigner: false, isWritable: true },
|
|
480
|
-
{
|
|
581
|
+
{
|
|
582
|
+
pubkey: args.associatedBondingCurve,
|
|
583
|
+
isSigner: false,
|
|
584
|
+
isWritable: true,
|
|
585
|
+
},
|
|
481
586
|
{ pubkey: args.userAta, isSigner: false, isWritable: true },
|
|
482
587
|
{ pubkey: args.wallet, isSigner: true, isWritable: true },
|
|
483
588
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
@@ -486,16 +591,20 @@ export class PumpTrader {
|
|
|
486
591
|
{ pubkey: args.eventAuthority, isSigner: false, isWritable: false },
|
|
487
592
|
{ pubkey: args.pumpProgram, isSigner: false, isWritable: false },
|
|
488
593
|
{ pubkey: args.feeConfig, isSigner: false, isWritable: false },
|
|
489
|
-
{ pubkey: args.feeProgram, isSigner: false, isWritable: false }
|
|
594
|
+
{ pubkey: args.feeProgram, isSigner: false, isWritable: false },
|
|
490
595
|
];
|
|
491
596
|
|
|
492
597
|
if (args.isCashbackCoin) {
|
|
493
|
-
keys.push({
|
|
598
|
+
keys.push({
|
|
599
|
+
pubkey: args.userVolumeAccumulator,
|
|
600
|
+
isSigner: false,
|
|
601
|
+
isWritable: true,
|
|
602
|
+
});
|
|
494
603
|
}
|
|
495
604
|
|
|
496
605
|
keys.push(
|
|
497
606
|
{ pubkey: args.bondingCurveV2, isSigner: false, isWritable: false },
|
|
498
|
-
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true }
|
|
607
|
+
{ pubkey: args.feeRecipient, isSigner: false, isWritable: true },
|
|
499
608
|
);
|
|
500
609
|
|
|
501
610
|
return keys;
|
|
@@ -521,6 +630,19 @@ export class PumpTrader {
|
|
|
521
630
|
const creator = new PublicKey(data.slice(offset, offset + 32));
|
|
522
631
|
offset += 32;
|
|
523
632
|
|
|
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) {
|
|
636
|
+
state.quoteMint = new PublicKey(data.slice(offset, offset + 32));
|
|
637
|
+
offset += 32;
|
|
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
|
+
}
|
|
645
|
+
|
|
524
646
|
state.isMayhemMode = offset < data.length ? data[offset] === 1 : false;
|
|
525
647
|
offset += 1;
|
|
526
648
|
state.isCashbackCoin = offset < data.length ? data[offset] === 1 : false;
|
|
@@ -532,25 +654,29 @@ export class PumpTrader {
|
|
|
532
654
|
|
|
533
655
|
calcBuy(solIn: bigint, state: BondingCurveState): bigint {
|
|
534
656
|
const newVirtualSol = state.virtualSolReserves + solIn;
|
|
535
|
-
const newVirtualToken =
|
|
657
|
+
const newVirtualToken =
|
|
658
|
+
(state.virtualSolReserves * state.virtualTokenReserves) / newVirtualSol;
|
|
536
659
|
return state.virtualTokenReserves - newVirtualToken;
|
|
537
660
|
}
|
|
538
661
|
|
|
539
662
|
calcSell(tokenIn: bigint, state: BondingCurveState): bigint {
|
|
540
663
|
const newVirtualToken = state.virtualTokenReserves + tokenIn;
|
|
541
|
-
const newVirtualSol =
|
|
664
|
+
const newVirtualSol =
|
|
665
|
+
(state.virtualSolReserves * state.virtualTokenReserves) / newVirtualToken;
|
|
542
666
|
return state.virtualSolReserves - newVirtualSol;
|
|
543
667
|
}
|
|
544
668
|
|
|
545
669
|
calculateAmmBuyOutput(quoteIn: bigint, reserves: PoolReserves): bigint {
|
|
546
|
-
const quoteInAfterFee =
|
|
670
|
+
const quoteInAfterFee =
|
|
671
|
+
(quoteIn * (BPS_DENOMINATOR - AMM_FEE_BPS)) / BPS_DENOMINATOR;
|
|
547
672
|
const numerator = reserves.baseAmount * quoteInAfterFee;
|
|
548
673
|
const denominator = reserves.quoteAmount + quoteInAfterFee;
|
|
549
674
|
return numerator / denominator;
|
|
550
675
|
}
|
|
551
676
|
|
|
552
677
|
calculateAmmSellOutput(baseIn: bigint, reserves: PoolReserves): bigint {
|
|
553
|
-
const baseInAfterFee =
|
|
678
|
+
const baseInAfterFee =
|
|
679
|
+
(baseIn * (BPS_DENOMINATOR - AMM_FEE_BPS)) / BPS_DENOMINATOR;
|
|
554
680
|
const numerator = reserves.quoteAmount * baseInAfterFee;
|
|
555
681
|
const denominator = reserves.baseAmount + baseInAfterFee;
|
|
556
682
|
return numerator / denominator;
|
|
@@ -558,7 +684,9 @@ export class PumpTrader {
|
|
|
558
684
|
|
|
559
685
|
/* ---------- 价格查询 ---------- */
|
|
560
686
|
|
|
561
|
-
async getPriceAndStatus(
|
|
687
|
+
async getPriceAndStatus(
|
|
688
|
+
tokenAddr: string,
|
|
689
|
+
): Promise<{ price: number; completed: boolean }> {
|
|
562
690
|
const mint = new PublicKey(tokenAddr);
|
|
563
691
|
const { state } = await this.loadBonding(mint);
|
|
564
692
|
|
|
@@ -576,13 +704,19 @@ export class PumpTrader {
|
|
|
576
704
|
async getAmmPrice(mint: PublicKey): Promise<number> {
|
|
577
705
|
const [poolCreator] = PublicKey.findProgramAddressSync(
|
|
578
706
|
[Buffer.from("pool-authority"), mint.toBuffer()],
|
|
579
|
-
PROGRAM_IDS.PUMP
|
|
707
|
+
PROGRAM_IDS.PUMP,
|
|
580
708
|
);
|
|
581
709
|
|
|
582
710
|
const indexBuffer = new BN(0).toArrayLike(Buffer, "le", 2);
|
|
583
711
|
const [pool] = PublicKey.findProgramAddressSync(
|
|
584
|
-
[
|
|
585
|
-
|
|
712
|
+
[
|
|
713
|
+
Buffer.from("pool"),
|
|
714
|
+
indexBuffer,
|
|
715
|
+
poolCreator.toBuffer(),
|
|
716
|
+
mint.toBuffer(),
|
|
717
|
+
SOL_MINT.toBuffer(),
|
|
718
|
+
],
|
|
719
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
586
720
|
);
|
|
587
721
|
|
|
588
722
|
const acc = await this.connection.getAccountInfo(pool);
|
|
@@ -591,7 +725,7 @@ export class PumpTrader {
|
|
|
591
725
|
const poolKeys = parsePoolKeys(acc.data);
|
|
592
726
|
const [baseInfo, quoteInfo] = await Promise.all([
|
|
593
727
|
this.connection.getTokenAccountBalance(poolKeys.poolBaseTokenAccount),
|
|
594
|
-
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount)
|
|
728
|
+
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
|
|
595
729
|
]);
|
|
596
730
|
|
|
597
731
|
return quoteInfo.value.uiAmount! / baseInfo.value.uiAmount!;
|
|
@@ -604,15 +738,26 @@ export class PumpTrader {
|
|
|
604
738
|
* @param tokenAddr - 代币地址(可选),如果不传则返回所有代币
|
|
605
739
|
* @returns 如果传入地址则返回该代币的余额数字,否则返回所有代币的详细信息
|
|
606
740
|
*/
|
|
607
|
-
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
|
+
> {
|
|
608
750
|
if (tokenAddr) {
|
|
609
751
|
// 查询单个代币
|
|
610
752
|
const mint = new PublicKey(tokenAddr);
|
|
611
753
|
const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner(
|
|
612
754
|
this.wallet.publicKey,
|
|
613
|
-
{ mint }
|
|
755
|
+
{ mint },
|
|
756
|
+
);
|
|
757
|
+
return (
|
|
758
|
+
tokenAccounts.value[0]?.account.data.parsed.info.tokenAmount.uiAmount ||
|
|
759
|
+
0
|
|
614
760
|
);
|
|
615
|
-
return tokenAccounts.value[0]?.account.data.parsed.info.tokenAmount.uiAmount || 0;
|
|
616
761
|
} else {
|
|
617
762
|
// 查询所有代币
|
|
618
763
|
return this.getAllTokenBalances();
|
|
@@ -623,16 +768,18 @@ export class PumpTrader {
|
|
|
623
768
|
* 获取账户所有代币余额(仅显示余额 > 0 的)
|
|
624
769
|
* @returns 代币信息数组,包含mint地址、余额等信息
|
|
625
770
|
*/
|
|
626
|
-
async getAllTokenBalances(): Promise<
|
|
771
|
+
async getAllTokenBalances(): Promise<
|
|
772
|
+
Array<{ mint: string; amount: number; decimals: number; uiAmount: number }>
|
|
773
|
+
> {
|
|
627
774
|
const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner(
|
|
628
775
|
this.wallet.publicKey,
|
|
629
|
-
{ programId: TOKEN_PROGRAM_ID }
|
|
776
|
+
{ programId: TOKEN_PROGRAM_ID },
|
|
630
777
|
);
|
|
631
778
|
|
|
632
779
|
const balances = tokenAccounts.value
|
|
633
780
|
.map((account) => {
|
|
634
781
|
const parsed = account.account.data.parsed;
|
|
635
|
-
if (parsed.type !==
|
|
782
|
+
if (parsed.type !== "account") return null;
|
|
636
783
|
|
|
637
784
|
const tokenAmount = parsed.info.tokenAmount;
|
|
638
785
|
if (Number(tokenAmount.amount) === 0) return null; // 跳过余额为0的
|
|
@@ -641,21 +788,27 @@ export class PumpTrader {
|
|
|
641
788
|
mint: parsed.info.mint,
|
|
642
789
|
amount: BigInt(tokenAmount.amount),
|
|
643
790
|
decimals: tokenAmount.decimals,
|
|
644
|
-
uiAmount: tokenAmount.uiAmount || 0
|
|
791
|
+
uiAmount: tokenAmount.uiAmount || 0,
|
|
645
792
|
};
|
|
646
793
|
})
|
|
647
|
-
.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
|
+
}>;
|
|
648
800
|
|
|
649
801
|
// 同时查询 TOKEN_2022_PROGRAM_ID
|
|
650
|
-
const token2022Accounts =
|
|
651
|
-
this.
|
|
652
|
-
|
|
653
|
-
|
|
802
|
+
const token2022Accounts =
|
|
803
|
+
await this.connection.getParsedTokenAccountsByOwner(
|
|
804
|
+
this.wallet.publicKey,
|
|
805
|
+
{ programId: TOKEN_2022_PROGRAM_ID },
|
|
806
|
+
);
|
|
654
807
|
|
|
655
808
|
const token2022Balances = token2022Accounts.value
|
|
656
809
|
.map((account) => {
|
|
657
810
|
const parsed = account.account.data.parsed;
|
|
658
|
-
if (parsed.type !==
|
|
811
|
+
if (parsed.type !== "account") return null;
|
|
659
812
|
|
|
660
813
|
const tokenAmount = parsed.info.tokenAmount;
|
|
661
814
|
if (Number(tokenAmount.amount) === 0) return null; // 跳过余额为0的
|
|
@@ -664,10 +817,15 @@ export class PumpTrader {
|
|
|
664
817
|
mint: parsed.info.mint,
|
|
665
818
|
amount: BigInt(tokenAmount.amount),
|
|
666
819
|
decimals: tokenAmount.decimals,
|
|
667
|
-
uiAmount: tokenAmount.uiAmount || 0
|
|
820
|
+
uiAmount: tokenAmount.uiAmount || 0,
|
|
668
821
|
};
|
|
669
822
|
})
|
|
670
|
-
.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
|
+
}>;
|
|
671
829
|
|
|
672
830
|
// 合并并去重
|
|
673
831
|
const allBalances = [...balances, ...token2022Balances];
|
|
@@ -682,7 +840,7 @@ export class PumpTrader {
|
|
|
682
840
|
mint: b.mint,
|
|
683
841
|
amount: Number(b.amount),
|
|
684
842
|
decimals: b.decimals,
|
|
685
|
-
uiAmount: b.uiAmount
|
|
843
|
+
uiAmount: b.uiAmount,
|
|
686
844
|
}));
|
|
687
845
|
|
|
688
846
|
return uniqueBalances;
|
|
@@ -695,14 +853,18 @@ export class PumpTrader {
|
|
|
695
853
|
|
|
696
854
|
/* ---------- ATA 管理 ---------- */
|
|
697
855
|
|
|
698
|
-
async ensureAta(
|
|
856
|
+
async ensureAta(
|
|
857
|
+
tx: Transaction,
|
|
858
|
+
mint: PublicKey,
|
|
859
|
+
tokenProgram?: PublicKey,
|
|
860
|
+
): Promise<PublicKey> {
|
|
699
861
|
const program = tokenProgram || TOKEN_2022_PROGRAM_ID;
|
|
700
862
|
const ata = getAssociatedTokenAddressSync(
|
|
701
863
|
mint,
|
|
702
864
|
this.wallet.publicKey,
|
|
703
865
|
false,
|
|
704
866
|
program,
|
|
705
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
867
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
706
868
|
);
|
|
707
869
|
|
|
708
870
|
const acc = await this.connection.getAccountInfo(ata);
|
|
@@ -714,8 +876,8 @@ export class PumpTrader {
|
|
|
714
876
|
this.wallet.publicKey,
|
|
715
877
|
mint,
|
|
716
878
|
program,
|
|
717
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
718
|
-
)
|
|
879
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
880
|
+
),
|
|
719
881
|
);
|
|
720
882
|
}
|
|
721
883
|
|
|
@@ -726,14 +888,14 @@ export class PumpTrader {
|
|
|
726
888
|
tx: Transaction,
|
|
727
889
|
owner: PublicKey,
|
|
728
890
|
mode: "buy" | "sell",
|
|
729
|
-
lamports?: bigint
|
|
891
|
+
lamports?: bigint,
|
|
730
892
|
): Promise<PublicKey> {
|
|
731
893
|
const wsolAta = getAssociatedTokenAddressSync(
|
|
732
894
|
SOL_MINT,
|
|
733
895
|
owner,
|
|
734
896
|
false,
|
|
735
897
|
TOKEN_PROGRAM_ID,
|
|
736
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
898
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
737
899
|
);
|
|
738
900
|
|
|
739
901
|
const acc = await this.connection.getAccountInfo(wsolAta);
|
|
@@ -746,18 +908,18 @@ export class PumpTrader {
|
|
|
746
908
|
owner,
|
|
747
909
|
SOL_MINT,
|
|
748
910
|
TOKEN_PROGRAM_ID,
|
|
749
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
750
|
-
)
|
|
911
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
912
|
+
),
|
|
751
913
|
);
|
|
752
914
|
}
|
|
753
915
|
|
|
754
|
-
if (mode ===
|
|
916
|
+
if (mode === "buy" && lamports) {
|
|
755
917
|
tx.add(
|
|
756
918
|
SystemProgram.transfer({
|
|
757
919
|
fromPubkey: owner,
|
|
758
920
|
toPubkey: wsolAta,
|
|
759
|
-
lamports: Number(lamports)
|
|
760
|
-
})
|
|
921
|
+
lamports: Number(lamports),
|
|
922
|
+
}),
|
|
761
923
|
);
|
|
762
924
|
tx.add(createSyncNativeInstruction(wsolAta));
|
|
763
925
|
}
|
|
@@ -771,7 +933,9 @@ export class PumpTrader {
|
|
|
771
933
|
if (!priorityOpt?.enableRandom || !priorityOpt.randomRange) {
|
|
772
934
|
return priorityOpt.base;
|
|
773
935
|
}
|
|
774
|
-
return
|
|
936
|
+
return (
|
|
937
|
+
priorityOpt.base + Math.floor(Math.random() * priorityOpt.randomRange)
|
|
938
|
+
);
|
|
775
939
|
}
|
|
776
940
|
|
|
777
941
|
calcSlippage({ tradeSize, reserve, slippageOpt }: any): number {
|
|
@@ -819,31 +983,57 @@ export class PumpTrader {
|
|
|
819
983
|
|
|
820
984
|
/**
|
|
821
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)
|
|
822
988
|
*/
|
|
823
|
-
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> {
|
|
824
996
|
const mode = await this.getTradeMode(tokenAddr);
|
|
825
997
|
if (mode === "bonding") {
|
|
998
|
+
if (useV2) {
|
|
999
|
+
return this.buyV2(tokenAddr, totalSolIn, tradeOpt, quoteMint);
|
|
1000
|
+
}
|
|
826
1001
|
return this.buy(tokenAddr, totalSolIn, tradeOpt);
|
|
827
1002
|
} else {
|
|
828
|
-
return this.ammBuy(tokenAddr, totalSolIn, tradeOpt);
|
|
1003
|
+
return this.ammBuy(tokenAddr, totalSolIn, tradeOpt, quoteMint);
|
|
829
1004
|
}
|
|
830
1005
|
}
|
|
831
1006
|
|
|
832
1007
|
/**
|
|
833
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)
|
|
834
1011
|
*/
|
|
835
|
-
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> {
|
|
836
1019
|
const mode = await this.getTradeMode(tokenAddr);
|
|
837
1020
|
if (mode === "bonding") {
|
|
1021
|
+
if (useV2) {
|
|
1022
|
+
return this.sellV2(tokenAddr, totalTokenIn, tradeOpt, quoteMint);
|
|
1023
|
+
}
|
|
838
1024
|
return this.sell(tokenAddr, totalTokenIn, tradeOpt);
|
|
839
1025
|
} else {
|
|
840
|
-
return this.ammSell(tokenAddr, totalTokenIn, tradeOpt);
|
|
1026
|
+
return this.ammSell(tokenAddr, totalTokenIn, tradeOpt, quoteMint);
|
|
841
1027
|
}
|
|
842
1028
|
}
|
|
843
1029
|
|
|
844
1030
|
/* ---------- 内盘交易 ---------- */
|
|
845
1031
|
|
|
846
|
-
async buy(
|
|
1032
|
+
async buy(
|
|
1033
|
+
tokenAddr: string,
|
|
1034
|
+
totalSolIn: bigint,
|
|
1035
|
+
tradeOpt: TradeOptions,
|
|
1036
|
+
): Promise<TradeResult> {
|
|
847
1037
|
const mint = new PublicKey(tokenAddr);
|
|
848
1038
|
const tokenProgram = await this.detectTokenProgram(tokenAddr);
|
|
849
1039
|
const bondingCurveV2 = this.deriveBondingCurveV2(mint);
|
|
@@ -862,27 +1052,30 @@ export class PumpTrader {
|
|
|
862
1052
|
bonding,
|
|
863
1053
|
true,
|
|
864
1054
|
tokenProgram.programId,
|
|
865
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1055
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
866
1056
|
);
|
|
867
1057
|
|
|
868
1058
|
const [creatorVault] = PublicKey.findProgramAddressSync(
|
|
869
1059
|
[Buffer.from("creator-vault"), creator.toBuffer()],
|
|
870
|
-
PROGRAM_IDS.PUMP
|
|
1060
|
+
PROGRAM_IDS.PUMP,
|
|
871
1061
|
);
|
|
872
1062
|
|
|
873
1063
|
const [globalVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
874
1064
|
[Buffer.from("global_volume_accumulator")],
|
|
875
|
-
PROGRAM_IDS.PUMP
|
|
1065
|
+
PROGRAM_IDS.PUMP,
|
|
876
1066
|
);
|
|
877
1067
|
|
|
878
1068
|
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
879
|
-
[
|
|
880
|
-
|
|
1069
|
+
[
|
|
1070
|
+
Buffer.from("user_volume_accumulator"),
|
|
1071
|
+
this.wallet.publicKey.toBuffer(),
|
|
1072
|
+
],
|
|
1073
|
+
PROGRAM_IDS.PUMP,
|
|
881
1074
|
);
|
|
882
1075
|
|
|
883
1076
|
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
884
1077
|
[Buffer.from("fee_config"), SEEDS.FEE_CONFIG],
|
|
885
|
-
PROGRAM_IDS.FEE
|
|
1078
|
+
PROGRAM_IDS.FEE,
|
|
886
1079
|
);
|
|
887
1080
|
const feeRecipient = this.pickFeeRecipient();
|
|
888
1081
|
|
|
@@ -893,14 +1086,14 @@ export class PumpTrader {
|
|
|
893
1086
|
const slippageBps = this.calcSlippage({
|
|
894
1087
|
tradeSize: solIn,
|
|
895
1088
|
reserve: state.virtualSolReserves,
|
|
896
|
-
slippageOpt: tradeOpt.slippage
|
|
1089
|
+
slippageOpt: tradeOpt.slippage,
|
|
897
1090
|
});
|
|
898
1091
|
const maxSol = (solIn * BigInt(10_000 + slippageBps)) / 10_000n;
|
|
899
1092
|
const priority = this.genPriority(tradeOpt.priority);
|
|
900
1093
|
|
|
901
1094
|
const tx = new Transaction().add(
|
|
902
1095
|
ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
|
|
903
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority })
|
|
1096
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
904
1097
|
);
|
|
905
1098
|
|
|
906
1099
|
const userAta = await this.ensureAta(tx, mint, tokenProgram.programId);
|
|
@@ -925,32 +1118,39 @@ export class PumpTrader {
|
|
|
925
1118
|
feeProgram: PROGRAM_IDS.FEE,
|
|
926
1119
|
bondingCurveV2,
|
|
927
1120
|
feeRecipient,
|
|
928
|
-
tokenProgramId: tokenProgram.programId
|
|
1121
|
+
tokenProgramId: tokenProgram.programId,
|
|
929
1122
|
}),
|
|
930
|
-
data: Buffer.concat([
|
|
931
|
-
|
|
1123
|
+
data: Buffer.concat([
|
|
1124
|
+
DISCRIMINATORS.BUY,
|
|
1125
|
+
u64(tokenOut),
|
|
1126
|
+
u64(maxSol),
|
|
1127
|
+
]),
|
|
1128
|
+
}),
|
|
932
1129
|
);
|
|
933
1130
|
|
|
934
1131
|
const { blockhash, lastValidBlockHeight } =
|
|
935
|
-
await this.connection.getLatestBlockhash(
|
|
1132
|
+
await this.connection.getLatestBlockhash("finalized");
|
|
936
1133
|
tx.recentBlockhash = blockhash;
|
|
937
1134
|
tx.feePayer = this.wallet.publicKey;
|
|
938
1135
|
tx.sign(this.wallet);
|
|
939
1136
|
|
|
940
|
-
const signature = await this.connection.sendRawTransaction(
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1137
|
+
const signature = await this.connection.sendRawTransaction(
|
|
1138
|
+
tx.serialize(),
|
|
1139
|
+
{
|
|
1140
|
+
skipPreflight: false,
|
|
1141
|
+
maxRetries: 2,
|
|
1142
|
+
},
|
|
1143
|
+
);
|
|
944
1144
|
|
|
945
1145
|
pendingTransactions.push({
|
|
946
1146
|
signature,
|
|
947
1147
|
lastValidBlockHeight,
|
|
948
|
-
index: i
|
|
1148
|
+
index: i,
|
|
949
1149
|
});
|
|
950
1150
|
} catch (e) {
|
|
951
1151
|
failedTransactions.push({
|
|
952
1152
|
index: i,
|
|
953
|
-
error: (e as Error).message
|
|
1153
|
+
error: (e as Error).message,
|
|
954
1154
|
});
|
|
955
1155
|
}
|
|
956
1156
|
}
|
|
@@ -958,7 +1158,11 @@ export class PumpTrader {
|
|
|
958
1158
|
return { pendingTransactions, failedTransactions };
|
|
959
1159
|
}
|
|
960
1160
|
|
|
961
|
-
async sell(
|
|
1161
|
+
async sell(
|
|
1162
|
+
tokenAddr: string,
|
|
1163
|
+
totalTokenIn: bigint,
|
|
1164
|
+
tradeOpt: TradeOptions,
|
|
1165
|
+
): Promise<TradeResult> {
|
|
962
1166
|
const mint = new PublicKey(tokenAddr);
|
|
963
1167
|
const tokenProgram = await this.detectTokenProgram(tokenAddr);
|
|
964
1168
|
const bondingCurveV2 = this.deriveBondingCurveV2(mint);
|
|
@@ -969,12 +1173,15 @@ export class PumpTrader {
|
|
|
969
1173
|
if (state.complete) throw new Error("Bonding curve already completed");
|
|
970
1174
|
|
|
971
1175
|
const totalSolOut = this.calcSell(totalTokenIn, state);
|
|
972
|
-
const tokenChunks =
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
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
|
+
);
|
|
978
1185
|
|
|
979
1186
|
const pendingTransactions: PendingTransaction[] = [];
|
|
980
1187
|
const failedTransactions: FailedTransaction[] = [];
|
|
@@ -984,7 +1191,7 @@ export class PumpTrader {
|
|
|
984
1191
|
bonding,
|
|
985
1192
|
true,
|
|
986
1193
|
tokenProgram.programId,
|
|
987
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1194
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
988
1195
|
);
|
|
989
1196
|
|
|
990
1197
|
const userAta = getAssociatedTokenAddressSync(
|
|
@@ -992,22 +1199,25 @@ export class PumpTrader {
|
|
|
992
1199
|
this.wallet.publicKey,
|
|
993
1200
|
false,
|
|
994
1201
|
tokenProgram.programId,
|
|
995
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1202
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
996
1203
|
);
|
|
997
1204
|
|
|
998
1205
|
const [creatorVault] = PublicKey.findProgramAddressSync(
|
|
999
1206
|
[Buffer.from("creator-vault"), creator.toBuffer()],
|
|
1000
|
-
PROGRAM_IDS.PUMP
|
|
1207
|
+
PROGRAM_IDS.PUMP,
|
|
1001
1208
|
);
|
|
1002
1209
|
|
|
1003
1210
|
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
1004
1211
|
[Buffer.from("fee_config"), SEEDS.FEE_CONFIG],
|
|
1005
|
-
PROGRAM_IDS.FEE
|
|
1212
|
+
PROGRAM_IDS.FEE,
|
|
1006
1213
|
);
|
|
1007
1214
|
|
|
1008
1215
|
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
1009
|
-
[
|
|
1010
|
-
|
|
1216
|
+
[
|
|
1217
|
+
Buffer.from("user_volume_accumulator"),
|
|
1218
|
+
this.wallet.publicKey.toBuffer(),
|
|
1219
|
+
],
|
|
1220
|
+
PROGRAM_IDS.PUMP,
|
|
1011
1221
|
);
|
|
1012
1222
|
const feeRecipient = this.pickFeeRecipient();
|
|
1013
1223
|
|
|
@@ -1018,14 +1228,14 @@ export class PumpTrader {
|
|
|
1018
1228
|
const slippageBps = this.calcSlippage({
|
|
1019
1229
|
tradeSize: tokenIn,
|
|
1020
1230
|
reserve: state.virtualTokenReserves,
|
|
1021
|
-
slippageOpt: tradeOpt.slippage
|
|
1231
|
+
slippageOpt: tradeOpt.slippage,
|
|
1022
1232
|
});
|
|
1023
1233
|
const minSol = (solOut * BigInt(10_000 - slippageBps)) / 10_000n;
|
|
1024
1234
|
const priority = this.genPriority(tradeOpt.priority);
|
|
1025
1235
|
|
|
1026
1236
|
const tx = new Transaction().add(
|
|
1027
1237
|
ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
|
|
1028
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority })
|
|
1238
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
1029
1239
|
);
|
|
1030
1240
|
|
|
1031
1241
|
tx.add(
|
|
@@ -1048,14 +1258,14 @@ export class PumpTrader {
|
|
|
1048
1258
|
feeRecipient,
|
|
1049
1259
|
isCashbackCoin: !!state.isCashbackCoin,
|
|
1050
1260
|
userVolumeAccumulator,
|
|
1051
|
-
tokenProgramId: tokenProgram.programId
|
|
1261
|
+
tokenProgramId: tokenProgram.programId,
|
|
1052
1262
|
}),
|
|
1053
1263
|
data: Buffer.concat([
|
|
1054
1264
|
DISCRIMINATORS.SELL,
|
|
1055
1265
|
u64(tokenIn),
|
|
1056
|
-
u64(minSol > 0n ? minSol : 1n)
|
|
1057
|
-
])
|
|
1058
|
-
})
|
|
1266
|
+
u64(minSol > 0n ? minSol : 1n),
|
|
1267
|
+
]),
|
|
1268
|
+
}),
|
|
1059
1269
|
);
|
|
1060
1270
|
|
|
1061
1271
|
const { blockhash, lastValidBlockHeight } =
|
|
@@ -1064,16 +1274,18 @@ export class PumpTrader {
|
|
|
1064
1274
|
tx.feePayer = this.wallet.publicKey;
|
|
1065
1275
|
tx.sign(this.wallet);
|
|
1066
1276
|
|
|
1067
|
-
const signature = await this.connection.sendRawTransaction(
|
|
1277
|
+
const signature = await this.connection.sendRawTransaction(
|
|
1278
|
+
tx.serialize(),
|
|
1279
|
+
);
|
|
1068
1280
|
pendingTransactions.push({
|
|
1069
1281
|
signature,
|
|
1070
1282
|
lastValidBlockHeight,
|
|
1071
|
-
index: i
|
|
1283
|
+
index: i,
|
|
1072
1284
|
});
|
|
1073
1285
|
} catch (e) {
|
|
1074
1286
|
failedTransactions.push({
|
|
1075
1287
|
index: i,
|
|
1076
|
-
error: (e as Error).message
|
|
1288
|
+
error: (e as Error).message,
|
|
1077
1289
|
});
|
|
1078
1290
|
}
|
|
1079
1291
|
}
|
|
@@ -1083,12 +1295,21 @@ export class PumpTrader {
|
|
|
1083
1295
|
|
|
1084
1296
|
/* ---------- 外盘交易 ---------- */
|
|
1085
1297
|
|
|
1086
|
-
async ammBuy(
|
|
1298
|
+
async ammBuy(
|
|
1299
|
+
tokenAddr: string,
|
|
1300
|
+
totalSolIn: bigint,
|
|
1301
|
+
tradeOpt: TradeOptions,
|
|
1302
|
+
quoteMint: PublicKey = SOL_MINT,
|
|
1303
|
+
): Promise<TradeResult> {
|
|
1087
1304
|
const mint = new PublicKey(tokenAddr);
|
|
1088
|
-
const poolInfo = await this.getAmmPoolInfo(mint);
|
|
1305
|
+
const poolInfo = await this.getAmmPoolInfo(mint, quoteMint);
|
|
1089
1306
|
const reserves = await this.getAmmPoolReserves(poolInfo.poolKeys);
|
|
1090
1307
|
const solChunks = this.splitByMax(totalSolIn, tradeOpt.maxSolPerTx);
|
|
1091
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);
|
|
1092
1313
|
const pendingTransactions: PendingTransaction[] = [];
|
|
1093
1314
|
const failedTransactions: FailedTransaction[] = [];
|
|
1094
1315
|
|
|
@@ -1099,23 +1320,29 @@ export class PumpTrader {
|
|
|
1099
1320
|
const slippageBps = this.calcSlippage({
|
|
1100
1321
|
tradeSize: solIn,
|
|
1101
1322
|
reserve: reserves.quoteAmount,
|
|
1102
|
-
slippageOpt: tradeOpt.slippage
|
|
1323
|
+
slippageOpt: tradeOpt.slippage,
|
|
1103
1324
|
});
|
|
1104
1325
|
const maxQuoteIn = (solIn * BigInt(10_000 + slippageBps)) / 10_000n;
|
|
1105
1326
|
const priority = this.genPriority(tradeOpt.priority);
|
|
1106
1327
|
|
|
1107
1328
|
const tx = new Transaction().add(
|
|
1108
1329
|
ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }),
|
|
1109
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority })
|
|
1330
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
1110
1331
|
);
|
|
1111
1332
|
|
|
1112
|
-
const userBaseAta = await this.ensureAta(
|
|
1113
|
-
const userQuoteAta = await this.ensureWSOLAta(
|
|
1333
|
+
const userBaseAta = await this.ensureAta(
|
|
1114
1334
|
tx,
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
maxQuoteIn
|
|
1335
|
+
poolInfo.poolKeys.baseMint,
|
|
1336
|
+
tokenProgram.programId,
|
|
1118
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);
|
|
1119
1346
|
|
|
1120
1347
|
const buyIx = this.createAmmBuyInstruction(
|
|
1121
1348
|
poolInfo,
|
|
@@ -1123,38 +1350,43 @@ export class PumpTrader {
|
|
|
1123
1350
|
userQuoteAta,
|
|
1124
1351
|
baseAmountOut,
|
|
1125
1352
|
maxQuoteIn,
|
|
1126
|
-
tokenProgram.programId
|
|
1353
|
+
tokenProgram.programId,
|
|
1127
1354
|
);
|
|
1128
1355
|
|
|
1129
1356
|
tx.add(buyIx);
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1357
|
+
if (isSolQuote) {
|
|
1358
|
+
tx.add(
|
|
1359
|
+
createCloseAccountInstruction(
|
|
1360
|
+
userQuoteAta,
|
|
1361
|
+
this.wallet.publicKey,
|
|
1362
|
+
this.wallet.publicKey,
|
|
1363
|
+
),
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1137
1366
|
|
|
1138
1367
|
const { blockhash, lastValidBlockHeight } =
|
|
1139
|
-
await this.connection.getLatestBlockhash(
|
|
1368
|
+
await this.connection.getLatestBlockhash("finalized");
|
|
1140
1369
|
tx.recentBlockhash = blockhash;
|
|
1141
1370
|
tx.feePayer = this.wallet.publicKey;
|
|
1142
1371
|
tx.sign(this.wallet);
|
|
1143
1372
|
|
|
1144
|
-
const signature = await this.connection.sendRawTransaction(
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1373
|
+
const signature = await this.connection.sendRawTransaction(
|
|
1374
|
+
tx.serialize(),
|
|
1375
|
+
{
|
|
1376
|
+
skipPreflight: false,
|
|
1377
|
+
maxRetries: 2,
|
|
1378
|
+
},
|
|
1379
|
+
);
|
|
1148
1380
|
|
|
1149
1381
|
pendingTransactions.push({
|
|
1150
1382
|
signature,
|
|
1151
1383
|
lastValidBlockHeight,
|
|
1152
|
-
index: i
|
|
1384
|
+
index: i,
|
|
1153
1385
|
});
|
|
1154
1386
|
} catch (e) {
|
|
1155
1387
|
failedTransactions.push({
|
|
1156
1388
|
index: i,
|
|
1157
|
-
error: (e as Error).message
|
|
1389
|
+
error: (e as Error).message,
|
|
1158
1390
|
});
|
|
1159
1391
|
}
|
|
1160
1392
|
}
|
|
@@ -1162,18 +1394,30 @@ export class PumpTrader {
|
|
|
1162
1394
|
return { pendingTransactions, failedTransactions };
|
|
1163
1395
|
}
|
|
1164
1396
|
|
|
1165
|
-
async ammSell(
|
|
1397
|
+
async ammSell(
|
|
1398
|
+
tokenAddr: string,
|
|
1399
|
+
totalTokenIn: bigint,
|
|
1400
|
+
tradeOpt: TradeOptions,
|
|
1401
|
+
quoteMint: PublicKey = SOL_MINT,
|
|
1402
|
+
): Promise<TradeResult> {
|
|
1166
1403
|
const mint = new PublicKey(tokenAddr);
|
|
1167
|
-
const poolInfo = await this.getAmmPoolInfo(mint);
|
|
1404
|
+
const poolInfo = await this.getAmmPoolInfo(mint, quoteMint);
|
|
1168
1405
|
const reserves = await this.getAmmPoolReserves(poolInfo.poolKeys);
|
|
1169
1406
|
const totalSolOut = this.calculateAmmSellOutput(totalTokenIn, reserves);
|
|
1170
1407
|
const tokenProgram = await this.detectTokenProgram(tokenAddr);
|
|
1171
|
-
const
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
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
|
+
);
|
|
1177
1421
|
|
|
1178
1422
|
const pendingTransactions: PendingTransaction[] = [];
|
|
1179
1423
|
const failedTransactions: FailedTransaction[] = [];
|
|
@@ -1185,18 +1429,24 @@ export class PumpTrader {
|
|
|
1185
1429
|
const slippageBps = this.calcSlippage({
|
|
1186
1430
|
tradeSize: tokenIn,
|
|
1187
1431
|
reserve: reserves.baseAmount,
|
|
1188
|
-
slippageOpt: tradeOpt.slippage
|
|
1432
|
+
slippageOpt: tradeOpt.slippage,
|
|
1189
1433
|
});
|
|
1190
1434
|
const minQuoteOut = (solOut * BigInt(10_000 - slippageBps)) / 10_000n;
|
|
1191
1435
|
const priority = this.genPriority(tradeOpt.priority);
|
|
1192
1436
|
|
|
1193
1437
|
const tx = new Transaction().add(
|
|
1194
1438
|
ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }),
|
|
1195
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority })
|
|
1439
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priority }),
|
|
1196
1440
|
);
|
|
1197
1441
|
|
|
1198
|
-
const userBaseAta = await this.ensureAta(
|
|
1199
|
-
|
|
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);
|
|
1200
1450
|
|
|
1201
1451
|
const sellIx = this.createAmmSellInstruction(
|
|
1202
1452
|
poolInfo,
|
|
@@ -1204,38 +1454,43 @@ export class PumpTrader {
|
|
|
1204
1454
|
userQuoteAta,
|
|
1205
1455
|
tokenIn,
|
|
1206
1456
|
minQuoteOut,
|
|
1207
|
-
tokenProgram.programId
|
|
1457
|
+
tokenProgram.programId,
|
|
1208
1458
|
);
|
|
1209
1459
|
|
|
1210
1460
|
tx.add(sellIx);
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1461
|
+
if (isSolQuote) {
|
|
1462
|
+
tx.add(
|
|
1463
|
+
createCloseAccountInstruction(
|
|
1464
|
+
userQuoteAta,
|
|
1465
|
+
this.wallet.publicKey,
|
|
1466
|
+
this.wallet.publicKey,
|
|
1467
|
+
),
|
|
1468
|
+
);
|
|
1469
|
+
}
|
|
1218
1470
|
|
|
1219
1471
|
const { blockhash, lastValidBlockHeight } =
|
|
1220
|
-
await this.connection.getLatestBlockhash(
|
|
1472
|
+
await this.connection.getLatestBlockhash("finalized");
|
|
1221
1473
|
tx.recentBlockhash = blockhash;
|
|
1222
1474
|
tx.feePayer = this.wallet.publicKey;
|
|
1223
1475
|
tx.sign(this.wallet);
|
|
1224
1476
|
|
|
1225
|
-
const signature = await this.connection.sendRawTransaction(
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1477
|
+
const signature = await this.connection.sendRawTransaction(
|
|
1478
|
+
tx.serialize(),
|
|
1479
|
+
{
|
|
1480
|
+
skipPreflight: false,
|
|
1481
|
+
maxRetries: 2,
|
|
1482
|
+
},
|
|
1483
|
+
);
|
|
1229
1484
|
|
|
1230
1485
|
pendingTransactions.push({
|
|
1231
1486
|
signature,
|
|
1232
1487
|
lastValidBlockHeight,
|
|
1233
|
-
index: i
|
|
1488
|
+
index: i,
|
|
1234
1489
|
});
|
|
1235
1490
|
} catch (e) {
|
|
1236
1491
|
failedTransactions.push({
|
|
1237
1492
|
index: i,
|
|
1238
|
-
error: (e as Error).message
|
|
1493
|
+
error: (e as Error).message,
|
|
1239
1494
|
});
|
|
1240
1495
|
}
|
|
1241
1496
|
}
|
|
@@ -1245,10 +1500,13 @@ export class PumpTrader {
|
|
|
1245
1500
|
|
|
1246
1501
|
/* ---------- AMM 池信息 ---------- */
|
|
1247
1502
|
|
|
1248
|
-
async getAmmPoolInfo(
|
|
1503
|
+
async getAmmPoolInfo(
|
|
1504
|
+
mint: PublicKey,
|
|
1505
|
+
quoteMint: PublicKey = SOL_MINT,
|
|
1506
|
+
): Promise<PoolInfo> {
|
|
1249
1507
|
const [poolAuthority] = PublicKey.findProgramAddressSync(
|
|
1250
1508
|
[Buffer.from("pool-authority"), mint.toBuffer()],
|
|
1251
|
-
PROGRAM_IDS.PUMP
|
|
1509
|
+
PROGRAM_IDS.PUMP,
|
|
1252
1510
|
);
|
|
1253
1511
|
|
|
1254
1512
|
const [pool] = PublicKey.findProgramAddressSync(
|
|
@@ -1257,9 +1515,9 @@ export class PumpTrader {
|
|
|
1257
1515
|
new BN(0).toArrayLike(Buffer, "le", 2),
|
|
1258
1516
|
poolAuthority.toBuffer(),
|
|
1259
1517
|
mint.toBuffer(),
|
|
1260
|
-
|
|
1518
|
+
quoteMint.toBuffer(),
|
|
1261
1519
|
],
|
|
1262
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1520
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1263
1521
|
);
|
|
1264
1522
|
|
|
1265
1523
|
const acc = await this.connection.getAccountInfo(pool);
|
|
@@ -1269,13 +1527,17 @@ export class PumpTrader {
|
|
|
1269
1527
|
|
|
1270
1528
|
const [globalConfigPda] = PublicKey.findProgramAddressSync(
|
|
1271
1529
|
[Buffer.from("global_config")],
|
|
1272
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1530
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1273
1531
|
);
|
|
1274
1532
|
|
|
1275
|
-
const globalConfigAcc =
|
|
1533
|
+
const globalConfigAcc =
|
|
1534
|
+
await this.connection.getAccountInfo(globalConfigPda);
|
|
1276
1535
|
if (!globalConfigAcc) throw new Error("Global config not found");
|
|
1277
1536
|
|
|
1278
|
-
const globalConfig = this.parseAmmGlobalConfig(
|
|
1537
|
+
const globalConfig = this.parseAmmGlobalConfig(
|
|
1538
|
+
globalConfigAcc.data,
|
|
1539
|
+
globalConfigPda,
|
|
1540
|
+
);
|
|
1279
1541
|
|
|
1280
1542
|
return { pool, poolAuthority, poolKeys, globalConfig };
|
|
1281
1543
|
}
|
|
@@ -1292,7 +1554,9 @@ export class PumpTrader {
|
|
|
1292
1554
|
|
|
1293
1555
|
const protocolFeeRecipients: PublicKey[] = [];
|
|
1294
1556
|
for (let i = 0; i < 8; i++) {
|
|
1295
|
-
protocolFeeRecipients.push(
|
|
1557
|
+
protocolFeeRecipients.push(
|
|
1558
|
+
new PublicKey(data.slice(offset, offset + 32)),
|
|
1559
|
+
);
|
|
1296
1560
|
offset += 32;
|
|
1297
1561
|
}
|
|
1298
1562
|
|
|
@@ -1302,21 +1566,21 @@ export class PumpTrader {
|
|
|
1302
1566
|
async getAmmPoolReserves(poolKeys: any): Promise<PoolReserves> {
|
|
1303
1567
|
const [baseInfo, quoteInfo] = await Promise.all([
|
|
1304
1568
|
this.connection.getTokenAccountBalance(poolKeys.poolBaseTokenAccount),
|
|
1305
|
-
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount)
|
|
1569
|
+
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
|
|
1306
1570
|
]);
|
|
1307
1571
|
|
|
1308
1572
|
return {
|
|
1309
1573
|
baseAmount: BigInt(baseInfo.value.amount),
|
|
1310
1574
|
quoteAmount: BigInt(quoteInfo.value.amount),
|
|
1311
1575
|
baseDecimals: baseInfo.value.decimals,
|
|
1312
|
-
quoteDecimals: quoteInfo.value.decimals
|
|
1576
|
+
quoteDecimals: quoteInfo.value.decimals,
|
|
1313
1577
|
};
|
|
1314
1578
|
}
|
|
1315
1579
|
|
|
1316
1580
|
deriveAmmPoolV2(baseMint: PublicKey): PublicKey {
|
|
1317
1581
|
return PublicKey.findProgramAddressSync(
|
|
1318
1582
|
[Buffer.from("pool-v2"), baseMint.toBuffer()],
|
|
1319
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1583
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1320
1584
|
)[0];
|
|
1321
1585
|
}
|
|
1322
1586
|
|
|
@@ -1328,19 +1592,19 @@ export class PumpTrader {
|
|
|
1328
1592
|
userQuoteAta: PublicKey,
|
|
1329
1593
|
baseAmountOut: bigint,
|
|
1330
1594
|
maxQuoteAmountIn: bigint,
|
|
1331
|
-
tokenProgramId: PublicKey
|
|
1595
|
+
tokenProgramId: PublicKey,
|
|
1332
1596
|
): TransactionInstruction {
|
|
1333
1597
|
const { pool, poolKeys, globalConfig } = poolInfo;
|
|
1334
1598
|
const poolV2 = this.deriveAmmPoolV2(poolKeys.baseMint);
|
|
1335
1599
|
|
|
1336
1600
|
const [eventAuthority] = PublicKey.findProgramAddressSync(
|
|
1337
1601
|
[Buffer.from("__event_authority")],
|
|
1338
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1602
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1339
1603
|
);
|
|
1340
1604
|
|
|
1341
1605
|
const [coinCreatorVaultAuthority] = PublicKey.findProgramAddressSync(
|
|
1342
1606
|
[Buffer.from("creator_vault"), poolKeys.coinCreator.toBuffer()],
|
|
1343
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1607
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1344
1608
|
);
|
|
1345
1609
|
|
|
1346
1610
|
const coinCreatorVaultAta = getAssociatedTokenAddressSync(
|
|
@@ -1348,22 +1612,25 @@ export class PumpTrader {
|
|
|
1348
1612
|
coinCreatorVaultAuthority,
|
|
1349
1613
|
true,
|
|
1350
1614
|
TOKEN_PROGRAM_ID,
|
|
1351
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1615
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1352
1616
|
);
|
|
1353
1617
|
|
|
1354
1618
|
const [globalVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
1355
1619
|
[Buffer.from("global_volume_accumulator")],
|
|
1356
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1620
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1357
1621
|
);
|
|
1358
1622
|
|
|
1359
1623
|
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
1360
|
-
[
|
|
1361
|
-
|
|
1624
|
+
[
|
|
1625
|
+
Buffer.from("user_volume_accumulator"),
|
|
1626
|
+
this.wallet.publicKey.toBuffer(),
|
|
1627
|
+
],
|
|
1628
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1362
1629
|
);
|
|
1363
1630
|
|
|
1364
1631
|
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
1365
1632
|
[Buffer.from("fee_config"), SEEDS.AMM_FEE_CONFIG],
|
|
1366
|
-
PROGRAM_IDS.FEE
|
|
1633
|
+
PROGRAM_IDS.FEE,
|
|
1367
1634
|
);
|
|
1368
1635
|
|
|
1369
1636
|
const protocolFeeRecipient = globalConfig.protocolFeeRecipients[0];
|
|
@@ -1372,7 +1639,7 @@ export class PumpTrader {
|
|
|
1372
1639
|
protocolFeeRecipient,
|
|
1373
1640
|
true,
|
|
1374
1641
|
TOKEN_PROGRAM_ID,
|
|
1375
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1642
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1376
1643
|
);
|
|
1377
1644
|
const newFeeRecipient = this.pickFeeRecipient();
|
|
1378
1645
|
const newFeeRecipientTokenAccount = getAssociatedTokenAddressSync(
|
|
@@ -1380,7 +1647,7 @@ export class PumpTrader {
|
|
|
1380
1647
|
newFeeRecipient,
|
|
1381
1648
|
true,
|
|
1382
1649
|
TOKEN_PROGRAM_ID,
|
|
1383
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1650
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1384
1651
|
);
|
|
1385
1652
|
|
|
1386
1653
|
const remainingKeys = [];
|
|
@@ -1390,14 +1657,22 @@ export class PumpTrader {
|
|
|
1390
1657
|
userVolumeAccumulator,
|
|
1391
1658
|
true,
|
|
1392
1659
|
TOKEN_PROGRAM_ID,
|
|
1393
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1660
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1394
1661
|
);
|
|
1395
|
-
remainingKeys.push({
|
|
1662
|
+
remainingKeys.push({
|
|
1663
|
+
pubkey: userVolumeAccumulatorWsolAta,
|
|
1664
|
+
isSigner: false,
|
|
1665
|
+
isWritable: true,
|
|
1666
|
+
});
|
|
1396
1667
|
}
|
|
1397
1668
|
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
1398
1669
|
remainingKeys.push(
|
|
1399
1670
|
{ pubkey: newFeeRecipient, isSigner: false, isWritable: false },
|
|
1400
|
-
{
|
|
1671
|
+
{
|
|
1672
|
+
pubkey: newFeeRecipientTokenAccount,
|
|
1673
|
+
isSigner: false,
|
|
1674
|
+
isWritable: true,
|
|
1675
|
+
},
|
|
1401
1676
|
);
|
|
1402
1677
|
|
|
1403
1678
|
return new TransactionInstruction({
|
|
@@ -1410,30 +1685,50 @@ export class PumpTrader {
|
|
|
1410
1685
|
{ pubkey: poolKeys.quoteMint, isSigner: false, isWritable: false },
|
|
1411
1686
|
{ pubkey: userBaseAta, isSigner: false, isWritable: true },
|
|
1412
1687
|
{ pubkey: userQuoteAta, isSigner: false, isWritable: true },
|
|
1413
|
-
{
|
|
1414
|
-
|
|
1688
|
+
{
|
|
1689
|
+
pubkey: poolKeys.poolBaseTokenAccount,
|
|
1690
|
+
isSigner: false,
|
|
1691
|
+
isWritable: true,
|
|
1692
|
+
},
|
|
1693
|
+
{
|
|
1694
|
+
pubkey: poolKeys.poolQuoteTokenAccount,
|
|
1695
|
+
isSigner: false,
|
|
1696
|
+
isWritable: true,
|
|
1697
|
+
},
|
|
1415
1698
|
{ pubkey: protocolFeeRecipient, isSigner: false, isWritable: false },
|
|
1416
|
-
{
|
|
1699
|
+
{
|
|
1700
|
+
pubkey: protocolFeeRecipientTokenAccount,
|
|
1701
|
+
isSigner: false,
|
|
1702
|
+
isWritable: true,
|
|
1703
|
+
},
|
|
1417
1704
|
{ pubkey: tokenProgramId, isSigner: false, isWritable: false },
|
|
1418
1705
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1419
1706
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1420
|
-
{
|
|
1707
|
+
{
|
|
1708
|
+
pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1709
|
+
isSigner: false,
|
|
1710
|
+
isWritable: false,
|
|
1711
|
+
},
|
|
1421
1712
|
{ pubkey: eventAuthority, isSigner: false, isWritable: false },
|
|
1422
1713
|
{ pubkey: PROGRAM_IDS.PUMP_AMM, isSigner: false, isWritable: false },
|
|
1423
1714
|
{ pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true },
|
|
1424
|
-
{
|
|
1715
|
+
{
|
|
1716
|
+
pubkey: coinCreatorVaultAuthority,
|
|
1717
|
+
isSigner: false,
|
|
1718
|
+
isWritable: false,
|
|
1719
|
+
},
|
|
1425
1720
|
{ pubkey: globalVolumeAccumulator, isSigner: false, isWritable: false },
|
|
1426
1721
|
{ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
1427
1722
|
{ pubkey: feeConfig, isSigner: false, isWritable: false },
|
|
1428
1723
|
{ pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
|
|
1429
|
-
...remainingKeys
|
|
1724
|
+
...remainingKeys,
|
|
1430
1725
|
],
|
|
1431
1726
|
data: Buffer.concat([
|
|
1432
1727
|
DISCRIMINATORS.BUY,
|
|
1433
1728
|
u64(baseAmountOut),
|
|
1434
1729
|
u64(maxQuoteAmountIn),
|
|
1435
|
-
Buffer.from([1, 1])
|
|
1436
|
-
])
|
|
1730
|
+
Buffer.from([1, 1]),
|
|
1731
|
+
]),
|
|
1437
1732
|
});
|
|
1438
1733
|
}
|
|
1439
1734
|
|
|
@@ -1443,19 +1738,19 @@ export class PumpTrader {
|
|
|
1443
1738
|
userQuoteAta: PublicKey,
|
|
1444
1739
|
baseAmountIn: bigint,
|
|
1445
1740
|
minQuoteAmountOut: bigint,
|
|
1446
|
-
tokenProgramId: PublicKey
|
|
1741
|
+
tokenProgramId: PublicKey,
|
|
1447
1742
|
): TransactionInstruction {
|
|
1448
1743
|
const { pool, poolKeys, globalConfig } = poolInfo;
|
|
1449
1744
|
const poolV2 = this.deriveAmmPoolV2(poolKeys.baseMint);
|
|
1450
1745
|
|
|
1451
1746
|
const [eventAuthority] = PublicKey.findProgramAddressSync(
|
|
1452
1747
|
[Buffer.from("__event_authority")],
|
|
1453
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1748
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1454
1749
|
);
|
|
1455
1750
|
|
|
1456
1751
|
const [coinCreatorVaultAuthority] = PublicKey.findProgramAddressSync(
|
|
1457
1752
|
[Buffer.from("creator_vault"), poolKeys.coinCreator.toBuffer()],
|
|
1458
|
-
PROGRAM_IDS.PUMP_AMM
|
|
1753
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1459
1754
|
);
|
|
1460
1755
|
|
|
1461
1756
|
const coinCreatorVaultAta = getAssociatedTokenAddressSync(
|
|
@@ -1463,12 +1758,12 @@ export class PumpTrader {
|
|
|
1463
1758
|
coinCreatorVaultAuthority,
|
|
1464
1759
|
true,
|
|
1465
1760
|
TOKEN_PROGRAM_ID,
|
|
1466
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1761
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1467
1762
|
);
|
|
1468
1763
|
|
|
1469
1764
|
const [feeConfig] = PublicKey.findProgramAddressSync(
|
|
1470
1765
|
[Buffer.from("fee_config"), SEEDS.AMM_FEE_CONFIG],
|
|
1471
|
-
PROGRAM_IDS.FEE
|
|
1766
|
+
PROGRAM_IDS.FEE,
|
|
1472
1767
|
);
|
|
1473
1768
|
|
|
1474
1769
|
const protocolFeeRecipient = globalConfig.protocolFeeRecipients[0];
|
|
@@ -1477,7 +1772,7 @@ export class PumpTrader {
|
|
|
1477
1772
|
protocolFeeRecipient,
|
|
1478
1773
|
true,
|
|
1479
1774
|
TOKEN_PROGRAM_ID,
|
|
1480
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1775
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1481
1776
|
);
|
|
1482
1777
|
const newFeeRecipient = this.pickFeeRecipient();
|
|
1483
1778
|
const newFeeRecipientTokenAccount = getAssociatedTokenAddressSync(
|
|
@@ -1485,12 +1780,15 @@ export class PumpTrader {
|
|
|
1485
1780
|
newFeeRecipient,
|
|
1486
1781
|
true,
|
|
1487
1782
|
TOKEN_PROGRAM_ID,
|
|
1488
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1783
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1489
1784
|
);
|
|
1490
1785
|
|
|
1491
1786
|
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
|
|
1492
|
-
[
|
|
1493
|
-
|
|
1787
|
+
[
|
|
1788
|
+
Buffer.from("user_volume_accumulator"),
|
|
1789
|
+
this.wallet.publicKey.toBuffer(),
|
|
1790
|
+
],
|
|
1791
|
+
PROGRAM_IDS.PUMP_AMM,
|
|
1494
1792
|
);
|
|
1495
1793
|
|
|
1496
1794
|
const userVolumeAccumulatorWsolAta = getAssociatedTokenAddressSync(
|
|
@@ -1498,20 +1796,28 @@ export class PumpTrader {
|
|
|
1498
1796
|
userVolumeAccumulator,
|
|
1499
1797
|
true,
|
|
1500
1798
|
TOKEN_PROGRAM_ID,
|
|
1501
|
-
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
1799
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1502
1800
|
);
|
|
1503
1801
|
|
|
1504
1802
|
const remainingKeys = [];
|
|
1505
1803
|
if (poolKeys.isCashbackCoin) {
|
|
1506
1804
|
remainingKeys.push(
|
|
1507
|
-
{
|
|
1508
|
-
|
|
1805
|
+
{
|
|
1806
|
+
pubkey: userVolumeAccumulatorWsolAta,
|
|
1807
|
+
isSigner: false,
|
|
1808
|
+
isWritable: true,
|
|
1809
|
+
},
|
|
1810
|
+
{ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
1509
1811
|
);
|
|
1510
1812
|
}
|
|
1511
1813
|
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
1512
1814
|
remainingKeys.push(
|
|
1513
1815
|
{ pubkey: newFeeRecipient, isSigner: false, isWritable: false },
|
|
1514
|
-
{
|
|
1816
|
+
{
|
|
1817
|
+
pubkey: newFeeRecipientTokenAccount,
|
|
1818
|
+
isSigner: false,
|
|
1819
|
+
isWritable: true,
|
|
1820
|
+
},
|
|
1515
1821
|
);
|
|
1516
1822
|
|
|
1517
1823
|
return new TransactionInstruction({
|
|
@@ -1524,71 +1830,819 @@ export class PumpTrader {
|
|
|
1524
1830
|
{ pubkey: poolKeys.quoteMint, isSigner: false, isWritable: false },
|
|
1525
1831
|
{ pubkey: userBaseAta, isSigner: false, isWritable: true },
|
|
1526
1832
|
{ pubkey: userQuoteAta, isSigner: false, isWritable: true },
|
|
1527
|
-
{
|
|
1528
|
-
|
|
1833
|
+
{
|
|
1834
|
+
pubkey: poolKeys.poolBaseTokenAccount,
|
|
1835
|
+
isSigner: false,
|
|
1836
|
+
isWritable: true,
|
|
1837
|
+
},
|
|
1838
|
+
{
|
|
1839
|
+
pubkey: poolKeys.poolQuoteTokenAccount,
|
|
1840
|
+
isSigner: false,
|
|
1841
|
+
isWritable: true,
|
|
1842
|
+
},
|
|
1529
1843
|
{ pubkey: protocolFeeRecipient, isSigner: false, isWritable: false },
|
|
1530
|
-
{
|
|
1844
|
+
{
|
|
1845
|
+
pubkey: protocolFeeRecipientTokenAccount,
|
|
1846
|
+
isSigner: false,
|
|
1847
|
+
isWritable: true,
|
|
1848
|
+
},
|
|
1531
1849
|
{ pubkey: tokenProgramId, isSigner: false, isWritable: false },
|
|
1532
1850
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1533
1851
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1534
|
-
{
|
|
1852
|
+
{
|
|
1853
|
+
pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1854
|
+
isSigner: false,
|
|
1855
|
+
isWritable: false,
|
|
1856
|
+
},
|
|
1535
1857
|
{ pubkey: eventAuthority, isSigner: false, isWritable: false },
|
|
1536
1858
|
{ pubkey: PROGRAM_IDS.PUMP_AMM, isSigner: false, isWritable: false },
|
|
1537
1859
|
{ pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true },
|
|
1538
|
-
{
|
|
1860
|
+
{
|
|
1861
|
+
pubkey: coinCreatorVaultAuthority,
|
|
1862
|
+
isSigner: false,
|
|
1863
|
+
isWritable: false,
|
|
1864
|
+
},
|
|
1539
1865
|
{ pubkey: feeConfig, isSigner: false, isWritable: false },
|
|
1540
1866
|
{ pubkey: PROGRAM_IDS.FEE, isSigner: false, isWritable: false },
|
|
1541
|
-
...remainingKeys
|
|
1867
|
+
...remainingKeys,
|
|
1542
1868
|
],
|
|
1543
1869
|
data: Buffer.concat([
|
|
1544
1870
|
DISCRIMINATORS.SELL,
|
|
1545
1871
|
u64(baseAmountIn),
|
|
1546
|
-
u64(minQuoteAmountOut > 0n ? minQuoteAmountOut : 1n)
|
|
1547
|
-
])
|
|
1872
|
+
u64(minQuoteAmountOut > 0n ? minQuoteAmountOut : 1n),
|
|
1873
|
+
]),
|
|
1548
1874
|
});
|
|
1549
1875
|
}
|
|
1550
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
|
+
|
|
1551
2602
|
/* ---------- 交易确认 ---------- */
|
|
1552
2603
|
|
|
1553
2604
|
async confirmTransactionWithPolling(
|
|
1554
2605
|
signature: string,
|
|
1555
2606
|
lastValidBlockHeight: number,
|
|
1556
2607
|
maxAttempts: number = 5,
|
|
1557
|
-
delayMs: number = 2000
|
|
2608
|
+
delayMs: number = 2000,
|
|
1558
2609
|
): Promise<string> {
|
|
1559
|
-
console.log(
|
|
1560
|
-
console.log(
|
|
2610
|
+
console.log("✅ 交易已发送:", signature);
|
|
2611
|
+
console.log("🔗 查看交易: https://solscan.io/tx/" + signature);
|
|
1561
2612
|
|
|
1562
2613
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1563
|
-
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
2614
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1564
2615
|
|
|
1565
2616
|
try {
|
|
1566
2617
|
console.log(`🔍 检查交易状态 (${attempt}/${maxAttempts})...`);
|
|
1567
2618
|
|
|
1568
2619
|
const txInfo = await this.connection.getTransaction(signature, {
|
|
1569
|
-
commitment:
|
|
1570
|
-
maxSupportedTransactionVersion: 0
|
|
2620
|
+
commitment: "confirmed",
|
|
2621
|
+
maxSupportedTransactionVersion: 0,
|
|
1571
2622
|
});
|
|
1572
2623
|
|
|
1573
2624
|
if (txInfo) {
|
|
1574
2625
|
if (txInfo.meta?.err) {
|
|
1575
|
-
console.error(
|
|
1576
|
-
throw new Error(
|
|
2626
|
+
console.error("❌ 交易失败:", txInfo.meta.err);
|
|
2627
|
+
throw new Error("交易失败: " + JSON.stringify(txInfo.meta.err));
|
|
1577
2628
|
}
|
|
1578
2629
|
|
|
1579
|
-
console.log(
|
|
2630
|
+
console.log("✅ 交易已确认!");
|
|
1580
2631
|
return signature;
|
|
1581
2632
|
}
|
|
1582
2633
|
|
|
1583
|
-
const currentBlockHeight =
|
|
2634
|
+
const currentBlockHeight =
|
|
2635
|
+
await this.connection.getBlockHeight("finalized");
|
|
1584
2636
|
if (currentBlockHeight > lastValidBlockHeight) {
|
|
1585
|
-
console.log(
|
|
1586
|
-
throw new Error(
|
|
2637
|
+
console.log("⚠️ 交易已过期(超过有效区块高度)");
|
|
2638
|
+
throw new Error("交易过期:未在有效区块高度内确认");
|
|
1587
2639
|
}
|
|
1588
|
-
|
|
1589
2640
|
} catch (error) {
|
|
1590
2641
|
const err = error as Error;
|
|
1591
|
-
if (
|
|
2642
|
+
if (
|
|
2643
|
+
err.message?.includes("交易失败") ||
|
|
2644
|
+
err.message?.includes("交易过期")
|
|
2645
|
+
) {
|
|
1592
2646
|
throw error;
|
|
1593
2647
|
}
|
|
1594
2648
|
console.log(`⚠️ 查询出错,继续重试: ${err.message}`);
|
|
@@ -1596,20 +2650,26 @@ export class PumpTrader {
|
|
|
1596
2650
|
}
|
|
1597
2651
|
|
|
1598
2652
|
throw new Error(
|
|
1599
|
-
`交易确认超时(已尝试 ${maxAttempts} 次),签名: ${signature}
|
|
2653
|
+
`交易确认超时(已尝试 ${maxAttempts} 次),签名: ${signature}。请手动检查交易状态。`,
|
|
1600
2654
|
);
|
|
1601
2655
|
}
|
|
1602
2656
|
|
|
1603
2657
|
/* ---------- 事件监听 ---------- */
|
|
1604
2658
|
|
|
1605
|
-
listenTrades(
|
|
2659
|
+
listenTrades(
|
|
2660
|
+
callback: (event: TradeEvent) => void,
|
|
2661
|
+
mintFilter?: PublicKey | null,
|
|
2662
|
+
) {
|
|
1606
2663
|
return this.connection.onLogs(
|
|
1607
2664
|
PROGRAM_IDS.PUMP,
|
|
1608
2665
|
(log) => {
|
|
1609
2666
|
for (const logLine of log.logs) {
|
|
1610
2667
|
if (!logLine.startsWith("Program data: ")) continue;
|
|
1611
2668
|
|
|
1612
|
-
const buf = Buffer.from(
|
|
2669
|
+
const buf = Buffer.from(
|
|
2670
|
+
logLine.replace("Program data: ", ""),
|
|
2671
|
+
"base64",
|
|
2672
|
+
);
|
|
1613
2673
|
|
|
1614
2674
|
if (!buf.subarray(0, 8).equals(DISCRIMINATORS.TRADE_EVENT)) continue;
|
|
1615
2675
|
|
|
@@ -1635,11 +2695,11 @@ export class PumpTrader {
|
|
|
1635
2695
|
isBuy,
|
|
1636
2696
|
user: user.toBase58(),
|
|
1637
2697
|
timestamp,
|
|
1638
|
-
signature: log.signature
|
|
2698
|
+
signature: log.signature,
|
|
1639
2699
|
});
|
|
1640
2700
|
}
|
|
1641
2701
|
},
|
|
1642
|
-
"confirmed"
|
|
2702
|
+
"confirmed",
|
|
1643
2703
|
);
|
|
1644
2704
|
}
|
|
1645
2705
|
|
|
@@ -1653,18 +2713,22 @@ export class PumpTrader {
|
|
|
1653
2713
|
this.connection,
|
|
1654
2714
|
mint,
|
|
1655
2715
|
"confirmed",
|
|
1656
|
-
TOKEN_2022_PROGRAM_ID
|
|
2716
|
+
TOKEN_2022_PROGRAM_ID,
|
|
1657
2717
|
);
|
|
1658
2718
|
|
|
1659
2719
|
return {
|
|
1660
2720
|
name: metadata?.name || "",
|
|
1661
2721
|
symbol: metadata?.symbol || "",
|
|
1662
|
-
uri: metadata?.uri || ""
|
|
2722
|
+
uri: metadata?.uri || "",
|
|
1663
2723
|
};
|
|
1664
2724
|
} catch (e) {
|
|
1665
2725
|
const metadataPda = PublicKey.findProgramAddressSync(
|
|
1666
|
-
[
|
|
1667
|
-
|
|
2726
|
+
[
|
|
2727
|
+
Buffer.from("metadata"),
|
|
2728
|
+
PROGRAM_IDS.METADATA.toBuffer(),
|
|
2729
|
+
mint.toBuffer(),
|
|
2730
|
+
],
|
|
2731
|
+
PROGRAM_IDS.METADATA,
|
|
1668
2732
|
)[0];
|
|
1669
2733
|
|
|
1670
2734
|
const acc = await this.connection.getAccountInfo(metadataPda);
|
|
@@ -1673,9 +2737,9 @@ export class PumpTrader {
|
|
|
1673
2737
|
const meta = parseMetadataAccount(acc.data);
|
|
1674
2738
|
|
|
1675
2739
|
return {
|
|
1676
|
-
name: meta?.name?.replace(/\u0000/g,
|
|
1677
|
-
symbol: meta?.symbol?.replace(/\u0000/g,
|
|
1678
|
-
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, "") || "",
|
|
1679
2743
|
};
|
|
1680
2744
|
}
|
|
1681
2745
|
}
|
|
@@ -1721,5 +2785,5 @@ export type {
|
|
|
1721
2785
|
GlobalState,
|
|
1722
2786
|
TokenProgramType,
|
|
1723
2787
|
PoolInfo,
|
|
1724
|
-
MetadataInfo
|
|
2788
|
+
MetadataInfo,
|
|
1725
2789
|
};
|