four-flap-meme-sdk 1.4.16 → 1.4.17
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/contracts/tm-bundle-merkle/swap-buy-first.js +9 -16
- package/dist/contracts/tm-bundle-merkle/swap.js +14 -14
- package/dist/contracts/tm-bundle-merkle/utils.js +19 -8
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +2 -20
- package/dist/flap/portal-bundle-merkle/swap.js +20 -19
- package/dist/pancake/bundle-buy-first.js +53 -54
- package/dist/pancake/bundle-swap.js +20 -19
- package/dist/utils/constants.d.ts +3 -1
- package/dist/utils/constants.js +3 -1
- package/package.json +1 -1
|
@@ -7,7 +7,8 @@ import { ethers, Contract, Wallet } from 'ethers';
|
|
|
7
7
|
import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
|
|
8
8
|
import { ADDRESSES } from '../../utils/constants.js';
|
|
9
9
|
import { TM_ABI, HELPER3_ABI, TM_ADDRESS } from './swap-internal.js';
|
|
10
|
-
import { getTxType, getGasPriceConfig, getProfitRecipient,
|
|
10
|
+
import { getTxType, getGasPriceConfig, getProfitRecipient, getBribeAmount } from './config.js';
|
|
11
|
+
import { PROFIT_CONFIG } from '../../utils/constants.js';
|
|
11
12
|
import { trySell } from '../tm.js';
|
|
12
13
|
// ✅ BlockRazor Builder EOA 地址(用于贿赂)
|
|
13
14
|
const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
@@ -88,7 +89,7 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
88
89
|
const minBuyAmount = 0n;
|
|
89
90
|
// 预先规划 nonces
|
|
90
91
|
const extractProfit = true;
|
|
91
|
-
const profitRateBps =
|
|
92
|
+
const profitRateBps = PROFIT_CONFIG.RATE_BPS_CAPITAL; // 万分之三
|
|
92
93
|
// ✅ 获取贿赂金额
|
|
93
94
|
const bribeAmount = getBribeAmount(config);
|
|
94
95
|
const needBribeTx = bribeAmount > 0n;
|
|
@@ -101,25 +102,17 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
101
102
|
sellerNonceCount++; // 授权交易
|
|
102
103
|
if (extractProfit)
|
|
103
104
|
sellerNonceCount++; // 利润交易
|
|
104
|
-
// ✅ 优化:使用批量 nonce
|
|
105
|
-
// 同一地址时使用 getNextNonceBatch 获取连续 nonce
|
|
106
|
-
// 不同地址时使用 getNextNoncesForWallets 一次性获取两个地址的初始 nonce
|
|
105
|
+
// ✅ 优化:使用批量 nonce 获取(单次网络往返)
|
|
107
106
|
const getNoncesPromise = sameAddress
|
|
108
107
|
? nonceManager.getNextNonceBatch(seller, buyerNonceCount + sellerNonceCount)
|
|
109
108
|
.then(nonces => ({ buyerNonces: [], sellerNonces: nonces }))
|
|
110
|
-
: (
|
|
111
|
-
|
|
112
|
-
const initialNonces = await nonceManager.getNextNoncesForWallets([buyer, seller]);
|
|
109
|
+
: nonceManager.getNextNoncesForWallets([buyer, seller])
|
|
110
|
+
.then(initialNonces => ({
|
|
113
111
|
// buyer 只需要 1 个 nonce
|
|
114
|
-
|
|
112
|
+
buyerNonces: [initialNonces[0]],
|
|
115
113
|
// seller 需要 sellerNonceCount 个连续 nonce,从初始 nonce 开始
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
for (let i = 1; i < sellerNonceCount; i++) {
|
|
119
|
-
await nonceManager.getNextNonce(seller); // 递增缓存
|
|
120
|
-
}
|
|
121
|
-
return { buyerNonces, sellerNonces };
|
|
122
|
-
})();
|
|
114
|
+
sellerNonces: Array.from({ length: sellerNonceCount }, (_, i) => initialNonces[1] + i)
|
|
115
|
+
}));
|
|
123
116
|
const [sellResult, buyUnsigned, sellUnsigned, noncesResult] = await Promise.all([
|
|
124
117
|
trySell('BSC', config.rpcUrl, tokenAddress, sellAmountWei),
|
|
125
118
|
tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, buyerFundsWei, minBuyAmount, { value: buyerFundsWei }),
|
|
@@ -64,7 +64,7 @@ export async function fourBundleSwapMerkle(params) {
|
|
|
64
64
|
const needApproval = currentAllowance < APPROVAL_THRESHOLD;
|
|
65
65
|
// 利润配置
|
|
66
66
|
const extractProfit = true;
|
|
67
|
-
const profitRateBps = PROFIT_CONFIG.
|
|
67
|
+
const profitRateBps = PROFIT_CONFIG.RATE_BPS_CAPITAL;
|
|
68
68
|
const profitAmount = extractProfit && sellerWillGetBNB > 0n
|
|
69
69
|
? (sellerWillGetBNB * BigInt(profitRateBps)) / 10000n
|
|
70
70
|
: 0n;
|
|
@@ -282,7 +282,7 @@ export async function fourBatchSwapMerkle(params) {
|
|
|
282
282
|
const APPROVAL_THRESHOLD = ethers.parseUnits('1000000000', decimals);
|
|
283
283
|
const needApproval = currentAllowance < APPROVAL_THRESHOLD;
|
|
284
284
|
// 利润配置
|
|
285
|
-
const profitRateBps = PROFIT_CONFIG.
|
|
285
|
+
const profitRateBps = PROFIT_CONFIG.RATE_BPS_CAPITAL;
|
|
286
286
|
const profitAmount = totalBuyerFunds > 0n
|
|
287
287
|
? (totalBuyerFunds * BigInt(profitRateBps)) / 10000n
|
|
288
288
|
: 0n;
|
|
@@ -489,8 +489,8 @@ export async function fourQuickBatchSwapMerkle(params) {
|
|
|
489
489
|
const estimatedBNBOut = sellQuote.funds;
|
|
490
490
|
console.log(`[fourQuickBatchSwapMerkle] 卖出数量: ${ethers.formatUnits(sellAmountWei, decimals)}`);
|
|
491
491
|
console.log(`[fourQuickBatchSwapMerkle] 预估卖出所得: ${ethers.formatEther(estimatedBNBOut)} BNB`);
|
|
492
|
-
// ✅
|
|
493
|
-
const profitRateBps = PROFIT_CONFIG.
|
|
492
|
+
// ✅ 计算利润(万分之三)
|
|
493
|
+
const profitRateBps = PROFIT_CONFIG.RATE_BPS_CAPITAL;
|
|
494
494
|
const profitAmount = estimatedBNBOut > 0n
|
|
495
495
|
? (estimatedBNBOut * BigInt(profitRateBps)) / 10000n
|
|
496
496
|
: 0n;
|
|
@@ -553,24 +553,24 @@ export async function fourQuickBatchSwapMerkle(params) {
|
|
|
553
553
|
type: txType
|
|
554
554
|
});
|
|
555
555
|
console.log(`[fourQuickBatchSwapMerkle] 卖出交易已签名`);
|
|
556
|
-
// ==================== 3.
|
|
557
|
-
const transferTxs = [];
|
|
556
|
+
// ==================== 3. 转账交易(并行签名)====================
|
|
558
557
|
const reserveGas = ethers.parseEther((config.reserveGasBNB || 0.0005).toString());
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
558
|
+
const buyerGasCost = gasPrice * finalGasLimit;
|
|
559
|
+
// ✅ 预分配 nonce,然后并行签名
|
|
560
|
+
const transferNonces = buyers.map((_, i) => sellerNonce + i);
|
|
561
|
+
sellerNonce += buyers.length; // 更新 sellerNonce
|
|
562
|
+
const transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
562
563
|
const transferValue = transferAmountsWei[i] + reserveGas + buyerGasCost;
|
|
563
|
-
|
|
564
|
-
to:
|
|
564
|
+
return seller.signTransaction({
|
|
565
|
+
to: buyer.address,
|
|
565
566
|
value: transferValue,
|
|
566
|
-
nonce:
|
|
567
|
+
nonce: transferNonces[i],
|
|
567
568
|
gasPrice,
|
|
568
569
|
gasLimit: 21000n,
|
|
569
570
|
chainId: chainIdNum,
|
|
570
571
|
type: txType
|
|
571
572
|
});
|
|
572
|
-
|
|
573
|
-
}
|
|
573
|
+
}));
|
|
574
574
|
console.log(`[fourQuickBatchSwapMerkle] ${transferTxs.length} 笔转账交易已签名`);
|
|
575
575
|
// ==================== 4. 买入交易 ====================
|
|
576
576
|
const buyerNonces = await Promise.all(buyers.map(buyer => nonceManager.getNextNonce(buyer)));
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import { ethers, Wallet, Contract } from 'ethers';
|
|
2
2
|
// import { MerkleClient } from '../../clients/merkle.js';
|
|
3
3
|
import { getOptimizedGasPrice, NonceManager } from '../../utils/bundle-helpers.js';
|
|
4
|
-
import {
|
|
4
|
+
import { PROFIT_CONFIG } from '../../utils/constants.js';
|
|
5
|
+
import { getTxType, getGasPriceConfig, shouldExtractProfit, getProfitRecipient } from './config.js';
|
|
5
6
|
import { getErc20DecimalsMerkle as _getErc20DecimalsMerkle, generateHopWallets as _generateHopWallets, normalizeAmounts as _normalizeAmounts, batchGetBalances as _batchGetBalances, calculateGasLimit as _calculateGasLimit, isNativeTokenAddress as _isNativeTokenAddress } from './internal.js';
|
|
7
|
+
// ==================== 本地利润计算(万分之三)====================
|
|
8
|
+
/**
|
|
9
|
+
* 计算利润金额(万分之三)
|
|
10
|
+
* ✅ 归集和分散专用:3 bps = 0.03%
|
|
11
|
+
*/
|
|
12
|
+
function calculateProfit(amount) {
|
|
13
|
+
const profit = (amount * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
14
|
+
const remaining = amount - profit;
|
|
15
|
+
return { profit, remaining };
|
|
16
|
+
}
|
|
6
17
|
// ==================== ERC20 → 原生代币报价 ====================
|
|
7
18
|
// BSC 链常量
|
|
8
19
|
const BSC_PANCAKE_V2_ROUTER = '0x10ED43C718714eb63d5aA57B78B54704E256024E';
|
|
@@ -122,7 +133,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
122
133
|
totalAmountBeforeProfit += originalAmount;
|
|
123
134
|
let actualAmount = originalAmount;
|
|
124
135
|
if (extractProfit) {
|
|
125
|
-
const { profit, remaining } = calculateProfit(originalAmount
|
|
136
|
+
const { profit, remaining } = calculateProfit(originalAmount);
|
|
126
137
|
actualAmount = remaining;
|
|
127
138
|
totalProfit += profit;
|
|
128
139
|
}
|
|
@@ -163,7 +174,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
163
174
|
totalAmountBeforeProfit += originalAmount;
|
|
164
175
|
let actualAmount = originalAmount;
|
|
165
176
|
if (extractProfit) {
|
|
166
|
-
const { profit, remaining } = calculateProfit(originalAmount
|
|
177
|
+
const { profit, remaining } = calculateProfit(originalAmount);
|
|
167
178
|
actualAmount = remaining;
|
|
168
179
|
totalTokenProfit += profit; // 累计 ERC20 代币利润
|
|
169
180
|
}
|
|
@@ -249,7 +260,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
249
260
|
totalAmountBeforeProfit += originalAmountWei;
|
|
250
261
|
let amountWei = originalAmountWei;
|
|
251
262
|
if (extractProfit) {
|
|
252
|
-
const { profit, remaining } = calculateProfit(originalAmountWei
|
|
263
|
+
const { profit, remaining } = calculateProfit(originalAmountWei);
|
|
253
264
|
amountWei = remaining;
|
|
254
265
|
if (isNative) {
|
|
255
266
|
totalProfit += profit; // 原生币直接累加
|
|
@@ -519,7 +530,7 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
519
530
|
}
|
|
520
531
|
// 累计总利润
|
|
521
532
|
if (extractProfit && toSend > 0n) {
|
|
522
|
-
const { profit } = calculateProfit(toSend
|
|
533
|
+
const { profit } = calculateProfit(toSend);
|
|
523
534
|
totalProfit += profit;
|
|
524
535
|
}
|
|
525
536
|
}
|
|
@@ -537,7 +548,7 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
537
548
|
totalProfit = 0n;
|
|
538
549
|
for (let i = 0; i < sweepAmounts.length; i++) {
|
|
539
550
|
if (sweepAmounts[i] > 0n) {
|
|
540
|
-
totalProfit += calculateProfit(sweepAmounts[i]
|
|
551
|
+
totalProfit += calculateProfit(sweepAmounts[i]).profit;
|
|
541
552
|
}
|
|
542
553
|
}
|
|
543
554
|
}
|
|
@@ -666,7 +677,7 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
666
677
|
}
|
|
667
678
|
// 累计总代币利润(ERC20 归集)
|
|
668
679
|
if (extractProfit && toSend > 0n) {
|
|
669
|
-
const { profit } = calculateProfit(toSend
|
|
680
|
+
const { profit } = calculateProfit(toSend);
|
|
670
681
|
totalProfit += profit; // 先累计代币利润
|
|
671
682
|
}
|
|
672
683
|
}
|
|
@@ -1013,7 +1024,7 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
1013
1024
|
let totalTokenProfit = 0n;
|
|
1014
1025
|
for (let i = 0; i < sweepAmounts.length; i++) {
|
|
1015
1026
|
if (sweepAmounts[i] > 0n) {
|
|
1016
|
-
const { profit } = calculateProfit(sweepAmounts[i]
|
|
1027
|
+
const { profit } = calculateProfit(sweepAmounts[i]);
|
|
1017
1028
|
if (isNative) {
|
|
1018
1029
|
totalProfit += profit; // 原生币直接累加
|
|
1019
1030
|
}
|
|
@@ -393,26 +393,8 @@ function calculateProfitAmount(expectedFunds) {
|
|
|
393
393
|
if (expectedFunds <= 0n) {
|
|
394
394
|
return 0n;
|
|
395
395
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
async function validateNativeBalances({ sameAddress, buyerBalance, buyerFundsWei, gasLimit, gasPrice, buyer, seller, provider, nativeToken }) {
|
|
399
|
-
const effectiveGasPrice = gasPrice;
|
|
400
|
-
const gasCost = gasLimit * effectiveGasPrice;
|
|
401
|
-
if (sameAddress) {
|
|
402
|
-
const combinedRequired = buyerFundsWei + gasCost * 2n;
|
|
403
|
-
if (buyerBalance < combinedRequired) {
|
|
404
|
-
throw new Error(`账户余额不足: 需要 ${ethers.formatEther(combinedRequired)} ${nativeToken}(${ethers.formatEther(buyerFundsWei)} 购买 + ${ethers.formatEther(gasCost * 2n)} 两笔Gas),实际 ${ethers.formatEther(buyerBalance)} ${nativeToken}`);
|
|
405
|
-
}
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
const buyerRequiredBalance = buyerFundsWei + gasCost;
|
|
409
|
-
if (buyerBalance < buyerRequiredBalance) {
|
|
410
|
-
throw new Error(`买方余额不足: 需要 ${ethers.formatEther(buyerRequiredBalance)} ${nativeToken}(${ethers.formatEther(buyerFundsWei)} 购买 + ${ethers.formatEther(gasCost)} Gas),实际 ${ethers.formatEther(buyerBalance)} ${nativeToken}`);
|
|
411
|
-
}
|
|
412
|
-
const sellerBalance = await provider.getBalance(seller.address);
|
|
413
|
-
if (sellerBalance < gasCost) {
|
|
414
|
-
throw new Error(`卖方余额不足: 需要 ${ethers.formatEther(gasCost)} ${nativeToken} (Gas),实际 ${ethers.formatEther(sellerBalance)} ${nativeToken}`);
|
|
415
|
-
}
|
|
396
|
+
// 万分之三
|
|
397
|
+
return (expectedFunds * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
416
398
|
}
|
|
417
399
|
/**
|
|
418
400
|
* ✅ 优化:使用批量 nonce 获取
|
|
@@ -458,7 +458,7 @@ function calculateProfitAmount(quotedNative) {
|
|
|
458
458
|
if (quotedNative <= 0n) {
|
|
459
459
|
return 0n;
|
|
460
460
|
}
|
|
461
|
-
return (quotedNative * BigInt(PROFIT_CONFIG.
|
|
461
|
+
return (quotedNative * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
462
462
|
}
|
|
463
463
|
function countTruthy(values) {
|
|
464
464
|
return values.filter(Boolean).length;
|
|
@@ -736,7 +736,7 @@ export async function flapQuickBatchSwapMerkle(params) {
|
|
|
736
736
|
console.log(`[flapQuickBatchSwapMerkle] 模式: ${useNativeToken ? '原生代币' : 'ERC20'}`);
|
|
737
737
|
console.log(`[flapQuickBatchSwapMerkle] 卖出数量: ${ethers.formatUnits(sellAmountWei, decimals)}`);
|
|
738
738
|
console.log(`[flapQuickBatchSwapMerkle] 预估卖出所得: ${outputFormatted}`);
|
|
739
|
-
// ✅
|
|
739
|
+
// ✅ 计算利润(万分之三)
|
|
740
740
|
let tokenProfitAmount = calculateProfitAmount(estimatedOutput);
|
|
741
741
|
let nativeProfitAmount = tokenProfitAmount;
|
|
742
742
|
if (!useNativeToken && tokenProfitAmount > 0n) {
|
|
@@ -827,48 +827,49 @@ export async function flapQuickBatchSwapMerkle(params) {
|
|
|
827
827
|
});
|
|
828
828
|
const signedSell = await seller.signTransaction(sellTx);
|
|
829
829
|
console.log(`[flapQuickBatchSwapMerkle] 卖出交易已签名`);
|
|
830
|
-
// ==================== 3.
|
|
831
|
-
const transferTxs = [];
|
|
830
|
+
// ==================== 3. 转账交易(并行签名)====================
|
|
832
831
|
const reserveGas = ethers.parseEther((config.reserveGasETH || 0.0005).toString());
|
|
832
|
+
const buyerGasCost = gasPrice * finalGasLimit;
|
|
833
|
+
// ✅ 预分配 nonce,然后并行签名
|
|
834
|
+
const transferNonces = buyers.map((_, i) => sellerNonce + i);
|
|
835
|
+
sellerNonce += buyers.length; // 更新 sellerNonce
|
|
836
|
+
let transferTxs;
|
|
833
837
|
if (useNativeToken) {
|
|
834
|
-
//
|
|
835
|
-
|
|
836
|
-
const buyerGasCost = gasPrice * finalGasLimit;
|
|
838
|
+
// 原生代币模式:直接转账(并行签名)
|
|
839
|
+
transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
837
840
|
const transferValue = transferAmountsWei[i] + reserveGas + buyerGasCost;
|
|
838
|
-
|
|
839
|
-
to:
|
|
841
|
+
return seller.signTransaction({
|
|
842
|
+
to: buyer.address,
|
|
840
843
|
value: transferValue,
|
|
841
|
-
nonce:
|
|
844
|
+
nonce: transferNonces[i],
|
|
842
845
|
gasPrice,
|
|
843
846
|
gasLimit: 21000n,
|
|
844
847
|
chainId: chainContext.chainId,
|
|
845
848
|
type: txType
|
|
846
849
|
});
|
|
847
|
-
|
|
848
|
-
}
|
|
850
|
+
}));
|
|
849
851
|
}
|
|
850
852
|
else {
|
|
851
|
-
// ERC20 模式:ERC20 transfer
|
|
853
|
+
// ERC20 模式:ERC20 transfer(并行签名)
|
|
852
854
|
const erc20Interface = new ethers.Interface([
|
|
853
855
|
'function transfer(address to, uint256 amount) returns (bool)'
|
|
854
856
|
]);
|
|
855
|
-
|
|
857
|
+
transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
856
858
|
const transferData = erc20Interface.encodeFunctionData('transfer', [
|
|
857
|
-
|
|
859
|
+
buyer.address,
|
|
858
860
|
transferAmountsWei[i]
|
|
859
861
|
]);
|
|
860
|
-
|
|
862
|
+
return seller.signTransaction({
|
|
861
863
|
to: quoteToken,
|
|
862
864
|
data: transferData,
|
|
863
865
|
value: 0n,
|
|
864
|
-
nonce:
|
|
866
|
+
nonce: transferNonces[i],
|
|
865
867
|
gasPrice,
|
|
866
868
|
gasLimit: ERC20_TRANSFER_GAS,
|
|
867
869
|
chainId: chainContext.chainId,
|
|
868
870
|
type: txType
|
|
869
871
|
});
|
|
870
|
-
|
|
871
|
-
}
|
|
872
|
+
}));
|
|
872
873
|
}
|
|
873
874
|
console.log(`[flapQuickBatchSwapMerkle] ${transferTxs.length} 笔转账交易已签名`);
|
|
874
875
|
// ==================== 4. 买入交易 ====================
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { ethers, Contract, Wallet } from 'ethers';
|
|
7
7
|
import { NonceManager } from '../utils/bundle-helpers.js';
|
|
8
8
|
import { ADDRESSES, PROFIT_CONFIG } from '../utils/constants.js';
|
|
9
|
+
import { quoteV2, quoteV3, getTokenToNativeQuote, getWrappedNativeAddress } from '../utils/quote-helpers.js';
|
|
9
10
|
// ✅ BlockRazor Builder EOA 地址(用于贿赂)
|
|
10
11
|
// 参考文档: https://blockrazor.gitbook.io/blockrazor/bsc/block-builder/send-bundle
|
|
11
12
|
const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
@@ -38,18 +39,7 @@ const PANCAKE_PROXY_ABI = [
|
|
|
38
39
|
'function swapV3Single(address tokenIn, address tokenOut, uint24 fee, uint256 amountIn, uint256 amountOutMin, address to) payable returns (uint256)',
|
|
39
40
|
'function swapV3MultiHop(address[] calldata lpAddresses, address exactTokenIn, uint256 amountIn, uint256 amountOutMin, address to) payable returns (uint256)'
|
|
40
41
|
];
|
|
41
|
-
// Pancake V2 Router ABI(用于报价)
|
|
42
|
-
const PANCAKE_V2_ROUTER_ABI = [
|
|
43
|
-
'function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)'
|
|
44
|
-
];
|
|
45
|
-
// Pancake V3 QuoterV2 ABI(用于报价)
|
|
46
|
-
const PANCAKE_V3_QUOTER_ABI = [
|
|
47
|
-
'function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96)) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)',
|
|
48
|
-
'function quoteExactInput(bytes memory path, uint256 amountIn) external returns (uint256 amountOut, uint160[] memory sqrtPriceX96AfterList, uint32[] memory initializedTicksCrossedList, uint256 gasEstimate)'
|
|
49
|
-
];
|
|
50
42
|
const PANCAKE_PROXY_ADDRESS = ADDRESSES.BSC.PancakeProxy;
|
|
51
|
-
const PANCAKE_V2_ROUTER_ADDRESS = '0x10ED43C718714eb63d5aA57B78B54704E256024E';
|
|
52
|
-
const PANCAKE_V3_QUOTER_ADDRESS = '0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997';
|
|
53
43
|
const FLAT_FEE = 0n; // ✅ 已移除合约固定手续费
|
|
54
44
|
const WBNB_ADDRESS = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
55
45
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
@@ -96,7 +86,8 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
96
86
|
const quotedNative = await quoteSellerNative({
|
|
97
87
|
provider: context.provider,
|
|
98
88
|
tokenAddress,
|
|
99
|
-
sellAmountToken: quoteResult.quotedTokenOut
|
|
89
|
+
sellAmountToken: quoteResult.quotedTokenOut,
|
|
90
|
+
routeParams // ✅ 传递路由参数
|
|
100
91
|
});
|
|
101
92
|
const buyerNeed = calculateBuyerNeed({
|
|
102
93
|
quotedNative,
|
|
@@ -123,9 +114,11 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
123
114
|
const estimatedProfitFromSell = await estimateProfitAmount({
|
|
124
115
|
provider: context.provider,
|
|
125
116
|
tokenAddress,
|
|
126
|
-
sellAmountToken: quoteResult.quotedTokenOut
|
|
117
|
+
sellAmountToken: quoteResult.quotedTokenOut,
|
|
118
|
+
routeParams // ✅ 传递路由参数
|
|
127
119
|
});
|
|
128
|
-
|
|
120
|
+
// 万分之三
|
|
121
|
+
const profitBase = estimatedProfitFromSell > 0n ? estimatedProfitFromSell : (buyerFundsInfo.buyerFundsWei * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
129
122
|
const profitAmount = profitBase;
|
|
130
123
|
// ✅ 获取贿赂金额
|
|
131
124
|
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
@@ -277,56 +270,55 @@ async function calculateBuyerFunds({ buyer, buyerFunds, buyerFundsPercentage, re
|
|
|
277
270
|
}
|
|
278
271
|
return { buyerFundsWei, buyerBalance, reserveGas: reserveGasWei };
|
|
279
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* ✅ 使用 quote-helpers 统一报价
|
|
275
|
+
*/
|
|
280
276
|
async function quoteTokenOutput({ routeParams, buyerFundsWei, provider }) {
|
|
277
|
+
// V2 路由
|
|
281
278
|
if (routeParams.routeType === 'v2') {
|
|
282
279
|
const { v2Path } = routeParams;
|
|
283
|
-
const
|
|
284
|
-
const
|
|
285
|
-
|
|
280
|
+
const tokenIn = v2Path[0];
|
|
281
|
+
const tokenOut = v2Path[v2Path.length - 1];
|
|
282
|
+
const result = await quoteV2(provider, tokenIn, tokenOut, buyerFundsWei, 'BSC');
|
|
283
|
+
if (result.amountOut <= 0n) {
|
|
284
|
+
throw new Error('V2 报价失败');
|
|
285
|
+
}
|
|
286
|
+
return { quotedTokenOut: result.amountOut };
|
|
286
287
|
}
|
|
288
|
+
// V3 Single 路由
|
|
287
289
|
if (routeParams.routeType === 'v3-single') {
|
|
288
290
|
const paramsV3 = routeParams;
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
tokenIn: paramsV3.v3TokenIn,
|
|
293
|
-
tokenOut: paramsV3.v3TokenOut,
|
|
294
|
-
amountIn: buyerFundsWei,
|
|
295
|
-
fee: paramsV3.v3Fee,
|
|
296
|
-
sqrtPriceLimitX96: 0
|
|
297
|
-
});
|
|
298
|
-
return { quotedTokenOut: result[0] };
|
|
299
|
-
}
|
|
300
|
-
catch {
|
|
301
|
-
if (paramsV3.v2Path && paramsV3.v2Path.length >= 2) {
|
|
302
|
-
const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
|
|
303
|
-
const amounts = await v2Router.getAmountsOut(buyerFundsWei, paramsV3.v2Path);
|
|
304
|
-
return { quotedTokenOut: amounts[amounts.length - 1] };
|
|
305
|
-
}
|
|
306
|
-
throw new Error('V3 报价失败且未提供 v2Path 备用');
|
|
291
|
+
const result = await quoteV3(provider, paramsV3.v3TokenIn, paramsV3.v3TokenOut, buyerFundsWei, 'BSC', paramsV3.v3Fee);
|
|
292
|
+
if (result.amountOut <= 0n) {
|
|
293
|
+
throw new Error('V3 报价失败');
|
|
307
294
|
}
|
|
295
|
+
return { quotedTokenOut: result.amountOut };
|
|
308
296
|
}
|
|
297
|
+
// V3 Multi 路由
|
|
309
298
|
const paramsV3m = routeParams;
|
|
310
|
-
if (paramsV3m.v2Path
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
299
|
+
if (!paramsV3m.v2Path || paramsV3m.v2Path.length < 2) {
|
|
300
|
+
throw new Error('V3 多跳需要提供 v2Path 用于路径推断');
|
|
301
|
+
}
|
|
302
|
+
const tokenIn = paramsV3m.v2Path[0];
|
|
303
|
+
const tokenOut = paramsV3m.v2Path[paramsV3m.v2Path.length - 1];
|
|
304
|
+
const result = await quoteV3(provider, tokenIn, tokenOut, buyerFundsWei, 'BSC');
|
|
305
|
+
if (result.amountOut <= 0n) {
|
|
306
|
+
throw new Error('V3 多跳报价失败');
|
|
314
307
|
}
|
|
315
|
-
|
|
308
|
+
return { quotedTokenOut: result.amountOut };
|
|
316
309
|
}
|
|
317
310
|
async function getTokenDecimals(provider, tokenAddress) {
|
|
318
311
|
const erc20 = new Contract(tokenAddress, ['function decimals() view returns (uint8)'], provider);
|
|
319
312
|
return await erc20.decimals();
|
|
320
313
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
314
|
+
/**
|
|
315
|
+
* ✅ 使用 quote-helpers 统一报价(卖出 → 原生代币)
|
|
316
|
+
*/
|
|
317
|
+
async function quoteSellerNative({ provider, tokenAddress, sellAmountToken, routeParams }) {
|
|
318
|
+
const wbnb = getWrappedNativeAddress('BSC');
|
|
319
|
+
const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
|
|
320
|
+
const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
|
|
321
|
+
return await getTokenToNativeQuote(provider, tokenAddress, sellAmountToken, 'BSC', version, fee);
|
|
330
322
|
}
|
|
331
323
|
function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas }) {
|
|
332
324
|
// ✅ 已移除滑点保护:直接使用报价金额
|
|
@@ -391,12 +383,19 @@ async function buildRouteTransactions({ routeParams, buyerFundsWei, sellAmountTo
|
|
|
391
383
|
const sellUnsigned = await proxySeller.swapV3MultiHop.populateTransaction(v3LpAddresses, exactTokenOut, sellAmountToken, 0n, seller.address, { value: FLAT_FEE });
|
|
392
384
|
return { buyUnsigned, sellUnsigned };
|
|
393
385
|
}
|
|
394
|
-
|
|
386
|
+
/**
|
|
387
|
+
* ✅ 使用 quote-helpers 统一报价(估算利润)
|
|
388
|
+
*/
|
|
389
|
+
async function estimateProfitAmount({ provider, tokenAddress, sellAmountToken, routeParams }) {
|
|
395
390
|
try {
|
|
396
|
-
const
|
|
397
|
-
const
|
|
398
|
-
const estimatedSellFunds =
|
|
399
|
-
|
|
391
|
+
const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
|
|
392
|
+
const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
|
|
393
|
+
const estimatedSellFunds = await getTokenToNativeQuote(provider, tokenAddress, sellAmountToken, 'BSC', version, fee);
|
|
394
|
+
if (estimatedSellFunds <= 0n) {
|
|
395
|
+
return 0n;
|
|
396
|
+
}
|
|
397
|
+
// 万分之三
|
|
398
|
+
return (estimatedSellFunds * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
400
399
|
}
|
|
401
400
|
catch {
|
|
402
401
|
return 0n;
|
|
@@ -161,7 +161,7 @@ function calculateProfitAmount(estimatedBNBOut) {
|
|
|
161
161
|
if (estimatedBNBOut <= 0n) {
|
|
162
162
|
return 0n;
|
|
163
163
|
}
|
|
164
|
-
return (estimatedBNBOut * BigInt(PROFIT_CONFIG.
|
|
164
|
+
return (estimatedBNBOut * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
167
167
|
* ✅ 获取 ERC20 代币 → 原生代币(BNB)的报价
|
|
@@ -779,7 +779,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
779
779
|
? ethers.formatEther(estimatedOutput)
|
|
780
780
|
: ethers.formatUnits(estimatedOutput, quoteTokenDecimals);
|
|
781
781
|
console.log(`[pancakeQuickBatchSwapMerkle] 预估卖出所得: ${outputFormatted} ${useNativeToken ? 'BNB' : 'ERC20'}`);
|
|
782
|
-
// ✅
|
|
782
|
+
// ✅ 计算利润(万分之三)
|
|
783
783
|
let profitAmount;
|
|
784
784
|
if (useNativeToken) {
|
|
785
785
|
profitAmount = calculateProfitAmount(estimatedOutput);
|
|
@@ -881,47 +881,48 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
881
881
|
type: txType
|
|
882
882
|
});
|
|
883
883
|
console.log(`[pancakeQuickBatchSwapMerkle] 卖出交易已签名`);
|
|
884
|
-
// ==================== 3.
|
|
885
|
-
const
|
|
884
|
+
// ==================== 3. 转账交易(并行签名)====================
|
|
885
|
+
const buyerGasCost = gasPrice * finalGasLimit;
|
|
886
|
+
// ✅ 预分配 nonce,然后并行签名
|
|
887
|
+
const transferNonces = buyers.map((_, i) => sellerNonce + i);
|
|
888
|
+
sellerNonce += buyers.length; // 更新 sellerNonce
|
|
889
|
+
let transferTxs;
|
|
886
890
|
if (useNativeToken) {
|
|
887
|
-
// ✅ 原生代币模式:直接 BNB
|
|
888
|
-
|
|
889
|
-
const buyerGasCost = gasPrice * finalGasLimit;
|
|
891
|
+
// ✅ 原生代币模式:直接 BNB 转账(并行签名)
|
|
892
|
+
transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
890
893
|
const transferValue = transferAmountsWei[i] + FLAT_FEE + buyerGasCost;
|
|
891
|
-
|
|
892
|
-
to:
|
|
894
|
+
return seller.signTransaction({
|
|
895
|
+
to: buyer.address,
|
|
893
896
|
value: transferValue,
|
|
894
|
-
nonce:
|
|
897
|
+
nonce: transferNonces[i],
|
|
895
898
|
gasPrice,
|
|
896
899
|
gasLimit: 21000n,
|
|
897
900
|
chainId: context.chainId,
|
|
898
901
|
type: txType
|
|
899
902
|
});
|
|
900
|
-
|
|
901
|
-
}
|
|
903
|
+
}));
|
|
902
904
|
}
|
|
903
905
|
else {
|
|
904
|
-
// ✅ ERC20 模式:ERC20 transfer
|
|
906
|
+
// ✅ ERC20 模式:ERC20 transfer(并行签名)
|
|
905
907
|
const erc20Interface = new ethers.Interface([
|
|
906
908
|
'function transfer(address to, uint256 amount) returns (bool)'
|
|
907
909
|
]);
|
|
908
|
-
|
|
910
|
+
transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
909
911
|
const transferData = erc20Interface.encodeFunctionData('transfer', [
|
|
910
|
-
|
|
912
|
+
buyer.address,
|
|
911
913
|
transferAmountsWei[i]
|
|
912
914
|
]);
|
|
913
|
-
|
|
915
|
+
return seller.signTransaction({
|
|
914
916
|
to: quoteToken,
|
|
915
917
|
data: transferData,
|
|
916
918
|
value: 0n,
|
|
917
|
-
nonce:
|
|
919
|
+
nonce: transferNonces[i],
|
|
918
920
|
gasPrice,
|
|
919
921
|
gasLimit: ERC20_TRANSFER_GAS,
|
|
920
922
|
chainId: context.chainId,
|
|
921
923
|
type: txType
|
|
922
924
|
});
|
|
923
|
-
|
|
924
|
-
}
|
|
925
|
+
}));
|
|
925
926
|
}
|
|
926
927
|
console.log(`[pancakeQuickBatchSwapMerkle] ${transferTxs.length} 笔转账交易已签名`);
|
|
927
928
|
// ==================== 4. 买入交易 ====================
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export declare const PROFIT_CONFIG: {
|
|
2
2
|
/** 利润接收地址 */
|
|
3
3
|
readonly RECIPIENT: "0xe8D0334fAf713884133640CAEe4ECdd2106AF103";
|
|
4
|
-
/** 利润比例(基点):30 bps = 0.3% =
|
|
4
|
+
/** 利润比例(基点):30 bps = 0.3% = 千分之三(普通模式) */
|
|
5
5
|
readonly RATE_BPS: 30;
|
|
6
|
+
/** 利润比例(基点):3 bps = 0.03% = 万分之三(资金利用率模式) */
|
|
7
|
+
readonly RATE_BPS_CAPITAL: 3;
|
|
6
8
|
};
|
|
7
9
|
export declare const CHAIN: {
|
|
8
10
|
BSC: {
|
package/dist/utils/constants.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
export const PROFIT_CONFIG = {
|
|
3
3
|
/** 利润接收地址 */
|
|
4
4
|
RECIPIENT: '0xe8D0334fAf713884133640CAEe4ECdd2106AF103',
|
|
5
|
-
/** 利润比例(基点):30 bps = 0.3% =
|
|
5
|
+
/** 利润比例(基点):30 bps = 0.3% = 千分之三(普通模式) */
|
|
6
6
|
RATE_BPS: 30,
|
|
7
|
+
/** 利润比例(基点):3 bps = 0.03% = 万分之三(资金利用率模式) */
|
|
8
|
+
RATE_BPS_CAPITAL: 3,
|
|
7
9
|
};
|
|
8
10
|
export const CHAIN = {
|
|
9
11
|
BSC: {
|