pump-trader 1.1.9 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -2
- package/dist/index.js +102 -20
- package/index.js +80 -10
- package/index.ts +105 -11
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -126,11 +126,13 @@ export declare class PumpTrader {
|
|
|
126
126
|
calcSell(tokenIn: bigint, state: BondingCurveState): bigint;
|
|
127
127
|
calculateAmmBuyOutput(quoteIn: bigint, reserves: PoolReserves): bigint;
|
|
128
128
|
calculateAmmSellOutput(baseIn: bigint, reserves: PoolReserves): bigint;
|
|
129
|
-
|
|
129
|
+
private solPriceCache;
|
|
130
|
+
getSolPriceInUsdc(): Promise<number>;
|
|
131
|
+
getPriceAndStatus(tokenAddr: string, quoteMint?: PublicKey): Promise<{
|
|
130
132
|
price: number;
|
|
131
133
|
completed: boolean;
|
|
132
134
|
}>;
|
|
133
|
-
getAmmPrice(mint: PublicKey): Promise<number>;
|
|
135
|
+
getAmmPrice(mint: PublicKey, quoteMint?: PublicKey): Promise<number>;
|
|
134
136
|
/**
|
|
135
137
|
* 查询代币余额
|
|
136
138
|
* @param tokenAddr - 代币地址(可选),如果不传则返回所有代币
|
package/dist/index.js
CHANGED
|
@@ -153,15 +153,11 @@ function parsePoolKeys(data) {
|
|
|
153
153
|
/* ================= PumpTrader 类 ================= */
|
|
154
154
|
class PumpTrader {
|
|
155
155
|
constructor(rpc, wallet) {
|
|
156
|
+
/* ---------- 价格查询 ---------- */
|
|
157
|
+
this.solPriceCache = null;
|
|
156
158
|
this.connection = new web3_js_1.Connection(rpc, "confirmed");
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
this.publicKey = wallet.publicKey;
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
this._wallet = wallet;
|
|
163
|
-
this.publicKey = wallet.publicKey;
|
|
164
|
-
}
|
|
159
|
+
this._wallet = wallet;
|
|
160
|
+
this.publicKey = wallet.publicKey;
|
|
165
161
|
this.global = web3_js_1.PublicKey.findProgramAddressSync([SEEDS.GLOBAL], PROGRAM_IDS.PUMP)[0];
|
|
166
162
|
this.globalState = null;
|
|
167
163
|
this.tokenProgramCache = new Map();
|
|
@@ -169,9 +165,12 @@ class PumpTrader {
|
|
|
169
165
|
async signTx(tx) {
|
|
170
166
|
if (this._wallet instanceof web3_js_1.Keypair) {
|
|
171
167
|
tx.sign(this._wallet);
|
|
172
|
-
return tx;
|
|
173
168
|
}
|
|
174
|
-
|
|
169
|
+
else {
|
|
170
|
+
const signed = await this._wallet.signTransaction(tx);
|
|
171
|
+
// Copy signatures back to the original tx (adapter returns a new tx)
|
|
172
|
+
tx.signatures = signed.signatures;
|
|
173
|
+
}
|
|
175
174
|
}
|
|
176
175
|
/* ---------- Token Program 检测 ---------- */
|
|
177
176
|
/**
|
|
@@ -422,20 +421,77 @@ class PumpTrader {
|
|
|
422
421
|
const denominator = reserves.baseAmount + baseInAfterFee;
|
|
423
422
|
return numerator / denominator;
|
|
424
423
|
}
|
|
425
|
-
|
|
426
|
-
|
|
424
|
+
async getSolPriceInUsdc() {
|
|
425
|
+
if (this.solPriceCache && Date.now() - this.solPriceCache.timestamp < 60000) {
|
|
426
|
+
return this.solPriceCache.price;
|
|
427
|
+
}
|
|
428
|
+
try {
|
|
429
|
+
// Use Orca USDC/SOL whirlpool (7qbRF6YsyGuLUVs6Y1q64bdVrfe4ZcUUz1JRdoVNUJnm)
|
|
430
|
+
// Read token vault balances directly to compute price
|
|
431
|
+
const poolAddr = new web3_js_1.PublicKey("7qbRF6YsyGuLUVs6Y1q64bdVrfe4ZcUUz1JRdoVNUJnm");
|
|
432
|
+
const acc = await this.connection.getAccountInfo(poolAddr);
|
|
433
|
+
if (!acc || acc.data.length < 304)
|
|
434
|
+
throw new Error("Invalid pool data");
|
|
435
|
+
// Whirlpool: tokenMintA at offset 40, tokenMintB at offset 72, vaultA at 104, vaultB at 136
|
|
436
|
+
const tokenMintA = new web3_js_1.PublicKey(acc.data.slice(40, 72));
|
|
437
|
+
const tokenVaultA = new web3_js_1.PublicKey(acc.data.slice(104, 136));
|
|
438
|
+
const tokenVaultB = new web3_js_1.PublicKey(acc.data.slice(136, 168));
|
|
439
|
+
const [balanceA, balanceB] = await Promise.all([
|
|
440
|
+
this.connection.getTokenAccountBalance(tokenVaultA),
|
|
441
|
+
this.connection.getTokenAccountBalance(tokenVaultB),
|
|
442
|
+
]);
|
|
443
|
+
// Determine which vault holds SOL by checking tokenMintA
|
|
444
|
+
const SOL_ADDR = "So11111111111111111111111111111111111111112";
|
|
445
|
+
const solBalance = tokenMintA.toBase58() === SOL_ADDR
|
|
446
|
+
? Number(balanceA.value.amount)
|
|
447
|
+
: Number(balanceB.value.amount);
|
|
448
|
+
const usdcBalance = tokenMintA.toBase58() === SOL_ADDR
|
|
449
|
+
? Number(balanceB.value.amount)
|
|
450
|
+
: Number(balanceA.value.amount);
|
|
451
|
+
if (solBalance === 0)
|
|
452
|
+
throw new Error("Zero balance");
|
|
453
|
+
const price = usdcBalance / solBalance;
|
|
454
|
+
this.solPriceCache = { price, timestamp: Date.now() };
|
|
455
|
+
return price;
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
return 175;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
async getPriceAndStatus(tokenAddr, quoteMint) {
|
|
427
462
|
const mint = new web3_js_1.PublicKey(tokenAddr);
|
|
428
463
|
const { state } = await this.loadBonding(mint);
|
|
429
464
|
if (state.complete) {
|
|
430
|
-
const
|
|
465
|
+
const qm = quoteMint || (state.quoteMint && !state.quoteMint.equals(SOL_MINT) ? state.quoteMint : SOL_MINT);
|
|
466
|
+
const price = await this.getAmmPrice(mint, qm);
|
|
431
467
|
return { price, completed: true };
|
|
432
468
|
}
|
|
469
|
+
const qm = quoteMint || state.quoteMint || SOL_MINT;
|
|
470
|
+
const isSolQuote = qm.equals(SOL_MINT);
|
|
433
471
|
const oneToken = BigInt(1_000_000);
|
|
434
|
-
|
|
435
|
-
|
|
472
|
+
if (isSolQuote) {
|
|
473
|
+
const solOut = this.calcSell(oneToken, state);
|
|
474
|
+
const price = Number(solOut) / 1e9;
|
|
475
|
+
return { price, completed: false };
|
|
476
|
+
}
|
|
477
|
+
// USDC-paired bonding curve: pump stores USDC raw amount (6 decimals) in the
|
|
478
|
+
// virtualSolReserves field. calcSell returns USDC raw, divide by 1e6 for USDC price.
|
|
479
|
+
// Then convert to SOL using Orca USDC/SOL pool.
|
|
480
|
+
let quotePrice;
|
|
481
|
+
if (state.virtualQuoteReserves !== undefined) {
|
|
482
|
+
const newVirtualToken = state.virtualTokenReserves + oneToken;
|
|
483
|
+
const quoteOut = state.virtualQuoteReserves - (state.virtualQuoteReserves * state.virtualTokenReserves) / newVirtualToken;
|
|
484
|
+
quotePrice = Number(quoteOut) / 1e6;
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
const rawOut = this.calcSell(oneToken, state);
|
|
488
|
+
quotePrice = Number(rawOut) / 1e6;
|
|
489
|
+
}
|
|
490
|
+
const solPrice = await this.getSolPriceInUsdc();
|
|
491
|
+
const price = quotePrice / solPrice;
|
|
436
492
|
return { price, completed: false };
|
|
437
493
|
}
|
|
438
|
-
async getAmmPrice(mint) {
|
|
494
|
+
async getAmmPrice(mint, quoteMint = SOL_MINT) {
|
|
439
495
|
const [poolCreator] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("pool-authority"), mint.toBuffer()], PROGRAM_IDS.PUMP);
|
|
440
496
|
const indexBuffer = new bn_js_1.default(0).toArrayLike(Buffer, "le", 2);
|
|
441
497
|
const [pool] = web3_js_1.PublicKey.findProgramAddressSync([
|
|
@@ -443,7 +499,7 @@ class PumpTrader {
|
|
|
443
499
|
indexBuffer,
|
|
444
500
|
poolCreator.toBuffer(),
|
|
445
501
|
mint.toBuffer(),
|
|
446
|
-
|
|
502
|
+
quoteMint.toBuffer(),
|
|
447
503
|
], PROGRAM_IDS.PUMP_AMM);
|
|
448
504
|
const acc = await this.connection.getAccountInfo(pool);
|
|
449
505
|
if (!acc)
|
|
@@ -453,7 +509,13 @@ class PumpTrader {
|
|
|
453
509
|
this.connection.getTokenAccountBalance(poolKeys.poolBaseTokenAccount),
|
|
454
510
|
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
|
|
455
511
|
]);
|
|
456
|
-
|
|
512
|
+
let price = quoteInfo.value.uiAmount / baseInfo.value.uiAmount;
|
|
513
|
+
// If pool is not SOL-quoted, convert to SOL price
|
|
514
|
+
if (!quoteMint.equals(SOL_MINT)) {
|
|
515
|
+
const solPrice = await this.getSolPriceInUsdc();
|
|
516
|
+
price = price / solPrice;
|
|
517
|
+
}
|
|
518
|
+
return price;
|
|
457
519
|
}
|
|
458
520
|
/* ---------- 余额查询 ---------- */
|
|
459
521
|
/**
|
|
@@ -997,7 +1059,17 @@ class PumpTrader {
|
|
|
997
1059
|
isWritable: true,
|
|
998
1060
|
});
|
|
999
1061
|
}
|
|
1000
|
-
|
|
1062
|
+
const POOL_DEFAULT_COIN_CREATOR = new web3_js_1.PublicKey("11111111111111111111111111111111");
|
|
1063
|
+
if (poolKeys.coinCreator && !poolKeys.coinCreator.equals(POOL_DEFAULT_COIN_CREATOR)) {
|
|
1064
|
+
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
1065
|
+
}
|
|
1066
|
+
else {
|
|
1067
|
+
remainingKeys.push({
|
|
1068
|
+
pubkey: PUMP_BUYBACK_FEE_RECIPIENTS[0],
|
|
1069
|
+
isSigner: false,
|
|
1070
|
+
isWritable: true,
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1001
1073
|
remainingKeys.push({ pubkey: newFeeRecipient, isSigner: false, isWritable: false }, {
|
|
1002
1074
|
pubkey: newFeeRecipientTokenAccount,
|
|
1003
1075
|
isSigner: false,
|
|
@@ -1083,7 +1155,17 @@ class PumpTrader {
|
|
|
1083
1155
|
isWritable: true,
|
|
1084
1156
|
}, { pubkey: userVolumeAccumulator, isSigner: false, isWritable: true });
|
|
1085
1157
|
}
|
|
1086
|
-
|
|
1158
|
+
const POOL_DEFAULT_COIN_CREATOR = new web3_js_1.PublicKey("11111111111111111111111111111111");
|
|
1159
|
+
if (poolKeys.coinCreator && !poolKeys.coinCreator.equals(POOL_DEFAULT_COIN_CREATOR)) {
|
|
1160
|
+
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
1161
|
+
}
|
|
1162
|
+
else {
|
|
1163
|
+
remainingKeys.push({
|
|
1164
|
+
pubkey: PUMP_BUYBACK_FEE_RECIPIENTS[0],
|
|
1165
|
+
isSigner: false,
|
|
1166
|
+
isWritable: true,
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1087
1169
|
remainingKeys.push({ pubkey: newFeeRecipient, isSigner: false, isWritable: false }, {
|
|
1088
1170
|
pubkey: newFeeRecipientTokenAccount,
|
|
1089
1171
|
isSigner: false,
|
package/index.js
CHANGED
|
@@ -193,9 +193,11 @@ export class PumpTrader {
|
|
|
193
193
|
async signTx(tx) {
|
|
194
194
|
if (this._wallet instanceof Keypair) {
|
|
195
195
|
tx.sign(this._wallet);
|
|
196
|
-
|
|
196
|
+
} else {
|
|
197
|
+
const signed = await this._wallet.signTransaction(tx);
|
|
198
|
+
// Copy signatures back to the original tx (adapter returns a new tx)
|
|
199
|
+
tx.signatures = signed.signatures;
|
|
197
200
|
}
|
|
198
|
-
return this._wallet.signTransaction(tx);
|
|
199
201
|
}
|
|
200
202
|
|
|
201
203
|
/* ---------- Token Program 检测 ---------- */
|
|
@@ -490,22 +492,74 @@ export class PumpTrader {
|
|
|
490
492
|
|
|
491
493
|
/* ---------- 价格查询 ---------- */
|
|
492
494
|
|
|
493
|
-
async
|
|
495
|
+
async getSolPriceInUsdc() {
|
|
496
|
+
if (this._solPriceCache && Date.now() - this._solPriceCache.timestamp < 60000) {
|
|
497
|
+
return this._solPriceCache.price;
|
|
498
|
+
}
|
|
499
|
+
try {
|
|
500
|
+
const poolAddr = new PublicKey("7qbRF6YsyGuLUVs6Y1q64bdVrfe4ZcUUz1JRdoVNUJnm");
|
|
501
|
+
const acc = await this.connection.getAccountInfo(poolAddr);
|
|
502
|
+
if (!acc || acc.data.length < 304) throw new Error("Invalid pool data");
|
|
503
|
+
const tokenMintA = new PublicKey(acc.data.slice(40, 72));
|
|
504
|
+
const tokenVaultA = new PublicKey(acc.data.slice(104, 136));
|
|
505
|
+
const tokenVaultB = new PublicKey(acc.data.slice(136, 168));
|
|
506
|
+
const [balanceA, balanceB] = await Promise.all([
|
|
507
|
+
this.connection.getTokenAccountBalance(tokenVaultA),
|
|
508
|
+
this.connection.getTokenAccountBalance(tokenVaultB),
|
|
509
|
+
]);
|
|
510
|
+
const SOL_ADDR = "So11111111111111111111111111111111111111112";
|
|
511
|
+
const solBalance = tokenMintA.toBase58() === SOL_ADDR
|
|
512
|
+
? Number(balanceA.value.amount)
|
|
513
|
+
: Number(balanceB.value.amount);
|
|
514
|
+
const usdcBalance = tokenMintA.toBase58() === SOL_ADDR
|
|
515
|
+
? Number(balanceB.value.amount)
|
|
516
|
+
: Number(balanceA.value.amount);
|
|
517
|
+
if (solBalance === 0) throw new Error("Zero balance");
|
|
518
|
+
const price = usdcBalance / solBalance;
|
|
519
|
+
this._solPriceCache = { price, timestamp: Date.now() };
|
|
520
|
+
return price;
|
|
521
|
+
} catch {
|
|
522
|
+
return 175;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async getPriceAndStatus(tokenAddr, quoteMint = null) {
|
|
494
527
|
const mint = new PublicKey(tokenAddr);
|
|
495
528
|
const { state } = await this.loadBonding(mint);
|
|
496
529
|
|
|
497
530
|
if (state.complete) {
|
|
498
|
-
const
|
|
531
|
+
const qm = quoteMint || (state.quoteMint && !state.quoteMint.equals(SOL_MINT) ? state.quoteMint : SOL_MINT);
|
|
532
|
+
const price = await this.getAmmPrice(mint, qm);
|
|
499
533
|
return { price, completed: true };
|
|
500
534
|
}
|
|
501
535
|
|
|
536
|
+
const qm = quoteMint || state.quoteMint || SOL_MINT;
|
|
537
|
+
const isSolQuote = qm.equals(SOL_MINT);
|
|
538
|
+
|
|
502
539
|
const oneToken = BigInt(1_000_000);
|
|
503
|
-
|
|
504
|
-
|
|
540
|
+
|
|
541
|
+
if (isSolQuote) {
|
|
542
|
+
const solOut = this.calcSell(oneToken, state);
|
|
543
|
+
const price = Number(solOut) / 1e9;
|
|
544
|
+
return { price, completed: false };
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
let quotePrice;
|
|
548
|
+
if (state.virtualQuoteReserves !== undefined) {
|
|
549
|
+
const newVirtualToken = state.virtualTokenReserves + oneToken;
|
|
550
|
+
const quoteOut = state.virtualQuoteReserves - (state.virtualQuoteReserves * state.virtualTokenReserves) / newVirtualToken;
|
|
551
|
+
quotePrice = Number(quoteOut) / 1e6;
|
|
552
|
+
} else {
|
|
553
|
+
const rawOut = this.calcSell(oneToken, state);
|
|
554
|
+
quotePrice = Number(rawOut) / 1e6;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const solPrice = await this.getSolPriceInUsdc();
|
|
558
|
+
const price = quotePrice / solPrice;
|
|
505
559
|
return { price, completed: false };
|
|
506
560
|
}
|
|
507
561
|
|
|
508
|
-
async getAmmPrice(mint) {
|
|
562
|
+
async getAmmPrice(mint, quoteMint = SOL_MINT) {
|
|
509
563
|
const [poolCreator] = PublicKey.findProgramAddressSync(
|
|
510
564
|
[Buffer.from("pool-authority"), mint.toBuffer()],
|
|
511
565
|
PROGRAM_IDS.PUMP,
|
|
@@ -518,7 +572,7 @@ export class PumpTrader {
|
|
|
518
572
|
indexBuffer,
|
|
519
573
|
poolCreator.toBuffer(),
|
|
520
574
|
mint.toBuffer(),
|
|
521
|
-
|
|
575
|
+
quoteMint.toBuffer(),
|
|
522
576
|
],
|
|
523
577
|
PROGRAM_IDS.PUMP_AMM,
|
|
524
578
|
);
|
|
@@ -532,7 +586,14 @@ export class PumpTrader {
|
|
|
532
586
|
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
|
|
533
587
|
]);
|
|
534
588
|
|
|
535
|
-
|
|
589
|
+
let price = quoteInfo.value.uiAmount / baseInfo.value.uiAmount;
|
|
590
|
+
|
|
591
|
+
if (!quoteMint.equals(SOL_MINT)) {
|
|
592
|
+
const solPrice = await this.getSolPriceInUsdc();
|
|
593
|
+
price = price / solPrice;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return price;
|
|
536
597
|
}
|
|
537
598
|
|
|
538
599
|
/* ---------- 余额查询 ---------- */
|
|
@@ -1415,7 +1476,16 @@ export class PumpTrader {
|
|
|
1415
1476
|
isWritable: true,
|
|
1416
1477
|
});
|
|
1417
1478
|
}
|
|
1418
|
-
|
|
1479
|
+
const POOL_DEFAULT_COIN_CREATOR = new PublicKey("11111111111111111111111111111111");
|
|
1480
|
+
if (poolKeys.coinCreator && !poolKeys.coinCreator.equals(POOL_DEFAULT_COIN_CREATOR)) {
|
|
1481
|
+
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
1482
|
+
} else {
|
|
1483
|
+
remainingKeys.push({
|
|
1484
|
+
pubkey: PUMP_BUYBACK_FEE_RECIPIENTS[0],
|
|
1485
|
+
isSigner: false,
|
|
1486
|
+
isWritable: true,
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1419
1489
|
remainingKeys.push(
|
|
1420
1490
|
{ pubkey: newFeeRecipient, isSigner: false, isWritable: false },
|
|
1421
1491
|
{
|
package/index.ts
CHANGED
|
@@ -335,12 +335,14 @@ export class PumpTrader {
|
|
|
335
335
|
this.tokenProgramCache = new Map();
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
-
private async signTx(tx: Transaction): Promise<
|
|
338
|
+
private async signTx(tx: Transaction): Promise<void> {
|
|
339
339
|
if (this._wallet instanceof Keypair) {
|
|
340
340
|
tx.sign(this._wallet);
|
|
341
|
-
|
|
341
|
+
} else {
|
|
342
|
+
const signed = await this._wallet.signTransaction(tx);
|
|
343
|
+
// Copy signatures back to the original tx (adapter returns a new tx)
|
|
344
|
+
tx.signatures = signed.signatures;
|
|
342
345
|
}
|
|
343
|
-
return this._wallet.signTransaction(tx);
|
|
344
346
|
}
|
|
345
347
|
|
|
346
348
|
/* ---------- Token Program 检测 ---------- */
|
|
@@ -698,24 +700,90 @@ export class PumpTrader {
|
|
|
698
700
|
|
|
699
701
|
/* ---------- 价格查询 ---------- */
|
|
700
702
|
|
|
703
|
+
private solPriceCache: { price: number; timestamp: number } | null = null;
|
|
704
|
+
|
|
705
|
+
async getSolPriceInUsdc(): Promise<number> {
|
|
706
|
+
if (this.solPriceCache && Date.now() - this.solPriceCache.timestamp < 60000) {
|
|
707
|
+
return this.solPriceCache.price;
|
|
708
|
+
}
|
|
709
|
+
try {
|
|
710
|
+
// Use Orca USDC/SOL whirlpool (7qbRF6YsyGuLUVs6Y1q64bdVrfe4ZcUUz1JRdoVNUJnm)
|
|
711
|
+
// Read token vault balances directly to compute price
|
|
712
|
+
const poolAddr = new PublicKey("7qbRF6YsyGuLUVs6Y1q64bdVrfe4ZcUUz1JRdoVNUJnm");
|
|
713
|
+
const acc = await this.connection.getAccountInfo(poolAddr);
|
|
714
|
+
if (!acc || acc.data.length < 304) throw new Error("Invalid pool data");
|
|
715
|
+
|
|
716
|
+
// Whirlpool: tokenMintA at offset 40, tokenMintB at offset 72, vaultA at 104, vaultB at 136
|
|
717
|
+
const tokenMintA = new PublicKey(acc.data.slice(40, 72));
|
|
718
|
+
const tokenVaultA = new PublicKey(acc.data.slice(104, 136));
|
|
719
|
+
const tokenVaultB = new PublicKey(acc.data.slice(136, 168));
|
|
720
|
+
|
|
721
|
+
const [balanceA, balanceB] = await Promise.all([
|
|
722
|
+
this.connection.getTokenAccountBalance(tokenVaultA),
|
|
723
|
+
this.connection.getTokenAccountBalance(tokenVaultB),
|
|
724
|
+
]);
|
|
725
|
+
|
|
726
|
+
// Determine which vault holds SOL by checking tokenMintA
|
|
727
|
+
const SOL_ADDR = "So11111111111111111111111111111111111111112";
|
|
728
|
+
const solBalance = tokenMintA.toBase58() === SOL_ADDR
|
|
729
|
+
? Number(balanceA.value.amount)
|
|
730
|
+
: Number(balanceB.value.amount);
|
|
731
|
+
const usdcBalance = tokenMintA.toBase58() === SOL_ADDR
|
|
732
|
+
? Number(balanceB.value.amount)
|
|
733
|
+
: Number(balanceA.value.amount);
|
|
734
|
+
|
|
735
|
+
if (solBalance === 0) throw new Error("Zero balance");
|
|
736
|
+
const price = usdcBalance / solBalance;
|
|
737
|
+
this.solPriceCache = { price, timestamp: Date.now() };
|
|
738
|
+
return price;
|
|
739
|
+
} catch {
|
|
740
|
+
return 175;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
701
744
|
async getPriceAndStatus(
|
|
702
745
|
tokenAddr: string,
|
|
746
|
+
quoteMint?: PublicKey,
|
|
703
747
|
): Promise<{ price: number; completed: boolean }> {
|
|
704
748
|
const mint = new PublicKey(tokenAddr);
|
|
705
749
|
const { state } = await this.loadBonding(mint);
|
|
706
750
|
|
|
707
751
|
if (state.complete) {
|
|
708
|
-
const
|
|
752
|
+
const qm = quoteMint || (state.quoteMint && !state.quoteMint.equals(SOL_MINT) ? state.quoteMint : SOL_MINT);
|
|
753
|
+
const price = await this.getAmmPrice(mint, qm);
|
|
709
754
|
return { price, completed: true };
|
|
710
755
|
}
|
|
711
756
|
|
|
757
|
+
const qm = quoteMint || state.quoteMint || SOL_MINT;
|
|
758
|
+
const isSolQuote = qm.equals(SOL_MINT);
|
|
759
|
+
|
|
712
760
|
const oneToken = BigInt(1_000_000);
|
|
713
|
-
|
|
714
|
-
|
|
761
|
+
|
|
762
|
+
if (isSolQuote) {
|
|
763
|
+
const solOut = this.calcSell(oneToken, state);
|
|
764
|
+
const price = Number(solOut) / 1e9;
|
|
765
|
+
return { price, completed: false };
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// USDC-paired bonding curve: pump stores USDC raw amount (6 decimals) in the
|
|
769
|
+
// virtualSolReserves field. calcSell returns USDC raw, divide by 1e6 for USDC price.
|
|
770
|
+
// Then convert to SOL using Orca USDC/SOL pool.
|
|
771
|
+
let quotePrice: number;
|
|
772
|
+
if (state.virtualQuoteReserves !== undefined) {
|
|
773
|
+
const newVirtualToken = state.virtualTokenReserves + oneToken;
|
|
774
|
+
const quoteOut = state.virtualQuoteReserves - (state.virtualQuoteReserves * state.virtualTokenReserves) / newVirtualToken;
|
|
775
|
+
quotePrice = Number(quoteOut) / 1e6;
|
|
776
|
+
} else {
|
|
777
|
+
const rawOut = this.calcSell(oneToken, state);
|
|
778
|
+
quotePrice = Number(rawOut) / 1e6;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const solPrice = await this.getSolPriceInUsdc();
|
|
782
|
+
const price = quotePrice / solPrice;
|
|
715
783
|
return { price, completed: false };
|
|
716
784
|
}
|
|
717
785
|
|
|
718
|
-
async getAmmPrice(mint: PublicKey): Promise<number> {
|
|
786
|
+
async getAmmPrice(mint: PublicKey, quoteMint: PublicKey = SOL_MINT): Promise<number> {
|
|
719
787
|
const [poolCreator] = PublicKey.findProgramAddressSync(
|
|
720
788
|
[Buffer.from("pool-authority"), mint.toBuffer()],
|
|
721
789
|
PROGRAM_IDS.PUMP,
|
|
@@ -728,7 +796,7 @@ export class PumpTrader {
|
|
|
728
796
|
indexBuffer,
|
|
729
797
|
poolCreator.toBuffer(),
|
|
730
798
|
mint.toBuffer(),
|
|
731
|
-
|
|
799
|
+
quoteMint.toBuffer(),
|
|
732
800
|
],
|
|
733
801
|
PROGRAM_IDS.PUMP_AMM,
|
|
734
802
|
);
|
|
@@ -742,7 +810,15 @@ export class PumpTrader {
|
|
|
742
810
|
this.connection.getTokenAccountBalance(poolKeys.poolQuoteTokenAccount),
|
|
743
811
|
]);
|
|
744
812
|
|
|
745
|
-
|
|
813
|
+
let price = quoteInfo.value.uiAmount! / baseInfo.value.uiAmount!;
|
|
814
|
+
|
|
815
|
+
// If pool is not SOL-quoted, convert to SOL price
|
|
816
|
+
if (!quoteMint.equals(SOL_MINT)) {
|
|
817
|
+
const solPrice = await this.getSolPriceInUsdc();
|
|
818
|
+
price = price / solPrice;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
return price;
|
|
746
822
|
}
|
|
747
823
|
|
|
748
824
|
/* ---------- 余额查询 ---------- */
|
|
@@ -1679,7 +1755,16 @@ export class PumpTrader {
|
|
|
1679
1755
|
isWritable: true,
|
|
1680
1756
|
});
|
|
1681
1757
|
}
|
|
1682
|
-
|
|
1758
|
+
const POOL_DEFAULT_COIN_CREATOR = new PublicKey("11111111111111111111111111111111");
|
|
1759
|
+
if (poolKeys.coinCreator && !poolKeys.coinCreator.equals(POOL_DEFAULT_COIN_CREATOR)) {
|
|
1760
|
+
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
1761
|
+
} else {
|
|
1762
|
+
remainingKeys.push({
|
|
1763
|
+
pubkey: PUMP_BUYBACK_FEE_RECIPIENTS[0],
|
|
1764
|
+
isSigner: false,
|
|
1765
|
+
isWritable: true,
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1683
1768
|
remainingKeys.push(
|
|
1684
1769
|
{ pubkey: newFeeRecipient, isSigner: false, isWritable: false },
|
|
1685
1770
|
{
|
|
@@ -1824,7 +1909,16 @@ export class PumpTrader {
|
|
|
1824
1909
|
{ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true },
|
|
1825
1910
|
);
|
|
1826
1911
|
}
|
|
1827
|
-
|
|
1912
|
+
const POOL_DEFAULT_COIN_CREATOR = new PublicKey("11111111111111111111111111111111");
|
|
1913
|
+
if (poolKeys.coinCreator && !poolKeys.coinCreator.equals(POOL_DEFAULT_COIN_CREATOR)) {
|
|
1914
|
+
remainingKeys.push({ pubkey: poolV2, isSigner: false, isWritable: false });
|
|
1915
|
+
} else {
|
|
1916
|
+
remainingKeys.push({
|
|
1917
|
+
pubkey: PUMP_BUYBACK_FEE_RECIPIENTS[0],
|
|
1918
|
+
isSigner: false,
|
|
1919
|
+
isWritable: true,
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1828
1922
|
remainingKeys.push(
|
|
1829
1923
|
{ pubkey: newFeeRecipient, isSigner: false, isWritable: false },
|
|
1830
1924
|
{
|