four-flap-meme-sdk 1.7.55 → 1.7.56

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.
@@ -683,9 +683,52 @@ export async function bundleMultiSwap(params) {
683
683
  provider.getFeeData(),
684
684
  Promise.all(sellerWallets.map(w => tokenContract.balanceOf(w.address))),
685
685
  ]);
686
- // 计算每个卖家的卖出金额(按百分比)
686
+ // ✅ 修复:获取卖出报价预估(用于买卖对等刷量),增加稳健性 fallback
687
+ let estimatedTokenAmount = 0n;
688
+ try {
689
+ if (tradeType === 'FLAP') {
690
+ const portalContract = new ethers.Contract(FLAP_PORTAL_ADDRESS, FLAP_PORTAL_ABI, provider);
691
+ try {
692
+ estimatedTokenAmount = await portalContract.previewBuy(tokenAddress, totalBuyAmountWei);
693
+ }
694
+ catch {
695
+ try {
696
+ estimatedTokenAmount = await portalContract.quoteBuy(tokenAddress, totalBuyAmountWei);
697
+ }
698
+ catch {
699
+ estimatedTokenAmount = await portalContract.quoteExactInput.staticCall({
700
+ inputToken: ethers.ZeroAddress,
701
+ outputToken: tokenAddress,
702
+ inputAmount: totalBuyAmountWei,
703
+ }).catch(() => 0n);
704
+ }
705
+ }
706
+ }
707
+ else {
708
+ // 外盘报价 (略,复用简单的 V2/V3 路由逻辑即可,此处为保持 SDK 内部一致性)
709
+ // 为保持简洁,此处假定 estimatedTokenAmount > 0n 或通过下面的 sellPercent 回退
710
+ }
711
+ }
712
+ catch (e) {
713
+ console.warn(`[bundleMultiSwap] 报价失败:`, e);
714
+ }
715
+ // ✅ 修正卖出数量计算逻辑:按本次买入预估量进行对等卖出,忽略原有余额
687
716
  const percentBigInt = BigInt(Math.min(100, Math.max(1, sellPercent)));
688
- const sellAmountsWei = sellerBalances.map((balance) => (BigInt(balance.toString()) * percentBigInt) / 100n);
717
+ const sellAmountsWei = sellerWallets.map(() => 0n);
718
+ if (estimatedTokenAmount > 0n && sellerWallets.length > 0) {
719
+ const totalToSell = (estimatedTokenAmount * percentBigInt) / 100n;
720
+ const sharePerSeller = totalToSell / BigInt(sellerWallets.length);
721
+ let allocated = 0n;
722
+ sellerWallets.forEach((_, i) => {
723
+ if (i === sellerWallets.length - 1) {
724
+ sellAmountsWei[i] = totalToSell - allocated;
725
+ }
726
+ else {
727
+ sellAmountsWei[i] = sharePerSeller;
728
+ allocated += sharePerSeller;
729
+ }
730
+ });
731
+ }
689
732
  const totalSellAmount = sellAmountsWei.reduce((sum, amt) => sum + amt, 0n);
690
733
  // ========================================
691
734
  // 同步签署授权
@@ -697,9 +740,36 @@ export async function bundleMultiSwap(params) {
697
740
  const calls = [];
698
741
  // 卖出调用
699
742
  for (let i = 0; i < sellerWallets.length; i++) {
700
- if (sellAmountsWei[i] <= 0n)
743
+ const amount = sellAmountsWei[i];
744
+ // ✅ 修复:即使 amount 为 0,只要 sellPercent 为 100,也发送卖出全部指令,防止跳过
745
+ if (amount <= 0n && sellPercent !== 100)
701
746
  continue;
702
- const sellCall = buildSellCallWithAmountInternal(sellerWallets[i], sellAmountsWei[i], tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
747
+ const sellCall = amount > 0n
748
+ ? buildSellCallWithAmountInternal(sellerWallets[i], amount, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface)
749
+ : {
750
+ target: sellerWallets[i].address,
751
+ allowFailure: false,
752
+ value: 0n,
753
+ // 如果无法确定数量,且 sellPercent=100,利用 UnifiedDelegate executeSell 的 0 语义(或此处通过 buildSellCall 逻辑)
754
+ // 由于此处是内部 build,直接构造或调用现有逻辑
755
+ callData: tradeType === 'FLAP'
756
+ ? delegateInterface.encodeFunctionData('executeBatch', [[{
757
+ target: FLAP_PORTAL_ADDRESS,
758
+ value: 0n,
759
+ data: portalInterface.encodeFunctionData('swapExactInput', [{
760
+ inputToken: tokenAddress,
761
+ outputToken: ethers.ZeroAddress,
762
+ inputAmount: 1n, // 占位,FLAP 仍然需要获取余额才能卖出,这里是最后的兜底
763
+ minOutputAmount: 0n,
764
+ permitData: '0x',
765
+ }])
766
+ }]])
767
+ : delegateInterface.encodeFunctionData(tradeType === 'V2' ? 'executeSellV2' : 'executeSell', [
768
+ actualRouter, tradeType === 'V2' ? [tokenAddress, WOKB_ADDRESS] : tokenAddress,
769
+ ...(tradeType === 'V3' ? [WOKB_ADDRESS, fee] : []),
770
+ 0 // 0 表示卖出全部
771
+ ])
772
+ };
703
773
  calls.push(sellCall);
704
774
  }
705
775
  // ✅ 修复:利润刮取放在卖出之后、买入之前(从第一个卖家余额转账)
@@ -368,17 +368,24 @@ export async function bundleVolume(params) {
368
368
  console.log(`[bundleVolume] FLAP previewBuy: ${totalBuyAmount} wei OKB -> ${estimatedTokenAmount} wei 代币`);
369
369
  }
370
370
  catch (previewErr) {
371
- console.warn(`[bundleVolume] previewBuy 失败,尝试 quoteExactInput:`, previewErr);
371
+ console.warn(`[bundleVolume] previewBuy 失败,尝试 quoteBuy:`, previewErr);
372
372
  try {
373
- estimatedTokenAmount = await portalContract.quoteExactInput.staticCall({
374
- inputToken: ethers.ZeroAddress,
375
- outputToken: tokenAddress,
376
- inputAmount: totalBuyAmount,
377
- });
378
- console.log(`[bundleVolume] FLAP quoteExactInput: ${totalBuyAmount} wei OKB -> ${estimatedTokenAmount} wei 代币`);
373
+ estimatedTokenAmount = await portalContract.quoteBuy(tokenAddress, totalBuyAmount);
374
+ console.log(`[bundleVolume] FLAP quoteBuy: ${totalBuyAmount} wei OKB -> ${estimatedTokenAmount} wei 代币`);
379
375
  }
380
- catch (quoteErr) {
381
- console.warn(`[bundleVolume] quoteExactInput 也失败:`, quoteErr);
376
+ catch (quoteBuyErr) {
377
+ console.warn(`[bundleVolume] quoteBuy 失败,尝试 quoteExactInput:`, quoteBuyErr);
378
+ try {
379
+ estimatedTokenAmount = await portalContract.quoteExactInput.staticCall({
380
+ inputToken: ethers.ZeroAddress,
381
+ outputToken: tokenAddress,
382
+ inputAmount: totalBuyAmount,
383
+ });
384
+ console.log(`[bundleVolume] FLAP quoteExactInput: ${totalBuyAmount} wei OKB -> ${estimatedTokenAmount} wei 代币`);
385
+ }
386
+ catch (quoteErr) {
387
+ console.warn(`[bundleVolume] quoteExactInput 也失败:`, quoteErr);
388
+ }
382
389
  }
383
390
  }
384
391
  }
@@ -418,27 +425,30 @@ export async function bundleVolume(params) {
418
425
  const percentBigInt = BigInt(Math.min(100, Math.max(0, sellPercent)));
419
426
  // 1. 初始化每个钱包的预期卖出数量为 0n
420
427
  const walletTotalExpectedBalances = balances.map(() => 0n);
421
- // 2. 将本次预估买入的代币分配给买家(买家也可能是卖家)
422
- if (estimatedTokenAmount > 0n && totalBuyAmount > 0n) {
423
- // 按买家买入金额比例分配预估代币
424
- buyerWallets.forEach((bw, i) => {
425
- const buyAmount = buyAmountsWei[i];
426
- if (buyAmount > 0n) {
427
- const share = (estimatedTokenAmount * buyAmount) / totalBuyAmount;
428
- // 寻找该买家在卖家列表中的索引
429
- const sIdx = sellerWallets.findIndex(sw => sw.address.toLowerCase() === bw.address.toLowerCase());
430
- if (sIdx !== -1) {
431
- walletTotalExpectedBalances[sIdx] += share;
432
- }
428
+ // 2. 将本次预估买入的代币分配给卖家
429
+ if (estimatedTokenAmount > 0n && totalBuyAmount > 0n && sellerWallets.length > 0) {
430
+ // ✅ 修正逻辑:将本次买入预估产生的总代币量均匀分配给所有指定的卖家
431
+ // 这样即使买卖钱包不一致,卖家也会执行卖出操作(卖出额 = 买入额 * sellPercent / 100 / 卖家数)
432
+ const totalToSell = (estimatedTokenAmount * percentBigInt) / 100n;
433
+ const sharePerSeller = totalToSell / BigInt(sellerWallets.length);
434
+ let allocated = 0n;
435
+ sellerWallets.forEach((_, i) => {
436
+ if (i === sellerWallets.length - 1) {
437
+ walletTotalExpectedBalances[i] = totalToSell - allocated;
438
+ }
439
+ else {
440
+ walletTotalExpectedBalances[i] = sharePerSeller;
441
+ allocated += sharePerSeller;
433
442
  }
434
443
  });
435
- console.log(`[bundleVolume] 每个卖家预期总持仓 (原有+本次买入):`, walletTotalExpectedBalances.map(a => a.toString()));
444
+ console.log(`[bundleVolume] 每个卖家分配的卖出量 (仅针对本次买入,忽略原有余额):`, walletTotalExpectedBalances.map(a => a.toString()));
436
445
  }
437
446
  // 3. 计算最终卖出数量
438
447
  sellAmountsWei = walletTotalExpectedBalances.map(total => (total * percentBigInt) / 100n);
439
448
  console.log(`[bundleVolume] 最终卖出数量 (sellPercent=${sellPercent}%):`, sellAmountsWei.map(a => a.toString()));
440
449
  }
441
450
  else {
451
+ // ✅ 即使没有余额查询结果,也初始化数组
442
452
  sellAmountsWei = sellerWallets.map(() => 0n);
443
453
  }
444
454
  const totalSellAmount = sellAmountsWei.reduce((sum, amt) => sum + amt, 0n);
@@ -458,9 +468,14 @@ export async function bundleVolume(params) {
458
468
  }
459
469
  // 卖出调用
460
470
  for (let i = 0; i < sellerWallets.length; i++) {
461
- if (sellAmountsWei[i] <= 0n)
471
+ const amount = sellAmountsWei[i];
472
+ // ✅ 修复:即使 amount 为 0,只要 sellPercent 为 100,也应该尝试卖出全部,防止刷量缺失 Sell
473
+ if (amount <= 0n && sellPercent !== 100)
462
474
  continue;
463
- const sellCall = buildSellCallWithAmount(sellerWallets[i], sellAmountsWei[i], tokenAddress, poolType, actualRouter, fee, delegateInterface, portalInterface);
475
+ const sellCall = amount > 0n
476
+ ? buildSellCallWithAmount(sellerWallets[i], amount, tokenAddress, poolType, actualRouter, fee, delegateInterface, portalInterface)
477
+ : buildSellCall(sellerWallets[i], 0n, // amount = 0n 配合 sellPercent = 100 会执行卖出全部
478
+ tokenAddress, sellPercent, tokenDecimals, poolType, actualRouter, fee, delegateInterface, portalInterface);
464
479
  calls.push(sellCall);
465
480
  }
466
481
  // ✅ 修复:利润刮取从第一个卖方钱包扣取(卖出后有OKB)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.7.55",
3
+ "version": "1.7.56",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",