four-flap-meme-sdk 1.4.24 → 1.4.25

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.
@@ -893,7 +893,7 @@ export async function sweepWithBundleMerkle(params) {
893
893
  }
894
894
  else {
895
895
  // ========== 有多跳:构建跳转链归集 ==========
896
- // 分离有跳转和无跳转的地址(不需要 RPC)
896
+ // 优化版:批量计算 + 批量获取 nonce + 并行签名
897
897
  const sourceWallets = actualKeys.map(pk => new Wallet(pk, provider));
898
898
  const withHopIndexes = [];
899
899
  const withoutHopIndexes = [];
@@ -906,7 +906,7 @@ export async function sweepWithBundleMerkle(params) {
906
906
  }
907
907
  }
908
908
  const sourceAddresses = sourceWallets.map(w => w.address);
909
- // ✅ 优化:并行获取 gasPrice、decimals、余额(传入 chainIdNum 避免 NETWORK_ERROR)
909
+ // ✅ 优化:并行获取 gasPrice、decimals、余额
910
910
  const [gasPrice, decimals, balances, bnbBalances] = await Promise.all([
911
911
  getOptimizedGasPrice(provider, getGasPriceConfig(config)),
912
912
  isNative ? Promise.resolve(18) : Promise.resolve(tokenDecimals ?? await _getErc20DecimalsMerkle(provider, tokenAddress, chainIdNum)),
@@ -916,122 +916,107 @@ export async function sweepWithBundleMerkle(params) {
916
916
  const iface = isNative ? null : new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
917
917
  const gasFeePerHop = finalGasLimit * gasPrice;
918
918
  const nonceManager = new NonceManager(provider);
919
- // ✅ 用于记录每个钱包的归集金额(用于计算利润)
919
+ // ✅ 用于记录每个钱包的归集金额
920
920
  const sweepAmounts = new Array(sourceWallets.length).fill(0n);
921
- // 处理无跳转的地址(批量)
922
- if (withoutHopIndexes.length > 0) {
923
- for (let idx = 0; idx < withoutHopIndexes.length; idx++) {
924
- const i = withoutHopIndexes[idx];
925
- const sourceWallet = sourceWallets[i];
926
- const bal = balances[i];
927
- let toSend = 0n;
928
- if (isNative) {
929
- const gasCost = nativeGasLimit * gasPrice;
930
- const ratioForI = (() => {
931
- if (sources && sources[i] && sources[i].ratioPct !== undefined)
932
- return clamp(sources[i].ratioPct);
933
- if (ratios && ratios[i] !== undefined)
934
- return clamp(ratios[i]);
935
- return ratio;
936
- })();
937
- const amountStrForI = (() => {
938
- if (sources && sources[i] && sources[i].amount !== undefined)
939
- return String(sources[i].amount);
940
- if (amounts && amounts[i] !== undefined)
941
- return String(amounts[i]);
942
- return amount !== undefined ? String(amount) : undefined;
943
- })();
944
- if (ratioForI !== undefined) {
945
- const want = (bal * BigInt(ratioForI)) / 100n;
946
- const maxSendable = bal > gasCost ? (bal - gasCost) : 0n;
947
- toSend = want > maxSendable ? maxSendable : want;
948
- }
949
- else if (amountStrForI && amountStrForI.trim().length > 0) {
950
- const amt = ethers.parseEther(amountStrForI);
951
- const need = amt + gasCost;
952
- if (!skipIfInsufficient || bal >= need)
953
- toSend = amt;
954
- }
955
- if (toSend > 0n) {
956
- sweepAmounts[i] = toSend; // ✅ 记录归集金额
957
- totalAmountBeforeProfit += toSend;
958
- const nonce = await nonceManager.getNextNonce(sourceWallet);
959
- const tx = await sourceWallet.signTransaction({
960
- to: target,
961
- value: toSend,
962
- nonce,
963
- gasPrice,
964
- gasLimit: nativeGasLimit,
965
- chainId: chainIdNum,
966
- type: txType
967
- });
968
- signedTxs.push(tx);
969
- }
921
+ // ✅ 辅助函数:获取比例和金额
922
+ const getRatioForI = (i) => {
923
+ if (sources && sources[i] && sources[i].ratioPct !== undefined)
924
+ return clamp(sources[i].ratioPct);
925
+ if (ratios && ratios[i] !== undefined)
926
+ return clamp(ratios[i]);
927
+ return ratio;
928
+ };
929
+ const getAmountStrForI = (i) => {
930
+ if (sources && sources[i] && sources[i].amount !== undefined)
931
+ return String(sources[i].amount);
932
+ if (amounts && amounts[i] !== undefined)
933
+ return String(amounts[i]);
934
+ return amount !== undefined ? String(amount) : undefined;
935
+ };
936
+ const noHopTxDataList = [];
937
+ for (const i of withoutHopIndexes) {
938
+ const bal = balances[i];
939
+ let toSend = 0n;
940
+ const ratioForI = getRatioForI(i);
941
+ const amountStrForI = getAmountStrForI(i);
942
+ if (isNative) {
943
+ const gasCost = nativeGasLimit * gasPrice;
944
+ if (ratioForI !== undefined) {
945
+ const want = (bal * BigInt(ratioForI)) / 100n;
946
+ const maxSendable = bal > gasCost ? (bal - gasCost) : 0n;
947
+ toSend = want > maxSendable ? maxSendable : want;
970
948
  }
971
- else {
972
- const ratioForI = (() => {
973
- if (sources && sources[i] && sources[i].ratioPct !== undefined)
974
- return clamp(sources[i].ratioPct);
975
- if (ratios && ratios[i] !== undefined)
976
- return clamp(ratios[i]);
977
- return ratio;
978
- })();
979
- const amountStrForI = (() => {
980
- if (sources && sources[i] && sources[i].amount !== undefined)
981
- return String(sources[i].amount);
982
- if (amounts && amounts[i] !== undefined)
983
- return String(amounts[i]);
984
- return amount !== undefined ? String(amount) : undefined;
985
- })();
986
- if (ratioForI !== undefined) {
987
- toSend = (bal * BigInt(ratioForI)) / 100n;
988
- }
989
- else if (amountStrForI && amountStrForI.trim().length > 0) {
990
- toSend = ethers.parseUnits(amountStrForI, decimals);
991
- }
992
- if (toSend > 0n && (!skipIfInsufficient || bal >= toSend)) {
993
- sweepAmounts[i] = toSend; // ✅ 记录归集金额
994
- totalAmountBeforeProfit += toSend;
995
- const nonce = await nonceManager.getNextNonce(sourceWallet);
996
- const data = iface.encodeFunctionData('transfer', [target, toSend]);
997
- const tx = await sourceWallet.signTransaction({
998
- to: tokenAddress,
999
- data,
1000
- value: 0n,
1001
- nonce,
1002
- gasPrice,
1003
- gasLimit: finalGasLimit,
1004
- chainId: chainIdNum,
1005
- type: txType
1006
- });
1007
- signedTxs.push(tx);
1008
- }
949
+ else if (amountStrForI && amountStrForI.trim().length > 0) {
950
+ const amt = ethers.parseEther(amountStrForI);
951
+ const need = amt + gasCost;
952
+ if (!skipIfInsufficient || bal >= need)
953
+ toSend = amt;
1009
954
  }
1010
955
  }
956
+ else {
957
+ if (ratioForI !== undefined) {
958
+ toSend = (bal * BigInt(ratioForI)) / 100n;
959
+ }
960
+ else if (amountStrForI && amountStrForI.trim().length > 0) {
961
+ toSend = ethers.parseUnits(amountStrForI, decimals);
962
+ }
963
+ if (skipIfInsufficient && bal < toSend)
964
+ toSend = 0n;
965
+ }
966
+ if (toSend > 0n) {
967
+ sweepAmounts[i] = toSend;
968
+ totalAmountBeforeProfit += toSend;
969
+ noHopTxDataList.push({ index: i, wallet: sourceWallets[i], toSend });
970
+ }
971
+ }
972
+ // ========== 第2步:批量获取无跳转钱包的 nonces ==========
973
+ const noHopWallets = noHopTxDataList.map(d => d.wallet);
974
+ const noHopNonces = noHopWallets.length > 0
975
+ ? await nonceManager.getNextNoncesForWallets(noHopWallets)
976
+ : [];
977
+ // ========== 第3步:并行签名无跳转交易 ==========
978
+ const noHopTxPromises = noHopTxDataList.map((data, idx) => {
979
+ const { wallet, toSend } = data;
980
+ const nonce = noHopNonces[idx];
981
+ if (isNative) {
982
+ return wallet.signTransaction({
983
+ to: target,
984
+ value: toSend,
985
+ nonce,
986
+ gasPrice,
987
+ gasLimit: nativeGasLimit,
988
+ chainId: chainIdNum,
989
+ type: txType
990
+ });
991
+ }
992
+ else {
993
+ const txData = iface.encodeFunctionData('transfer', [target, toSend]);
994
+ return wallet.signTransaction({
995
+ to: tokenAddress,
996
+ data: txData,
997
+ value: 0n,
998
+ nonce,
999
+ gasPrice,
1000
+ gasLimit: finalGasLimit,
1001
+ chainId: chainIdNum,
1002
+ type: txType
1003
+ });
1004
+ }
1005
+ });
1006
+ if (noHopTxPromises.length > 0) {
1007
+ const noHopSignedTxs = await Promise.all(noHopTxPromises);
1008
+ signedTxs.push(...noHopSignedTxs);
1011
1009
  }
1012
- // 处理有跳转的地址
1010
+ const hopTxDataList = [];
1013
1011
  for (const i of withHopIndexes) {
1014
1012
  const sourceWallet = sourceWallets[i];
1015
1013
  const hopChain = preparedHops[i];
1016
- // 计算要归集的金额
1017
- let toSend = 0n;
1018
1014
  const bal = balances[i];
1015
+ let toSend = 0n;
1016
+ const ratioForI = getRatioForI(i);
1017
+ const amountStrForI = getAmountStrForI(i);
1019
1018
  if (isNative) {
1020
1019
  const totalGasCost = finalGasLimit * gasPrice * BigInt(hopChain.length + 1);
1021
- const ratioForI = (() => {
1022
- if (sources && sources[i] && sources[i].ratioPct !== undefined)
1023
- return clamp(sources[i].ratioPct);
1024
- if (ratios && ratios[i] !== undefined)
1025
- return clamp(ratios[i]);
1026
- return ratio;
1027
- })();
1028
- const amountStrForI = (() => {
1029
- if (sources && sources[i] && sources[i].amount !== undefined)
1030
- return String(sources[i].amount);
1031
- if (amounts && amounts[i] !== undefined)
1032
- return String(amounts[i]);
1033
- return amount !== undefined ? String(amount) : undefined;
1034
- })();
1035
1020
  if (ratioForI !== undefined) {
1036
1021
  const want = (bal * BigInt(ratioForI)) / 100n;
1037
1022
  const maxSendable = bal > totalGasCost ? (bal - totalGasCost) : 0n;
@@ -1045,25 +1030,10 @@ export async function sweepWithBundleMerkle(params) {
1045
1030
  }
1046
1031
  }
1047
1032
  else {
1048
- // ERC20:检查 BNB 余额是否足够支付 gas
1049
1033
  const bnbBal = bnbBalances[i];
1050
1034
  const bnbNeeded = (gasFeePerHop * BigInt(hopChain.length)) + (finalGasLimit * gasPrice);
1051
1035
  if (bnbBal < bnbNeeded && skipIfInsufficient)
1052
1036
  continue;
1053
- const ratioForI = (() => {
1054
- if (sources && sources[i] && sources[i].ratioPct !== undefined)
1055
- return clamp(sources[i].ratioPct);
1056
- if (ratios && ratios[i] !== undefined)
1057
- return clamp(ratios[i]);
1058
- return ratio;
1059
- })();
1060
- const amountStrForI = (() => {
1061
- if (sources && sources[i] && sources[i].amount !== undefined)
1062
- return String(sources[i].amount);
1063
- if (amounts && amounts[i] !== undefined)
1064
- return String(amounts[i]);
1065
- return amount !== undefined ? String(amount) : undefined;
1066
- })();
1067
1037
  if (ratioForI !== undefined) {
1068
1038
  toSend = (bal * BigInt(ratioForI)) / 100n;
1069
1039
  }
@@ -1075,76 +1045,96 @@ export async function sweepWithBundleMerkle(params) {
1075
1045
  }
1076
1046
  if (toSend <= 0n)
1077
1047
  continue;
1078
- // ✅ 记录归集金额
1079
1048
  sweepAmounts[i] = toSend;
1080
1049
  totalAmountBeforeProfit += toSend;
1081
- // 构建跳转链: 子钱包 -> 中转1 -> 中转2 -> ... -> 目标地址
1082
1050
  const fullChain = [sourceWallet, ...hopChain.map(pk => new Wallet(pk, provider))];
1083
1051
  const addresses = [...fullChain.map(w => w.address), target];
1052
+ hopTxDataList.push({ index: i, sourceWallet, toSend, hopChain, fullChain, addresses });
1053
+ }
1054
+ // ========== 第5步:计算每个有跳转钱包需要的 nonce 数量并批量获取 ==========
1055
+ // 每个源钱包需要的 nonce 数量:
1056
+ // - ERC20: hopChain.length (gas费交易) + 1 (主转账)
1057
+ // - Native: 1 (主转账)
1058
+ const hopNonceNeeds = hopTxDataList.map(d => isNative ? 1 : d.hopChain.length + 1);
1059
+ const hopSourceWallets = hopTxDataList.map(d => d.sourceWallet);
1060
+ // ✅ 批量获取所有有跳转钱包的 nonces
1061
+ const hopNoncesFlat = [];
1062
+ if (hopSourceWallets.length > 0) {
1063
+ const batchNoncePromises = hopSourceWallets.map((w, idx) => nonceManager.getNextNonceBatch(w, hopNonceNeeds[idx]));
1064
+ const batchNonces = await Promise.all(batchNoncePromises);
1065
+ batchNonces.forEach(nonces => hopNoncesFlat.push(...nonces));
1066
+ }
1067
+ const hopTxsToSign = [];
1068
+ let nonceOffset = 0;
1069
+ for (let dataIdx = 0; dataIdx < hopTxDataList.length; dataIdx++) {
1070
+ const { sourceWallet, toSend, hopChain, fullChain, addresses } = hopTxDataList[dataIdx];
1071
+ const nonceCount = hopNonceNeeds[dataIdx];
1072
+ const nonces = hopNoncesFlat.slice(nonceOffset, nonceOffset + nonceCount);
1073
+ nonceOffset += nonceCount;
1074
+ let nonceIdx = 0;
1084
1075
  // ERC20 多跳:先给中转钱包转 gas 费
1085
1076
  if (!isNative) {
1086
- const gasNonces = await nonceManager.getNextNonceBatch(sourceWallet, hopChain.length);
1087
- const gasTxs = [];
1088
1077
  for (let j = 0; j < hopChain.length; j++) {
1089
- const tx = await sourceWallet.signTransaction({
1090
- to: fullChain[j + 1].address,
1091
- value: gasFeePerHop,
1092
- nonce: gasNonces[j],
1093
- gasPrice,
1094
- gasLimit: nativeGasLimit,
1095
- chainId: chainIdNum,
1096
- type: txType
1078
+ hopTxsToSign.push({
1079
+ wallet: sourceWallet,
1080
+ tx: {
1081
+ to: fullChain[j + 1].address,
1082
+ value: gasFeePerHop,
1083
+ nonce: nonces[nonceIdx++],
1084
+ gasPrice,
1085
+ gasLimit: nativeGasLimit,
1086
+ chainId: chainIdNum,
1087
+ type: txType
1088
+ }
1097
1089
  });
1098
- gasTxs.push(tx);
1099
1090
  }
1100
- signedTxs.push(...gasTxs);
1101
1091
  }
1102
1092
  // 执行主要的归集链
1103
1093
  for (let j = 0; j < addresses.length - 1; j++) {
1104
1094
  const fromWallet = fullChain[j];
1105
1095
  const toAddress = addresses[j + 1];
1106
- // 使用 NonceManager:源钱包查询,中转钱包从 0 开始
1107
- const nonce = j === 0
1108
- ? await nonceManager.getNextNonce(sourceWallet)
1109
- : 0;
1096
+ const nonce = j === 0 ? nonces[nonceIdx++] : 0;
1110
1097
  if (isNative) {
1111
- // 原生币归集:每一跳需要附加后续跳数的gas费
1112
- // toSend = 最终主钱包收到的净金额
1113
- // 剩余跳数 = 还需要几跳才能到目标地址
1114
1098
  const remainingHops = addresses.length - 2 - j;
1115
1099
  const additionalGas = gasFeePerHop * BigInt(remainingHops);
1116
1100
  const valueToTransfer = toSend + additionalGas;
1117
- // 示例:hopCount=1(2跳),toSend=0.002874
1118
- // j=0: 子钱包->中转1,剩余1跳,转 0.002874 + 1*gas = 0.002937
1119
- // j=1: 中转1->主钱包,剩余0跳,转 0.002874 + 0 = 0.002874
1120
- const tx = await fromWallet.signTransaction({
1121
- to: toAddress,
1122
- value: valueToTransfer,
1123
- nonce,
1124
- gasPrice,
1125
- gasLimit: finalGasLimit,
1126
- chainId: chainIdNum,
1127
- type: txType
1101
+ hopTxsToSign.push({
1102
+ wallet: fromWallet,
1103
+ tx: {
1104
+ to: toAddress,
1105
+ value: valueToTransfer,
1106
+ nonce,
1107
+ gasPrice,
1108
+ gasLimit: finalGasLimit,
1109
+ chainId: chainIdNum,
1110
+ type: txType
1111
+ }
1128
1112
  });
1129
- signedTxs.push(tx);
1130
1113
  }
1131
1114
  else {
1132
1115
  const data = iface.encodeFunctionData('transfer', [toAddress, toSend]);
1133
- const tx = await fromWallet.signTransaction({
1134
- to: tokenAddress,
1135
- data,
1136
- value: 0n,
1137
- nonce,
1138
- gasPrice,
1139
- gasLimit: finalGasLimit,
1140
- chainId: chainIdNum,
1141
- type: txType
1116
+ hopTxsToSign.push({
1117
+ wallet: fromWallet,
1118
+ tx: {
1119
+ to: tokenAddress,
1120
+ data,
1121
+ value: 0n,
1122
+ nonce,
1123
+ gasPrice,
1124
+ gasLimit: finalGasLimit,
1125
+ chainId: chainIdNum,
1126
+ type: txType
1127
+ }
1142
1128
  });
1143
- signedTxs.push(tx);
1144
1129
  }
1145
1130
  }
1146
1131
  }
1147
- // ✅ 多跳模式:计算利润并添加利润转账
1132
+ // ✅ 并行签名所有有跳转的交易
1133
+ if (hopTxsToSign.length > 0) {
1134
+ const hopSignedTxs = await Promise.all(hopTxsToSign.map(({ wallet, tx }) => wallet.signTransaction(tx)));
1135
+ signedTxs.push(...hopSignedTxs);
1136
+ }
1137
+ // ========== 第7步:计算利润并添加利润转账 ==========
1148
1138
  if (extractProfit && totalAmountBeforeProfit > 0n) {
1149
1139
  // 找出归集金额最大的钱包作为支付者
1150
1140
  let maxSweepIndex = -1;
@@ -1161,26 +1151,26 @@ export async function sweepWithBundleMerkle(params) {
1161
1151
  if (sweepAmounts[i] > 0n) {
1162
1152
  const { profit } = calculateProfit(sweepAmounts[i]);
1163
1153
  if (isNative) {
1164
- totalProfit += profit; // 原生币直接累加
1154
+ totalProfit += profit;
1165
1155
  }
1166
1156
  else {
1167
- totalTokenProfit += profit; // ERC20 累计代币利润
1157
+ totalTokenProfit += profit;
1168
1158
  }
1169
1159
  }
1170
1160
  }
1171
- // ✅ ERC20 多跳归集:获取代币利润等值的原生代币报价
1161
+ // ✅ ERC20:获取代币利润等值的原生代币报价
1172
1162
  let nativeProfitAmount = isNative ? totalProfit : 0n;
1173
1163
  if (!isNative && totalTokenProfit > 0n) {
1174
1164
  nativeProfitAmount = await getTokenToNativeQuote(provider, tokenAddress, totalTokenProfit, chainIdNum, tokenPoolType, quoteToken, config.rpcUrl);
1175
- totalProfit = nativeProfitAmount; // 更新为原生代币利润
1165
+ totalProfit = nativeProfitAmount;
1176
1166
  }
1177
- // 由归集金额最大的钱包支付利润(转等值原生代币)
1167
+ // 由归集金额最大的钱包支付利润
1178
1168
  if (nativeProfitAmount > 0n && maxSweepIndex >= 0) {
1179
1169
  const payerWallet = sourceWallets[maxSweepIndex];
1180
1170
  const profitNonce = await nonceManager.getNextNonce(payerWallet);
1181
1171
  const profitTx = await payerWallet.signTransaction({
1182
1172
  to: getProfitRecipient(),
1183
- value: nativeProfitAmount, // ✅ 转等值原生代币
1173
+ value: nativeProfitAmount,
1184
1174
  nonce: profitNonce,
1185
1175
  gasPrice,
1186
1176
  gasLimit: 21000n,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.4.24",
3
+ "version": "1.4.25",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",