four-flap-meme-sdk 1.4.35 → 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.
@@ -133,8 +133,8 @@ export function calculateGasLimit(config, isNative, hasHops, hopCount = 0) {
133
133
  if (config.gasLimit !== undefined) {
134
134
  return BigInt(config.gasLimit);
135
135
  }
136
- // ✅ 原生代币: 21000, ERC20 标准 transfer: 55000(实际约 45000-52000
137
- const baseGas = isNative ? 21000 : 55000;
136
+ // ✅ 原生代币: 21000, ERC20 标准 transfer: 48000(USDT 最低约 46815
137
+ const baseGas = isNative ? 21000 : 46815;
138
138
  // ✅ 多跳时只需要累加单次转账的 gas,不需要额外乘数
139
139
  // 每个中转钱包只执行一笔 transfer
140
140
  if (hasHops && hopCount > 0) {
@@ -355,26 +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
- // ✅ 原生代币多跳使用 21500 gas limit(21000 + 500 余量),避免中转钱包残留 BNB
359
- const hopGasLimit = isNative ? 21200n : finalGasLimit;
360
- const gasFeePerHop = hopGasLimit * gasPrice;
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;
361
368
  // ✅ 优化:预先计算主钱包需要的总 nonce 数量
369
+ // - 原生代币多跳:主钱包只需要 1 个 nonce(一笔转账包含后续所有 gas)
370
+ // - ERC20 多跳:主钱包需要 2 个 nonce(转 gas + 转 ERC20)
362
371
  let mainWalletNonceCount = 0;
363
- const recipientNonceNeeds = [];
364
372
  for (let i = 0; i < recipients.length; i++) {
365
373
  const hopChain = preparedHops[i];
366
374
  if (hopChain.length === 0) {
367
375
  // 无跳转:1 个 nonce
368
- recipientNonceNeeds.push(1);
369
376
  mainWalletNonceCount += 1;
370
377
  }
371
378
  else {
372
379
  // 有跳转:
373
- // - ERC20: hopChain.length (gas 费) + 1 (主转账)
374
- // - Native: 1 (主转账)
375
- const nonceNeed = isNative ? 1 : (hopChain.length + 1);
376
- recipientNonceNeeds.push(nonceNeed);
377
- mainWalletNonceCount += nonceNeed;
380
+ // - Native: 1(一笔转账)
381
+ // - ERC20: 2(转 gas + 转 ERC20)
382
+ mainWalletNonceCount += isNative ? 1 : 2;
378
383
  }
379
384
  }
380
385
  // 利润交易需要额外 1 个 nonce
@@ -444,32 +449,14 @@ export async function disperseWithBundleMerkle(params) {
444
449
  // 有跳转
445
450
  const fullChain = [mainWallet, ...hopChain.map(pk => new Wallet(pk, provider))];
446
451
  const addresses = [...fullChain.map(w => w.address), finalRecipient];
447
- // ERC20 多跳:先给中转钱包转 gas 费
448
- if (!isNative) {
449
- for (let j = 0; j < hopChain.length; j++) {
450
- const nonce = allMainNonces[mainNonceIdx++];
451
- txsToSign.push({
452
- wallet: mainWallet,
453
- tx: {
454
- to: fullChain[j + 1].address,
455
- value: gasFeePerHop,
456
- nonce,
457
- gasPrice,
458
- gasLimit: nativeGasLimit,
459
- chainId: chainIdNum,
460
- type: txType
461
- }
462
- });
463
- }
464
- }
465
- // 执行主要的转账链
466
- for (let j = 0; j < addresses.length - 1; j++) {
467
- const fromWallet = fullChain[j];
468
- const toAddress = addresses[j + 1];
469
- const nonce = j === 0 ? allMainNonces[mainNonceIdx++] : 0;
470
- 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;
471
458
  const remainingHops = addresses.length - 2 - j;
472
- const additionalGas = gasFeePerHop * BigInt(remainingHops);
459
+ const additionalGas = nativeHopGasFee * BigInt(remainingHops);
473
460
  const transferValue = amountWei + additionalGas;
474
461
  txsToSign.push({
475
462
  wallet: fromWallet,
@@ -478,27 +465,112 @@ export async function disperseWithBundleMerkle(params) {
478
465
  value: transferValue,
479
466
  nonce,
480
467
  gasPrice,
481
- gasLimit: hopGasLimit, // ✅ 原生代币使用精确的 21000
468
+ gasLimit: nativeTransferGasLimit,
482
469
  chainId: chainIdNum,
483
470
  type: txType
484
471
  }
485
472
  });
486
473
  }
487
- else {
488
- const data = iface.encodeFunctionData('transfer', [toAddress, amountWei]);
489
- txsToSign.push({
490
- wallet: fromWallet,
491
- tx: {
492
- to: tokenAddress,
493
- data,
494
- value: 0n,
495
- nonce,
496
- gasPrice,
497
- gasLimit: hopGasLimit, // ✅ ERC20 使用优化后的 60500
498
- chainId: chainIdNum,
499
- type: txType
500
- }
501
- });
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
+ }
502
574
  }
503
575
  }
504
576
  }
@@ -915,9 +987,14 @@ export async function sweepWithBundleMerkle(params) {
915
987
  isNative ? Promise.resolve([]) : _batchGetBalances(provider, sourceAddresses)
916
988
  ]);
917
989
  const iface = isNative ? null : new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
918
- // ✅ 原生代币多跳使用 21500 gas limit(21000 + 500 余量),避免中转钱包残留 BNB
919
- const hopGasLimit = isNative ? 21500n : finalGasLimit;
920
- const gasFeePerHop = hopGasLimit * gasPrice;
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;
921
998
  const nonceManager = new NonceManager(provider);
922
999
  // ✅ 用于记录每个钱包的归集金额
923
1000
  const sweepAmounts = new Array(sourceWallets.length).fill(0n);
@@ -1019,7 +1096,8 @@ export async function sweepWithBundleMerkle(params) {
1019
1096
  const ratioForI = getRatioForI(i);
1020
1097
  const amountStrForI = getAmountStrForI(i);
1021
1098
  if (isNative) {
1022
- const totalGasCost = finalGasLimit * gasPrice * BigInt(hopChain.length + 1);
1099
+ // 原生代币多跳每跳只需要 21000 gas
1100
+ const totalGasCost = nativeTransferGasLimit * gasPrice * BigInt(hopChain.length + 1);
1023
1101
  if (ratioForI !== undefined) {
1024
1102
  const want = (bal * BigInt(ratioForI)) / 100n;
1025
1103
  const maxSendable = bal > totalGasCost ? (bal - totalGasCost) : 0n;
@@ -1034,7 +1112,13 @@ export async function sweepWithBundleMerkle(params) {
1034
1112
  }
1035
1113
  else {
1036
1114
  const bnbBal = bnbBalances[i];
1037
- const bnbNeeded = (gasFeePerHop * BigInt(hopChain.length)) + (finalGasLimit * gasPrice);
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;
1038
1122
  if (bnbBal < bnbNeeded && skipIfInsufficient)
1039
1123
  continue;
1040
1124
  if (ratioForI !== undefined) {
@@ -1056,9 +1140,9 @@ export async function sweepWithBundleMerkle(params) {
1056
1140
  }
1057
1141
  // ========== 第5步:计算每个有跳转钱包需要的 nonce 数量并批量获取 ==========
1058
1142
  // 每个源钱包需要的 nonce 数量:
1059
- // - ERC20: hopChain.length (gas费交易) + 1 (主转账)
1060
- // - Native: 1 (主转账)
1061
- const hopNonceNeeds = hopTxDataList.map(d => isNative ? 1 : d.hopChain.length + 1);
1143
+ // - Native: 1(一笔转账包含后续所有 gas
1144
+ // - ERC20: 2(转 gas + 转 ERC20)
1145
+ const hopNonceNeeds = hopTxDataList.map(() => isNative ? 1 : 2);
1062
1146
  const hopSourceWallets = hopTxDataList.map(d => d.sourceWallet);
1063
1147
  // ✅ 批量获取所有有跳转钱包的 nonces
1064
1148
  const hopNoncesFlat = [];
@@ -1074,32 +1158,14 @@ export async function sweepWithBundleMerkle(params) {
1074
1158
  const nonceCount = hopNonceNeeds[dataIdx];
1075
1159
  const nonces = hopNoncesFlat.slice(nonceOffset, nonceOffset + nonceCount);
1076
1160
  nonceOffset += nonceCount;
1077
- let nonceIdx = 0;
1078
- // ERC20 多跳:先给中转钱包转 gas
1079
- if (!isNative) {
1080
- for (let j = 0; j < hopChain.length; j++) {
1081
- hopTxsToSign.push({
1082
- wallet: sourceWallet,
1083
- tx: {
1084
- to: fullChain[j + 1].address,
1085
- value: gasFeePerHop,
1086
- nonce: nonces[nonceIdx++],
1087
- gasPrice,
1088
- gasLimit: nativeGasLimit,
1089
- chainId: chainIdNum,
1090
- type: txType
1091
- }
1092
- });
1093
- }
1094
- }
1095
- // 执行主要的归集链
1096
- for (let j = 0; j < addresses.length - 1; j++) {
1097
- const fromWallet = fullChain[j];
1098
- const toAddress = addresses[j + 1];
1099
- const nonce = j === 0 ? nonces[nonceIdx++] : 0;
1100
- 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;
1101
1167
  const remainingHops = addresses.length - 2 - j;
1102
- const additionalGas = gasFeePerHop * BigInt(remainingHops);
1168
+ const additionalGas = nativeHopGasFee * BigInt(remainingHops);
1103
1169
  const valueToTransfer = toSend + additionalGas;
1104
1170
  hopTxsToSign.push({
1105
1171
  wallet: fromWallet,
@@ -1108,27 +1174,110 @@ export async function sweepWithBundleMerkle(params) {
1108
1174
  value: valueToTransfer,
1109
1175
  nonce,
1110
1176
  gasPrice,
1111
- gasLimit: hopGasLimit, // ✅ 原生代币使用精确的 21000
1177
+ gasLimit: nativeTransferGasLimit,
1112
1178
  chainId: chainIdNum,
1113
1179
  type: txType
1114
1180
  }
1115
1181
  });
1116
1182
  }
1117
- else {
1118
- const data = iface.encodeFunctionData('transfer', [toAddress, toSend]);
1119
- hopTxsToSign.push({
1120
- wallet: fromWallet,
1121
- tx: {
1122
- to: tokenAddress,
1123
- data,
1124
- value: 0n,
1125
- nonce,
1126
- gasPrice,
1127
- gasLimit: hopGasLimit, // ✅ ERC20 使用优化后的 60500
1128
- chainId: chainIdNum,
1129
- type: txType
1130
- }
1131
- });
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
+ }
1132
1281
  }
1133
1282
  }
1134
1283
  }
@@ -137,8 +137,8 @@ function calculateGasLimit(config, isNative, hasHops, _hopCount = 0) {
137
137
  if (config.gasLimit !== undefined) {
138
138
  return BigInt(config.gasLimit);
139
139
  }
140
- // ✅ 原生代币: 21000, ERC20 标准 transfer: 55000(实际约 45000-52000
141
- const baseGas = isNative ? 21000 : 55000;
140
+ // ✅ 原生代币: 21000, ERC20 标准 transfer: 48000(USDT 最低约 46815
141
+ const baseGas = isNative ? 21000 : 48000;
142
142
  // ✅ 多跳时每个中转钱包只执行一笔 transfer,使用较小的安全系数
143
143
  const multiplier = config.gasLimitMultiplier ?? 1.1;
144
144
  return BigInt(Math.ceil(baseGas * multiplier));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.4.35",
3
+ "version": "1.4.36",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",