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 =
|
|
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
|
-
|
|
743
|
+
const amount = sellAmountsWei[i];
|
|
744
|
+
// ✅ 修复:即使 amount 为 0,只要 sellPercent 为 100,也发送卖出全部指令,防止跳过
|
|
745
|
+
if (amount <= 0n && sellPercent !== 100)
|
|
701
746
|
continue;
|
|
702
|
-
const sellCall =
|
|
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 失败,尝试
|
|
371
|
+
console.warn(`[bundleVolume] previewBuy 失败,尝试 quoteBuy:`, previewErr);
|
|
372
372
|
try {
|
|
373
|
-
estimatedTokenAmount = await portalContract.
|
|
374
|
-
|
|
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 (
|
|
381
|
-
console.warn(`[bundleVolume] quoteExactInput
|
|
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
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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]
|
|
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
|
-
|
|
471
|
+
const amount = sellAmountsWei[i];
|
|
472
|
+
// ✅ 修复:即使 amount 为 0,只要 sellPercent 为 100,也应该尝试卖出全部,防止刷量缺失 Sell
|
|
473
|
+
if (amount <= 0n && sellPercent !== 100)
|
|
462
474
|
continue;
|
|
463
|
-
const sellCall =
|
|
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)
|