four-flap-meme-sdk 1.4.1 → 1.4.2
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/core.js +4 -23
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +50 -60
- package/dist/contracts/tm-bundle-merkle/private.js +151 -96
- package/dist/flap/portal-bundle-merkle/pancake-proxy.js +58 -71
- package/dist/flap/portal-bundle-merkle/private.js +185 -72
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ import { FourClient, buildLoginMessage } from '../../clients/four.js';
|
|
|
5
5
|
import { getErrorMessage, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, getProfitRecipient, getBribeAmount } from './config.js';
|
|
6
6
|
// ✅ BlockRazor Builder EOA 地址(用于贿赂)
|
|
7
7
|
const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
8
|
-
|
|
8
|
+
// ✅ 已移除授权检查(前端负责确保授权)
|
|
9
9
|
import { trySell } from '../tm.js';
|
|
10
10
|
import Helper3Abi from '../../abis/TokenManagerHelper3.json' with { type: 'json' };
|
|
11
11
|
const MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
|
|
@@ -347,8 +347,8 @@ async function executeSellFlow(options) {
|
|
|
347
347
|
// ✅ 获取贿赂金额
|
|
348
348
|
const bribeAmount = getBribeAmount(config);
|
|
349
349
|
const needBribeTx = bribeAmount > 0n;
|
|
350
|
-
// ✅ 优化:并行执行 resolveSellOutputs、ensureSellerBalances
|
|
351
|
-
const [sellOutputs, ,
|
|
350
|
+
// ✅ 优化:并行执行 resolveSellOutputs、ensureSellerBalances 和批量获取 nonces(已移除授权检查)
|
|
351
|
+
const [sellOutputs, , nonces] = await Promise.all([
|
|
352
352
|
resolveSellOutputs({
|
|
353
353
|
minOutputAmounts,
|
|
354
354
|
sellers: wallets,
|
|
@@ -357,13 +357,6 @@ async function executeSellFlow(options) {
|
|
|
357
357
|
tokenAddress
|
|
358
358
|
}),
|
|
359
359
|
ensureSellerBalances({ provider, tokenAddress, sellers: wallets, amountsWei }),
|
|
360
|
-
ensureSellAllowances({
|
|
361
|
-
provider,
|
|
362
|
-
tokenAddress,
|
|
363
|
-
sellers: wallets,
|
|
364
|
-
amountsWei,
|
|
365
|
-
spender: contractAddress
|
|
366
|
-
}),
|
|
367
360
|
nonceManager.getNextNoncesForWallets(wallets) // ✅ 批量获取 nonce(JSON-RPC 批量请求)
|
|
368
361
|
]);
|
|
369
362
|
// ✅ 找出收益最多的钱包作为 payer(贿赂和利润都由它发送)
|
|
@@ -540,19 +533,7 @@ async function ensureSellerBalances(params) {
|
|
|
540
533
|
}
|
|
541
534
|
}
|
|
542
535
|
}
|
|
543
|
-
|
|
544
|
-
const { provider, tokenAddress, sellers, amountsWei, spender } = params;
|
|
545
|
-
const allowances = await batchCheckAllowances(provider, tokenAddress, sellers.map((wallet) => wallet.address), spender);
|
|
546
|
-
const needApprovalIndexes = [];
|
|
547
|
-
for (let i = 0; i < sellers.length; i++) {
|
|
548
|
-
if (allowances[i] < amountsWei[i]) {
|
|
549
|
-
needApprovalIndexes.push(i);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
if (needApprovalIndexes.length > 0) {
|
|
553
|
-
throw new Error(`${needApprovalIndexes.length} 个钱包需要授权。请先调用 approveFourTokenManagerBatch({ amounts: ['max', ...] })`);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
536
|
+
// ✅ 已移除 ensureSellAllowances(前端负责确保授权)
|
|
556
537
|
async function populateSellTransactions(params) {
|
|
557
538
|
const { sellers, contractAddress, tokenAddress, amountsWei, minOuts } = params;
|
|
558
539
|
const contracts = sellers.map((wallet) => new ethers.Contract(contractAddress, TM2_ABI, wallet));
|
|
@@ -2,7 +2,7 @@ import { ethers, Wallet, JsonRpcProvider, Contract, Interface } from 'ethers';
|
|
|
2
2
|
import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
|
|
3
3
|
import { ADDRESSES } from '../../utils/constants.js';
|
|
4
4
|
import { getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit, getProfitRecipient, getBribeAmount } from './config.js';
|
|
5
|
-
|
|
5
|
+
// ✅ 已移除授权检查(前端负责确保授权)
|
|
6
6
|
// ✅ BlockRazor Builder EOA 地址(用于贿赂)
|
|
7
7
|
const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
8
8
|
const MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
|
|
@@ -369,10 +369,11 @@ export async function fourPancakeProxyBatchBuyMerkle(params) {
|
|
|
369
369
|
else {
|
|
370
370
|
throw new Error(`Unsupported routeType: ${routeType}`);
|
|
371
371
|
}
|
|
372
|
-
// ✅
|
|
373
|
-
const
|
|
372
|
+
// ✅ 并行签名所有交易(贿赂、买入、利润)
|
|
373
|
+
const signPromises = [];
|
|
374
|
+
// 贿赂交易(索引 0)
|
|
374
375
|
if (needBribeTx && bribeNonce !== undefined) {
|
|
375
|
-
|
|
376
|
+
signPromises.push(buyers[0].signTransaction({
|
|
376
377
|
to: BLOCKRAZOR_BUILDER_EOA,
|
|
377
378
|
value: bribeAmount,
|
|
378
379
|
nonce: bribeNonce,
|
|
@@ -380,24 +381,24 @@ export async function fourPancakeProxyBatchBuyMerkle(params) {
|
|
|
380
381
|
gasLimit: 21000n,
|
|
381
382
|
chainId: 56,
|
|
382
383
|
type: txType
|
|
383
|
-
});
|
|
384
|
-
signedTxs.push(bribeTx);
|
|
384
|
+
}));
|
|
385
385
|
}
|
|
386
|
-
//
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
386
|
+
// 买入交易(索引 1 ~ N)
|
|
387
|
+
unsignedBuys.forEach((unsigned, i) => {
|
|
388
|
+
signPromises.push(buyers[i].signTransaction({
|
|
389
|
+
...unsigned,
|
|
390
|
+
from: buyers[i].address,
|
|
391
|
+
nonce: nonces[i],
|
|
392
|
+
gasLimit: finalGasLimit,
|
|
393
|
+
gasPrice,
|
|
394
|
+
chainId: 56,
|
|
395
|
+
type: txType,
|
|
396
|
+
value: unsigned.value
|
|
397
|
+
}));
|
|
398
|
+
});
|
|
399
|
+
// 利润交易(索引 N+1)
|
|
399
400
|
if (extractProfit && totalProfit > 0n && profitNonce !== undefined) {
|
|
400
|
-
|
|
401
|
+
signPromises.push(buyers[0].signTransaction({
|
|
401
402
|
to: getProfitRecipient(),
|
|
402
403
|
value: totalProfit,
|
|
403
404
|
nonce: profitNonce,
|
|
@@ -405,9 +406,10 @@ export async function fourPancakeProxyBatchBuyMerkle(params) {
|
|
|
405
406
|
gasLimit: 21000n,
|
|
406
407
|
chainId: 56,
|
|
407
408
|
type: txType
|
|
408
|
-
});
|
|
409
|
-
signedTxs.push(profitTx);
|
|
409
|
+
}));
|
|
410
410
|
}
|
|
411
|
+
// ✅ 并行签名完成后按顺序返回
|
|
412
|
+
const signedTxs = await Promise.all(signPromises);
|
|
411
413
|
// ✅ 清理临时 nonce 缓存
|
|
412
414
|
nonceManager.clearTemp();
|
|
413
415
|
// ✅ 直接返回签名交易(不提交到Merkle)
|
|
@@ -440,26 +442,12 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
|
|
|
440
442
|
// ✅ 获取贿赂金额
|
|
441
443
|
const bribeAmount = getBribeAmount(config);
|
|
442
444
|
const needBribeTx = bribeAmount > 0n;
|
|
443
|
-
// ✅ 优化:并行获取 gasPrice
|
|
444
|
-
const [gasPrice, tokenDecimals
|
|
445
|
+
// ✅ 优化:并行获取 gasPrice 和 tokenDecimals(已移除授权检查,前端负责)
|
|
446
|
+
const [gasPrice, tokenDecimals] = await Promise.all([
|
|
445
447
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
446
|
-
getTokenDecimals(tokenAddress, provider)
|
|
447
|
-
batchCheckAllowances(provider, tokenAddress, sellers.map(w => w.address), pancakeProxyAddress)
|
|
448
|
+
getTokenDecimals(tokenAddress, provider)
|
|
448
449
|
]);
|
|
449
450
|
const amountsWei = sellAmounts.map(a => ethers.parseUnits(a, tokenDecimals));
|
|
450
|
-
// 找出需要授权的钱包索引
|
|
451
|
-
const needApprovalIndexes = [];
|
|
452
|
-
const APPROVAL_THRESHOLD = ethers.MaxUint256 / 2n;
|
|
453
|
-
for (let i = 0; i < sellers.length; i++) {
|
|
454
|
-
if (allowances[i] < APPROVAL_THRESHOLD) {
|
|
455
|
-
needApprovalIndexes.push(i);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
// ✅ Step 2: 如果需要授权,抛出错误提示
|
|
459
|
-
// ⚠️ SDK不再处理授权,需要前端先单独授权
|
|
460
|
-
if (needApprovalIndexes.length > 0) {
|
|
461
|
-
throw new Error(`需要授权: ${needApprovalIndexes.length} 个钱包尚未授权。请先完成授权后再卖出。`);
|
|
462
|
-
}
|
|
463
451
|
// ✅ 获取报价(用于计算利润),已移除滑点保护
|
|
464
452
|
let quotedOutputs;
|
|
465
453
|
// ✅ 使用 Multicall3 批量获取报价(仅 V2 路由支持)
|
|
@@ -577,10 +565,11 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
|
|
|
577
565
|
else {
|
|
578
566
|
throw new Error(`Unsupported routeType: ${routeType}`);
|
|
579
567
|
}
|
|
580
|
-
// ✅
|
|
581
|
-
const
|
|
568
|
+
// ✅ 并行签名所有交易(贿赂、卖出、利润)
|
|
569
|
+
const signPromises = [];
|
|
570
|
+
// 贿赂交易(索引 0)
|
|
582
571
|
if (needBribeTx && bribeNonce !== undefined) {
|
|
583
|
-
|
|
572
|
+
signPromises.push(sellers[maxRevenueIndex].signTransaction({
|
|
584
573
|
to: BLOCKRAZOR_BUILDER_EOA,
|
|
585
574
|
value: bribeAmount,
|
|
586
575
|
nonce: bribeNonce,
|
|
@@ -588,24 +577,24 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
|
|
|
588
577
|
gasLimit: 21000n,
|
|
589
578
|
chainId: 56,
|
|
590
579
|
type: txType
|
|
591
|
-
});
|
|
592
|
-
signedTxs.push(bribeTx);
|
|
580
|
+
}));
|
|
593
581
|
}
|
|
594
|
-
//
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
582
|
+
// 卖出交易(索引 1 ~ N)
|
|
583
|
+
unsignedSells.forEach((unsigned, i) => {
|
|
584
|
+
signPromises.push(sellers[i].signTransaction({
|
|
585
|
+
...unsigned,
|
|
586
|
+
from: sellers[i].address,
|
|
587
|
+
nonce: nonces[i],
|
|
588
|
+
gasLimit: finalGasLimit,
|
|
589
|
+
gasPrice,
|
|
590
|
+
chainId: 56,
|
|
591
|
+
type: txType,
|
|
592
|
+
value: unsigned.value
|
|
593
|
+
}));
|
|
594
|
+
});
|
|
595
|
+
// 利润交易(索引 N+1)
|
|
607
596
|
if (extractProfit && totalProfit > 0n && profitNonce !== undefined) {
|
|
608
|
-
|
|
597
|
+
signPromises.push(sellers[maxRevenueIndex].signTransaction({
|
|
609
598
|
to: getProfitRecipient(),
|
|
610
599
|
value: totalProfit,
|
|
611
600
|
nonce: profitNonce,
|
|
@@ -613,9 +602,10 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
|
|
|
613
602
|
gasLimit: 21000n,
|
|
614
603
|
chainId: 56,
|
|
615
604
|
type: txType
|
|
616
|
-
});
|
|
617
|
-
signedTxs.push(profitTx);
|
|
605
|
+
}));
|
|
618
606
|
}
|
|
607
|
+
// ✅ 并行签名完成后按顺序返回
|
|
608
|
+
const signedTxs = await Promise.all(signPromises);
|
|
619
609
|
// ✅ 清理临时 nonce 缓存
|
|
620
610
|
nonceManager.clearTemp();
|
|
621
611
|
// ✅ 直接返回签名交易(不提交到Merkle)
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { ethers, Wallet } from 'ethers';
|
|
2
2
|
import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
|
|
3
3
|
import { ADDRESSES } from '../../utils/constants.js';
|
|
4
|
-
import { getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit, getProfitRecipient
|
|
5
|
-
|
|
4
|
+
import { getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit, getProfitRecipient, getBribeAmount // ✅ 添加贿赂金额获取函数
|
|
5
|
+
} from './config.js';
|
|
6
|
+
// ✅ BlockRazor Builder EOA 地址(用于贿赂)
|
|
7
|
+
const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
8
|
+
// ✅ 已移除授权检查(前端负责确保授权)
|
|
6
9
|
import { trySell } from '../tm.js';
|
|
7
10
|
const CHAIN_ID = 56;
|
|
8
11
|
const DEFAULT_GAS_LIMIT = 800000;
|
|
@@ -52,36 +55,59 @@ export async function fourPrivateBuyMerkle(params) {
|
|
|
52
55
|
actualFundsWei = remaining;
|
|
53
56
|
}
|
|
54
57
|
const tm2 = new ethers.Contract(tmAddr, TM2_ABI, wallet);
|
|
58
|
+
// ✅ 获取贿赂金额
|
|
59
|
+
const bribeAmount = getBribeAmount(config);
|
|
60
|
+
const needBribeTx = bribeAmount > 0n;
|
|
61
|
+
const needProfitTx = extractProfit && profitWei > 0n;
|
|
55
62
|
// ✅ 优化:并行获取 gasPrice、nonce 和构建未签名交易
|
|
56
|
-
const [gasPrice,
|
|
63
|
+
const [gasPrice, baseNonce, unsigned] = await Promise.all([
|
|
57
64
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
58
65
|
wallet.getNonce(),
|
|
59
66
|
tm2.buyTokenAMAP.populateTransaction(0n, tokenAddress, to ?? wallet.address, actualFundsWei, 0n, { value: actualFundsWei })
|
|
60
67
|
]);
|
|
61
|
-
// ✅
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
// ✅ 分配 nonces:贿赂 → 买入 → 利润
|
|
69
|
+
let nonceIdx = 0;
|
|
70
|
+
const bribeNonce = needBribeTx ? baseNonce + nonceIdx++ : undefined;
|
|
71
|
+
const buyNonce = baseNonce + nonceIdx++;
|
|
72
|
+
const profitNonce = needProfitTx ? baseNonce + nonceIdx : undefined;
|
|
73
|
+
// ✅ 并行签名所有交易(贿赂、买入、利润)
|
|
74
|
+
const signPromises = [];
|
|
75
|
+
// 贿赂交易
|
|
76
|
+
if (needBribeTx && bribeNonce !== undefined) {
|
|
77
|
+
signPromises.push(wallet.signTransaction({
|
|
78
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
79
|
+
value: bribeAmount,
|
|
80
|
+
nonce: bribeNonce,
|
|
68
81
|
gasPrice,
|
|
82
|
+
gasLimit: 21000n,
|
|
69
83
|
chainId: CHAIN_ID,
|
|
70
|
-
type: txType
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
type: txType
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
// 买入交易
|
|
88
|
+
signPromises.push(wallet.signTransaction({
|
|
89
|
+
...unsigned,
|
|
90
|
+
from: wallet.address,
|
|
91
|
+
nonce: buyNonce,
|
|
92
|
+
gasLimit,
|
|
93
|
+
gasPrice,
|
|
94
|
+
chainId: CHAIN_ID,
|
|
95
|
+
type: txType,
|
|
96
|
+
value: actualFundsWei
|
|
97
|
+
}));
|
|
98
|
+
// 利润交易
|
|
99
|
+
if (needProfitTx && profitNonce !== undefined) {
|
|
75
100
|
signPromises.push(wallet.signTransaction({
|
|
76
101
|
to: getProfitRecipient(),
|
|
77
102
|
value: profitWei,
|
|
78
|
-
nonce:
|
|
103
|
+
nonce: profitNonce,
|
|
79
104
|
gasPrice,
|
|
80
105
|
gasLimit: 21000n,
|
|
81
106
|
chainId: CHAIN_ID,
|
|
82
107
|
type: txType
|
|
83
108
|
}));
|
|
84
109
|
}
|
|
110
|
+
// ✅ 并行签名完成后按顺序返回
|
|
85
111
|
const signedTxs = await Promise.all(signPromises);
|
|
86
112
|
return {
|
|
87
113
|
signedTransactions: signedTxs
|
|
@@ -106,47 +132,64 @@ export async function fourPrivateSellMerkle(params) {
|
|
|
106
132
|
const sellGasLimit = getGasLimit(config);
|
|
107
133
|
const amountWei = ethers.parseUnits(amount, 18);
|
|
108
134
|
const minOut = minFunds ?? 0n;
|
|
109
|
-
const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
|
|
110
135
|
const tm2 = new ethers.Contract(tmAddr, TM2_ABI, wallet);
|
|
111
|
-
// ✅ 优化:并行获取 gasPrice、
|
|
112
|
-
const [gasPrice,
|
|
136
|
+
// ✅ 优化:并行获取 gasPrice、nonce 和构建未签名交易(已移除授权检查)
|
|
137
|
+
const [gasPrice, sellNonce, sellUnsigned] = await Promise.all([
|
|
113
138
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
114
|
-
tokenContract.allowance(wallet.address, tmAddr),
|
|
115
139
|
wallet.getNonce(),
|
|
116
|
-
tm2.sellToken.populateTransaction(0n, tokenAddress, amountWei, minOut
|
|
140
|
+
tm2.sellToken.populateTransaction(0n, tokenAddress, amountWei, 0n) // ✅ 已移除滑点保护:minOut 固定为 0
|
|
117
141
|
]);
|
|
118
|
-
// ✅
|
|
119
|
-
const APPROVAL_THRESHOLD = ethers.MaxUint256 / 2n;
|
|
120
|
-
if (currentAllowance < APPROVAL_THRESHOLD) {
|
|
121
|
-
throw new Error(`需要授权:钱包 ${wallet.address} 尚未授权。请先完成授权后再卖出。`);
|
|
122
|
-
}
|
|
142
|
+
// ✅ 已移除授权检查(前端负责确保授权)
|
|
123
143
|
// ✅ 利润计算(同步)
|
|
124
144
|
const extractProfit = shouldExtractProfit(config);
|
|
125
145
|
const { profit } = extractProfit ? calculateProfit(minOut, config) : { profit: 0n };
|
|
126
|
-
// ✅
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
146
|
+
// ✅ 获取贿赂金额
|
|
147
|
+
const bribeAmount = getBribeAmount(config);
|
|
148
|
+
const needBribeTx = bribeAmount > 0n;
|
|
149
|
+
const needProfitTx = extractProfit && profit > 0n;
|
|
150
|
+
// ✅ 分配 nonces:贿赂 → 卖出 → 利润
|
|
151
|
+
let nonceIdx = 0;
|
|
152
|
+
const bribeNonce = needBribeTx ? sellNonce + nonceIdx++ : undefined;
|
|
153
|
+
const actualSellNonce = sellNonce + nonceIdx++;
|
|
154
|
+
const profitNonce = needProfitTx ? sellNonce + nonceIdx : undefined;
|
|
155
|
+
// ✅ 并行签名所有交易(贿赂、卖出、利润)
|
|
156
|
+
const signPromises = [];
|
|
157
|
+
// 贿赂交易
|
|
158
|
+
if (needBribeTx && bribeNonce !== undefined) {
|
|
159
|
+
signPromises.push(wallet.signTransaction({
|
|
160
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
161
|
+
value: bribeAmount,
|
|
162
|
+
nonce: bribeNonce,
|
|
133
163
|
gasPrice,
|
|
164
|
+
gasLimit: 21000n,
|
|
134
165
|
chainId: CHAIN_ID,
|
|
135
|
-
type: txType
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
166
|
+
type: txType
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
// 卖出交易
|
|
170
|
+
signPromises.push(wallet.signTransaction({
|
|
171
|
+
...sellUnsigned,
|
|
172
|
+
from: wallet.address,
|
|
173
|
+
nonce: actualSellNonce,
|
|
174
|
+
gasLimit: sellGasLimit,
|
|
175
|
+
gasPrice,
|
|
176
|
+
chainId: CHAIN_ID,
|
|
177
|
+
type: txType,
|
|
178
|
+
value: 0n
|
|
179
|
+
}));
|
|
180
|
+
// 利润交易
|
|
181
|
+
if (needProfitTx && profitNonce !== undefined) {
|
|
140
182
|
signPromises.push(wallet.signTransaction({
|
|
141
183
|
to: getProfitRecipient(),
|
|
142
184
|
value: profit,
|
|
143
|
-
nonce:
|
|
185
|
+
nonce: profitNonce,
|
|
144
186
|
gasPrice,
|
|
145
187
|
gasLimit: 21000n,
|
|
146
188
|
chainId: CHAIN_ID,
|
|
147
189
|
type: txType
|
|
148
190
|
}));
|
|
149
191
|
}
|
|
192
|
+
// ✅ 并行签名完成后按顺序返回
|
|
150
193
|
const signedTxs = await Promise.all(signPromises);
|
|
151
194
|
return {
|
|
152
195
|
signedTransactions: signedTxs
|
|
@@ -176,7 +219,10 @@ export async function fourBatchPrivateBuyMerkle(params) {
|
|
|
176
219
|
const extractProfit = shouldExtractProfit(config);
|
|
177
220
|
const { totalProfit, remainingAmounts } = calculateBatchProfit(originalAmountsWei, config);
|
|
178
221
|
const actualAmountsWei = remainingAmounts;
|
|
179
|
-
// ✅
|
|
222
|
+
// ✅ 获取贿赂金额
|
|
223
|
+
const bribeAmount = getBribeAmount(config);
|
|
224
|
+
const needBribeTx = bribeAmount > 0n;
|
|
225
|
+
// ✅ 找出投入金额最多的钱包(作为贿赂和利润支付者)- 同步计算
|
|
180
226
|
let maxFundsIndex = 0;
|
|
181
227
|
let maxFunds = originalAmountsWei[0];
|
|
182
228
|
for (let i = 1; i < originalAmountsWei.length; i++) {
|
|
@@ -188,15 +234,16 @@ export async function fourBatchPrivateBuyMerkle(params) {
|
|
|
188
234
|
const tm2Contracts = wallets.map((w) => new ethers.Contract(tmAddr, TM2_ABI, w));
|
|
189
235
|
const nonceManager = new NonceManager(provider);
|
|
190
236
|
const needProfitTx = extractProfit && totalProfit > 0n;
|
|
237
|
+
// ✅ 计算 payer 需要的 nonce 数量:贿赂(可选) + 买入 + 利润(可选)
|
|
238
|
+
const payerNonceCount = 1 + (needBribeTx ? 1 : 0) + (needProfitTx ? 1 : 0);
|
|
191
239
|
// ✅ 优化:并行获取 gasPrice、nonces 和构建未签名交易
|
|
192
|
-
// 支付者需要 2 个 nonce,其他钱包各需要 1 个 nonce
|
|
193
240
|
const [gasPrice, unsignedList, noncesResult] = await Promise.all([
|
|
194
241
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
195
242
|
Promise.all(tm2Contracts.map((c, i) => c.buyTokenAMAP.populateTransaction(0n, tokenAddress, wallets[i].address, actualAmountsWei[i], 0n, { value: actualAmountsWei[i] }))),
|
|
196
243
|
(async () => {
|
|
197
|
-
if (
|
|
198
|
-
//
|
|
199
|
-
const payerNonces = await nonceManager.getNextNonceBatch(wallets[maxFundsIndex],
|
|
244
|
+
if (payerNonceCount > 1) {
|
|
245
|
+
// payer 需要多个连续 nonce
|
|
246
|
+
const payerNonces = await nonceManager.getNextNonceBatch(wallets[maxFundsIndex], payerNonceCount);
|
|
200
247
|
// 其他钱包各需要 1 个 nonce
|
|
201
248
|
const otherWallets = wallets.filter((_, i) => i !== maxFundsIndex);
|
|
202
249
|
const otherNonces = otherWallets.length > 0
|
|
@@ -205,24 +252,41 @@ export async function fourBatchPrivateBuyMerkle(params) {
|
|
|
205
252
|
// 组装最终的 nonces 数组(保持原顺序)
|
|
206
253
|
const nonces = [];
|
|
207
254
|
let otherIdx = 0;
|
|
255
|
+
let nonceIdx = 0;
|
|
256
|
+
const bribeNonce = needBribeTx ? payerNonces[nonceIdx++] : undefined;
|
|
208
257
|
for (let i = 0; i < wallets.length; i++) {
|
|
209
258
|
if (i === maxFundsIndex) {
|
|
210
|
-
nonces.push(payerNonces[
|
|
259
|
+
nonces.push(payerNonces[nonceIdx++]); // 买入交易
|
|
211
260
|
}
|
|
212
261
|
else {
|
|
213
262
|
nonces.push(otherNonces[otherIdx++]);
|
|
214
263
|
}
|
|
215
264
|
}
|
|
216
|
-
|
|
265
|
+
const profitNonce = needProfitTx ? payerNonces[nonceIdx] : undefined;
|
|
266
|
+
return { nonces, bribeNonce, profitNonce };
|
|
217
267
|
}
|
|
218
268
|
else {
|
|
219
269
|
// 所有钱包各 1 个 nonce
|
|
220
270
|
const nonces = await nonceManager.getNextNoncesForWallets(wallets);
|
|
221
|
-
return { nonces, profitNonce: undefined };
|
|
271
|
+
return { nonces, bribeNonce: undefined, profitNonce: undefined };
|
|
222
272
|
}
|
|
223
273
|
})()
|
|
224
274
|
]);
|
|
225
|
-
const { nonces, profitNonce } = noncesResult;
|
|
275
|
+
const { nonces, bribeNonce, profitNonce } = noncesResult;
|
|
276
|
+
// ✅ 贿赂交易放在首位(提高 BlockRazor 打包优先级)
|
|
277
|
+
const signedTxs = [];
|
|
278
|
+
if (needBribeTx && bribeNonce !== undefined) {
|
|
279
|
+
const bribeTx = await wallets[maxFundsIndex].signTransaction({
|
|
280
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
281
|
+
value: bribeAmount,
|
|
282
|
+
nonce: bribeNonce,
|
|
283
|
+
gasPrice,
|
|
284
|
+
gasLimit: 21000n,
|
|
285
|
+
chainId: CHAIN_ID,
|
|
286
|
+
type: txType
|
|
287
|
+
});
|
|
288
|
+
signedTxs.push(bribeTx);
|
|
289
|
+
}
|
|
226
290
|
// ✅ 并行签名所有买入交易
|
|
227
291
|
const signedList = await Promise.all(unsignedList.map((unsigned, i) => wallets[i].signTransaction({
|
|
228
292
|
...unsigned,
|
|
@@ -234,8 +298,8 @@ export async function fourBatchPrivateBuyMerkle(params) {
|
|
|
234
298
|
type: txType,
|
|
235
299
|
value: actualAmountsWei[i]
|
|
236
300
|
})));
|
|
237
|
-
|
|
238
|
-
// ✅
|
|
301
|
+
signedTxs.push(...signedList);
|
|
302
|
+
// ✅ 利润交易放在末尾
|
|
239
303
|
if (needProfitTx && profitNonce !== undefined) {
|
|
240
304
|
const profitTx = await wallets[maxFundsIndex].signTransaction({
|
|
241
305
|
to: getProfitRecipient(),
|
|
@@ -275,10 +339,10 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
275
339
|
const wallets = privateKeys.map((k) => new Wallet(k, provider));
|
|
276
340
|
const amountsWei = amounts.map((a) => ethers.parseUnits(a, 18));
|
|
277
341
|
const rpcUrl = config.rpcUrl;
|
|
278
|
-
// ✅
|
|
279
|
-
const [gasPrice, quotedOutputs
|
|
342
|
+
// ✅ 优化:并行获取 gasPrice 和 quotedOutputs(已移除授权和余额检查)
|
|
343
|
+
const [gasPrice, quotedOutputs] = await Promise.all([
|
|
280
344
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
281
|
-
//
|
|
345
|
+
// 获取预期收益(用于计算利润)
|
|
282
346
|
minFundsEach !== undefined
|
|
283
347
|
? Promise.resolve((() => {
|
|
284
348
|
const minOutWei = typeof minFundsEach === 'string' ? ethers.parseEther(minFundsEach) : minFundsEach;
|
|
@@ -292,41 +356,10 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
292
356
|
catch {
|
|
293
357
|
return 0n;
|
|
294
358
|
}
|
|
295
|
-
}))
|
|
296
|
-
// 检查代币余额
|
|
297
|
-
(async () => {
|
|
298
|
-
const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
|
|
299
|
-
return Promise.all(wallets.map(w => tokenContract.balanceOf(w.address)));
|
|
300
|
-
})(),
|
|
301
|
-
// 检查授权状态
|
|
302
|
-
batchCheckAllowances(provider, tokenAddress, wallets.map(w => w.address), tmAddr),
|
|
303
|
-
// 检查 BNB 余额
|
|
304
|
-
Promise.all(wallets.map(w => provider.getBalance(w.address)))
|
|
359
|
+
}))
|
|
305
360
|
]);
|
|
306
|
-
//
|
|
307
|
-
const minOuts =
|
|
308
|
-
? new Array(wallets.length).fill(typeof minFundsEach === 'string' ? ethers.parseEther(minFundsEach) : minFundsEach)
|
|
309
|
-
: quotedOutputs.map(() => 0n);
|
|
310
|
-
// ✅ 验证代币余额
|
|
311
|
-
for (let i = 0; i < wallets.length; i++) {
|
|
312
|
-
if (balances[i] < amountsWei[i]) {
|
|
313
|
-
throw new Error(`钱包 ${i} 代币余额不足:需要 ${ethers.formatUnits(amountsWei[i], 18)},实际 ${ethers.formatUnits(balances[i], 18)}`);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
// ✅ 验证授权状态
|
|
317
|
-
const needApprovalIndexes = wallets
|
|
318
|
-
.map((_, i) => i)
|
|
319
|
-
.filter(i => allowances[i] < amountsWei[i]);
|
|
320
|
-
if (needApprovalIndexes.length > 0) {
|
|
321
|
-
throw new Error(`${needApprovalIndexes.length} 个钱包需要授权。请先调用 approveFourTokenManagerBatch({ amounts: ['max', ...] })`);
|
|
322
|
-
}
|
|
323
|
-
// ✅ 验证 BNB 余额
|
|
324
|
-
const estimatedGasCost = sellGasLimit * gasPrice;
|
|
325
|
-
for (let i = 0; i < wallets.length; i++) {
|
|
326
|
-
if (bnbBalances[i] < estimatedGasCost) {
|
|
327
|
-
throw new Error(`钱包 ${i} BNB 不足:需要 ${ethers.formatEther(estimatedGasCost)} BNB,实际 ${ethers.formatEther(bnbBalances[i])} BNB`);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
361
|
+
// ✅ 已移除滑点保护:minOuts 固定为 0
|
|
362
|
+
const minOuts = new Array(wallets.length).fill(0n);
|
|
330
363
|
// ✅ 计算总利润和找出收益最高的钱包(同步计算)
|
|
331
364
|
const extractProfit = shouldExtractProfit(config);
|
|
332
365
|
let totalProfit = 0n;
|
|
@@ -344,16 +377,21 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
344
377
|
}
|
|
345
378
|
}
|
|
346
379
|
}
|
|
380
|
+
// ✅ 获取贿赂金额
|
|
381
|
+
const bribeAmount = getBribeAmount(config);
|
|
382
|
+
const needBribeTx = bribeAmount > 0n;
|
|
347
383
|
const tm2Contracts = wallets.map((w) => new ethers.Contract(tmAddr, TM2_ABI, w));
|
|
348
384
|
const nonceManager = new NonceManager(provider);
|
|
349
385
|
const needProfitTx = extractProfit && totalProfit > 0n;
|
|
386
|
+
// ✅ 计算 payer 需要的 nonce 数量:贿赂(可选) + 卖出 + 利润(可选)
|
|
387
|
+
const payerNonceCount = 1 + (needBribeTx ? 1 : 0) + (needProfitTx ? 1 : 0);
|
|
350
388
|
// ✅ 优化:第二批并行获取 - nonces 和构建未签名交易
|
|
351
389
|
const [sellUnsigned, noncesResult] = await Promise.all([
|
|
352
390
|
Promise.all(tm2Contracts.map((c, i) => c.sellToken.populateTransaction(0n, tokenAddress, amountsWei[i], minOuts[i]))),
|
|
353
391
|
(async () => {
|
|
354
|
-
if (
|
|
355
|
-
//
|
|
356
|
-
const payerNonces = await nonceManager.getNextNonceBatch(wallets[maxRevenueIndex],
|
|
392
|
+
if (payerNonceCount > 1) {
|
|
393
|
+
// payer 需要多个连续 nonce
|
|
394
|
+
const payerNonces = await nonceManager.getNextNonceBatch(wallets[maxRevenueIndex], payerNonceCount);
|
|
357
395
|
// 其他钱包各需要 1 个 nonce
|
|
358
396
|
const otherWallets = wallets.filter((_, i) => i !== maxRevenueIndex);
|
|
359
397
|
const otherNonces = otherWallets.length > 0
|
|
@@ -362,24 +400,41 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
362
400
|
// 组装最终的 nonces 数组(保持原顺序)
|
|
363
401
|
const nonces = [];
|
|
364
402
|
let otherIdx = 0;
|
|
403
|
+
let nonceIdx = 0;
|
|
404
|
+
const bribeNonce = needBribeTx ? payerNonces[nonceIdx++] : undefined;
|
|
365
405
|
for (let i = 0; i < wallets.length; i++) {
|
|
366
406
|
if (i === maxRevenueIndex) {
|
|
367
|
-
nonces.push(payerNonces[
|
|
407
|
+
nonces.push(payerNonces[nonceIdx++]); // 卖出交易
|
|
368
408
|
}
|
|
369
409
|
else {
|
|
370
410
|
nonces.push(otherNonces[otherIdx++]);
|
|
371
411
|
}
|
|
372
412
|
}
|
|
373
|
-
|
|
413
|
+
const profitNonce = needProfitTx ? payerNonces[nonceIdx] : undefined;
|
|
414
|
+
return { nonces, bribeNonce, profitNonce };
|
|
374
415
|
}
|
|
375
416
|
else {
|
|
376
417
|
// 所有钱包各 1 个 nonce
|
|
377
418
|
const nonces = await nonceManager.getNextNoncesForWallets(wallets);
|
|
378
|
-
return { nonces, profitNonce: undefined };
|
|
419
|
+
return { nonces, bribeNonce: undefined, profitNonce: undefined };
|
|
379
420
|
}
|
|
380
421
|
})()
|
|
381
422
|
]);
|
|
382
|
-
const { nonces, profitNonce } = noncesResult;
|
|
423
|
+
const { nonces, bribeNonce, profitNonce } = noncesResult;
|
|
424
|
+
// ✅ 贿赂交易放在首位(提高 BlockRazor 打包优先级)
|
|
425
|
+
const signedTxs = [];
|
|
426
|
+
if (needBribeTx && bribeNonce !== undefined) {
|
|
427
|
+
const bribeTx = await wallets[maxRevenueIndex].signTransaction({
|
|
428
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
429
|
+
value: bribeAmount,
|
|
430
|
+
nonce: bribeNonce,
|
|
431
|
+
gasPrice,
|
|
432
|
+
gasLimit: 21000n,
|
|
433
|
+
chainId: CHAIN_ID,
|
|
434
|
+
type: txType
|
|
435
|
+
});
|
|
436
|
+
signedTxs.push(bribeTx);
|
|
437
|
+
}
|
|
383
438
|
// ✅ 并行签名所有卖出交易
|
|
384
439
|
const signedSells = await Promise.all(sellUnsigned.map((unsigned, i) => wallets[i].signTransaction({
|
|
385
440
|
...unsigned,
|
|
@@ -391,8 +446,8 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
391
446
|
type: txType,
|
|
392
447
|
value: 0n
|
|
393
448
|
})));
|
|
394
|
-
|
|
395
|
-
// ✅
|
|
449
|
+
signedTxs.push(...signedSells);
|
|
450
|
+
// ✅ 利润交易放在末尾
|
|
396
451
|
if (needProfitTx && profitNonce !== undefined) {
|
|
397
452
|
const profitTx = await wallets[maxRevenueIndex].signTransaction({
|
|
398
453
|
to: getProfitRecipient(),
|
|
@@ -2,7 +2,6 @@ import { ethers, Wallet, JsonRpcProvider, Contract, Interface } from 'ethers';
|
|
|
2
2
|
import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
|
|
3
3
|
import { ADDRESSES } from '../../utils/constants.js';
|
|
4
4
|
import { CHAIN_ID_MAP, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit, getProfitRecipient, getBribeAmount, BLOCKRAZOR_BUILDER_EOA } from './config.js';
|
|
5
|
-
import { batchCheckAllowances } from '../../utils/erc20.js';
|
|
6
5
|
const MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
|
|
7
6
|
const MULTICALL3_ABI = [
|
|
8
7
|
'function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) external payable returns (tuple(bool success, bytes returnData)[])'
|
|
@@ -322,58 +321,61 @@ export async function pancakeProxyBatchBuyMerkle(params) {
|
|
|
322
321
|
minOuts,
|
|
323
322
|
needBNB
|
|
324
323
|
});
|
|
325
|
-
// ✅
|
|
324
|
+
// ✅ 并行签名所有交易(贿赂、买入、利润)
|
|
326
325
|
const bribeAmount = getBribeAmount(config);
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
326
|
+
const needBribeTx = bribeAmount > 0n && maxFundsIndex >= 0 && buyers.length > 0;
|
|
327
|
+
const txType = getTxType(config);
|
|
328
|
+
// 调整 maxFundsIndex 钱包的 nonce(如果有贿赂交易)
|
|
329
|
+
let bribeNonce;
|
|
330
|
+
if (needBribeTx) {
|
|
331
|
+
bribeNonce = nonces[maxFundsIndex];
|
|
332
|
+
nonces[maxFundsIndex] = bribeNonce + 1;
|
|
333
|
+
}
|
|
334
|
+
const signPromises = [];
|
|
335
|
+
// 贿赂交易(索引 0)
|
|
336
|
+
if (needBribeTx && bribeNonce !== undefined) {
|
|
337
|
+
signPromises.push(buyers[maxFundsIndex].signTransaction({
|
|
331
338
|
to: BLOCKRAZOR_BUILDER_EOA,
|
|
332
339
|
value: bribeAmount,
|
|
333
340
|
nonce: bribeNonce,
|
|
334
341
|
gasPrice,
|
|
335
342
|
gasLimit: 21000n,
|
|
336
343
|
chainId,
|
|
337
|
-
type:
|
|
338
|
-
});
|
|
339
|
-
bribeTxs.push(bribeTx);
|
|
340
|
-
// 调整 maxFundsIndex 钱包的 nonce(买入交易 +1)
|
|
341
|
-
nonces[maxFundsIndex] = bribeNonce + 1;
|
|
344
|
+
type: txType
|
|
345
|
+
}));
|
|
342
346
|
}
|
|
343
|
-
//
|
|
344
|
-
|
|
345
|
-
// ✅ ERC20 购买时 value 只需要 FLAT_FEE,不需要发送代币金额
|
|
347
|
+
// 买入交易(索引 1 ~ N)
|
|
348
|
+
unsignedBuys.forEach((unsigned, i) => {
|
|
346
349
|
const txValue = useNativeToken ? unsigned.value : FLAT_FEE;
|
|
347
|
-
|
|
350
|
+
signPromises.push(buyers[i].signTransaction({
|
|
348
351
|
...unsigned,
|
|
349
352
|
from: buyers[i].address,
|
|
350
353
|
nonce: nonces[i],
|
|
351
354
|
gasLimit: finalGasLimit,
|
|
352
355
|
gasPrice,
|
|
353
356
|
chainId,
|
|
354
|
-
type:
|
|
357
|
+
type: txType,
|
|
355
358
|
value: txValue
|
|
356
|
-
});
|
|
357
|
-
})
|
|
358
|
-
//
|
|
359
|
-
const profitTxs = [];
|
|
359
|
+
}));
|
|
360
|
+
});
|
|
361
|
+
// 利润交易(索引 N+1)
|
|
360
362
|
if (shouldExtractProfitForBuy && nativeProfitAmount > 0n && maxFundsIndex >= 0) {
|
|
361
363
|
const profitNonce = nonces[maxFundsIndex] + 1;
|
|
362
|
-
|
|
364
|
+
signPromises.push(buyers[maxFundsIndex].signTransaction({
|
|
363
365
|
to: getProfitRecipient(),
|
|
364
366
|
value: nativeProfitAmount,
|
|
365
367
|
nonce: profitNonce,
|
|
366
368
|
gasPrice,
|
|
367
369
|
gasLimit: 21000n,
|
|
368
370
|
chainId,
|
|
369
|
-
type:
|
|
370
|
-
});
|
|
371
|
-
profitTxs.push(profitTx);
|
|
371
|
+
type: txType
|
|
372
|
+
}));
|
|
372
373
|
}
|
|
374
|
+
// ✅ 并行签名完成后按顺序返回
|
|
375
|
+
const signedTxs = await Promise.all(signPromises);
|
|
373
376
|
nonceManager.clearTemp();
|
|
374
|
-
// ✅ 组装顺序:贿赂 → 买入 → 利润
|
|
375
377
|
return {
|
|
376
|
-
signedTransactions:
|
|
378
|
+
signedTransactions: signedTxs
|
|
377
379
|
};
|
|
378
380
|
}
|
|
379
381
|
/**
|
|
@@ -397,21 +399,14 @@ export async function pancakeProxyBatchSellMerkle(params) {
|
|
|
397
399
|
// ✅ 优化:如果前端传入了 gasPrice 和 nonces,跳过 RPC 调用
|
|
398
400
|
const presetGasPrice = config.gasPrice;
|
|
399
401
|
const presetNonces = config.nonces;
|
|
400
|
-
// ✅
|
|
402
|
+
// ✅ 优化:并行获取 gasPrice 和 tokenDecimals(已移除授权检查)
|
|
401
403
|
const [gasPrice, tokenDecimals] = await Promise.all([
|
|
402
404
|
// gasPrice:优先使用前端传入的
|
|
403
405
|
presetGasPrice !== undefined
|
|
404
406
|
? Promise.resolve(presetGasPrice)
|
|
405
407
|
: getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
406
408
|
// tokenDecimals:有缓存
|
|
407
|
-
getTokenDecimals(tokenAddress, provider)
|
|
408
|
-
// allowances:必须检查
|
|
409
|
-
ensureAllowances({
|
|
410
|
-
provider,
|
|
411
|
-
tokenAddress,
|
|
412
|
-
owners: sellers.map(w => w.address),
|
|
413
|
-
spender: ADDRESSES.BSC.PancakeProxy
|
|
414
|
-
})
|
|
409
|
+
getTokenDecimals(tokenAddress, provider)
|
|
415
410
|
]);
|
|
416
411
|
const amountsWei = sellAmounts.map(amount => ethers.parseUnits(amount, tokenDecimals));
|
|
417
412
|
const proxies = createPancakeProxies(sellers, ADDRESSES.BSC.PancakeProxy);
|
|
@@ -489,20 +484,7 @@ export async function pancakeProxyBatchSellMerkle(params) {
|
|
|
489
484
|
// 不需要额外交易,所有钱包各 1 个 nonce
|
|
490
485
|
nonces = await nonceManager.getNextNoncesForWallets(sellers);
|
|
491
486
|
}
|
|
492
|
-
// ✅
|
|
493
|
-
const bribeTxs = [];
|
|
494
|
-
if (needBribeTx && bribeNonce !== undefined) {
|
|
495
|
-
const bribeTx = await sellers[maxRevenueIndex].signTransaction({
|
|
496
|
-
to: BLOCKRAZOR_BUILDER_EOA,
|
|
497
|
-
value: bribeAmount,
|
|
498
|
-
nonce: bribeNonce,
|
|
499
|
-
gasPrice,
|
|
500
|
-
gasLimit: 21000n,
|
|
501
|
-
chainId,
|
|
502
|
-
type: getTxType(config)
|
|
503
|
-
});
|
|
504
|
-
bribeTxs.push(bribeTx);
|
|
505
|
-
}
|
|
487
|
+
// ✅ 并行构建未签名交易和签名
|
|
506
488
|
const unsignedSells = await buildSellTransactions({
|
|
507
489
|
routeType,
|
|
508
490
|
params,
|
|
@@ -512,38 +494,51 @@ export async function pancakeProxyBatchSellMerkle(params) {
|
|
|
512
494
|
amountsWei,
|
|
513
495
|
minOuts
|
|
514
496
|
});
|
|
515
|
-
|
|
516
|
-
const
|
|
497
|
+
const txType = getTxType(config);
|
|
498
|
+
const signPromises = [];
|
|
499
|
+
// 贿赂交易(索引 0)
|
|
500
|
+
if (needBribeTx && bribeNonce !== undefined) {
|
|
501
|
+
signPromises.push(sellers[maxRevenueIndex].signTransaction({
|
|
502
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
503
|
+
value: bribeAmount,
|
|
504
|
+
nonce: bribeNonce,
|
|
505
|
+
gasPrice,
|
|
506
|
+
gasLimit: 21000n,
|
|
507
|
+
chainId,
|
|
508
|
+
type: txType
|
|
509
|
+
}));
|
|
510
|
+
}
|
|
511
|
+
// 卖出交易(索引 1 ~ N)
|
|
512
|
+
unsignedSells.forEach((unsigned, i) => {
|
|
517
513
|
const txValue = unsigned.value;
|
|
518
|
-
|
|
514
|
+
signPromises.push(sellers[i].signTransaction({
|
|
519
515
|
...unsigned,
|
|
520
516
|
from: sellers[i].address,
|
|
521
517
|
nonce: nonces[i],
|
|
522
518
|
gasLimit: finalGasLimit,
|
|
523
519
|
gasPrice,
|
|
524
520
|
chainId,
|
|
525
|
-
type:
|
|
521
|
+
type: txType,
|
|
526
522
|
value: txValue
|
|
527
|
-
});
|
|
528
|
-
})
|
|
529
|
-
//
|
|
530
|
-
const profitTxs = [];
|
|
523
|
+
}));
|
|
524
|
+
});
|
|
525
|
+
// 利润交易(索引 N+1)
|
|
531
526
|
if (needProfitTx && profitNonce !== undefined) {
|
|
532
|
-
|
|
527
|
+
signPromises.push(sellers[maxRevenueIndex].signTransaction({
|
|
533
528
|
to: getProfitRecipient(),
|
|
534
529
|
value: totalProfit,
|
|
535
530
|
nonce: profitNonce,
|
|
536
531
|
gasPrice,
|
|
537
532
|
gasLimit: 21000n,
|
|
538
533
|
chainId,
|
|
539
|
-
type:
|
|
540
|
-
});
|
|
541
|
-
profitTxs.push(profitTx);
|
|
534
|
+
type: txType
|
|
535
|
+
}));
|
|
542
536
|
}
|
|
537
|
+
// ✅ 并行签名完成后按顺序返回
|
|
538
|
+
const signedTxs = await Promise.all(signPromises);
|
|
543
539
|
nonceManager.clearTemp();
|
|
544
|
-
// ✅ 组装顺序:贿赂 → 卖出 → 利润
|
|
545
540
|
return {
|
|
546
|
-
signedTransactions:
|
|
541
|
+
signedTransactions: signedTxs
|
|
547
542
|
};
|
|
548
543
|
}
|
|
549
544
|
// ✅ Provider 缓存(复用连接,减少初始化开销)
|
|
@@ -639,14 +634,6 @@ async function allocateProfitAwareNonces(wallets, extractProfit, maxIndex, total
|
|
|
639
634
|
}
|
|
640
635
|
return nonces;
|
|
641
636
|
}
|
|
642
|
-
async function ensureAllowances({ provider, tokenAddress, owners, spender }) {
|
|
643
|
-
const allowances = await batchCheckAllowances(provider, tokenAddress, owners, spender);
|
|
644
|
-
const APPROVAL_THRESHOLD = ethers.MaxUint256 / 2n;
|
|
645
|
-
const needApproval = allowances.filter(a => a < APPROVAL_THRESHOLD).length;
|
|
646
|
-
if (needApproval > 0) {
|
|
647
|
-
throw new Error(`需要授权: ${needApproval} 个钱包尚未授权。请先完成授权后再卖出。`);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
637
|
/**
|
|
651
638
|
* ✅ 获取卖出报价(用于计算利润,不用于滑点保护)
|
|
652
639
|
* ✅ 已移除滑点保护:minOutput 固定为 0
|
|
@@ -2,7 +2,7 @@ import { ethers, Wallet } from 'ethers';
|
|
|
2
2
|
import { MerkleClient } from '../../clients/merkle.js';
|
|
3
3
|
import { getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
|
|
4
4
|
import { FLAP_PORTAL_ADDRESSES } from '../constants.js';
|
|
5
|
-
import { CHAIN_ID_MAP, PORTAL_ABI, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit, getProfitRecipient } from './config.js';
|
|
5
|
+
import { CHAIN_ID_MAP, PORTAL_ABI, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit, getProfitRecipient, getBribeAmount, BLOCKRAZOR_BUILDER_EOA } from './config.js';
|
|
6
6
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
7
7
|
const DEFAULT_GAS_LIMIT = 800000;
|
|
8
8
|
/**
|
|
@@ -32,27 +32,55 @@ export async function flapPrivateBuyMerkle(params) {
|
|
|
32
32
|
}, { value: actualAmountWei })
|
|
33
33
|
]);
|
|
34
34
|
const gasLimit = resolveGasLimit(config.gasLimitMultiplier);
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// ✅ 获取贿赂金额
|
|
36
|
+
const bribeAmount = getBribeAmount(config);
|
|
37
|
+
const needBribeTx = bribeAmount > 0n;
|
|
38
|
+
const needProfitTx = extractProfit && profitWei > 0n;
|
|
39
|
+
// ✅ 分配 nonces:贿赂 → 买入 → 利润
|
|
40
|
+
let nonceIdx = 0;
|
|
41
|
+
const bribeNonce = needBribeTx ? currentNonce + nonceIdx++ : undefined;
|
|
42
|
+
const buyNonce = currentNonce + nonceIdx++;
|
|
43
|
+
const profitNonce = needProfitTx ? currentNonce + nonceIdx : undefined;
|
|
44
|
+
// ✅ 并行签名所有交易(贿赂、买入、利润)
|
|
45
|
+
const signPromises = [];
|
|
46
|
+
const txType = getTxType(config);
|
|
47
|
+
// 贿赂交易
|
|
48
|
+
if (needBribeTx && bribeNonce !== undefined) {
|
|
49
|
+
signPromises.push(wallet.signTransaction({
|
|
50
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
51
|
+
value: bribeAmount,
|
|
52
|
+
nonce: bribeNonce,
|
|
53
|
+
gasPrice,
|
|
54
|
+
gasLimit: 21000n,
|
|
55
|
+
chainId,
|
|
56
|
+
type: txType
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
// 买入交易
|
|
60
|
+
signPromises.push(wallet.signTransaction({
|
|
37
61
|
...unsigned,
|
|
38
62
|
from: wallet.address,
|
|
39
|
-
nonce:
|
|
63
|
+
nonce: buyNonce,
|
|
40
64
|
gasLimit,
|
|
41
65
|
gasPrice,
|
|
42
66
|
chainId,
|
|
43
|
-
type:
|
|
67
|
+
type: txType,
|
|
44
68
|
value: actualAmountWei
|
|
45
69
|
}));
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
70
|
+
// 利润交易
|
|
71
|
+
if (needProfitTx && profitNonce !== undefined) {
|
|
72
|
+
signPromises.push(wallet.signTransaction({
|
|
73
|
+
to: getProfitRecipient(),
|
|
74
|
+
value: profitWei,
|
|
75
|
+
nonce: profitNonce,
|
|
76
|
+
gasPrice,
|
|
77
|
+
gasLimit: 21000n,
|
|
78
|
+
chainId,
|
|
79
|
+
type: txType
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
// ✅ 并行签名完成后按顺序返回
|
|
83
|
+
const signedTxs = await Promise.all(signPromises);
|
|
56
84
|
return { signedTransactions: signedTxs };
|
|
57
85
|
}
|
|
58
86
|
/**
|
|
@@ -65,41 +93,73 @@ export async function flapPrivateSellMerkle(params) {
|
|
|
65
93
|
const portalAddr = FLAP_PORTAL_ADDRESSES[chain];
|
|
66
94
|
const amountWei = ethers.parseUnits(amount, 18);
|
|
67
95
|
const portal = new ethers.Contract(portalAddr, PORTAL_ABI, wallet);
|
|
68
|
-
// ✅ 并行获取 gasPrice、nonce
|
|
69
|
-
const [gasPrice, currentNonce, { quotedOutput,
|
|
96
|
+
// ✅ 并行获取 gasPrice、nonce、报价和构建未签名交易
|
|
97
|
+
const [gasPrice, currentNonce, { quotedOutput }, unsigned] = await Promise.all([
|
|
70
98
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
71
99
|
wallet.getNonce(),
|
|
72
|
-
resolveSingleSellOutputs(portal, tokenAddress, amountWei, minOutputAmount)
|
|
100
|
+
resolveSingleSellOutputs(portal, tokenAddress, amountWei, minOutputAmount),
|
|
101
|
+
portal.swapExactInput.populateTransaction({
|
|
102
|
+
inputToken: tokenAddress,
|
|
103
|
+
outputToken: ZERO_ADDRESS,
|
|
104
|
+
inputAmount: amountWei,
|
|
105
|
+
minOutputAmount: 0n, // ✅ 已移除滑点保护
|
|
106
|
+
permitData: '0x',
|
|
107
|
+
})
|
|
73
108
|
]);
|
|
74
|
-
// 构建未签名交易
|
|
75
|
-
const unsigned = await portal.swapExactInput.populateTransaction({
|
|
76
|
-
inputToken: tokenAddress,
|
|
77
|
-
outputToken: ZERO_ADDRESS,
|
|
78
|
-
inputAmount: amountWei,
|
|
79
|
-
minOutputAmount: minOut,
|
|
80
|
-
permitData: '0x',
|
|
81
|
-
});
|
|
82
109
|
const gasLimit = resolveGasLimit(config.gasLimitMultiplier);
|
|
83
|
-
const
|
|
84
|
-
|
|
110
|
+
const txType = getTxType(config);
|
|
111
|
+
// ✅ 获取贿赂金额
|
|
112
|
+
const bribeAmount = getBribeAmount(config);
|
|
113
|
+
const needBribeTx = bribeAmount > 0n;
|
|
114
|
+
// ✅ 利润计算
|
|
115
|
+
const extractProfit = shouldExtractProfit(config);
|
|
116
|
+
const { profit: profitWei } = extractProfit && quotedOutput > 0n
|
|
117
|
+
? calculateProfit(quotedOutput, config)
|
|
118
|
+
: { profit: 0n };
|
|
119
|
+
const needProfitTx = extractProfit && profitWei > 0n;
|
|
120
|
+
// ✅ 分配 nonces:贿赂 → 卖出 → 利润
|
|
121
|
+
let nonceIdx = 0;
|
|
122
|
+
const bribeNonce = needBribeTx ? currentNonce + nonceIdx++ : undefined;
|
|
123
|
+
const sellNonce = currentNonce + nonceIdx++;
|
|
124
|
+
const profitNonce = needProfitTx ? currentNonce + nonceIdx : undefined;
|
|
125
|
+
// ✅ 并行签名所有交易(贿赂、卖出、利润)
|
|
126
|
+
const signPromises = [];
|
|
127
|
+
// 贿赂交易
|
|
128
|
+
if (needBribeTx && bribeNonce !== undefined) {
|
|
129
|
+
signPromises.push(wallet.signTransaction({
|
|
130
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
131
|
+
value: bribeAmount,
|
|
132
|
+
nonce: bribeNonce,
|
|
133
|
+
gasPrice,
|
|
134
|
+
gasLimit: 21000n,
|
|
135
|
+
chainId,
|
|
136
|
+
type: txType
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
// 卖出交易
|
|
140
|
+
signPromises.push(wallet.signTransaction({
|
|
85
141
|
...unsigned,
|
|
86
142
|
from: wallet.address,
|
|
87
|
-
nonce:
|
|
143
|
+
nonce: sellNonce,
|
|
88
144
|
gasLimit,
|
|
89
145
|
gasPrice,
|
|
90
146
|
chainId,
|
|
91
|
-
type:
|
|
147
|
+
type: txType
|
|
92
148
|
}));
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
149
|
+
// 利润交易
|
|
150
|
+
if (needProfitTx && profitNonce !== undefined) {
|
|
151
|
+
signPromises.push(wallet.signTransaction({
|
|
152
|
+
to: getProfitRecipient(),
|
|
153
|
+
value: profitWei,
|
|
154
|
+
nonce: profitNonce,
|
|
155
|
+
gasPrice,
|
|
156
|
+
gasLimit: 21000n,
|
|
157
|
+
chainId,
|
|
158
|
+
type: txType
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
// ✅ 并行签名完成后按顺序返回
|
|
162
|
+
const signedTxs = await Promise.all(signPromises);
|
|
103
163
|
return { signedTransactions: signedTxs };
|
|
104
164
|
}
|
|
105
165
|
/**
|
|
@@ -121,6 +181,10 @@ export async function flapBatchPrivateBuyMerkle(params) {
|
|
|
121
181
|
const maxFundsIndex = findMaxIndex(originalAmountsWei);
|
|
122
182
|
const minOuts = resolveBatchMinOutputs(minOutputAmounts, wallets.length);
|
|
123
183
|
const portals = wallets.map(w => new ethers.Contract(portalAddr, PORTAL_ABI, w));
|
|
184
|
+
// ✅ 获取贿赂金额
|
|
185
|
+
const bribeAmount = getBribeAmount(config);
|
|
186
|
+
const needBribeTx = bribeAmount > 0n;
|
|
187
|
+
const needProfitTx = extractProfit && totalProfit > 0n;
|
|
124
188
|
// ✅ 并行获取 gasPrice、所有 nonces 和构建未签名交易
|
|
125
189
|
const [gasPrice, initialNonces, unsignedList] = await Promise.all([
|
|
126
190
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
@@ -133,14 +197,39 @@ export async function flapBatchPrivateBuyMerkle(params) {
|
|
|
133
197
|
permitData: '0x',
|
|
134
198
|
}, { value: actualAmountsWei[i] })))
|
|
135
199
|
]);
|
|
136
|
-
// ✅
|
|
200
|
+
// ✅ 分配 nonces:贿赂(可选) → 买入 → 利润(可选)
|
|
201
|
+
let payerNonceOffset = 0;
|
|
202
|
+
let bribeNonce;
|
|
203
|
+
if (needBribeTx && maxFundsIndex >= 0) {
|
|
204
|
+
bribeNonce = initialNonces[maxFundsIndex];
|
|
205
|
+
payerNonceOffset++;
|
|
206
|
+
}
|
|
137
207
|
const nonces = initialNonces.map((n, i) => {
|
|
138
|
-
|
|
208
|
+
if (i === maxFundsIndex) {
|
|
209
|
+
return n + payerNonceOffset; // payer 的买入 nonce 需要偏移
|
|
210
|
+
}
|
|
139
211
|
return n;
|
|
140
212
|
});
|
|
213
|
+
const profitNonce = needProfitTx && maxFundsIndex >= 0
|
|
214
|
+
? initialNonces[maxFundsIndex] + payerNonceOffset + 1
|
|
215
|
+
: undefined;
|
|
141
216
|
const gasLimit = resolveGasLimit(config.gasLimitMultiplier);
|
|
142
217
|
const gasLimits = new Array(wallets.length).fill(gasLimit);
|
|
143
|
-
|
|
218
|
+
const signedTxs = [];
|
|
219
|
+
// ✅ 贿赂交易放在首位
|
|
220
|
+
if (needBribeTx && bribeNonce !== undefined && maxFundsIndex >= 0) {
|
|
221
|
+
const bribeTx = await wallets[maxFundsIndex].signTransaction({
|
|
222
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
223
|
+
value: bribeAmount,
|
|
224
|
+
nonce: bribeNonce,
|
|
225
|
+
gasPrice,
|
|
226
|
+
gasLimit: 21000n,
|
|
227
|
+
chainId,
|
|
228
|
+
type: getTxType(config)
|
|
229
|
+
});
|
|
230
|
+
signedTxs.push(bribeTx);
|
|
231
|
+
}
|
|
232
|
+
// ✅ 并行签名所有买入交易
|
|
144
233
|
const signedList = await signBatchTransactions({
|
|
145
234
|
unsignedList,
|
|
146
235
|
wallets,
|
|
@@ -151,10 +240,9 @@ export async function flapBatchPrivateBuyMerkle(params) {
|
|
|
151
240
|
config,
|
|
152
241
|
values: actualAmountsWei
|
|
153
242
|
});
|
|
154
|
-
|
|
155
|
-
//
|
|
156
|
-
if (
|
|
157
|
-
const profitNonce = initialNonces[maxFundsIndex] + 1;
|
|
243
|
+
signedTxs.push(...signedList);
|
|
244
|
+
// ✅ 利润交易放在末尾
|
|
245
|
+
if (needProfitTx && profitNonce !== undefined && maxFundsIndex >= 0) {
|
|
158
246
|
const profitTx = await wallets[maxFundsIndex].signTransaction({
|
|
159
247
|
to: getProfitRecipient(),
|
|
160
248
|
value: totalProfit,
|
|
@@ -182,50 +270,75 @@ export async function flapBatchPrivateSellMerkle(params) {
|
|
|
182
270
|
const amountsWei = amounts.map(a => ethers.parseUnits(a, 18));
|
|
183
271
|
const portal = new ethers.Contract(portalAddr, PORTAL_ABI, provider);
|
|
184
272
|
const portals = wallets.map(w => new ethers.Contract(portalAddr, PORTAL_ABI, w));
|
|
185
|
-
// ✅ 并行获取 gasPrice、所有 nonces
|
|
186
|
-
const [gasPrice, initialNonces, quotedOutputs] = await Promise.all([
|
|
273
|
+
// ✅ 并行获取 gasPrice、所有 nonces、所有报价和构建未签名交易
|
|
274
|
+
const [gasPrice, initialNonces, quotedOutputs, unsignedList] = await Promise.all([
|
|
187
275
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
188
276
|
Promise.all(wallets.map(w => w.getNonce())),
|
|
189
277
|
Promise.all(amountsWei.map(amount => portal.quoteExactInput.staticCall({
|
|
190
278
|
inputToken: tokenAddress,
|
|
191
279
|
outputToken: ZERO_ADDRESS,
|
|
192
280
|
inputAmount: amount
|
|
193
|
-
}).catch(() => 0n)))
|
|
281
|
+
}).catch(() => 0n))),
|
|
282
|
+
// ✅ 并行构建未签名交易(已移除滑点保护:minOuts 固定为 0)
|
|
283
|
+
Promise.all(portals.map((p, i) => p.swapExactInput.populateTransaction({
|
|
284
|
+
inputToken: tokenAddress,
|
|
285
|
+
outputToken: ZERO_ADDRESS,
|
|
286
|
+
inputAmount: amountsWei[i],
|
|
287
|
+
minOutputAmount: 0n,
|
|
288
|
+
permitData: '0x'
|
|
289
|
+
})))
|
|
194
290
|
]);
|
|
195
|
-
// 计算 minOuts
|
|
196
|
-
let minOuts;
|
|
197
|
-
if (minOutputAmounts && minOutputAmounts.length === wallets.length) {
|
|
198
|
-
minOuts = minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
minOuts = quotedOutputs.map(quoted => quoted * 95n / 100n);
|
|
202
|
-
}
|
|
203
|
-
// ✅ 并行构建未签名交易
|
|
204
|
-
const unsignedList = await Promise.all(portals.map((p, i) => p.swapExactInput.populateTransaction({
|
|
205
|
-
inputToken: tokenAddress,
|
|
206
|
-
outputToken: ZERO_ADDRESS,
|
|
207
|
-
inputAmount: amountsWei[i],
|
|
208
|
-
minOutputAmount: minOuts[i],
|
|
209
|
-
permitData: '0x'
|
|
210
|
-
})));
|
|
211
291
|
const extractProfit = shouldExtractProfit(config);
|
|
212
292
|
const { totalProfit, maxRevenueIndex } = summarizeSellProfits(quotedOutputs, extractProfit, config);
|
|
293
|
+
// ✅ 获取贿赂金额
|
|
294
|
+
const bribeAmount = getBribeAmount(config);
|
|
295
|
+
const needBribeTx = bribeAmount > 0n;
|
|
296
|
+
const needProfitTx = extractProfit && totalProfit > 0n;
|
|
297
|
+
// ✅ 分配 nonces:贿赂(可选) → 卖出 → 利润(可选)
|
|
298
|
+
let payerNonceOffset = 0;
|
|
299
|
+
let bribeNonce;
|
|
300
|
+
if (needBribeTx && maxRevenueIndex >= 0) {
|
|
301
|
+
bribeNonce = initialNonces[maxRevenueIndex];
|
|
302
|
+
payerNonceOffset++;
|
|
303
|
+
}
|
|
304
|
+
const nonces = initialNonces.map((n, i) => {
|
|
305
|
+
if (i === maxRevenueIndex) {
|
|
306
|
+
return n + payerNonceOffset; // payer 的卖出 nonce 需要偏移
|
|
307
|
+
}
|
|
308
|
+
return n;
|
|
309
|
+
});
|
|
310
|
+
const profitNonce = needProfitTx && maxRevenueIndex >= 0
|
|
311
|
+
? initialNonces[maxRevenueIndex] + payerNonceOffset + 1
|
|
312
|
+
: undefined;
|
|
213
313
|
const gasLimit = resolveGasLimit(config.gasLimitMultiplier);
|
|
214
314
|
const gasLimits = new Array(wallets.length).fill(gasLimit);
|
|
315
|
+
const signedTxs = [];
|
|
316
|
+
// ✅ 贿赂交易放在首位
|
|
317
|
+
if (needBribeTx && bribeNonce !== undefined && maxRevenueIndex >= 0) {
|
|
318
|
+
const bribeTx = await wallets[maxRevenueIndex].signTransaction({
|
|
319
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
320
|
+
value: bribeAmount,
|
|
321
|
+
nonce: bribeNonce,
|
|
322
|
+
gasPrice,
|
|
323
|
+
gasLimit: 21000n,
|
|
324
|
+
chainId,
|
|
325
|
+
type: getTxType(config)
|
|
326
|
+
});
|
|
327
|
+
signedTxs.push(bribeTx);
|
|
328
|
+
}
|
|
215
329
|
// ✅ 并行签名所有卖出交易
|
|
216
330
|
const signedList = await signBatchTransactions({
|
|
217
331
|
unsignedList,
|
|
218
332
|
wallets,
|
|
219
|
-
nonces
|
|
333
|
+
nonces,
|
|
220
334
|
gasLimits,
|
|
221
335
|
gasPrice,
|
|
222
336
|
chainId,
|
|
223
337
|
config
|
|
224
338
|
});
|
|
225
|
-
|
|
226
|
-
//
|
|
227
|
-
if (
|
|
228
|
-
const profitNonce = initialNonces[maxRevenueIndex] + 1;
|
|
339
|
+
signedTxs.push(...signedList);
|
|
340
|
+
// ✅ 利润交易放在末尾
|
|
341
|
+
if (needProfitTx && profitNonce !== undefined && maxRevenueIndex >= 0) {
|
|
229
342
|
const profitTx = await wallets[maxRevenueIndex].signTransaction({
|
|
230
343
|
to: getProfitRecipient(),
|
|
231
344
|
value: totalProfit,
|
|
@@ -315,7 +428,7 @@ async function resolveSingleSellOutputs(portal, tokenAddress, amountWei, minOutp
|
|
|
315
428
|
});
|
|
316
429
|
return {
|
|
317
430
|
quotedOutput,
|
|
318
|
-
minOut:
|
|
431
|
+
minOut: 0n // ✅ 已移除滑点保护:minOut 固定为 0
|
|
319
432
|
};
|
|
320
433
|
}
|
|
321
434
|
catch {
|