four-flap-meme-sdk 1.2.21 → 1.2.23

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.
@@ -345,12 +345,17 @@ export async function sweepWithBundleMerkle(params) {
345
345
  const txType = getTxType(config);
346
346
  const nonceManager = new NonceManager(provider);
347
347
  if (isNative) {
348
- // ✅ 原生币:批量查询余额,扣除利润
348
+ // ✅ 原生币:批量查询余额,统一扣除利润
349
349
  const balances = await _batchGetBalances(provider, addresses);
350
- const extraTxPerWallet = extractProfit ? 1 : 0; // 每个钱包需要额外1笔利润转账
351
- const gasCostBase = nativeGasLimit * gasPrice; // 基础 gas
352
- const gasCost = gasCostBase * BigInt(1 + extraTxPerWallet); // 总 gas 费(含利润转账)
353
- const txPromises = wallets.map(async (w, i) => {
350
+ const gasCostBase = nativeGasLimit * gasPrice; // 主转账 gas 费
351
+ // 如果需要提取利润,预留利润转账的 gas(使用相同的 nativeGasLimit)
352
+ const profitTxGas = nativeGasLimit * gasPrice;
353
+ const gasCost = extractProfit ? (gasCostBase + profitTxGas) : gasCostBase;
354
+ // ✅ 第一步:计算所有钱包的归集金额和利润,找出归集金额最大的钱包
355
+ const sweepAmounts = [];
356
+ let maxSweepIndex = -1;
357
+ let maxSweepAmount = 0n;
358
+ for (let i = 0; i < wallets.length; i++) {
354
359
  const bal = balances[i];
355
360
  let toSend = 0n;
356
361
  const ratioForI = (() => {
@@ -378,63 +383,76 @@ export async function sweepWithBundleMerkle(params) {
378
383
  if (!skipIfInsufficient || bal >= need)
379
384
  toSend = amt;
380
385
  }
381
- if (toSend <= 0n)
382
- return [];
386
+ sweepAmounts.push(toSend);
383
387
  totalAmountBeforeProfit += toSend;
384
- // ✅ 计算利润并扣除
388
+ // 找出归集金额最大的钱包
389
+ if (toSend > maxSweepAmount) {
390
+ maxSweepAmount = toSend;
391
+ maxSweepIndex = i;
392
+ }
393
+ // 累计总利润
394
+ if (extractProfit && toSend > 0n) {
395
+ const { profit } = calculateProfit(toSend, config);
396
+ totalProfit += profit;
397
+ }
398
+ }
399
+ // ✅ 第二步:生成归集交易(扣除利润后的金额)
400
+ const txPromises = wallets.map(async (w, i) => {
401
+ const toSend = sweepAmounts[i];
402
+ if (toSend <= 0n)
403
+ return null;
385
404
  let actualToSend = toSend;
386
- let profitAmount = 0n;
387
405
  if (extractProfit) {
388
- const { profit, remaining } = calculateProfit(toSend, config);
406
+ const { remaining } = calculateProfit(toSend, config);
389
407
  actualToSend = remaining;
390
- profitAmount = profit;
391
- totalProfit += profit;
392
408
  }
393
409
  const nonce = await nonceManager.getNextNonce(w);
394
- const nonces = [nonce, nonce + 1];
395
- const txs = [];
396
- // 归集到target
397
410
  const mainTx = await w.signTransaction({
398
411
  to: target,
399
- value: actualToSend, // ✅ 扣除利润后的金额
400
- nonce: nonces[0],
412
+ value: actualToSend,
413
+ nonce,
401
414
  gasPrice,
402
415
  gasLimit: nativeGasLimit,
403
416
  chainId: chainIdNum,
404
417
  type: txType
405
418
  });
406
- txs.push(mainTx);
407
- // ✅ 利润转账
408
- if (extractProfit && profitAmount > 0n) {
409
- const profitTx = await w.signTransaction({
410
- to: config.profitRecipient,
411
- value: profitAmount,
412
- nonce: nonces[1],
413
- gasPrice,
414
- gasLimit: 21000n,
415
- chainId: chainIdNum,
416
- type: txType
417
- });
418
- txs.push(profitTx);
419
- }
420
- return txs;
419
+ return mainTx;
421
420
  });
422
- const allTxs = (await Promise.all(txPromises)).flat().filter(tx => tx !== null);
421
+ const allTxs = (await Promise.all(txPromises)).filter(tx => tx !== null);
423
422
  signedTxs.push(...allTxs);
423
+ // ✅ 第三步:由归集金额最大的钱包统一支付所有利润
424
+ if (extractProfit && totalProfit > 0n && maxSweepIndex >= 0) {
425
+ const payerWallet = wallets[maxSweepIndex];
426
+ const profitNonce = await nonceManager.getNextNonce(payerWallet);
427
+ const profitTx = await payerWallet.signTransaction({
428
+ to: config.profitRecipient,
429
+ value: totalProfit,
430
+ nonce: profitNonce,
431
+ gasPrice,
432
+ gasLimit: nativeGasLimit, // ✅ 使用用户配置的 gasLimit
433
+ chainId: chainIdNum,
434
+ type: txType
435
+ });
436
+ signedTxs.push(profitTx);
437
+ }
424
438
  }
425
439
  else {
426
- // ✅ ERC20:批量查询余额,扣除利润
440
+ // ✅ ERC20:批量查询余额,统一扣除利润
427
441
  const [decimals, balances, bnbBalances] = await Promise.all([
428
442
  Promise.resolve(tokenDecimals ?? (await _getErc20DecimalsMerkle(provider, tokenAddress))),
429
443
  _batchGetBalances(provider, addresses, tokenAddress),
430
444
  config.checkBnbForErc20NoHop ? _batchGetBalances(provider, addresses) : Promise.resolve([])
431
445
  ]);
432
446
  const iface = new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
433
- const extraTxPerWallet = extractProfit ? 1 : 0; // 每个钱包需要额外1笔利润转账
434
- const txPromises = wallets.map(async (w, i) => {
447
+ // 如果需要提取利润,预留利润转账的 gas(使用相同的 finalGasLimit)
448
+ const profitTxGas = finalGasLimit * gasPrice;
449
+ // ✅ 第一步:计算所有钱包的归集金额和利润,找出归集金额最大的钱包
450
+ const sweepAmounts = [];
451
+ let maxSweepIndex = -1;
452
+ let maxSweepAmount = 0n;
453
+ for (let i = 0; i < wallets.length; i++) {
435
454
  const bal = balances[i];
436
455
  let toSend = 0n;
437
- // 逐地址优先级:sources[i] > ratios[i]/amounts[i] > 全局 ratio/amount
438
456
  const ratioForI = (() => {
439
457
  if (sources && sources[i] && sources[i].ratioPct !== undefined)
440
458
  return clamp(sources[i].ratioPct);
@@ -455,60 +473,75 @@ export async function sweepWithBundleMerkle(params) {
455
473
  else if (amountStrForI && amountStrForI.trim().length > 0) {
456
474
  toSend = ethers.parseUnits(amountStrForI, decimals);
457
475
  }
458
- if (toSend <= 0n || (skipIfInsufficient && bal < toSend))
459
- return [];
460
- // ✅ 检查 BNB 余额(含利润转账的 gas)
461
- const totalGasNeeded = finalGasLimit * gasPrice * BigInt(1 + extraTxPerWallet);
476
+ if (toSend <= 0n || (skipIfInsufficient && bal < toSend)) {
477
+ sweepAmounts.push(0n);
478
+ continue;
479
+ }
480
+ // ✅ 检查 BNB 余额(归集 + 可能的利润转账 gas)
481
+ const totalGasNeeded = extractProfit ? (finalGasLimit * gasPrice + profitTxGas) : (finalGasLimit * gasPrice);
462
482
  if (config.checkBnbForErc20NoHop) {
463
483
  const bnb = bnbBalances[i] ?? 0n;
464
- if (skipIfInsufficient && bnb < totalGasNeeded)
465
- return [];
484
+ if (skipIfInsufficient && bnb < totalGasNeeded) {
485
+ sweepAmounts.push(0n);
486
+ continue;
487
+ }
466
488
  }
489
+ sweepAmounts.push(toSend);
467
490
  totalAmountBeforeProfit += toSend;
468
- // ✅ 计算利润并扣除
491
+ // 找出归集金额最大的钱包
492
+ if (toSend > maxSweepAmount) {
493
+ maxSweepAmount = toSend;
494
+ maxSweepIndex = i;
495
+ }
496
+ // 累计总利润
497
+ if (extractProfit && toSend > 0n) {
498
+ const { profit } = calculateProfit(toSend, config);
499
+ totalProfit += profit;
500
+ }
501
+ }
502
+ // ✅ 第二步:生成归集交易(扣除利润后的金额)
503
+ const txPromises = wallets.map(async (w, i) => {
504
+ const toSend = sweepAmounts[i];
505
+ if (toSend <= 0n)
506
+ return null;
469
507
  let actualToSend = toSend;
470
- let profitAmount = 0n;
471
508
  if (extractProfit) {
472
- const { profit, remaining } = calculateProfit(toSend, config);
509
+ const { remaining } = calculateProfit(toSend, config);
473
510
  actualToSend = remaining;
474
- profitAmount = profit;
475
- totalProfit += profit;
476
511
  }
477
512
  const nonce = await nonceManager.getNextNonce(w);
478
- const nonces = [nonce, nonce + 1];
479
- const txs = [];
480
- // 归集到target
481
- const data = iface.encodeFunctionData('transfer', [target, actualToSend]); // ✅ 扣除利润后的金额
513
+ const data = iface.encodeFunctionData('transfer', [target, actualToSend]);
482
514
  const mainTx = await w.signTransaction({
483
515
  to: tokenAddress,
484
516
  data,
485
517
  value: 0n,
486
- nonce: nonces[0],
518
+ nonce,
487
519
  gasPrice,
488
520
  gasLimit: finalGasLimit,
489
521
  chainId: chainIdNum,
490
522
  type: txType
491
523
  });
492
- txs.push(mainTx);
493
- // ✅ 利润转账
494
- if (extractProfit && profitAmount > 0n) {
495
- const profitData = iface.encodeFunctionData('transfer', [config.profitRecipient, profitAmount]);
496
- const profitTx = await w.signTransaction({
497
- to: tokenAddress,
498
- data: profitData,
499
- value: 0n,
500
- nonce: nonces[1],
501
- gasPrice,
502
- gasLimit: 60000n,
503
- chainId: chainIdNum,
504
- type: txType
505
- });
506
- txs.push(profitTx);
507
- }
508
- return txs;
524
+ return mainTx;
509
525
  });
510
- const allTxs = (await Promise.all(txPromises)).flat().filter(tx => tx !== null);
526
+ const allTxs = (await Promise.all(txPromises)).filter(tx => tx !== null);
511
527
  signedTxs.push(...allTxs);
528
+ // ✅ 第三步:由归集金额最大的钱包统一支付所有利润
529
+ if (extractProfit && totalProfit > 0n && maxSweepIndex >= 0) {
530
+ const payerWallet = wallets[maxSweepIndex];
531
+ const profitNonce = await nonceManager.getNextNonce(payerWallet);
532
+ const profitData = iface.encodeFunctionData('transfer', [config.profitRecipient, totalProfit]);
533
+ const profitTx = await payerWallet.signTransaction({
534
+ to: tokenAddress,
535
+ data: profitData,
536
+ value: 0n,
537
+ nonce: profitNonce,
538
+ gasPrice,
539
+ gasLimit: finalGasLimit, // ✅ 使用用户配置的 gasLimit
540
+ chainId: chainIdNum,
541
+ type: txType
542
+ });
543
+ signedTxs.push(profitTx);
544
+ }
512
545
  }
513
546
  }
514
547
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.2.21",
3
+ "version": "1.2.23",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",