four-flap-meme-sdk 1.9.41 → 1.9.42

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.
@@ -535,16 +535,19 @@ export async function directV2BatchBuy(params) {
535
535
  const flowAmounts = buyAmounts.map(amount => ethers.parseUnits(amount, quoteTokenDecimals));
536
536
  const totalFlowWei = flowAmounts.reduce((sum, amt) => sum + amt, 0n);
537
537
  const baseProfitWei = calculateProfitAmount(totalFlowWei);
538
- // ✅ 方案 B:并行获取 nonces、gasPriceERC20 报价
539
- const [nonces, gasPrice, nativeProfitWei] = await Promise.all([
538
+ // ✅ 方案 B:并行获取 nonces、gasPriceERC20 报价 以及(非原生代币时)授权额度
539
+ const quoteTokenContract = (!useNative && quoteToken) ? new Contract(quoteToken, ERC20_ABI, provider) : null;
540
+ const [nonces, gasPrice, nativeProfitWei, buyAllowances] = await Promise.all([
540
541
  startNonces && startNonces.length === wallets.length
541
542
  ? Promise.resolve(startNonces)
542
543
  : new NonceManager(provider).getNextNoncesForWallets(wallets),
543
544
  getGasPrice(provider, config),
544
- // ERC20 报价提前并行获取(V2 买入用 V2 报价)
545
545
  (!useNative && baseProfitWei > 0n && quoteToken)
546
546
  ? getTokenToNativeQuote(provider, quoteToken, baseProfitWei, chain, 'v2')
547
- : Promise.resolve(baseProfitWei)
547
+ : Promise.resolve(baseProfitWei),
548
+ quoteTokenContract
549
+ ? Promise.all(wallets.map(w => quoteTokenContract.allowance(w.address, routerAddress).catch(() => 0n)))
550
+ : Promise.resolve(wallets.map(() => ethers.MaxUint256)),
548
551
  ]);
549
552
  // 确定最终利润金额(ENI 链额外补偿利润转账的 gas 成本)
550
553
  let profitWei = nativeProfitWei > 0n ? nativeProfitWei : 0n;
@@ -554,6 +557,29 @@ export async function directV2BatchBuy(params) {
554
557
  const gasLimit = getGasLimit(config, 250000);
555
558
  const txType = config.txType ?? 0;
556
559
  const deadline = getDeadline();
560
+ // ✅ 自动授权:ERC20 报价代币买入时,检查钱包对 Router 的授权额度,不足则插入 approve
561
+ const buyApproveTxs = [];
562
+ if (!useNative && quoteTokenContract) {
563
+ for (let i = 0; i < wallets.length; i++) {
564
+ if (flowAmounts[i] <= 0n)
565
+ continue;
566
+ if (buyAllowances[i] < flowAmounts[i]) {
567
+ console.log(`🔓 [V2 Buy] 钱包 ${i} 报价代币授权不足 (${buyAllowances[i]} < ${flowAmounts[i]}), 自动 approve`);
568
+ const approveData = quoteTokenContract.interface.encodeFunctionData('approve', [routerAddress, ethers.MaxUint256]);
569
+ const approveTx = await wallets[i].signTransaction({
570
+ to: quoteToken,
571
+ data: approveData,
572
+ value: 0n,
573
+ nonce: nonces[i],
574
+ gasLimit: 60000n,
575
+ ...buildGasFields(txType, gasPrice),
576
+ chainId,
577
+ });
578
+ buyApproveTxs.push({ walletIndex: i, tx: approveTx });
579
+ nonces[i]++;
580
+ }
581
+ }
582
+ }
557
583
  // 构建路径
558
584
  const inputToken = useNative ? wrappedNative : quoteToken;
559
585
  const path = [inputToken, tokenAddress];
@@ -613,10 +639,11 @@ export async function directV2BatchBuy(params) {
613
639
  });
614
640
  // ✅ 并行执行所有签名
615
641
  const signedResults = await Promise.all(signPromises);
616
- // 按类型分组并按顺序组装:贿赂 → 交易
642
+ // 按类型分组并按顺序组装:approve贿赂 → 交易
643
+ const approveSignedTxs = buyApproveTxs.sort((a, b) => a.walletIndex - b.walletIndex).map(a => a.tx);
617
644
  const bribeTxs = signedResults.filter(r => r.type === 'bribe').map(r => r.tx);
618
645
  const swapTxs = signedResults.filter(r => r.type === 'swap').sort((a, b) => a.index - b.index).map(r => r.tx);
619
- const signedTxs = [...bribeTxs, ...swapTxs];
646
+ const signedTxs = [...approveSignedTxs, ...bribeTxs, ...swapTxs];
620
647
  // ✅ 检查是否使用分布式利润模式
621
648
  const profitMode = config.profitMode || 'single';
622
649
  const skipProfit = config.skipProfit === true;
@@ -704,20 +731,26 @@ export async function directV2BatchSell(params) {
704
731
  const gasLimit = getGasLimit(config, 300000);
705
732
  const txType = config.txType ?? 0;
706
733
  const deadline = getDeadline();
707
- // 计算卖出数量(同步操作,无 RPC
734
+ // 计算卖出数量(同步操作,无 RPC),并安全兜底不超过链上实际余额
708
735
  const sellAmountsWei = [];
709
736
  for (let i = 0; i < wallets.length; i++) {
737
+ let amount;
710
738
  if (sellAmounts && sellAmounts[i]) {
711
739
  const truncatedAmount = truncateDecimals(sellAmounts[i], tokenDecimals);
712
- sellAmountsWei.push(ethers.parseUnits(truncatedAmount, tokenDecimals));
740
+ amount = ethers.parseUnits(truncatedAmount, tokenDecimals);
713
741
  }
714
742
  else if (sellPercentages && sellPercentages[i]) {
715
743
  const pct = Math.min(100, Math.max(0, sellPercentages[i]));
716
- sellAmountsWei.push((balances[i] * BigInt(pct)) / 100n);
744
+ amount = (balances[i] * BigInt(pct)) / 100n;
717
745
  }
718
746
  else {
719
- sellAmountsWei.push(balances[i]);
747
+ amount = balances[i];
748
+ }
749
+ if (amount > balances[i]) {
750
+ console.log(`⚠️ [V2 Sell] 钱包 ${i} 卖出量(${amount}) > 链上余额(${balances[i]}),已自动调整为实际余额`);
751
+ amount = balances[i];
720
752
  }
753
+ sellAmountsWei.push(amount);
721
754
  }
722
755
  const totalSellAmount = sellAmountsWei.reduce((sum, o) => sum + o, 0n);
723
756
  // ✅ 自动授权:检查每个钱包对 Router 的授权额度,不足则插入 approve 交易
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.9.41",
3
+ "version": "1.9.42",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",