four-flap-meme-sdk 1.4.34 → 1.4.36
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/internal.d.ts +7 -0
- package/dist/contracts/tm-bundle-merkle/internal.js +18 -4
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +1 -1
- package/dist/contracts/tm-bundle-merkle/utils.js +249 -96
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +2 -2
- package/dist/flap/portal-bundle-merkle/utils.js +12 -6
- package/dist/pancake/bundle-buy-first.js +4 -4
- package/package.json +1 -1
|
@@ -12,5 +12,12 @@ export declare function normalizeAmounts(recipients: string[], amount?: AmountLi
|
|
|
12
12
|
* ✅ 优化:原生代币也使用 Multicall3 批量获取,减少 RPC 调用
|
|
13
13
|
*/
|
|
14
14
|
export declare function batchGetBalances(provider: JsonRpcProvider, addresses: string[], tokenAddress?: string): Promise<bigint[]>;
|
|
15
|
+
/**
|
|
16
|
+
* 计算 Gas Limit
|
|
17
|
+
* - 原生代币转账:21000 gas(固定)
|
|
18
|
+
* - ERC20 标准 transfer:约 45000-55000 gas,使用 55000 作为安全值
|
|
19
|
+
*
|
|
20
|
+
* ✅ 优化:降低 ERC20 gas limit,减少中转钱包 BNB 残留
|
|
21
|
+
*/
|
|
15
22
|
export declare function calculateGasLimit(config: any, isNative: boolean, hasHops: boolean, hopCount?: number): bigint;
|
|
16
23
|
export declare function isNativeTokenAddress(tokenAddress?: string): boolean;
|
|
@@ -122,15 +122,29 @@ export async function batchGetBalances(provider, addresses, tokenAddress) {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* 计算 Gas Limit
|
|
127
|
+
* - 原生代币转账:21000 gas(固定)
|
|
128
|
+
* - ERC20 标准 transfer:约 45000-55000 gas,使用 55000 作为安全值
|
|
129
|
+
*
|
|
130
|
+
* ✅ 优化:降低 ERC20 gas limit,减少中转钱包 BNB 残留
|
|
131
|
+
*/
|
|
125
132
|
export function calculateGasLimit(config, isNative, hasHops, hopCount = 0) {
|
|
126
133
|
if (config.gasLimit !== undefined) {
|
|
127
134
|
return BigInt(config.gasLimit);
|
|
128
135
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
136
|
+
// ✅ 原生代币: 21000, ERC20 标准 transfer: 48000(USDT 最低约 46815)
|
|
137
|
+
const baseGas = isNative ? 21000 : 46815;
|
|
138
|
+
// ✅ 多跳时只需要累加单次转账的 gas,不需要额外乘数
|
|
139
|
+
// 每个中转钱包只执行一笔 transfer
|
|
140
|
+
if (hasHops && hopCount > 0) {
|
|
141
|
+
// 多跳场景:返回单次转账的 gas limit(中转钱包只执行一笔交易)
|
|
142
|
+
// 这里不累加,因为每个中转钱包独立执行
|
|
143
|
+
const multiplier = config.gasLimitMultiplier ?? 1.1; // ✅ 降低安全系数
|
|
144
|
+
return BigInt(Math.ceil(baseGas * multiplier));
|
|
132
145
|
}
|
|
133
|
-
|
|
146
|
+
// 无多跳或单次调用:使用较小的安全系数
|
|
147
|
+
const multiplier = config.gasLimitMultiplier ?? 1.1;
|
|
134
148
|
return BigInt(Math.ceil(baseGas * multiplier));
|
|
135
149
|
}
|
|
136
150
|
export function isNativeTokenAddress(tokenAddress) {
|
|
@@ -77,7 +77,7 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
77
77
|
const tmSeller = new Contract(TM_ADDRESS, TM_ABI, seller);
|
|
78
78
|
// 预先规划 nonces
|
|
79
79
|
const extractProfit = true;
|
|
80
|
-
const profitRateBps = PROFIT_CONFIG.
|
|
80
|
+
const profitRateBps = PROFIT_CONFIG.RATE_BPS_SWAP; // 万分之六
|
|
81
81
|
// ✅ 获取贿赂金额
|
|
82
82
|
const bribeAmount = getBribeAmount(config);
|
|
83
83
|
const needBribeTx = bribeAmount > 0n;
|
|
@@ -355,24 +355,31 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
355
355
|
isNative ? Promise.resolve(18) : Promise.resolve(tokenDecimals ?? await _getErc20DecimalsMerkle(provider, tokenAddress, chainIdNum))
|
|
356
356
|
]);
|
|
357
357
|
const iface = isNative ? null : new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
|
|
358
|
-
|
|
358
|
+
// ✅ Gas limit 设置
|
|
359
|
+
// - 原生代币转账:21000(固定)
|
|
360
|
+
// - ERC20 转账:finalGasLimit(约 52800)
|
|
361
|
+
const nativeTransferGasLimit = 21000n;
|
|
362
|
+
const erc20TransferGasLimit = finalGasLimit;
|
|
363
|
+
// ✅ 原生代币多跳的 gas 费(每跳只需要 21000)
|
|
364
|
+
const nativeHopGasFee = nativeTransferGasLimit * gasPrice;
|
|
365
|
+
// ✅ ERC20 多跳时,中转钱包需要执行 2 笔交易(转 gas + 转 ERC20)
|
|
366
|
+
const erc20HopGasFee = erc20TransferGasLimit * gasPrice;
|
|
367
|
+
const nativeHopGasFeeForErc20 = nativeTransferGasLimit * gasPrice;
|
|
359
368
|
// ✅ 优化:预先计算主钱包需要的总 nonce 数量
|
|
369
|
+
// - 原生代币多跳:主钱包只需要 1 个 nonce(一笔转账包含后续所有 gas)
|
|
370
|
+
// - ERC20 多跳:主钱包需要 2 个 nonce(转 gas + 转 ERC20)
|
|
360
371
|
let mainWalletNonceCount = 0;
|
|
361
|
-
const recipientNonceNeeds = [];
|
|
362
372
|
for (let i = 0; i < recipients.length; i++) {
|
|
363
373
|
const hopChain = preparedHops[i];
|
|
364
374
|
if (hopChain.length === 0) {
|
|
365
375
|
// 无跳转:1 个 nonce
|
|
366
|
-
recipientNonceNeeds.push(1);
|
|
367
376
|
mainWalletNonceCount += 1;
|
|
368
377
|
}
|
|
369
378
|
else {
|
|
370
379
|
// 有跳转:
|
|
371
|
-
// -
|
|
372
|
-
// -
|
|
373
|
-
|
|
374
|
-
recipientNonceNeeds.push(nonceNeed);
|
|
375
|
-
mainWalletNonceCount += nonceNeed;
|
|
380
|
+
// - Native: 1(一笔转账)
|
|
381
|
+
// - ERC20: 2(转 gas + 转 ERC20)
|
|
382
|
+
mainWalletNonceCount += isNative ? 1 : 2;
|
|
376
383
|
}
|
|
377
384
|
}
|
|
378
385
|
// 利润交易需要额外 1 个 nonce
|
|
@@ -442,32 +449,14 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
442
449
|
// 有跳转
|
|
443
450
|
const fullChain = [mainWallet, ...hopChain.map(pk => new Wallet(pk, provider))];
|
|
444
451
|
const addresses = [...fullChain.map(w => w.address), finalRecipient];
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
for (let j = 0; j <
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
tx: {
|
|
452
|
-
to: fullChain[j + 1].address,
|
|
453
|
-
value: gasFeePerHop,
|
|
454
|
-
nonce,
|
|
455
|
-
gasPrice,
|
|
456
|
-
gasLimit: nativeGasLimit,
|
|
457
|
-
chainId: chainIdNum,
|
|
458
|
-
type: txType
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
// 执行主要的转账链
|
|
464
|
-
for (let j = 0; j < addresses.length - 1; j++) {
|
|
465
|
-
const fromWallet = fullChain[j];
|
|
466
|
-
const toAddress = addresses[j + 1];
|
|
467
|
-
const nonce = j === 0 ? allMainNonces[mainNonceIdx++] : 0;
|
|
468
|
-
if (isNative) {
|
|
452
|
+
if (isNative) {
|
|
453
|
+
// ========== 原生代币多跳:gas 包含在转账金额中逐层传递 ==========
|
|
454
|
+
for (let j = 0; j < addresses.length - 1; j++) {
|
|
455
|
+
const fromWallet = fullChain[j];
|
|
456
|
+
const toAddress = addresses[j + 1];
|
|
457
|
+
const nonce = j === 0 ? allMainNonces[mainNonceIdx++] : 0;
|
|
469
458
|
const remainingHops = addresses.length - 2 - j;
|
|
470
|
-
const additionalGas =
|
|
459
|
+
const additionalGas = nativeHopGasFee * BigInt(remainingHops);
|
|
471
460
|
const transferValue = amountWei + additionalGas;
|
|
472
461
|
txsToSign.push({
|
|
473
462
|
wallet: fromWallet,
|
|
@@ -476,27 +465,112 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
476
465
|
value: transferValue,
|
|
477
466
|
nonce,
|
|
478
467
|
gasPrice,
|
|
479
|
-
gasLimit:
|
|
468
|
+
gasLimit: nativeTransferGasLimit,
|
|
480
469
|
chainId: chainIdNum,
|
|
481
470
|
type: txType
|
|
482
471
|
}
|
|
483
472
|
});
|
|
484
473
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
// ========== ERC20 多跳:gas 也逐层传递(保护隐私)==========
|
|
477
|
+
// 计算每个中转钱包需要的 gas(从后往前)
|
|
478
|
+
// - 最后一个中转钱包:只需要 ERC20 转账的 gas
|
|
479
|
+
// - 其他中转钱包:需要 转 gas 的 gas + ERC20 转账的 gas + 后续所有的 gas
|
|
480
|
+
const hopGasNeeds = [];
|
|
481
|
+
for (let j = hopChain.length - 1; j >= 0; j--) {
|
|
482
|
+
if (j === hopChain.length - 1) {
|
|
483
|
+
// 最后一个中转钱包:只需要 ERC20 转账的 gas
|
|
484
|
+
hopGasNeeds.unshift(erc20HopGasFee);
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
// 其他中转钱包:转 gas(21000) + 转 ERC20 + 后续的 gas
|
|
488
|
+
const nextHopGas = hopGasNeeds[0];
|
|
489
|
+
hopGasNeeds.unshift(nativeHopGasFeeForErc20 + erc20HopGasFee + nextHopGas);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
// 第一步:主钱包给第一个中转钱包转 gas(包含所有后续的 gas)
|
|
493
|
+
const totalGasForFirstHop = hopGasNeeds[0];
|
|
494
|
+
txsToSign.push({
|
|
495
|
+
wallet: mainWallet,
|
|
496
|
+
tx: {
|
|
497
|
+
to: fullChain[1].address,
|
|
498
|
+
value: totalGasForFirstHop,
|
|
499
|
+
nonce: allMainNonces[mainNonceIdx++],
|
|
500
|
+
gasPrice,
|
|
501
|
+
gasLimit: nativeTransferGasLimit,
|
|
502
|
+
chainId: chainIdNum,
|
|
503
|
+
type: txType
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
// 第二步:主钱包给第一个中转钱包转 ERC20
|
|
507
|
+
const mainToFirstHopData = iface.encodeFunctionData('transfer', [fullChain[1].address, amountWei]);
|
|
508
|
+
txsToSign.push({
|
|
509
|
+
wallet: mainWallet,
|
|
510
|
+
tx: {
|
|
511
|
+
to: tokenAddress,
|
|
512
|
+
data: mainToFirstHopData,
|
|
513
|
+
value: 0n,
|
|
514
|
+
nonce: allMainNonces[mainNonceIdx++],
|
|
515
|
+
gasPrice,
|
|
516
|
+
gasLimit: erc20TransferGasLimit,
|
|
517
|
+
chainId: chainIdNum,
|
|
518
|
+
type: txType
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
// 第三步:中转钱包逐层传递(gas 和 ERC20)
|
|
522
|
+
for (let j = 1; j < fullChain.length; j++) {
|
|
523
|
+
const fromWallet = fullChain[j];
|
|
524
|
+
const toAddress = addresses[j + 1]; // 下一个地址(可能是中转钱包或最终接收者)
|
|
525
|
+
const isLastHop = j === fullChain.length - 1;
|
|
526
|
+
if (!isLastHop) {
|
|
527
|
+
// 非最后一个中转钱包:先转 gas 给下一个中转钱包
|
|
528
|
+
const gasToTransfer = hopGasNeeds[j]; // 下一个中转钱包需要的 gas
|
|
529
|
+
txsToSign.push({
|
|
530
|
+
wallet: fromWallet,
|
|
531
|
+
tx: {
|
|
532
|
+
to: toAddress,
|
|
533
|
+
value: gasToTransfer,
|
|
534
|
+
nonce: 0, // 中转钱包的第一笔交易
|
|
535
|
+
gasPrice,
|
|
536
|
+
gasLimit: nativeTransferGasLimit,
|
|
537
|
+
chainId: chainIdNum,
|
|
538
|
+
type: txType
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
// 再转 ERC20
|
|
542
|
+
const erc20Data = iface.encodeFunctionData('transfer', [toAddress, amountWei]);
|
|
543
|
+
txsToSign.push({
|
|
544
|
+
wallet: fromWallet,
|
|
545
|
+
tx: {
|
|
546
|
+
to: tokenAddress,
|
|
547
|
+
data: erc20Data,
|
|
548
|
+
value: 0n,
|
|
549
|
+
nonce: 1, // 中转钱包的第二笔交易
|
|
550
|
+
gasPrice,
|
|
551
|
+
gasLimit: erc20TransferGasLimit,
|
|
552
|
+
chainId: chainIdNum,
|
|
553
|
+
type: txType
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
// 最后一个中转钱包:只转 ERC20 给最终接收者
|
|
559
|
+
const erc20Data = iface.encodeFunctionData('transfer', [toAddress, amountWei]);
|
|
560
|
+
txsToSign.push({
|
|
561
|
+
wallet: fromWallet,
|
|
562
|
+
tx: {
|
|
563
|
+
to: tokenAddress,
|
|
564
|
+
data: erc20Data,
|
|
565
|
+
value: 0n,
|
|
566
|
+
nonce: 0, // 最后一个中转钱包只有一笔交易
|
|
567
|
+
gasPrice,
|
|
568
|
+
gasLimit: erc20TransferGasLimit,
|
|
569
|
+
chainId: chainIdNum,
|
|
570
|
+
type: txType
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
500
574
|
}
|
|
501
575
|
}
|
|
502
576
|
}
|
|
@@ -913,7 +987,14 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
913
987
|
isNative ? Promise.resolve([]) : _batchGetBalances(provider, sourceAddresses)
|
|
914
988
|
]);
|
|
915
989
|
const iface = isNative ? null : new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
|
|
916
|
-
|
|
990
|
+
// ✅ Gas limit 设置(与分散函数保持一致)
|
|
991
|
+
const nativeTransferGasLimit = 21000n;
|
|
992
|
+
const erc20TransferGasLimit = finalGasLimit;
|
|
993
|
+
// ✅ 原生代币多跳的 gas 费(每跳只需要 21000)
|
|
994
|
+
const nativeHopGasFee = nativeTransferGasLimit * gasPrice;
|
|
995
|
+
// ✅ ERC20 多跳时,中转钱包需要执行 2 笔交易(转 gas + 转 ERC20)
|
|
996
|
+
const erc20HopGasFee = erc20TransferGasLimit * gasPrice;
|
|
997
|
+
const nativeHopGasFeeForErc20 = nativeTransferGasLimit * gasPrice;
|
|
917
998
|
const nonceManager = new NonceManager(provider);
|
|
918
999
|
// ✅ 用于记录每个钱包的归集金额
|
|
919
1000
|
const sweepAmounts = new Array(sourceWallets.length).fill(0n);
|
|
@@ -1015,7 +1096,8 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
1015
1096
|
const ratioForI = getRatioForI(i);
|
|
1016
1097
|
const amountStrForI = getAmountStrForI(i);
|
|
1017
1098
|
if (isNative) {
|
|
1018
|
-
|
|
1099
|
+
// 原生代币多跳每跳只需要 21000 gas
|
|
1100
|
+
const totalGasCost = nativeTransferGasLimit * gasPrice * BigInt(hopChain.length + 1);
|
|
1019
1101
|
if (ratioForI !== undefined) {
|
|
1020
1102
|
const want = (bal * BigInt(ratioForI)) / 100n;
|
|
1021
1103
|
const maxSendable = bal > totalGasCost ? (bal - totalGasCost) : 0n;
|
|
@@ -1030,7 +1112,13 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
1030
1112
|
}
|
|
1031
1113
|
else {
|
|
1032
1114
|
const bnbBal = bnbBalances[i];
|
|
1033
|
-
|
|
1115
|
+
// 计算 ERC20 多跳所需的 BNB(逐层传递模式):
|
|
1116
|
+
// - 源钱包执行 2 笔交易的 gas(转 gas 21000 + 转 ERC20)
|
|
1117
|
+
// - 源钱包转给第一个中转钱包的 gas(包含后续所有)
|
|
1118
|
+
// 简化计算:每个中转钱包 ≈ (21000 + erc20GasLimit) * gasPrice
|
|
1119
|
+
const sourceGas = (nativeTransferGasLimit + erc20TransferGasLimit) * gasPrice;
|
|
1120
|
+
const hopGas = (nativeHopGasFeeForErc20 + erc20HopGasFee) * BigInt(hopChain.length);
|
|
1121
|
+
const bnbNeeded = sourceGas + hopGas;
|
|
1034
1122
|
if (bnbBal < bnbNeeded && skipIfInsufficient)
|
|
1035
1123
|
continue;
|
|
1036
1124
|
if (ratioForI !== undefined) {
|
|
@@ -1052,9 +1140,9 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
1052
1140
|
}
|
|
1053
1141
|
// ========== 第5步:计算每个有跳转钱包需要的 nonce 数量并批量获取 ==========
|
|
1054
1142
|
// 每个源钱包需要的 nonce 数量:
|
|
1055
|
-
// -
|
|
1056
|
-
// -
|
|
1057
|
-
const hopNonceNeeds = hopTxDataList.map(
|
|
1143
|
+
// - Native: 1(一笔转账包含后续所有 gas)
|
|
1144
|
+
// - ERC20: 2(转 gas + 转 ERC20)
|
|
1145
|
+
const hopNonceNeeds = hopTxDataList.map(() => isNative ? 1 : 2);
|
|
1058
1146
|
const hopSourceWallets = hopTxDataList.map(d => d.sourceWallet);
|
|
1059
1147
|
// ✅ 批量获取所有有跳转钱包的 nonces
|
|
1060
1148
|
const hopNoncesFlat = [];
|
|
@@ -1070,32 +1158,14 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
1070
1158
|
const nonceCount = hopNonceNeeds[dataIdx];
|
|
1071
1159
|
const nonces = hopNoncesFlat.slice(nonceOffset, nonceOffset + nonceCount);
|
|
1072
1160
|
nonceOffset += nonceCount;
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
tx: {
|
|
1080
|
-
to: fullChain[j + 1].address,
|
|
1081
|
-
value: gasFeePerHop,
|
|
1082
|
-
nonce: nonces[nonceIdx++],
|
|
1083
|
-
gasPrice,
|
|
1084
|
-
gasLimit: nativeGasLimit,
|
|
1085
|
-
chainId: chainIdNum,
|
|
1086
|
-
type: txType
|
|
1087
|
-
}
|
|
1088
|
-
});
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
// 执行主要的归集链
|
|
1092
|
-
for (let j = 0; j < addresses.length - 1; j++) {
|
|
1093
|
-
const fromWallet = fullChain[j];
|
|
1094
|
-
const toAddress = addresses[j + 1];
|
|
1095
|
-
const nonce = j === 0 ? nonces[nonceIdx++] : 0;
|
|
1096
|
-
if (isNative) {
|
|
1161
|
+
if (isNative) {
|
|
1162
|
+
// ========== 原生代币多跳:gas 包含在转账金额中逐层传递 ==========
|
|
1163
|
+
for (let j = 0; j < addresses.length - 1; j++) {
|
|
1164
|
+
const fromWallet = fullChain[j];
|
|
1165
|
+
const toAddress = addresses[j + 1];
|
|
1166
|
+
const nonce = j === 0 ? nonces[0] : 0;
|
|
1097
1167
|
const remainingHops = addresses.length - 2 - j;
|
|
1098
|
-
const additionalGas =
|
|
1168
|
+
const additionalGas = nativeHopGasFee * BigInt(remainingHops);
|
|
1099
1169
|
const valueToTransfer = toSend + additionalGas;
|
|
1100
1170
|
hopTxsToSign.push({
|
|
1101
1171
|
wallet: fromWallet,
|
|
@@ -1104,27 +1174,110 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
1104
1174
|
value: valueToTransfer,
|
|
1105
1175
|
nonce,
|
|
1106
1176
|
gasPrice,
|
|
1107
|
-
gasLimit:
|
|
1177
|
+
gasLimit: nativeTransferGasLimit,
|
|
1108
1178
|
chainId: chainIdNum,
|
|
1109
1179
|
type: txType
|
|
1110
1180
|
}
|
|
1111
1181
|
});
|
|
1112
1182
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
}
|
|
1183
|
+
}
|
|
1184
|
+
else {
|
|
1185
|
+
// ========== ERC20 多跳:gas 也逐层传递(保护隐私)==========
|
|
1186
|
+
// 计算每个中转钱包需要的 gas(从后往前)
|
|
1187
|
+
const hopGasNeeds = [];
|
|
1188
|
+
for (let j = hopChain.length - 1; j >= 0; j--) {
|
|
1189
|
+
if (j === hopChain.length - 1) {
|
|
1190
|
+
// 最后一个中转钱包:只需要 ERC20 转账的 gas
|
|
1191
|
+
hopGasNeeds.unshift(erc20HopGasFee);
|
|
1192
|
+
}
|
|
1193
|
+
else {
|
|
1194
|
+
// 其他中转钱包:转 gas(21000) + 转 ERC20 + 后续的 gas
|
|
1195
|
+
const nextHopGas = hopGasNeeds[0];
|
|
1196
|
+
hopGasNeeds.unshift(nativeHopGasFeeForErc20 + erc20HopGasFee + nextHopGas);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
// 第一步:源钱包给第一个中转钱包转 gas(包含所有后续的 gas)
|
|
1200
|
+
const totalGasForFirstHop = hopGasNeeds[0];
|
|
1201
|
+
hopTxsToSign.push({
|
|
1202
|
+
wallet: sourceWallet,
|
|
1203
|
+
tx: {
|
|
1204
|
+
to: fullChain[1].address,
|
|
1205
|
+
value: totalGasForFirstHop,
|
|
1206
|
+
nonce: nonces[0],
|
|
1207
|
+
gasPrice,
|
|
1208
|
+
gasLimit: nativeTransferGasLimit,
|
|
1209
|
+
chainId: chainIdNum,
|
|
1210
|
+
type: txType
|
|
1211
|
+
}
|
|
1212
|
+
});
|
|
1213
|
+
// 第二步:源钱包给第一个中转钱包转 ERC20
|
|
1214
|
+
const sourceToFirstHopData = iface.encodeFunctionData('transfer', [fullChain[1].address, toSend]);
|
|
1215
|
+
hopTxsToSign.push({
|
|
1216
|
+
wallet: sourceWallet,
|
|
1217
|
+
tx: {
|
|
1218
|
+
to: tokenAddress,
|
|
1219
|
+
data: sourceToFirstHopData,
|
|
1220
|
+
value: 0n,
|
|
1221
|
+
nonce: nonces[1],
|
|
1222
|
+
gasPrice,
|
|
1223
|
+
gasLimit: erc20TransferGasLimit,
|
|
1224
|
+
chainId: chainIdNum,
|
|
1225
|
+
type: txType
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
// 第三步:中转钱包逐层传递(gas 和 ERC20)
|
|
1229
|
+
for (let j = 1; j < fullChain.length; j++) {
|
|
1230
|
+
const fromWallet = fullChain[j];
|
|
1231
|
+
const toAddress = addresses[j + 1]; // 下一个地址(可能是中转钱包或最终目标)
|
|
1232
|
+
const isLastHop = j === fullChain.length - 1;
|
|
1233
|
+
if (!isLastHop) {
|
|
1234
|
+
// 非最后一个中转钱包:先转 gas 给下一个中转钱包
|
|
1235
|
+
const gasToTransfer = hopGasNeeds[j]; // 下一个中转钱包需要的 gas
|
|
1236
|
+
hopTxsToSign.push({
|
|
1237
|
+
wallet: fromWallet,
|
|
1238
|
+
tx: {
|
|
1239
|
+
to: toAddress,
|
|
1240
|
+
value: gasToTransfer,
|
|
1241
|
+
nonce: 0, // 中转钱包的第一笔交易
|
|
1242
|
+
gasPrice,
|
|
1243
|
+
gasLimit: nativeTransferGasLimit,
|
|
1244
|
+
chainId: chainIdNum,
|
|
1245
|
+
type: txType
|
|
1246
|
+
}
|
|
1247
|
+
});
|
|
1248
|
+
// 再转 ERC20
|
|
1249
|
+
const erc20Data = iface.encodeFunctionData('transfer', [toAddress, toSend]);
|
|
1250
|
+
hopTxsToSign.push({
|
|
1251
|
+
wallet: fromWallet,
|
|
1252
|
+
tx: {
|
|
1253
|
+
to: tokenAddress,
|
|
1254
|
+
data: erc20Data,
|
|
1255
|
+
value: 0n,
|
|
1256
|
+
nonce: 1, // 中转钱包的第二笔交易
|
|
1257
|
+
gasPrice,
|
|
1258
|
+
gasLimit: erc20TransferGasLimit,
|
|
1259
|
+
chainId: chainIdNum,
|
|
1260
|
+
type: txType
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
else {
|
|
1265
|
+
// 最后一个中转钱包:只转 ERC20 给最终目标
|
|
1266
|
+
const erc20Data = iface.encodeFunctionData('transfer', [toAddress, toSend]);
|
|
1267
|
+
hopTxsToSign.push({
|
|
1268
|
+
wallet: fromWallet,
|
|
1269
|
+
tx: {
|
|
1270
|
+
to: tokenAddress,
|
|
1271
|
+
data: erc20Data,
|
|
1272
|
+
value: 0n,
|
|
1273
|
+
nonce: 0, // 最后一个中转钱包只有一笔交易
|
|
1274
|
+
gasPrice,
|
|
1275
|
+
gasLimit: erc20TransferGasLimit,
|
|
1276
|
+
chainId: chainIdNum,
|
|
1277
|
+
type: txType
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1128
1281
|
}
|
|
1129
1282
|
}
|
|
1130
1283
|
}
|
|
@@ -337,8 +337,8 @@ function calculateProfitAmount(expectedFunds) {
|
|
|
337
337
|
if (expectedFunds <= 0n) {
|
|
338
338
|
return 0n;
|
|
339
339
|
}
|
|
340
|
-
//
|
|
341
|
-
return (expectedFunds * BigInt(PROFIT_CONFIG.
|
|
340
|
+
// 万分之六
|
|
341
|
+
return (expectedFunds * BigInt(PROFIT_CONFIG.RATE_BPS_SWAP)) / 10000n;
|
|
342
342
|
}
|
|
343
343
|
/**
|
|
344
344
|
* ✅ 规划 nonce
|
|
@@ -126,15 +126,21 @@ async function batchGetBalances(provider, addresses, tokenAddress) {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
-
|
|
129
|
+
/**
|
|
130
|
+
* 计算 Gas Limit
|
|
131
|
+
* - 原生代币转账:21000 gas(固定)
|
|
132
|
+
* - ERC20 标准 transfer:约 45000-55000 gas,使用 55000 作为安全值
|
|
133
|
+
*
|
|
134
|
+
* ✅ 优化:降低 ERC20 gas limit,减少中转钱包 BNB 残留
|
|
135
|
+
*/
|
|
136
|
+
function calculateGasLimit(config, isNative, hasHops, _hopCount = 0) {
|
|
130
137
|
if (config.gasLimit !== undefined) {
|
|
131
138
|
return BigInt(config.gasLimit);
|
|
132
139
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const multiplier = config.gasLimitMultiplier ?? 1.2;
|
|
140
|
+
// ✅ 原生代币: 21000, ERC20 标准 transfer: 48000(USDT 最低约 46815)
|
|
141
|
+
const baseGas = isNative ? 21000 : 48000;
|
|
142
|
+
// ✅ 多跳时每个中转钱包只执行一笔 transfer,使用较小的安全系数
|
|
143
|
+
const multiplier = config.gasLimitMultiplier ?? 1.1;
|
|
138
144
|
return BigInt(Math.ceil(baseGas * multiplier));
|
|
139
145
|
}
|
|
140
146
|
function isNativeTokenAddress(tokenAddress) {
|
|
@@ -99,8 +99,8 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
99
99
|
sellAmountToken: quoteResult.quotedTokenOut,
|
|
100
100
|
routeParams // ✅ 传递路由参数
|
|
101
101
|
});
|
|
102
|
-
//
|
|
103
|
-
const profitBase = estimatedProfitFromSell > 0n ? estimatedProfitFromSell : (buyerFundsInfo.buyerFundsWei * BigInt(PROFIT_CONFIG.
|
|
102
|
+
// 万分之六
|
|
103
|
+
const profitBase = estimatedProfitFromSell > 0n ? estimatedProfitFromSell : (buyerFundsInfo.buyerFundsWei * BigInt(PROFIT_CONFIG.RATE_BPS_SWAP)) / 10000n;
|
|
104
104
|
const profitAmount = profitBase;
|
|
105
105
|
// ✅ 获取贿赂金额
|
|
106
106
|
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
@@ -368,8 +368,8 @@ async function estimateProfitAmount({ provider, tokenAddress, sellAmountToken, r
|
|
|
368
368
|
if (estimatedSellFunds <= 0n) {
|
|
369
369
|
return 0n;
|
|
370
370
|
}
|
|
371
|
-
//
|
|
372
|
-
return (estimatedSellFunds * BigInt(PROFIT_CONFIG.
|
|
371
|
+
// 万分之六
|
|
372
|
+
return (estimatedSellFunds * BigInt(PROFIT_CONFIG.RATE_BPS_SWAP)) / 10000n;
|
|
373
373
|
}
|
|
374
374
|
catch {
|
|
375
375
|
return 0n;
|