four-flap-meme-sdk 1.3.95 → 1.3.99
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/dex/direct-router.d.ts +2 -0
- package/dist/dex/direct-router.js +172 -168
- package/package.json +1 -1
|
@@ -44,6 +44,8 @@ export interface DirectRouterSignConfig {
|
|
|
44
44
|
skipApprovalCheck?: boolean;
|
|
45
45
|
/** 滑点(基点),默认 100 = 1% */
|
|
46
46
|
slippageBps?: number;
|
|
47
|
+
/** BlockRazor 贿赂金额(BNB),仅 BSC 链有效,用于提高 bundle 打包优先级 */
|
|
48
|
+
bribeAmount?: number;
|
|
47
49
|
}
|
|
48
50
|
export interface DirectV2BuyParams {
|
|
49
51
|
chain: 'BSC' | 'MONAD' | 'XLAYER';
|
|
@@ -16,6 +16,8 @@ import { PROFIT_CONFIG } from '../utils/constants.js';
|
|
|
16
16
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
17
17
|
const DEFAULT_GAS_LIMIT = 300000;
|
|
18
18
|
const DEADLINE_MINUTES = 20;
|
|
19
|
+
// ✅ BlockRazor Builder EOA 地址(用于贿赂,仅 BSC 链)
|
|
20
|
+
const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
19
21
|
// ✅ Router ABI(用于 ERC20 → 原生代币报价)
|
|
20
22
|
const QUOTE_ROUTER_ABI = [
|
|
21
23
|
'function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)'
|
|
@@ -585,6 +587,33 @@ async function buildProfitTransaction(wallet, profitAmountWei, nonce, gasPrice,
|
|
|
585
587
|
};
|
|
586
588
|
return wallet.signTransaction(tx);
|
|
587
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* 获取贿赂金额(wei)
|
|
592
|
+
* ✅ 仅 BSC 链支持贿赂
|
|
593
|
+
*/
|
|
594
|
+
function getBribeAmount(config, chain) {
|
|
595
|
+
// 只有 BSC 链支持贿赂
|
|
596
|
+
if (chain.toUpperCase() !== 'BSC')
|
|
597
|
+
return 0n;
|
|
598
|
+
const bribeAmount = config.bribeAmount;
|
|
599
|
+
if (typeof bribeAmount !== 'number' || bribeAmount <= 0)
|
|
600
|
+
return 0n;
|
|
601
|
+
// 转换为 wei
|
|
602
|
+
return ethers.parseEther(String(bribeAmount));
|
|
603
|
+
}
|
|
604
|
+
/** 构建贿赂交易(向 BlockRazor Builder EOA 转账 BNB) */
|
|
605
|
+
async function buildBribeTransaction(wallet, bribeAmountWei, nonce, gasPrice, chainId, txType = 0) {
|
|
606
|
+
const tx = {
|
|
607
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
608
|
+
value: bribeAmountWei,
|
|
609
|
+
nonce,
|
|
610
|
+
gasLimit: 21000n,
|
|
611
|
+
gasPrice,
|
|
612
|
+
chainId,
|
|
613
|
+
type: txType,
|
|
614
|
+
};
|
|
615
|
+
return wallet.signTransaction(tx);
|
|
616
|
+
}
|
|
588
617
|
// ============================================================================
|
|
589
618
|
// V2 直接交易
|
|
590
619
|
// ============================================================================
|
|
@@ -647,11 +676,12 @@ export async function directV2BatchBuy(params) {
|
|
|
647
676
|
const wrappedNative = getWrappedNative(chain);
|
|
648
677
|
// 创建钱包
|
|
649
678
|
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
650
|
-
// 获取 nonce
|
|
651
679
|
const nonceManager = new NonceManager(provider);
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
680
|
+
// ✅ 性能优化:并行获取 nonce 和 gasPrice(减少 2 轮串行 → 1 轮并行)
|
|
681
|
+
const [nonces, gasPrice] = await Promise.all([
|
|
682
|
+
nonceManager.getNextNoncesForWallets(wallets),
|
|
683
|
+
getGasPrice(provider, config)
|
|
684
|
+
]);
|
|
655
685
|
const gasLimit = getGasLimit(config, 250000);
|
|
656
686
|
const txType = config.txType ?? 0;
|
|
657
687
|
const deadline = getDeadline();
|
|
@@ -717,22 +747,34 @@ export async function directV2BatchBuy(params) {
|
|
|
717
747
|
};
|
|
718
748
|
}
|
|
719
749
|
};
|
|
750
|
+
// ✅ 优化:选择金额最大的钱包支付贿赂和利润
|
|
751
|
+
const maxFlowIndex = findMaxFlowIndex(flowAmounts);
|
|
752
|
+
// ✅ 计算贿赂金额(仅 BSC 链)
|
|
753
|
+
const bribeWei = getBribeAmount(config, chain);
|
|
754
|
+
const hasBribe = bribeWei > 0n && wallets.length > 0;
|
|
755
|
+
// ✅ 计算 nonce 偏移:如果有贿赂交易,需要为金额最大的钱包预留 nonce
|
|
756
|
+
const nonceOffsets = wallets.map((_, i) => i === maxFlowIndex && hasBribe ? 1 : 0);
|
|
757
|
+
// ✅ 贿赂交易放在首位(提高 BlockRazor 打包优先级)
|
|
758
|
+
const bribeTxs = [];
|
|
759
|
+
if (hasBribe) {
|
|
760
|
+
const bribeTx = await buildBribeTransaction(wallets[maxFlowIndex], bribeWei, nonces[maxFlowIndex], // 使用原始 nonce
|
|
761
|
+
gasPrice, chainId, txType);
|
|
762
|
+
bribeTxs.push(bribeTx);
|
|
763
|
+
}
|
|
720
764
|
// ✅ 优化:并行签名所有交易
|
|
721
|
-
const
|
|
765
|
+
const swapTxs = await Promise.all(wallets.map(async (wallet, i) => {
|
|
722
766
|
const { txData, txValue } = buildTxData(wallet, flowAmounts[i]);
|
|
723
767
|
return wallet.signTransaction({
|
|
724
768
|
to: routerAddress,
|
|
725
769
|
data: txData,
|
|
726
770
|
value: txValue,
|
|
727
|
-
nonce: nonces[i],
|
|
771
|
+
nonce: nonces[i] + nonceOffsets[i], // ✅ 考虑贿赂交易的 nonce 偏移
|
|
728
772
|
gasLimit,
|
|
729
773
|
gasPrice,
|
|
730
774
|
chainId,
|
|
731
775
|
type: txType,
|
|
732
776
|
});
|
|
733
777
|
}));
|
|
734
|
-
// ✅ 优化:选择金额最大的钱包支付利润(和 core.ts 逻辑一致)
|
|
735
|
-
const maxFlowIndex = findMaxFlowIndex(flowAmounts);
|
|
736
778
|
let profitWei = calculateProfitAmount(totalFlowWei);
|
|
737
779
|
// ✅ 修复:ERC20 交易时,将利润转换为原生代币
|
|
738
780
|
if (!useNative && profitWei > 0n && quoteToken) {
|
|
@@ -744,11 +786,15 @@ export async function directV2BatchBuy(params) {
|
|
|
744
786
|
profitWei = 0n; // 报价失败,跳过利润
|
|
745
787
|
}
|
|
746
788
|
}
|
|
789
|
+
// ✅ 利润交易
|
|
790
|
+
const profitTxs = [];
|
|
747
791
|
if (profitWei > 0n) {
|
|
748
|
-
const profitNonce = nonces[maxFlowIndex] + 1; // ✅
|
|
792
|
+
const profitNonce = nonces[maxFlowIndex] + nonceOffsets[maxFlowIndex] + 1; // ✅ 考虑贿赂交易的 nonce 偏移
|
|
749
793
|
const profitTx = await buildProfitTransaction(wallets[maxFlowIndex], profitWei, profitNonce, gasPrice, chainId, txType);
|
|
750
|
-
|
|
794
|
+
profitTxs.push(profitTx);
|
|
751
795
|
}
|
|
796
|
+
// ✅ 组装最终交易列表:贿赂 → 交易 → 利润
|
|
797
|
+
const signedTxs = [...bribeTxs, ...swapTxs, ...profitTxs];
|
|
752
798
|
return {
|
|
753
799
|
signedTransactions: signedTxs,
|
|
754
800
|
metadata: {
|
|
@@ -771,19 +817,26 @@ export async function directV2BatchSell(params) {
|
|
|
771
817
|
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
772
818
|
// 获取代币合约
|
|
773
819
|
const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
820
|
+
const nonceManager = new NonceManager(provider);
|
|
821
|
+
// ✅ 性能优化:并行获取所有独立的 RPC 数据(减少串行 → 1 轮并行)
|
|
822
|
+
// ✅ 已移除授权检查(前端应确保已授权)
|
|
823
|
+
const [fetchedDecimals, balances, nonces, gasPrice] = await Promise.all([
|
|
824
|
+
// 1. 获取代币精度(如果未提供)
|
|
825
|
+
inputDecimals !== undefined
|
|
826
|
+
? Promise.resolve(inputDecimals)
|
|
827
|
+
: tokenContract.decimals().then((d) => Number(d)).catch(() => 18),
|
|
828
|
+
// 2. 批量获取余额
|
|
829
|
+
Promise.all(wallets.map(w => tokenContract.balanceOf(w.address))),
|
|
830
|
+
// 3. 批量获取 nonce
|
|
831
|
+
nonceManager.getNextNoncesForWallets(wallets),
|
|
832
|
+
// 4. 获取 gas price
|
|
833
|
+
getGasPrice(provider, config)
|
|
834
|
+
]);
|
|
835
|
+
const tokenDecimals = fetchedDecimals;
|
|
836
|
+
const gasLimit = getGasLimit(config, 300000);
|
|
837
|
+
const txType = config.txType ?? 0;
|
|
838
|
+
const deadline = getDeadline();
|
|
839
|
+
// 计算卖出数量(同步操作,无 RPC)
|
|
787
840
|
const sellAmountsWei = [];
|
|
788
841
|
for (let i = 0; i < wallets.length; i++) {
|
|
789
842
|
if (sellAmounts && sellAmounts[i]) {
|
|
@@ -799,13 +852,6 @@ export async function directV2BatchSell(params) {
|
|
|
799
852
|
sellAmountsWei.push(balances[i]); // 默认全部卖出
|
|
800
853
|
}
|
|
801
854
|
}
|
|
802
|
-
// 获取 nonce(每个钱包可能需要 2 个:授权 + 卖出)
|
|
803
|
-
const nonceManager = new NonceManager(provider);
|
|
804
|
-
const nonces = await nonceManager.getNextNoncesForWallets(wallets);
|
|
805
|
-
const gasPrice = await getGasPrice(provider, config);
|
|
806
|
-
const gasLimit = getGasLimit(config, 300000);
|
|
807
|
-
const txType = config.txType ?? 0;
|
|
808
|
-
const deadline = getDeadline();
|
|
809
855
|
// 构建路径
|
|
810
856
|
const outputToken = useNativeOutput ? wrappedNative : quoteToken;
|
|
811
857
|
const path = [tokenAddress, outputToken];
|
|
@@ -816,7 +862,6 @@ export async function directV2BatchSell(params) {
|
|
|
816
862
|
const routerIface = useSwapRouter02
|
|
817
863
|
? new ethers.Interface(SWAP_ROUTER02_V2_ABI)
|
|
818
864
|
: new ethers.Interface(V2_ROUTER_ABI);
|
|
819
|
-
const approveIface = new ethers.Interface(ERC20_ABI);
|
|
820
865
|
// ✅ 优化:构建卖出交易数据的辅助函数(同步)
|
|
821
866
|
const buildSellTxData = (wallet, sellAmount) => {
|
|
822
867
|
if (useDYORSwap) {
|
|
@@ -850,80 +895,38 @@ export async function directV2BatchSell(params) {
|
|
|
850
895
|
return routerIface.encodeFunctionData('swapExactTokensForTokensSupportingFeeOnTransferTokens', [sellAmount, 0n, path, wallet.address, deadline]);
|
|
851
896
|
}
|
|
852
897
|
};
|
|
853
|
-
// ✅
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
898
|
+
// ✅ 性能优化:直接用 sellAmountsWei 找最大输出钱包(跳过 N 次 getAmountsOut RPC 调用)
|
|
899
|
+
// 因为卖出金额越大,输出也越大(假设线性关系),无需额外报价
|
|
900
|
+
const maxOutputIndex = findMaxFlowIndex(sellAmountsWei);
|
|
901
|
+
// ✅ 计算贿赂金额(仅 BSC 链)
|
|
902
|
+
const bribeWei = getBribeAmount(config, chain);
|
|
903
|
+
const hasBribe = bribeWei > 0n && wallets.length > 0;
|
|
904
|
+
// ✅ 计算 nonce 偏移(仅贿赂,已移除授权)
|
|
905
|
+
const nonceOffsets = wallets.map((_, i) => i === maxOutputIndex && hasBribe ? 1 : 0);
|
|
906
|
+
// ✅ 贿赂交易放在首位(提高 BlockRazor 打包优先级)
|
|
907
|
+
const bribeTxs = [];
|
|
908
|
+
if (hasBribe) {
|
|
909
|
+
const bribeTx = await buildBribeTransaction(wallets[maxOutputIndex], bribeWei, nonces[maxOutputIndex], // 使用原始 nonce
|
|
910
|
+
gasPrice, chainId, txType);
|
|
911
|
+
bribeTxs.push(bribeTx);
|
|
857
912
|
}
|
|
858
|
-
// ✅
|
|
859
|
-
const
|
|
860
|
-
if (config.skipApprovalCheck)
|
|
861
|
-
return 0;
|
|
862
|
-
return allowances[i] < sellAmountsWei[i] ? 1 : 0;
|
|
863
|
-
});
|
|
864
|
-
// ✅ 优化:并行签名所有授权交易
|
|
865
|
-
const approvalTxPromises = wallets.map(async (wallet, i) => {
|
|
866
|
-
if (config.skipApprovalCheck || sellAmountsWei[i] <= 0n)
|
|
867
|
-
return null;
|
|
868
|
-
if (allowances[i] >= sellAmountsWei[i])
|
|
869
|
-
return null;
|
|
870
|
-
return wallet.signTransaction({
|
|
871
|
-
to: tokenAddress,
|
|
872
|
-
data: approveIface.encodeFunctionData('approve', [routerAddress, ethers.MaxUint256]),
|
|
873
|
-
value: 0n,
|
|
874
|
-
nonce: nonces[i],
|
|
875
|
-
gasLimit: 60000n,
|
|
876
|
-
gasPrice,
|
|
877
|
-
chainId,
|
|
878
|
-
type: txType,
|
|
879
|
-
});
|
|
880
|
-
});
|
|
881
|
-
const approvalTxResults = await Promise.all(approvalTxPromises);
|
|
882
|
-
// ✅ 优化:并行签名所有卖出交易
|
|
883
|
-
const sellTxPromises = wallets.map(async (wallet, i) => {
|
|
913
|
+
// ✅ 优化:并行签名所有卖出交易(已移除授权交易)
|
|
914
|
+
const swapTxs = await Promise.all(wallets.map(async (wallet, i) => {
|
|
884
915
|
if (sellAmountsWei[i] <= 0n)
|
|
885
916
|
return null;
|
|
886
917
|
return wallet.signTransaction({
|
|
887
918
|
to: routerAddress,
|
|
888
919
|
data: buildSellTxData(wallet, sellAmountsWei[i]),
|
|
889
920
|
value: 0n,
|
|
890
|
-
nonce: nonces[i] +
|
|
921
|
+
nonce: nonces[i] + nonceOffsets[i],
|
|
891
922
|
gasLimit,
|
|
892
923
|
gasPrice,
|
|
893
924
|
chainId,
|
|
894
925
|
type: txType,
|
|
895
926
|
});
|
|
896
|
-
});
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
const router = new Contract(routerAddress, V2_ROUTER_ABI, provider);
|
|
900
|
-
const quotePromises = wallets.map(async (_, i) => {
|
|
901
|
-
if (sellAmountsWei[i] <= 0n)
|
|
902
|
-
return sellAmountsWei[i];
|
|
903
|
-
try {
|
|
904
|
-
const amounts = await router.getAmountsOut(sellAmountsWei[i], path);
|
|
905
|
-
return amounts[amounts.length - 1];
|
|
906
|
-
}
|
|
907
|
-
catch {
|
|
908
|
-
return sellAmountsWei[i];
|
|
909
|
-
}
|
|
910
|
-
});
|
|
911
|
-
const outputEstimates = await Promise.all(quotePromises);
|
|
912
|
-
// ✅ 按顺序组装签名交易:先授权,后卖出
|
|
913
|
-
const signedTxs = [];
|
|
914
|
-
for (let i = 0; i < wallets.length; i++) {
|
|
915
|
-
const approvalTx = approvalTxResults[i];
|
|
916
|
-
const sellTx = sellTxResults[i];
|
|
917
|
-
if (approvalTx)
|
|
918
|
-
signedTxs.push(approvalTx);
|
|
919
|
-
if (sellTx) {
|
|
920
|
-
signedTxs.push(sellTx);
|
|
921
|
-
currentNonceOffset[i]++; // 更新 offset 供利润交易使用
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
const totalOutputEstimate = outputEstimates.reduce((sum, o) => sum + o, 0n);
|
|
925
|
-
// ✅ 优化:选择输出最大的钱包支付利润(和 core.ts 逻辑一致)
|
|
926
|
-
const maxOutputIndex = findMaxFlowIndex(outputEstimates);
|
|
927
|
+
})).then(results => results.filter((tx) => tx !== null));
|
|
928
|
+
// ✅ 使用 sellAmountsWei 作为输出估计(因为已跳过 getAmountsOut 调用)
|
|
929
|
+
const totalOutputEstimate = sellAmountsWei.reduce((sum, o) => sum + o, 0n);
|
|
927
930
|
let profitWei = calculateProfitAmount(totalOutputEstimate);
|
|
928
931
|
// ✅ 修复:ERC20 输出时,将利润转换为原生代币
|
|
929
932
|
if (!useNativeOutput && profitWei > 0n && quoteToken) {
|
|
@@ -935,11 +938,17 @@ export async function directV2BatchSell(params) {
|
|
|
935
938
|
profitWei = 0n;
|
|
936
939
|
}
|
|
937
940
|
}
|
|
941
|
+
// ✅ 利润交易
|
|
942
|
+
const profitTxs = [];
|
|
938
943
|
if (profitWei > 0n && wallets.length > 0) {
|
|
944
|
+
// 利润交易 nonce = 原始 nonce + 贿赂偏移 + 1(卖出交易)
|
|
945
|
+
const profitNonce = nonces[maxOutputIndex] + nonceOffsets[maxOutputIndex] + 1;
|
|
939
946
|
const profitTx = await buildProfitTransaction(wallets[maxOutputIndex], // ✅ 使用输出最大的钱包
|
|
940
|
-
profitWei,
|
|
941
|
-
|
|
947
|
+
profitWei, profitNonce, gasPrice, chainId, txType);
|
|
948
|
+
profitTxs.push(profitTx);
|
|
942
949
|
}
|
|
950
|
+
// ✅ 组装最终交易列表:贿赂 → 卖出 → 利润
|
|
951
|
+
const signedTxs = [...bribeTxs, ...swapTxs, ...profitTxs];
|
|
943
952
|
return {
|
|
944
953
|
signedTransactions: signedTxs,
|
|
945
954
|
metadata: {
|
|
@@ -973,8 +982,11 @@ export async function directV3BatchBuy(params) {
|
|
|
973
982
|
const routerAbi = useLegacyRouter ? V3_ROUTER_LEGACY_ABI : V3_ROUTER02_ABI;
|
|
974
983
|
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
975
984
|
const nonceManager = new NonceManager(provider);
|
|
976
|
-
|
|
977
|
-
const gasPrice = await
|
|
985
|
+
// ✅ 性能优化:并行获取 nonce 和 gasPrice
|
|
986
|
+
const [nonces, gasPrice] = await Promise.all([
|
|
987
|
+
nonceManager.getNextNoncesForWallets(wallets),
|
|
988
|
+
getGasPrice(provider, config)
|
|
989
|
+
]);
|
|
978
990
|
const gasLimit = getGasLimit(config, 300000);
|
|
979
991
|
const txType = config.txType ?? 0;
|
|
980
992
|
const routerIface = new ethers.Interface(routerAbi);
|
|
@@ -1031,22 +1043,33 @@ export async function directV3BatchBuy(params) {
|
|
|
1031
1043
|
}
|
|
1032
1044
|
}
|
|
1033
1045
|
};
|
|
1046
|
+
// ✅ 优化:选择金额最大的钱包支付贿赂和利润
|
|
1047
|
+
const maxFlowIndex = findMaxFlowIndex(flowAmounts);
|
|
1048
|
+
// ✅ 计算贿赂金额(仅 BSC 链)
|
|
1049
|
+
const bribeWei = getBribeAmount(config, chain);
|
|
1050
|
+
const hasBribe = bribeWei > 0n && wallets.length > 0;
|
|
1051
|
+
// ✅ 计算 nonce 偏移
|
|
1052
|
+
const nonceOffsets = wallets.map((_, i) => i === maxFlowIndex && hasBribe ? 1 : 0);
|
|
1053
|
+
// ✅ 贿赂交易放在首位(提高 BlockRazor 打包优先级)
|
|
1054
|
+
const bribeTxs = [];
|
|
1055
|
+
if (hasBribe) {
|
|
1056
|
+
const bribeTx = await buildBribeTransaction(wallets[maxFlowIndex], bribeWei, nonces[maxFlowIndex], gasPrice, chainId, txType);
|
|
1057
|
+
bribeTxs.push(bribeTx);
|
|
1058
|
+
}
|
|
1034
1059
|
// ✅ 优化:并行签名所有交易
|
|
1035
|
-
const
|
|
1060
|
+
const swapTxs = await Promise.all(wallets.map(async (wallet, i) => {
|
|
1036
1061
|
const { txData, txValue } = buildV3BuyTxData(wallet, flowAmounts[i]);
|
|
1037
1062
|
return wallet.signTransaction({
|
|
1038
1063
|
to: routerAddress,
|
|
1039
1064
|
data: txData,
|
|
1040
1065
|
value: txValue,
|
|
1041
|
-
nonce: nonces[i],
|
|
1066
|
+
nonce: nonces[i] + nonceOffsets[i],
|
|
1042
1067
|
gasLimit,
|
|
1043
1068
|
gasPrice,
|
|
1044
1069
|
chainId,
|
|
1045
1070
|
type: txType,
|
|
1046
1071
|
});
|
|
1047
1072
|
}));
|
|
1048
|
-
// ✅ 优化:选择金额最大的钱包支付利润
|
|
1049
|
-
const maxFlowIndex = findMaxFlowIndex(flowAmounts);
|
|
1050
1073
|
let profitWei = calculateProfitAmount(totalFlowWei);
|
|
1051
1074
|
// ✅ 修复:ERC20 交易时,将利润转换为原生代币
|
|
1052
1075
|
if (!useNative && profitWei > 0n && quoteToken) {
|
|
@@ -1058,11 +1081,14 @@ export async function directV3BatchBuy(params) {
|
|
|
1058
1081
|
profitWei = 0n;
|
|
1059
1082
|
}
|
|
1060
1083
|
}
|
|
1084
|
+
// ✅ 利润交易
|
|
1085
|
+
const profitTxs = [];
|
|
1061
1086
|
if (profitWei > 0n) {
|
|
1062
|
-
const profitTx = await buildProfitTransaction(wallets[maxFlowIndex],
|
|
1063
|
-
|
|
1064
|
-
signedTxs.push(profitTx);
|
|
1087
|
+
const profitTx = await buildProfitTransaction(wallets[maxFlowIndex], profitWei, nonces[maxFlowIndex] + nonceOffsets[maxFlowIndex] + 1, gasPrice, chainId, txType);
|
|
1088
|
+
profitTxs.push(profitTx);
|
|
1065
1089
|
}
|
|
1090
|
+
// ✅ 组装最终交易列表:贿赂 → 交易 → 利润
|
|
1091
|
+
const signedTxs = [...bribeTxs, ...swapTxs, ...profitTxs];
|
|
1066
1092
|
return {
|
|
1067
1093
|
signedTransactions: signedTxs,
|
|
1068
1094
|
metadata: {
|
|
@@ -1090,9 +1116,20 @@ export async function directV3BatchSell(params) {
|
|
|
1090
1116
|
const routerAbi = useLegacyRouter ? V3_ROUTER_LEGACY_ABI : V3_ROUTER02_ABI;
|
|
1091
1117
|
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
1092
1118
|
const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
//
|
|
1119
|
+
const nonceManager = new NonceManager(provider);
|
|
1120
|
+
// ✅ 性能优化:并行获取所有独立的 RPC 数据
|
|
1121
|
+
// ✅ 已移除授权检查(前端应确保已授权)
|
|
1122
|
+
const [balances, nonces, gasPrice] = await Promise.all([
|
|
1123
|
+
// 1. 批量获取余额
|
|
1124
|
+
Promise.all(wallets.map(w => tokenContract.balanceOf(w.address))),
|
|
1125
|
+
// 2. 批量获取 nonce
|
|
1126
|
+
nonceManager.getNextNoncesForWallets(wallets),
|
|
1127
|
+
// 3. 获取 gas price
|
|
1128
|
+
getGasPrice(provider, config)
|
|
1129
|
+
]);
|
|
1130
|
+
const gasLimit = getGasLimit(config, 350000);
|
|
1131
|
+
const txType = config.txType ?? 0;
|
|
1132
|
+
// 计算卖出数量(同步操作,无 RPC)
|
|
1096
1133
|
const sellAmountsWei = [];
|
|
1097
1134
|
for (let i = 0; i < wallets.length; i++) {
|
|
1098
1135
|
if (sellAmounts && sellAmounts[i]) {
|
|
@@ -1106,13 +1143,7 @@ export async function directV3BatchSell(params) {
|
|
|
1106
1143
|
sellAmountsWei.push(balances[i]);
|
|
1107
1144
|
}
|
|
1108
1145
|
}
|
|
1109
|
-
const nonceManager = new NonceManager(provider);
|
|
1110
|
-
const nonces = await nonceManager.getNextNoncesForWallets(wallets);
|
|
1111
|
-
const gasPrice = await getGasPrice(provider, config);
|
|
1112
|
-
const gasLimit = getGasLimit(config, 350000);
|
|
1113
|
-
const txType = config.txType ?? 0;
|
|
1114
1146
|
const routerIface = new ethers.Interface(routerAbi);
|
|
1115
|
-
const approveIface = new ethers.Interface(ERC20_ABI);
|
|
1116
1147
|
const outputToken = useNativeOutput ? wrappedNative : quoteToken;
|
|
1117
1148
|
const deadline = getDeadline();
|
|
1118
1149
|
// ✅ 优化:构建卖出交易数据的辅助函数
|
|
@@ -1157,71 +1188,38 @@ export async function directV3BatchSell(params) {
|
|
|
1157
1188
|
}
|
|
1158
1189
|
}
|
|
1159
1190
|
};
|
|
1160
|
-
// ✅
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1191
|
+
// ✅ 先找出输出最大的钱包索引(用于支付贿赂和利润)
|
|
1192
|
+
// 对于 V3 卖出,使用卖出金额作为输出估计
|
|
1193
|
+
const maxOutputIndex = findMaxFlowIndex(sellAmountsWei);
|
|
1194
|
+
// ✅ 计算贿赂金额(仅 BSC 链)
|
|
1195
|
+
const bribeWei = getBribeAmount(config, chain);
|
|
1196
|
+
const hasBribe = bribeWei > 0n && wallets.length > 0;
|
|
1197
|
+
// ✅ 计算 nonce 偏移(仅贿赂,已移除授权)
|
|
1198
|
+
const nonceOffsets = wallets.map((_, i) => i === maxOutputIndex && hasBribe ? 1 : 0);
|
|
1199
|
+
// ✅ 贿赂交易放在首位(提高 BlockRazor 打包优先级)
|
|
1200
|
+
const bribeTxs = [];
|
|
1201
|
+
if (hasBribe) {
|
|
1202
|
+
const bribeTx = await buildBribeTransaction(wallets[maxOutputIndex], bribeWei, nonces[maxOutputIndex], // 使用原始 nonce
|
|
1203
|
+
gasPrice, chainId, txType);
|
|
1204
|
+
bribeTxs.push(bribeTx);
|
|
1164
1205
|
}
|
|
1165
|
-
// ✅
|
|
1166
|
-
const
|
|
1167
|
-
if (config.skipApprovalCheck || sellAmountsWei[i] <= 0n)
|
|
1168
|
-
return 0;
|
|
1169
|
-
return allowances[i] < sellAmountsWei[i] ? 1 : 0;
|
|
1170
|
-
});
|
|
1171
|
-
// ✅ 优化:并行签名所有授权交易
|
|
1172
|
-
const approvalTxPromises = wallets.map(async (wallet, i) => {
|
|
1173
|
-
if (config.skipApprovalCheck || sellAmountsWei[i] <= 0n)
|
|
1174
|
-
return null;
|
|
1175
|
-
if (allowances[i] >= sellAmountsWei[i])
|
|
1176
|
-
return null;
|
|
1177
|
-
return wallet.signTransaction({
|
|
1178
|
-
to: tokenAddress,
|
|
1179
|
-
data: approveIface.encodeFunctionData('approve', [routerAddress, ethers.MaxUint256]),
|
|
1180
|
-
value: 0n,
|
|
1181
|
-
nonce: nonces[i],
|
|
1182
|
-
gasLimit: 60000n,
|
|
1183
|
-
gasPrice,
|
|
1184
|
-
chainId,
|
|
1185
|
-
type: txType,
|
|
1186
|
-
});
|
|
1187
|
-
});
|
|
1188
|
-
const approvalTxResults = await Promise.all(approvalTxPromises);
|
|
1189
|
-
// ✅ 优化:并行签名所有卖出交易
|
|
1190
|
-
const sellTxPromises = wallets.map(async (wallet, i) => {
|
|
1206
|
+
// ✅ 优化:并行签名所有卖出交易(已移除授权交易)
|
|
1207
|
+
const swapTxs = await Promise.all(wallets.map(async (wallet, i) => {
|
|
1191
1208
|
if (sellAmountsWei[i] <= 0n)
|
|
1192
1209
|
return null;
|
|
1193
1210
|
return wallet.signTransaction({
|
|
1194
1211
|
to: routerAddress,
|
|
1195
1212
|
data: buildV3SellTxData(wallet, sellAmountsWei[i]),
|
|
1196
1213
|
value: 0n,
|
|
1197
|
-
nonce: nonces[i] +
|
|
1214
|
+
nonce: nonces[i] + nonceOffsets[i],
|
|
1198
1215
|
gasLimit,
|
|
1199
1216
|
gasPrice,
|
|
1200
1217
|
chainId,
|
|
1201
1218
|
type: txType,
|
|
1202
1219
|
});
|
|
1203
|
-
});
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
const signedTxs = [];
|
|
1207
|
-
const outputEstimates = [];
|
|
1208
|
-
for (let i = 0; i < wallets.length; i++) {
|
|
1209
|
-
const approvalTx = approvalTxResults[i];
|
|
1210
|
-
const sellTx = sellTxResults[i];
|
|
1211
|
-
if (approvalTx)
|
|
1212
|
-
signedTxs.push(approvalTx);
|
|
1213
|
-
if (sellTx) {
|
|
1214
|
-
signedTxs.push(sellTx);
|
|
1215
|
-
outputEstimates.push(sellAmountsWei[i]);
|
|
1216
|
-
currentNonceOffset[i]++;
|
|
1217
|
-
}
|
|
1218
|
-
else {
|
|
1219
|
-
outputEstimates.push(0n);
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
const totalOutputEstimate = outputEstimates.reduce((sum, o) => sum + o, 0n);
|
|
1223
|
-
// ✅ 优化:选择输出最大的钱包支付利润
|
|
1224
|
-
const maxOutputIndex = findMaxFlowIndex(outputEstimates);
|
|
1220
|
+
})).then(results => results.filter((tx) => tx !== null));
|
|
1221
|
+
// ✅ 使用 sellAmountsWei 计算总输出
|
|
1222
|
+
const totalOutputEstimate = sellAmountsWei.reduce((sum, o) => sum + o, 0n);
|
|
1225
1223
|
let profitWei = calculateProfitAmount(totalOutputEstimate);
|
|
1226
1224
|
// ✅ 修复:ERC20 输出时,将利润转换为原生代币
|
|
1227
1225
|
if (!useNativeOutput && profitWei > 0n && quoteToken) {
|
|
@@ -1233,11 +1231,17 @@ export async function directV3BatchSell(params) {
|
|
|
1233
1231
|
profitWei = 0n;
|
|
1234
1232
|
}
|
|
1235
1233
|
}
|
|
1234
|
+
// ✅ 利润交易
|
|
1235
|
+
const profitTxs = [];
|
|
1236
1236
|
if (profitWei > 0n && wallets.length > 0) {
|
|
1237
|
+
// 利润交易 nonce = 原始 nonce + 贿赂偏移 + 1(卖出交易)
|
|
1238
|
+
const profitNonce = nonces[maxOutputIndex] + nonceOffsets[maxOutputIndex] + 1;
|
|
1237
1239
|
const profitTx = await buildProfitTransaction(wallets[maxOutputIndex], // ✅ 使用输出最大的钱包
|
|
1238
|
-
profitWei,
|
|
1239
|
-
|
|
1240
|
+
profitWei, profitNonce, gasPrice, chainId, txType);
|
|
1241
|
+
profitTxs.push(profitTx);
|
|
1240
1242
|
}
|
|
1243
|
+
// ✅ 组装最终交易列表:贿赂 → 卖出 → 利润
|
|
1244
|
+
const signedTxs = [...bribeTxs, ...swapTxs, ...profitTxs];
|
|
1241
1245
|
return {
|
|
1242
1246
|
signedTransactions: signedTxs,
|
|
1243
1247
|
metadata: {
|