four-flap-meme-sdk 1.4.38 → 1.4.40
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 +36 -28
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +25 -19
- package/dist/contracts/tm-bundle-merkle/private.js +47 -37
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +17 -19
- package/dist/contracts/tm-bundle-merkle/swap.js +51 -52
- package/dist/contracts/tm-bundle-merkle/utils.js +76 -97
- package/dist/contracts/tm-bundle.js +35 -25
- package/dist/dex/direct-router.js +37 -34
- package/dist/flap/portal-bundle-merkle/core.js +34 -28
- package/dist/flap/portal-bundle-merkle/pancake-proxy.js +25 -17
- package/dist/flap/portal-bundle-merkle/private.js +73 -53
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +26 -20
- package/dist/flap/portal-bundle-merkle/swap.js +60 -55
- package/dist/flap/portal-bundle-merkle/utils.js +69 -44
- package/dist/pancake/bundle-buy-first.js +27 -20
- package/dist/pancake/bundle-swap.js +56 -56
- package/dist/utils/bundle-helpers.d.ts +38 -0
- package/dist/utils/bundle-helpers.js +85 -1
- package/dist/utils/private-sale.js +22 -19
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ethers, Wallet } from 'ethers';
|
|
2
|
-
import { getOptimizedGasPrice, NonceManager } from '../../utils/bundle-helpers.js';
|
|
2
|
+
import { getOptimizedGasPrice, NonceManager, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../utils/bundle-helpers.js';
|
|
3
3
|
import { PROFIT_CONFIG, ZERO_ADDRESS } from '../../utils/constants.js';
|
|
4
4
|
import { getTxType, getGasPriceConfig, shouldExtractProfit, getProfitRecipient } from './config.js';
|
|
5
|
-
import { getErc20DecimalsMerkle as _getErc20DecimalsMerkle, generateHopWallets as _generateHopWallets, normalizeAmounts as _normalizeAmounts, batchGetBalances as _batchGetBalances, calculateGasLimit as _calculateGasLimit, isNativeTokenAddress as _isNativeTokenAddress
|
|
5
|
+
import { getErc20DecimalsMerkle as _getErc20DecimalsMerkle, generateHopWallets as _generateHopWallets, normalizeAmounts as _normalizeAmounts, batchGetBalances as _batchGetBalances, calculateGasLimit as _calculateGasLimit, isNativeTokenAddress as _isNativeTokenAddress } from './internal.js';
|
|
6
6
|
// ==================== 本地利润计算(万分之三)====================
|
|
7
7
|
/**
|
|
8
8
|
* 计算利润金额(万分之三)
|
|
@@ -283,19 +283,22 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
283
283
|
chainId: chainIdNum,
|
|
284
284
|
type: txType
|
|
285
285
|
}));
|
|
286
|
-
|
|
286
|
+
signedTxs.push(...(await Promise.all(txPromises)));
|
|
287
|
+
// ✅ 利润多跳转账(强制 2 跳中转)
|
|
287
288
|
if (extractProfit && totalProfit > 0n) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
289
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
290
|
+
provider,
|
|
291
|
+
payerWallet: mainWallet,
|
|
292
|
+
profitAmount: totalProfit,
|
|
293
|
+
profitRecipient: getProfitRecipient(),
|
|
294
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
292
295
|
gasPrice,
|
|
293
|
-
gasLimit: 21000n,
|
|
294
296
|
chainId: chainIdNum,
|
|
295
|
-
|
|
296
|
-
|
|
297
|
+
txType,
|
|
298
|
+
startNonce: nonces[recipients.length]
|
|
299
|
+
});
|
|
300
|
+
signedTxs.push(...profitHopResult.signedTransactions);
|
|
297
301
|
}
|
|
298
|
-
signedTxs.push(...(await Promise.all(txPromises)));
|
|
299
302
|
}
|
|
300
303
|
else {
|
|
301
304
|
// ✅ ERC20:并行获取 decimals(传入 chainIdNum 避免 NETWORK_ERROR)
|
|
@@ -332,19 +335,22 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
332
335
|
chainId: chainIdNum,
|
|
333
336
|
type: txType
|
|
334
337
|
}));
|
|
335
|
-
|
|
338
|
+
signedTxs.push(...(await Promise.all(txPromises)));
|
|
339
|
+
// ✅ 利润多跳转账(强制 2 跳中转)- ERC20 利润转等值原生代币
|
|
336
340
|
if (extractProfit && nativeProfitAmount > 0n) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
342
|
+
provider,
|
|
343
|
+
payerWallet: mainWallet,
|
|
344
|
+
profitAmount: nativeProfitAmount,
|
|
345
|
+
profitRecipient: getProfitRecipient(),
|
|
346
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
341
347
|
gasPrice,
|
|
342
|
-
gasLimit: 21000n,
|
|
343
348
|
chainId: chainIdNum,
|
|
344
|
-
|
|
345
|
-
|
|
349
|
+
txType,
|
|
350
|
+
startNonce: nonces[recipients.length]
|
|
351
|
+
});
|
|
352
|
+
signedTxs.push(...profitHopResult.signedTransactions);
|
|
346
353
|
}
|
|
347
|
-
signedTxs.push(...(await Promise.all(txPromises)));
|
|
348
354
|
}
|
|
349
355
|
}
|
|
350
356
|
else {
|
|
@@ -357,25 +363,9 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
357
363
|
const iface = isNative ? null : new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
|
|
358
364
|
// ✅ Gas limit 设置
|
|
359
365
|
// - 原生代币转账:21000(固定)
|
|
360
|
-
// - ERC20
|
|
366
|
+
// - ERC20 转账:65000(固定,因为 gas 波动较大,不再使用模拟预估)
|
|
361
367
|
const nativeTransferGasLimit = 21000n;
|
|
362
|
-
|
|
363
|
-
// ⚠️ 注意:多跳时中转钱包是新生成的地址,第一次发送 ERC20 的 gas 消耗可能高于主钱包
|
|
364
|
-
// 因此需要添加 10% 缓冲以确保交易成功
|
|
365
|
-
let erc20TransferGasLimit = finalGasLimit;
|
|
366
|
-
if (!isNative && config.estimateGas !== false) {
|
|
367
|
-
try {
|
|
368
|
-
// 使用第一个接收者模拟一笔转账,获取精确的 gas limit
|
|
369
|
-
const sampleAmount = ethers.parseUnits(normalizedAmounts[0], decimals);
|
|
370
|
-
const estimatedGas = await _estimateErc20TransferGas(provider, tokenAddress, mainWallet.address, recipients[0], sampleAmount, 10 // ✅ 添加 10% 缓冲,因为中转钱包首次发送 ERC20 gas 消耗可能更高
|
|
371
|
-
);
|
|
372
|
-
erc20TransferGasLimit = estimatedGas;
|
|
373
|
-
console.log(`[disperseWithBundleMerkle] 使用模拟估算的 ERC20 gas limit: ${erc20TransferGasLimit} (+10% buffer)`);
|
|
374
|
-
}
|
|
375
|
-
catch (error) {
|
|
376
|
-
console.warn(`[disperseWithBundleMerkle] 模拟估算失败,使用默认值:`, error);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
368
|
+
const erc20TransferGasLimit = 65000n;
|
|
379
369
|
// ✅ 原生代币多跳的 gas 费(每跳只需要 21000)
|
|
380
370
|
const nativeHopGasFee = nativeTransferGasLimit * gasPrice;
|
|
381
371
|
// ✅ ERC20 多跳时,中转钱包需要执行 2 笔交易(转 gas + 转 ERC20)
|
|
@@ -595,25 +585,25 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
595
585
|
const nativeProfitAmount = await getTokenToNativeQuote(provider, tokenAddress, totalTokenProfit, chainIdNum, tokenPoolType, quoteToken, config.rpcUrl);
|
|
596
586
|
totalProfit = nativeProfitAmount; // 更新为原生代币利润
|
|
597
587
|
}
|
|
598
|
-
//
|
|
588
|
+
// ✅ 并行签名所有交易
|
|
589
|
+
const signedTxsResult = await Promise.all(txsToSign.map(({ wallet, tx }) => wallet.signTransaction(tx)));
|
|
590
|
+
signedTxs.push(...signedTxsResult);
|
|
591
|
+
// ✅ 利润多跳转账(强制 2 跳中转)
|
|
599
592
|
if (extractProfit && totalProfit > 0n) {
|
|
600
593
|
const profitNonce = allMainNonces[mainNonceIdx++];
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
594
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
595
|
+
provider,
|
|
596
|
+
payerWallet: mainWallet,
|
|
597
|
+
profitAmount: totalProfit,
|
|
598
|
+
profitRecipient: getProfitRecipient(),
|
|
599
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
600
|
+
gasPrice,
|
|
601
|
+
chainId: chainIdNum,
|
|
602
|
+
txType,
|
|
603
|
+
startNonce: profitNonce
|
|
612
604
|
});
|
|
605
|
+
signedTxs.push(...profitHopResult.signedTransactions);
|
|
613
606
|
}
|
|
614
|
-
// ✅ 并行签名所有交易
|
|
615
|
-
const signedTxsResult = await Promise.all(txsToSign.map(({ wallet, tx }) => wallet.signTransaction(tx)));
|
|
616
|
-
signedTxs.push(...signedTxsResult);
|
|
617
607
|
}
|
|
618
608
|
// ✅ 简化返回:只返回签名交易、多跳钱包和元数据
|
|
619
609
|
return {
|
|
@@ -827,19 +817,21 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
827
817
|
});
|
|
828
818
|
const allTxs = (await Promise.all(txPromises)).filter(tx => tx !== null);
|
|
829
819
|
signedTxs.push(...allTxs);
|
|
830
|
-
// ✅
|
|
820
|
+
// ✅ 第三步:利润多跳转账(强制 2 跳中转)
|
|
831
821
|
if (extractProfit && totalProfit > 0n && maxSweepIndex >= 0 && payerProfitNonce !== undefined) {
|
|
832
822
|
const payerWallet = wallets[maxSweepIndex];
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
823
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
824
|
+
provider,
|
|
825
|
+
payerWallet,
|
|
826
|
+
profitAmount: totalProfit,
|
|
827
|
+
profitRecipient: getProfitRecipient(),
|
|
828
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
837
829
|
gasPrice,
|
|
838
|
-
gasLimit: nativeGasLimit,
|
|
839
830
|
chainId: chainIdNum,
|
|
840
|
-
|
|
831
|
+
txType,
|
|
832
|
+
startNonce: payerProfitNonce
|
|
841
833
|
});
|
|
842
|
-
signedTxs.push(
|
|
834
|
+
signedTxs.push(...profitHopResult.signedTransactions);
|
|
843
835
|
}
|
|
844
836
|
}
|
|
845
837
|
else {
|
|
@@ -963,20 +955,22 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
963
955
|
});
|
|
964
956
|
const allTxs = (await Promise.all(txPromises)).filter(tx => tx !== null);
|
|
965
957
|
signedTxs.push(...allTxs);
|
|
966
|
-
// ✅
|
|
958
|
+
// ✅ 第三步:利润多跳转账(强制 2 跳中转)- ERC20 利润转等值原生代币
|
|
967
959
|
if (extractProfit && nativeProfitAmount > 0n && maxSweepIndex >= 0 && payerProfitNonce !== undefined) {
|
|
968
960
|
const payerWallet = wallets[maxSweepIndex];
|
|
969
961
|
totalProfit = nativeProfitAmount; // 更新为原生代币利润
|
|
970
|
-
const
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
962
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
963
|
+
provider,
|
|
964
|
+
payerWallet,
|
|
965
|
+
profitAmount: nativeProfitAmount,
|
|
966
|
+
profitRecipient: getProfitRecipient(),
|
|
967
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
974
968
|
gasPrice,
|
|
975
|
-
gasLimit: 21000n,
|
|
976
969
|
chainId: chainIdNum,
|
|
977
|
-
|
|
970
|
+
txType,
|
|
971
|
+
startNonce: payerProfitNonce
|
|
978
972
|
});
|
|
979
|
-
signedTxs.push(
|
|
973
|
+
signedTxs.push(...profitHopResult.signedTransactions);
|
|
980
974
|
}
|
|
981
975
|
}
|
|
982
976
|
}
|
|
@@ -1004,27 +998,10 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
1004
998
|
]);
|
|
1005
999
|
const iface = isNative ? null : new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
|
|
1006
1000
|
// ✅ Gas limit 设置(与分散函数保持一致)
|
|
1001
|
+
// - 原生代币转账:21000(固定)
|
|
1002
|
+
// - ERC20 转账:65000(固定,因为 gas 波动较大,不再使用模拟预估)
|
|
1007
1003
|
const nativeTransferGasLimit = 21000n;
|
|
1008
|
-
|
|
1009
|
-
// ⚠️ 注意:多跳时中转钱包是新生成的地址,第一次发送 ERC20 的 gas 消耗可能高于已有余额的钱包
|
|
1010
|
-
// 因此需要添加 10% 缓冲以确保交易成功
|
|
1011
|
-
let erc20TransferGasLimit = finalGasLimit;
|
|
1012
|
-
if (!isNative && config.estimateGas !== false) {
|
|
1013
|
-
try {
|
|
1014
|
-
// 找到第一个有余额的钱包来模拟转账
|
|
1015
|
-
const firstWithBalance = balances.findIndex(b => b > 0n);
|
|
1016
|
-
if (firstWithBalance >= 0) {
|
|
1017
|
-
const sampleAmount = balances[firstWithBalance] > 0n ? balances[firstWithBalance] / 2n : 1n;
|
|
1018
|
-
const estimatedGas = await _estimateErc20TransferGas(provider, tokenAddress, sourceAddresses[firstWithBalance], target, sampleAmount, 10 // ✅ 添加 10% 缓冲,因为中转钱包首次发送 ERC20 gas 消耗可能更高
|
|
1019
|
-
);
|
|
1020
|
-
erc20TransferGasLimit = estimatedGas;
|
|
1021
|
-
console.log(`[sweepWithBundleMerkle] 使用模拟估算的 ERC20 gas limit: ${erc20TransferGasLimit} (+10% buffer)`);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
catch (error) {
|
|
1025
|
-
console.warn(`[sweepWithBundleMerkle] 模拟估算失败,使用默认值:`, error);
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1004
|
+
const erc20TransferGasLimit = 65000n;
|
|
1028
1005
|
// ✅ 原生代币多跳的 gas 费(每跳只需要 21000)
|
|
1029
1006
|
const nativeHopGasFee = nativeTransferGasLimit * gasPrice;
|
|
1030
1007
|
// ✅ ERC20 多跳时,中转钱包需要执行 2 笔交易(转 gas + 转 ERC20)
|
|
@@ -1351,20 +1328,22 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
1351
1328
|
nativeProfitAmount = await getTokenToNativeQuote(provider, tokenAddress, totalTokenProfit, chainIdNum, tokenPoolType, quoteToken, config.rpcUrl);
|
|
1352
1329
|
totalProfit = nativeProfitAmount;
|
|
1353
1330
|
}
|
|
1354
|
-
//
|
|
1331
|
+
// ✅ 利润多跳转账(强制 2 跳中转)
|
|
1355
1332
|
if (nativeProfitAmount > 0n && maxSweepIndex >= 0) {
|
|
1356
1333
|
const payerWallet = sourceWallets[maxSweepIndex];
|
|
1357
1334
|
const profitNonce = await nonceManager.getNextNonce(payerWallet);
|
|
1358
|
-
const
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1335
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
1336
|
+
provider,
|
|
1337
|
+
payerWallet,
|
|
1338
|
+
profitAmount: nativeProfitAmount,
|
|
1339
|
+
profitRecipient: getProfitRecipient(),
|
|
1340
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
1362
1341
|
gasPrice,
|
|
1363
|
-
gasLimit: 21000n,
|
|
1364
1342
|
chainId: chainIdNum,
|
|
1365
|
-
|
|
1343
|
+
txType,
|
|
1344
|
+
startNonce: profitNonce
|
|
1366
1345
|
});
|
|
1367
|
-
signedTxs.push(
|
|
1346
|
+
signedTxs.push(...profitHopResult.signedTransactions);
|
|
1368
1347
|
}
|
|
1369
1348
|
}
|
|
1370
1349
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ethers, Wallet, JsonRpcProvider } from 'ethers';
|
|
2
2
|
import { Club48Client, sendBatchPrivateTransactions, BUILDER_CONTROL_EOA } from '../clients/club48.js';
|
|
3
3
|
import { ADDRESSES, PROFIT_CONFIG } from '../utils/constants.js';
|
|
4
|
+
import { buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../utils/bundle-helpers.js';
|
|
4
5
|
import TM2Abi from '../abis/TokenManager2.json' with { type: 'json' };
|
|
5
6
|
import { FourClient, buildLoginMessage } from '../clients/four.js';
|
|
6
7
|
import { trySell } from './tm.js';
|
|
@@ -279,22 +280,25 @@ export async function createTokenWithBundleBuy(params) {
|
|
|
279
280
|
}));
|
|
280
281
|
signedTxs.push(...signedBuyTxList);
|
|
281
282
|
buyTxs.push(...signedBuyTxList);
|
|
282
|
-
// ✅
|
|
283
|
+
// ✅ 聚合利润:由买入最多的人支付总利润(强制 2 跳中转)
|
|
283
284
|
const totalBuyAmount = buyAmountsWei.reduce((sum, amount) => sum + amount, 0n);
|
|
284
285
|
const totalProfitAmount = (totalBuyAmount * BigInt(PROFIT_CONFIG.RATE_BPS)) / 10000n;
|
|
285
286
|
if (totalProfitAmount > 0n) {
|
|
286
287
|
const payerWallet = new Wallet(privateKeys[maxBuyerIndex + 1], provider);
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
288
|
+
const profitNonce = await getNextNonce(payerWallet);
|
|
289
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
290
|
+
provider,
|
|
291
|
+
payerWallet,
|
|
292
|
+
profitAmount: totalProfitAmount,
|
|
293
|
+
profitRecipient: PROFIT_CONFIG.RECIPIENT,
|
|
294
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
291
295
|
gasPrice,
|
|
292
|
-
gasLimit: 21000n,
|
|
293
296
|
chainId: 56,
|
|
294
|
-
|
|
297
|
+
txType: 0,
|
|
298
|
+
startNonce: profitNonce
|
|
295
299
|
});
|
|
296
|
-
signedTxs.push(
|
|
297
|
-
profitTxs.push(
|
|
300
|
+
signedTxs.push(...profitHopResult.signedTransactions);
|
|
301
|
+
profitTxs.push(...profitHopResult.signedTransactions);
|
|
298
302
|
}
|
|
299
303
|
// 可选 tipTx
|
|
300
304
|
if (config.tipAmountWei && config.tipAmountWei > 0n) {
|
|
@@ -472,21 +476,24 @@ export async function batchBuyWithBundle(params) {
|
|
|
472
476
|
return data.buyerWallet.signTransaction(buyTxRequest);
|
|
473
477
|
}));
|
|
474
478
|
signedTxs.push(...signedBuyTxList);
|
|
475
|
-
// ✅
|
|
479
|
+
// ✅ 聚合利润:由买入最多的人支付总利润(强制 2 跳中转)
|
|
476
480
|
const totalFunds = fundsWeiList.reduce((sum, f) => sum + f, 0n);
|
|
477
481
|
const totalProfit = (totalFunds * BigInt(PROFIT_CONFIG.RATE_BPS)) / 10000n;
|
|
478
482
|
if (totalProfit > 0n) {
|
|
479
483
|
const payerWallet = wallets[maxBuyerIndex];
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
+
const profitNonce = getNextNonceLocal(payerWallet);
|
|
485
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
486
|
+
provider,
|
|
487
|
+
payerWallet,
|
|
488
|
+
profitAmount: totalProfit,
|
|
489
|
+
profitRecipient: PROFIT_CONFIG.RECIPIENT,
|
|
490
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
484
491
|
gasPrice,
|
|
485
|
-
gasLimit: 21000n,
|
|
486
492
|
chainId: 56,
|
|
487
|
-
|
|
493
|
+
txType: 0,
|
|
494
|
+
startNonce: profitNonce
|
|
488
495
|
});
|
|
489
|
-
signedTxs.push(
|
|
496
|
+
signedTxs.push(...profitHopResult.signedTransactions);
|
|
490
497
|
}
|
|
491
498
|
// 可选 tipTx(置前)
|
|
492
499
|
if (config.tipAmountWei && config.tipAmountWei > 0n) {
|
|
@@ -609,21 +616,24 @@ export async function batchSellWithBundle(params) {
|
|
|
609
616
|
return data.sellerWallet.signTransaction(sellTxRequest);
|
|
610
617
|
}));
|
|
611
618
|
signedTxs.push(...signedSellTxList);
|
|
612
|
-
// ✅
|
|
619
|
+
// ✅ 聚合利润:由收益最高的人支付总利润(强制 2 跳中转)
|
|
613
620
|
const totalBnbOut = estimatedBnbOuts.reduce((sum, b) => sum + b, 0n);
|
|
614
621
|
const totalProfit = (totalBnbOut * BigInt(PROFIT_CONFIG.RATE_BPS)) / 10000n;
|
|
615
622
|
if (totalProfit > 0n) {
|
|
616
623
|
const payerWallet = wallets[maxSellerIndex];
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
624
|
+
const profitNonce = getNextNonceLocal(payerWallet);
|
|
625
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
626
|
+
provider,
|
|
627
|
+
payerWallet,
|
|
628
|
+
profitAmount: totalProfit,
|
|
629
|
+
profitRecipient: PROFIT_CONFIG.RECIPIENT,
|
|
630
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
621
631
|
gasPrice,
|
|
622
|
-
gasLimit: 21000n,
|
|
623
632
|
chainId: 56,
|
|
624
|
-
|
|
633
|
+
txType: 0,
|
|
634
|
+
startNonce: profitNonce
|
|
625
635
|
});
|
|
626
|
-
signedTxs.push(
|
|
636
|
+
signedTxs.push(...profitHopResult.signedTransactions);
|
|
627
637
|
}
|
|
628
638
|
// 可选 tipTx(置前)
|
|
629
639
|
if (config.tipAmountWei && config.tipAmountWei > 0n) {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 收费方式:交易末尾附加利润提取交易(和内盘一致)
|
|
9
9
|
*/
|
|
10
10
|
import { ethers, Wallet, JsonRpcProvider, Contract } from 'ethers';
|
|
11
|
-
import { NonceManager, getOptimizedGasPrice, getDeadline as _getDeadline } from '../utils/bundle-helpers.js';
|
|
11
|
+
import { NonceManager, getOptimizedGasPrice, getDeadline as _getDeadline, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../utils/bundle-helpers.js';
|
|
12
12
|
import { PROFIT_CONFIG, ZERO_ADDRESS, BLOCKRAZOR_BUILDER_EOA, ADDRESSES } from '../utils/constants.js';
|
|
13
13
|
import { V2_ROUTER_ABI as _V2_ROUTER_ABI, V3_ROUTER02_ABI as _V3_ROUTER02_ABI, V3_ROUTER_LEGACY_ABI as _V3_ROUTER_LEGACY_ABI, SWAP_ROUTER02_V2_ABI as _SWAP_ROUTER02_V2_ABI, ERC20_ABI } from '../abis/common.js';
|
|
14
14
|
import { getTokenToNativeQuote } from '../utils/quote-helpers.js';
|
|
@@ -354,18 +354,24 @@ function findMaxFlowIndex(amounts) {
|
|
|
354
354
|
function calculateProfitAmount(totalFlowWei) {
|
|
355
355
|
return (totalFlowWei * BigInt(PROFIT_CONFIG.RATE_BPS)) / 10000n;
|
|
356
356
|
}
|
|
357
|
-
/**
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
357
|
+
/**
|
|
358
|
+
* 构建利润多跳转账交易(强制 2 跳中转)
|
|
359
|
+
*/
|
|
360
|
+
async function buildProfitTransactionWithHops(provider, wallet, profitAmountWei, nonce, gasPrice, chainId, txType = 0) {
|
|
361
|
+
if (profitAmountWei <= 0n)
|
|
362
|
+
return [];
|
|
363
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
364
|
+
provider,
|
|
365
|
+
payerWallet: wallet,
|
|
366
|
+
profitAmount: profitAmountWei,
|
|
367
|
+
profitRecipient: PROFIT_CONFIG.RECIPIENT,
|
|
368
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
364
369
|
gasPrice,
|
|
365
370
|
chainId,
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
371
|
+
txType,
|
|
372
|
+
startNonce: nonce
|
|
373
|
+
});
|
|
374
|
+
return profitHopResult.signedTransactions;
|
|
369
375
|
}
|
|
370
376
|
/**
|
|
371
377
|
* 获取贿赂金额(wei)
|
|
@@ -535,19 +541,18 @@ export async function directV2BatchBuy(params) {
|
|
|
535
541
|
type: txType,
|
|
536
542
|
}).then(tx => ({ type: 'swap', index: i, tx })));
|
|
537
543
|
});
|
|
538
|
-
// 利润交易
|
|
539
|
-
if (hasProfit) {
|
|
540
|
-
const profitNonce = nonces[maxFlowIndex] + nonceOffsets[maxFlowIndex] + 1;
|
|
541
|
-
signPromises.push(buildProfitTransaction(wallets[maxFlowIndex], profitWei, profitNonce, gasPrice, chainId, txType)
|
|
542
|
-
.then(tx => ({ type: 'profit', index: 0, tx })));
|
|
543
|
-
}
|
|
544
544
|
// ✅ 并行执行所有签名
|
|
545
545
|
const signedResults = await Promise.all(signPromises);
|
|
546
|
-
// 按类型分组并按顺序组装:贿赂 → 交易
|
|
546
|
+
// 按类型分组并按顺序组装:贿赂 → 交易
|
|
547
547
|
const bribeTxs = signedResults.filter(r => r.type === 'bribe').map(r => r.tx);
|
|
548
548
|
const swapTxs = signedResults.filter(r => r.type === 'swap').sort((a, b) => a.index - b.index).map(r => r.tx);
|
|
549
|
-
const
|
|
550
|
-
|
|
549
|
+
const signedTxs = [...bribeTxs, ...swapTxs];
|
|
550
|
+
// ✅ 利润多跳转账(强制 2 跳中转)
|
|
551
|
+
if (hasProfit) {
|
|
552
|
+
const profitNonce = nonces[maxFlowIndex] + nonceOffsets[maxFlowIndex] + 1;
|
|
553
|
+
const profitHopTxs = await buildProfitTransactionWithHops(provider, wallets[maxFlowIndex], profitWei, profitNonce, gasPrice, chainId, txType);
|
|
554
|
+
signedTxs.push(...profitHopTxs);
|
|
555
|
+
}
|
|
551
556
|
return {
|
|
552
557
|
signedTransactions: signedTxs,
|
|
553
558
|
metadata: {
|
|
@@ -699,17 +704,16 @@ export async function directV2BatchSell(params) {
|
|
|
699
704
|
nativeProfitPromise
|
|
700
705
|
]);
|
|
701
706
|
const profitWei = nativeProfitWei > 0n ? nativeProfitWei : 0n;
|
|
702
|
-
//
|
|
703
|
-
let
|
|
707
|
+
// 利润多跳转账(强制 2 跳中转)
|
|
708
|
+
let profitTxs = [];
|
|
704
709
|
if (profitWei > 0n && wallets.length > 0) {
|
|
705
710
|
const profitNonce = nonces[maxOutputIndex] + nonceOffsets[maxOutputIndex] + 1;
|
|
706
|
-
|
|
711
|
+
profitTxs = await buildProfitTransactionWithHops(provider, wallets[maxOutputIndex], profitWei, profitNonce, gasPrice, chainId, txType);
|
|
707
712
|
}
|
|
708
713
|
// 按类型分组并按顺序组装
|
|
709
714
|
const validResults = signedResults.filter((r) => r !== null);
|
|
710
715
|
const bribeTxs = validResults.filter(r => r.type === 'bribe').map(r => r.tx);
|
|
711
716
|
const swapTxs = validResults.filter(r => r.type === 'swap').sort((a, b) => a.index - b.index).map(r => r.tx);
|
|
712
|
-
const profitTxs = profitTx ? [profitTx] : [];
|
|
713
717
|
const signedTxs = [...bribeTxs, ...swapTxs, ...profitTxs];
|
|
714
718
|
return {
|
|
715
719
|
signedTransactions: signedTxs,
|
|
@@ -801,15 +805,15 @@ export async function directV3BatchBuy(params) {
|
|
|
801
805
|
signPromises.push(wallet.signTransaction({ to: routerAddress, data: txData, value: txValue, nonce: nonces[i] + nonceOffsets[i], gasLimit, gasPrice, chainId, type: txType })
|
|
802
806
|
.then(tx => ({ type: 'swap', index: i, tx })));
|
|
803
807
|
});
|
|
804
|
-
if (hasProfit) {
|
|
805
|
-
const profitNonce = nonces[maxFlowIndex] + nonceOffsets[maxFlowIndex] + 1;
|
|
806
|
-
signPromises.push(buildProfitTransaction(wallets[maxFlowIndex], profitWei, profitNonce, gasPrice, chainId, txType)
|
|
807
|
-
.then(tx => ({ type: 'profit', index: 0, tx })));
|
|
808
|
-
}
|
|
809
808
|
const signedResults = await Promise.all(signPromises);
|
|
810
809
|
const bribeTxs = signedResults.filter(r => r.type === 'bribe').map(r => r.tx);
|
|
811
810
|
const swapTxs = signedResults.filter(r => r.type === 'swap').sort((a, b) => a.index - b.index).map(r => r.tx);
|
|
812
|
-
|
|
811
|
+
// 利润多跳转账(强制 2 跳中转)
|
|
812
|
+
let profitTxs = [];
|
|
813
|
+
if (hasProfit) {
|
|
814
|
+
const profitNonce = nonces[maxFlowIndex] + nonceOffsets[maxFlowIndex] + 1;
|
|
815
|
+
profitTxs = await buildProfitTransactionWithHops(provider, wallets[maxFlowIndex], profitWei, profitNonce, gasPrice, chainId, txType);
|
|
816
|
+
}
|
|
813
817
|
const signedTxs = [...bribeTxs, ...swapTxs, ...profitTxs];
|
|
814
818
|
return {
|
|
815
819
|
signedTransactions: signedTxs,
|
|
@@ -942,17 +946,16 @@ export async function directV3BatchSell(params) {
|
|
|
942
946
|
nativeProfitPromise
|
|
943
947
|
]);
|
|
944
948
|
const profitWei = nativeProfitWei > 0n ? nativeProfitWei : 0n;
|
|
945
|
-
//
|
|
946
|
-
let
|
|
949
|
+
// 利润多跳转账(强制 2 跳中转)
|
|
950
|
+
let profitTxs = [];
|
|
947
951
|
if (profitWei > 0n && wallets.length > 0) {
|
|
948
952
|
const profitNonce = nonces[maxOutputIndex] + nonceOffsets[maxOutputIndex] + 1;
|
|
949
|
-
|
|
953
|
+
profitTxs = await buildProfitTransactionWithHops(provider, wallets[maxOutputIndex], profitWei, profitNonce, gasPrice, chainId, txType);
|
|
950
954
|
}
|
|
951
955
|
// 按类型分组并按顺序组装
|
|
952
956
|
const validResults = signedResults.filter((r) => r !== null);
|
|
953
957
|
const bribeTxs = validResults.filter(r => r.type === 'bribe').map(r => r.tx);
|
|
954
958
|
const swapTxs = validResults.filter(r => r.type === 'swap').sort((a, b) => a.index - b.index).map(r => r.tx);
|
|
955
|
-
const profitTxs = profitTx ? [profitTx] : [];
|
|
956
959
|
const signedTxs = [...bribeTxs, ...swapTxs, ...profitTxs];
|
|
957
960
|
return {
|
|
958
961
|
signedTransactions: signedTxs,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ethers, Wallet, Contract, Interface } from 'ethers';
|
|
2
|
-
import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
|
|
2
|
+
import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../utils/bundle-helpers.js';
|
|
3
3
|
import { ADDRESSES, ZERO_ADDRESS } from '../../utils/constants.js';
|
|
4
4
|
import { MULTICALL3_ABI, V2_ROUTER_QUOTE_ABI } from '../../abis/common.js';
|
|
5
5
|
import { FLAP_PORTAL_ADDRESSES, FLAP_ORIGINAL_PORTAL_ADDRESSES } from '../constants.js';
|
|
@@ -180,23 +180,25 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
180
180
|
fundsList: adjustedFundsList, // ✅ 使用调整后的金额
|
|
181
181
|
useNativeToken // ✅ 传递是否使用原生代币
|
|
182
182
|
});
|
|
183
|
-
// ✅
|
|
183
|
+
// ✅ 利润多跳转账(强制 2 跳中转)
|
|
184
184
|
const profitTxs = [];
|
|
185
185
|
if (extractProfit && totalProfit > 0n && maxFundsIndex >= 0) {
|
|
186
186
|
const profitNonce = buyerNonces[maxFundsIndex] + 1;
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
187
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
188
|
+
provider,
|
|
189
|
+
payerWallet: buyers[maxFundsIndex],
|
|
190
|
+
profitAmount: totalProfit,
|
|
191
|
+
profitRecipient: getProfitRecipient(),
|
|
192
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
191
193
|
gasPrice,
|
|
192
|
-
gasLimit: 23000n,
|
|
193
194
|
chainId,
|
|
194
|
-
|
|
195
|
+
txType: getTxType(config),
|
|
196
|
+
startNonce: profitNonce
|
|
195
197
|
});
|
|
196
|
-
profitTxs.push(
|
|
198
|
+
profitTxs.push(...profitHopResult.signedTransactions);
|
|
197
199
|
}
|
|
198
200
|
nonceManager.clearTemp();
|
|
199
|
-
// ✅ 组装顺序:贿赂 → 创建代币 → 买入 →
|
|
201
|
+
// ✅ 组装顺序:贿赂 → 创建代币 → 买入 → 利润多跳
|
|
200
202
|
return {
|
|
201
203
|
signedTransactions: [...bribeTxs, ...signedTxs, ...signedBuys, ...profitTxs],
|
|
202
204
|
tokenAddress,
|
|
@@ -283,23 +285,25 @@ export async function batchBuyWithBundleMerkle(params) {
|
|
|
283
285
|
fundsList: adjustedFundsList, // ✅ 使用调整后的金额
|
|
284
286
|
useNativeToken // ✅ USDT 购买时 value=0,BNB 购买时 value=金额
|
|
285
287
|
});
|
|
286
|
-
// ✅
|
|
288
|
+
// ✅ 利润多跳转账(强制 2 跳中转)
|
|
287
289
|
const profitTxs = [];
|
|
288
290
|
if (extractProfit && nativeProfitAmount > 0n && maxFundsIndex >= 0) {
|
|
289
291
|
const profitNonce = buyerNonces[maxFundsIndex] + 1;
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
292
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
293
|
+
provider,
|
|
294
|
+
payerWallet: buyers[maxFundsIndex],
|
|
295
|
+
profitAmount: nativeProfitAmount,
|
|
296
|
+
profitRecipient: getProfitRecipient(),
|
|
297
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
294
298
|
gasPrice,
|
|
295
|
-
gasLimit: 23000n,
|
|
296
299
|
chainId,
|
|
297
|
-
|
|
300
|
+
txType: getTxType(config),
|
|
301
|
+
startNonce: profitNonce
|
|
298
302
|
});
|
|
299
|
-
profitTxs.push(
|
|
303
|
+
profitTxs.push(...profitHopResult.signedTransactions);
|
|
300
304
|
}
|
|
301
305
|
nonceManager.clearTemp();
|
|
302
|
-
// ✅ 组装顺序:贿赂 → 买入 →
|
|
306
|
+
// ✅ 组装顺序:贿赂 → 买入 → 利润多跳
|
|
303
307
|
return {
|
|
304
308
|
signedTransactions: [...bribeTxs, ...signedBuys, ...profitTxs],
|
|
305
309
|
metadata: buildProfitMetadata(extractProfit, totalBuyAmount, nativeProfitAmount, buyers.length)
|
|
@@ -436,7 +440,7 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
436
440
|
type: getTxType(config),
|
|
437
441
|
value: 0n // ✅ 卖出交易不发送原生代币
|
|
438
442
|
})));
|
|
439
|
-
// ✅
|
|
443
|
+
// ✅ 利润多跳转账(强制 2 跳中转)
|
|
440
444
|
const profitTxs = [];
|
|
441
445
|
if (needProfitTx && profitNonce !== undefined) {
|
|
442
446
|
// ERC20 输出时:获取代币利润等值的原生代币(BNB)报价
|
|
@@ -445,20 +449,22 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
445
449
|
nativeProfitAmount = await getTokenToNativeQuote(provider, outputToken, totalTokenProfit, chainId);
|
|
446
450
|
}
|
|
447
451
|
if (nativeProfitAmount > 0n) {
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
453
|
+
provider,
|
|
454
|
+
payerWallet: wallets[maxRevenueIndex],
|
|
455
|
+
profitAmount: nativeProfitAmount,
|
|
456
|
+
profitRecipient: getProfitRecipient(),
|
|
457
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
452
458
|
gasPrice,
|
|
453
|
-
gasLimit: 23000n,
|
|
454
459
|
chainId,
|
|
455
|
-
|
|
460
|
+
txType: getTxType(config),
|
|
461
|
+
startNonce: profitNonce
|
|
456
462
|
});
|
|
457
|
-
profitTxs.push(
|
|
463
|
+
profitTxs.push(...profitHopResult.signedTransactions);
|
|
458
464
|
}
|
|
459
465
|
}
|
|
460
466
|
nonceManager.clearTemp();
|
|
461
|
-
// ✅ 组装顺序:贿赂 → 卖出 →
|
|
467
|
+
// ✅ 组装顺序:贿赂 → 卖出 → 利润多跳
|
|
462
468
|
return {
|
|
463
469
|
signedTransactions: [...bribeTxs, ...signedList, ...profitTxs]
|
|
464
470
|
};
|