four-flap-meme-sdk 1.4.85 → 1.4.86
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/pancake/bundle-buy-first.js +24 -206
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { ethers, Contract, Wallet } from 'ethers';
|
|
7
7
|
import { NonceManager, getDeadline, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../utils/bundle-helpers.js';
|
|
8
8
|
import { ADDRESSES, PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA, ZERO_ADDRESS } from '../utils/constants.js';
|
|
9
|
-
import { quoteV2, quoteV3, getTokenToNativeQuote
|
|
9
|
+
import { quoteV2, quoteV3, getTokenToNativeQuote } from '../utils/quote-helpers.js';
|
|
10
10
|
import { V2_ROUTER_ABI, V3_ROUTER02_ABI, ERC20_BALANCE_ABI } from '../abis/common.js';
|
|
11
11
|
/**
|
|
12
12
|
* 获取 Gas Limit
|
|
@@ -180,31 +180,22 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
180
180
|
tokenAddress,
|
|
181
181
|
useNativeToken
|
|
182
182
|
});
|
|
183
|
-
const quotedNative = await quoteSellerNative({
|
|
184
|
-
provider: context.provider,
|
|
185
|
-
tokenAddress,
|
|
186
|
-
sellAmountToken: quoteResult.quotedTokenOut,
|
|
187
|
-
routeParams // ✅ 传递路由参数
|
|
188
|
-
});
|
|
189
|
-
const buyerNeed = calculateBuyerNeed({
|
|
190
|
-
quotedNative,
|
|
191
|
-
buyerBalance: buyerFundsInfo.buyerBalance,
|
|
192
|
-
reserveGas: buyerFundsInfo.reserveGas
|
|
193
|
-
});
|
|
194
183
|
const finalGasLimit = getGasLimit(config);
|
|
195
184
|
const gasPrice = await getGasPrice(context.provider, config);
|
|
196
185
|
const txType = config.txType ?? 0;
|
|
197
186
|
const nonceManager = new NonceManager(context.provider);
|
|
198
|
-
// ✅
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
187
|
+
// ✅ 直接使用买入金额作为利润基础(先买后卖模式)
|
|
188
|
+
// profitRateBps = 每笔交易利润率(bps) * 交易笔数
|
|
189
|
+
// 例如:v1用户,1买1卖 → 5 * 2 = 10 bps = 0.1%
|
|
190
|
+
const tokenProfitAmount = (buyerFundsInfo.buyerFundsWei * BigInt(profitRateBps)) / 10000n;
|
|
191
|
+
// ✅ ERC20 购买:将代币利润转换为等值 BNB(因为利润是用 BNB 转账的)
|
|
192
|
+
let profitAmount = tokenProfitAmount;
|
|
193
|
+
if (!useNativeToken && tokenProfitAmount > 0n && quoteToken) {
|
|
194
|
+
const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
|
|
195
|
+
const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
|
|
196
|
+
profitAmount = await getTokenToNativeQuote(context.provider, quoteToken, tokenProfitAmount, 'BSC', version, fee);
|
|
197
|
+
console.log(`[pancakeBundleBuyFirstMerkle] ERC20→BNB 利润转换: ${ethers.formatUnits(tokenProfitAmount, quoteTokenDecimals)} → ${ethers.formatEther(profitAmount)} BNB`);
|
|
198
|
+
}
|
|
208
199
|
// ✅ 获取贿赂金额
|
|
209
200
|
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
210
201
|
? ethers.parseEther(String(config.bribeAmount))
|
|
@@ -406,100 +397,6 @@ async function quoteTokenOutput({ routeParams, buyerFundsWei, provider }) {
|
|
|
406
397
|
}
|
|
407
398
|
return { quotedTokenOut: result.amountOut };
|
|
408
399
|
}
|
|
409
|
-
/**
|
|
410
|
-
* ✅ 使用 quote-helpers 统一报价(卖出 → 原生代币)
|
|
411
|
-
*/
|
|
412
|
-
async function quoteSellerNative({ provider, tokenAddress, sellAmountToken, routeParams }) {
|
|
413
|
-
const wbnb = getWrappedNativeAddress('BSC');
|
|
414
|
-
const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
|
|
415
|
-
const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
|
|
416
|
-
return await getTokenToNativeQuote(provider, tokenAddress, sellAmountToken, 'BSC', version, fee);
|
|
417
|
-
}
|
|
418
|
-
function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas }) {
|
|
419
|
-
const estimatedBuyerNeed = quotedNative;
|
|
420
|
-
const buyerNeedTotal = estimatedBuyerNeed + reserveGas;
|
|
421
|
-
if (buyerBalance < buyerNeedTotal) {
|
|
422
|
-
throw new Error(`买方余额不足:\n - 需要: ${ethers.formatEther(buyerNeedTotal)} BNB\n - 实际: ${ethers.formatEther(buyerBalance)} BNB`);
|
|
423
|
-
}
|
|
424
|
-
const maxBuyerValue = estimatedBuyerNeed > 0n
|
|
425
|
-
? estimatedBuyerNeed
|
|
426
|
-
: buyerBalance - reserveGas;
|
|
427
|
-
return { buyerNeedTotal, maxBuyerValue };
|
|
428
|
-
}
|
|
429
|
-
async function buildRouteTransactions({ routeParams, buyerFundsWei, sellAmountToken, buyer, seller, tokenAddress, useNativeToken = true }) {
|
|
430
|
-
const deadline = getDeadline();
|
|
431
|
-
if (routeParams.routeType === 'v2') {
|
|
432
|
-
const { v2Path } = routeParams;
|
|
433
|
-
const reversePath = [...v2Path].reverse();
|
|
434
|
-
// ✅ 使用官方 V2 Router
|
|
435
|
-
const v2RouterBuyer = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
|
|
436
|
-
const v2RouterSeller = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
|
|
437
|
-
if (useNativeToken) {
|
|
438
|
-
// ✅ BNB 池子:使用 swapExactETHForTokens / swapExactTokensForETH
|
|
439
|
-
const buyValue = buyerFundsWei + FLAT_FEE;
|
|
440
|
-
const buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, v2Path, buyer.address, deadline, { value: buyValue });
|
|
441
|
-
const sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountToken, 0n, reversePath, seller.address, deadline);
|
|
442
|
-
return { buyUnsigned, sellUnsigned };
|
|
443
|
-
}
|
|
444
|
-
else {
|
|
445
|
-
// ✅ ERC20 池子(如 USDT):使用 swapExactTokensForTokens
|
|
446
|
-
const buyUnsigned = await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyerFundsWei, 0n, v2Path, buyer.address, deadline);
|
|
447
|
-
const sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountToken, 0n, reversePath, seller.address, deadline);
|
|
448
|
-
return { buyUnsigned, sellUnsigned };
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
if (routeParams.routeType === 'v3-single') {
|
|
452
|
-
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
453
|
-
const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
|
|
454
|
-
const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
|
|
455
|
-
const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
|
|
456
|
-
// ✅ V3 池子的处理(ERC20 模式 value = 0,因为通过代币授权支付)
|
|
457
|
-
const buyValue = useNativeToken ? buyerFundsWei + FLAT_FEE : 0n;
|
|
458
|
-
// 买入交易
|
|
459
|
-
const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
460
|
-
tokenIn: v3TokenIn,
|
|
461
|
-
tokenOut: v3TokenOut,
|
|
462
|
-
fee: v3Fee,
|
|
463
|
-
recipient: buyer.address,
|
|
464
|
-
amountIn: buyerFundsWei,
|
|
465
|
-
amountOutMinimum: 0n,
|
|
466
|
-
sqrtPriceLimitX96: 0n
|
|
467
|
-
}]);
|
|
468
|
-
const buyUnsigned = await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
|
|
469
|
-
// 卖出交易
|
|
470
|
-
if (useNativeToken) {
|
|
471
|
-
// ✅ BNB 池子:卖出后 unwrap WBNB 为 BNB
|
|
472
|
-
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
473
|
-
tokenIn: v3TokenOut,
|
|
474
|
-
tokenOut: v3TokenIn,
|
|
475
|
-
fee: v3Fee,
|
|
476
|
-
recipient: PANCAKE_V3_ROUTER_ADDRESS,
|
|
477
|
-
amountIn: sellAmountToken,
|
|
478
|
-
amountOutMinimum: 0n,
|
|
479
|
-
sqrtPriceLimitX96: 0n
|
|
480
|
-
}]);
|
|
481
|
-
const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
|
|
482
|
-
const sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
|
|
483
|
-
return { buyUnsigned, sellUnsigned };
|
|
484
|
-
}
|
|
485
|
-
else {
|
|
486
|
-
// ✅ ERC20 池子:卖出后直接获得 ERC20
|
|
487
|
-
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
488
|
-
tokenIn: v3TokenOut,
|
|
489
|
-
tokenOut: v3TokenIn,
|
|
490
|
-
fee: v3Fee,
|
|
491
|
-
recipient: seller.address, // 直接发送到卖方地址
|
|
492
|
-
amountIn: sellAmountToken,
|
|
493
|
-
amountOutMinimum: 0n,
|
|
494
|
-
sqrtPriceLimitX96: 0n
|
|
495
|
-
}]);
|
|
496
|
-
const sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData]);
|
|
497
|
-
return { buyUnsigned, sellUnsigned };
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
// V3 多跳暂不支持官方合约
|
|
501
|
-
throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
|
|
502
|
-
}
|
|
503
400
|
/**
|
|
504
401
|
* ✅ 构建多笔买入和卖出交易
|
|
505
402
|
*/
|
|
@@ -583,60 +480,6 @@ async function buildMultiRouteTransactions({ routeParams, buyAmountsWei, sellAmo
|
|
|
583
480
|
}
|
|
584
481
|
throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
|
|
585
482
|
}
|
|
586
|
-
/**
|
|
587
|
-
* ✅ 使用 quote-helpers 统一报价(估算利润)
|
|
588
|
-
*/
|
|
589
|
-
async function estimateProfitAmount({ provider, tokenAddress, sellAmountToken, routeParams }) {
|
|
590
|
-
try {
|
|
591
|
-
const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
|
|
592
|
-
const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
|
|
593
|
-
const estimatedSellFunds = await getTokenToNativeQuote(provider, tokenAddress, sellAmountToken, 'BSC', version, fee);
|
|
594
|
-
if (estimatedSellFunds <= 0n) {
|
|
595
|
-
return 0n;
|
|
596
|
-
}
|
|
597
|
-
// 万分之六
|
|
598
|
-
return (estimatedSellFunds * BigInt(PROFIT_CONFIG.RATE_BPS_SWAP)) / 10000n;
|
|
599
|
-
}
|
|
600
|
-
catch {
|
|
601
|
-
return 0n;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
/**
|
|
605
|
-
* ✅ 规划 nonce
|
|
606
|
-
* 交易顺序:贿赂 → 买入 → 卖出 → 利润
|
|
607
|
-
*/
|
|
608
|
-
async function planNonces({ buyer, seller, sameAddress, extractProfit, needBribeTx, nonceManager }) {
|
|
609
|
-
if (sameAddress) {
|
|
610
|
-
// 同一地址:贿赂(可选) + 买入 + 卖出 + 利润(可选)
|
|
611
|
-
const txCount = countTruthy([needBribeTx, true, true, extractProfit]);
|
|
612
|
-
const nonces = await nonceManager.getNextNonceBatch(buyer, txCount);
|
|
613
|
-
let idx = 0;
|
|
614
|
-
const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
|
|
615
|
-
const buyerNonce = nonces[idx++];
|
|
616
|
-
const sellerNonce = nonces[idx++];
|
|
617
|
-
const profitNonce = extractProfit ? nonces[idx] : undefined;
|
|
618
|
-
return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
|
|
619
|
-
}
|
|
620
|
-
if (needBribeTx || extractProfit) {
|
|
621
|
-
// 卖方需要多个 nonce:贿赂(可选) + 卖出 + 利润(可选)
|
|
622
|
-
const sellerTxCount = countTruthy([needBribeTx, true, extractProfit]);
|
|
623
|
-
// ✅ 并行获取 seller 和 buyer 的 nonce
|
|
624
|
-
const [sellerNonces, buyerNonce] = await Promise.all([
|
|
625
|
-
nonceManager.getNextNonceBatch(seller, sellerTxCount),
|
|
626
|
-
nonceManager.getNextNonce(buyer)
|
|
627
|
-
]);
|
|
628
|
-
let idx = 0;
|
|
629
|
-
const bribeNonce = needBribeTx ? sellerNonces[idx++] : undefined;
|
|
630
|
-
const sellerNonce = sellerNonces[idx++];
|
|
631
|
-
const profitNonce = extractProfit ? sellerNonces[idx] : undefined;
|
|
632
|
-
return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
|
|
633
|
-
}
|
|
634
|
-
const [buyerNonce, sellerNonce] = await Promise.all([
|
|
635
|
-
nonceManager.getNextNonce(buyer),
|
|
636
|
-
nonceManager.getNextNonce(seller)
|
|
637
|
-
]);
|
|
638
|
-
return { buyerNonce, sellerNonce };
|
|
639
|
-
}
|
|
640
483
|
/**
|
|
641
484
|
* ✅ 规划多笔交易 nonce(与 Flap 一致)
|
|
642
485
|
* 交易顺序:贿赂 → 买入(多笔) → 卖出(多笔) → 利润
|
|
@@ -729,33 +572,6 @@ async function validateFinalBalances({ sameAddress, buyerFundsWei, buyerBalance,
|
|
|
729
572
|
}
|
|
730
573
|
// ERC20 余额已在 calculateBuyerFunds 中检查过
|
|
731
574
|
}
|
|
732
|
-
function countTruthy(values) {
|
|
733
|
-
return values.filter(Boolean).length;
|
|
734
|
-
}
|
|
735
|
-
/**
|
|
736
|
-
* ✅ 从前端传入的 startNonces 构建 NoncePlan(用于性能优化,避免 nonce 冲突)
|
|
737
|
-
* 顺序:同地址时 [baseNonce],不同地址时 [sellerNonce, buyerNonce]
|
|
738
|
-
*/
|
|
739
|
-
function buildNoncePlanFromStartNonces(startNonces, sameAddress, profitNeeded, needBribeTx) {
|
|
740
|
-
if (sameAddress) {
|
|
741
|
-
// 同一地址:贿赂(可选) + 买入 + 卖出 + 利润(可选)
|
|
742
|
-
let idx = 0;
|
|
743
|
-
const baseNonce = startNonces[0];
|
|
744
|
-
const bribeNonce = needBribeTx ? baseNonce + idx++ : undefined;
|
|
745
|
-
const buyerNonce = baseNonce + idx++;
|
|
746
|
-
const sellerNonce = baseNonce + idx++;
|
|
747
|
-
const profitNonce = profitNeeded ? baseNonce + idx : undefined;
|
|
748
|
-
return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
|
|
749
|
-
}
|
|
750
|
-
// 不同地址
|
|
751
|
-
let sellerIdx = 0;
|
|
752
|
-
const sellerBaseNonce = startNonces[0];
|
|
753
|
-
const bribeNonce = needBribeTx ? sellerBaseNonce + sellerIdx++ : undefined;
|
|
754
|
-
const sellerNonce = sellerBaseNonce + sellerIdx++;
|
|
755
|
-
const profitNonce = profitNeeded ? sellerBaseNonce + sellerIdx : undefined;
|
|
756
|
-
const buyerNonce = startNonces[1];
|
|
757
|
-
return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
|
|
758
|
-
}
|
|
759
575
|
/**
|
|
760
576
|
* ✅ 多钱包捆绑换手
|
|
761
577
|
* - 多个买方钱包执行买入(每个钱包1笔)
|
|
@@ -831,15 +647,17 @@ async function pancakeBundleBuyFirstMultiWallet(params) {
|
|
|
831
647
|
const buyAmountsWei = splitAmount(totalFundsWei, buyCount);
|
|
832
648
|
// ✅ 将代币平均分配给卖方
|
|
833
649
|
const sellAmountsWei = splitAmount(quoteResult.quotedTokenOut, sellCount);
|
|
834
|
-
// ✅
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
650
|
+
// ✅ 直接使用总交易金额作为利润基础(多钱包模式)
|
|
651
|
+
// profitRateBps = 每笔交易利润率(bps) * 交易笔数
|
|
652
|
+
const tokenProfitAmount = (totalFundsWei * BigInt(profitRateBps)) / 10000n;
|
|
653
|
+
// ✅ ERC20 购买:将代币利润转换为等值 BNB(因为利润是用 BNB 转账的)
|
|
654
|
+
let profitAmount = tokenProfitAmount;
|
|
655
|
+
if (!useNativeToken && tokenProfitAmount > 0n && quoteToken) {
|
|
656
|
+
const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
|
|
657
|
+
const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
|
|
658
|
+
profitAmount = await getTokenToNativeQuote(context.provider, quoteToken, tokenProfitAmount, 'BSC', version, fee);
|
|
659
|
+
console.log(`[pancakeBundleBuyFirstMultiWallet] ERC20→BNB 利润转换: ${ethers.formatUnits(tokenProfitAmount, quoteTokenDecimals)} → ${ethers.formatEther(profitAmount)} BNB`);
|
|
660
|
+
}
|
|
843
661
|
// ✅ 第三批并行:构建并签名所有交易
|
|
844
662
|
const allTransactions = [];
|
|
845
663
|
// 1. 贿赂交易(由主卖方支付)- 先处理以确定 nonce 偏移
|