four-flap-meme-sdk 1.7.50 → 1.7.52
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.
- package/dist/eip7702/xlayer/bundle-swap.js +104 -81
- package/dist/eip7702/xlayer/volume.js +101 -18
- package/package.json +1 -1
|
@@ -298,6 +298,7 @@ export async function bundleSwap(params) {
|
|
|
298
298
|
const delegateAddress = (config?.delegateAddress && config.delegateAddress.trim()) || UNIFIED_DELEGATE_ADDRESS;
|
|
299
299
|
const delegateInterface = new ethers.Interface(UNIFIED_DELEGATE_ABI);
|
|
300
300
|
const actualRouter = routerAddress ?? getDefaultRouter(tradeType);
|
|
301
|
+
const portalInterface = new ethers.Interface(FLAP_PORTAL_ABI);
|
|
301
302
|
// ========================================
|
|
302
303
|
// 同步操作 - 创建钱包
|
|
303
304
|
// ========================================
|
|
@@ -348,25 +349,48 @@ export async function bundleSwap(params) {
|
|
|
348
349
|
// ✅ 计算买入金额:卖出所得 OKB 扣除利润
|
|
349
350
|
const buyAmount = estimatedOkbOut - profitAmount;
|
|
350
351
|
console.log(`[Bundle Swap] 买入金额: ${ethers.formatEther(buyAmount)} OKB`);
|
|
351
|
-
const swapCalls = await buildSwapCalls(sellerWallet, buyerWallet, hopWallets, sellAmountWei, buyAmount, // ✅ 传入计算得到的买入金额
|
|
352
|
-
tokenAddress, tradeType, actualRouter, fee);
|
|
353
|
-
// ✅ 构建完整调用列表(利润刮取 + 换手调用)
|
|
354
352
|
const calls = [];
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
353
|
+
if (hopCount === 0) {
|
|
354
|
+
// ✅ 普通模式:卖买独立,不转账
|
|
355
|
+
// 1. 卖家卖出
|
|
356
|
+
const sellCall = buildSellCallWithAmountInternal(sellerWallet, sellAmountWei, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
357
|
+
calls.push(sellCall);
|
|
358
|
+
// 2. 利润刮取(从卖家余额扣除)
|
|
359
|
+
if (profitAmount > 0n) {
|
|
360
|
+
calls.push({
|
|
361
|
+
target: sellerWallet.address,
|
|
362
|
+
allowFailure: false,
|
|
363
|
+
value: 0n,
|
|
364
|
+
callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
// 3. 买家买入(使用买家自己的 OKB)
|
|
368
|
+
const buyCall = buildBuyCallInternal(buyerWallet, buyAmount, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
369
|
+
calls.push(buyCall);
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
// ✅ 资金利用率模式:进行转账
|
|
373
|
+
const swapCalls = await buildSwapCalls(sellerWallet, buyerWallet, hopWallets, sellAmountWei, buyAmount, tokenAddress, tradeType, actualRouter, fee);
|
|
374
|
+
// 1. 先添加卖出调用
|
|
375
|
+
calls.push(swapCalls[0]);
|
|
376
|
+
// 2. 利润刮取(卖出后立即进行)
|
|
377
|
+
if (profitAmount > 0n) {
|
|
378
|
+
calls.push({
|
|
379
|
+
target: sellerWallet.address,
|
|
380
|
+
allowFailure: false,
|
|
381
|
+
value: 0n,
|
|
382
|
+
callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
// 3. 添加剩余的调用(转账和买入)
|
|
386
|
+
for (let i = 1; i < swapCalls.length; i++) {
|
|
387
|
+
calls.push(swapCalls[i]);
|
|
388
|
+
}
|
|
363
389
|
}
|
|
364
|
-
// 添加换手调用
|
|
365
|
-
calls.push(...swapCalls);
|
|
366
390
|
// ========================================
|
|
367
391
|
// 同步构建交易
|
|
368
392
|
// ========================================
|
|
369
|
-
const signedTransaction = buildEIP7702TransactionSync(sellerWallet, resolvedAuthorizations, calls,
|
|
393
|
+
const signedTransaction = buildEIP7702TransactionSync(sellerWallet, resolvedAuthorizations, calls, 0n, // ✅ 修复:利润使用 transferAmount 从卖家余额转账,不需要通过 msg.value 传递
|
|
370
394
|
sellerNonce, feeData, config);
|
|
371
395
|
return {
|
|
372
396
|
signedTransaction,
|
|
@@ -439,83 +463,82 @@ export async function bundleBatchSwap(params) {
|
|
|
439
463
|
// 同步构建调用
|
|
440
464
|
// ========================================
|
|
441
465
|
const delegateInterface = new ethers.Interface(UNIFIED_DELEGATE_ABI);
|
|
466
|
+
const portalInterface = new ethers.Interface(FLAP_PORTAL_ABI);
|
|
442
467
|
const calls = [];
|
|
443
|
-
// ✅
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
// FLAP 模式需要额外的转账调用
|
|
449
|
-
if (tradeType === 'FLAP') {
|
|
450
|
-
calls.push({
|
|
451
|
-
target: sellerWallet.address,
|
|
452
|
-
allowFailure: false,
|
|
453
|
-
value: 0n,
|
|
454
|
-
callData: delegateInterface.encodeFunctionData('transferTo', [firstRecipient]),
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
// 中间钱包转账
|
|
458
|
-
for (let i = 0; i < hopWallets.length - 1; i++) {
|
|
459
|
-
calls.push({
|
|
460
|
-
target: hopWallets[i].address,
|
|
461
|
-
allowFailure: false,
|
|
462
|
-
value: 0n,
|
|
463
|
-
callData: delegateInterface.encodeFunctionData('transferTo', [hopWallets[i + 1].address]),
|
|
464
|
-
});
|
|
468
|
+
// ✅ 1. 卖家卖币
|
|
469
|
+
if (hopCount === 0) {
|
|
470
|
+
// 普通模式:只卖币,不转账
|
|
471
|
+
const sellCall = buildSellCallWithAmountInternal(sellerWallet, sellAmountWei, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
472
|
+
calls.push(sellCall);
|
|
465
473
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
474
|
+
else {
|
|
475
|
+
// 资金利用率模式:卖币并转账
|
|
476
|
+
const firstRecipient = hopWallets.length > 0 ? hopWallets[0].address : buyerWallets[0].address;
|
|
477
|
+
const sellCall = buildSellAndTransferCall(sellerWallet, sellAmountWei, tokenAddress, firstRecipient, tradeType, actualRouter, fee);
|
|
478
|
+
calls.push(sellCall);
|
|
479
|
+
// FLAP 模式需要额外的转账调用
|
|
480
|
+
if (tradeType === 'FLAP') {
|
|
481
|
+
calls.push({
|
|
482
|
+
target: sellerWallet.address,
|
|
483
|
+
allowFailure: false,
|
|
484
|
+
value: 0n,
|
|
485
|
+
callData: delegateInterface.encodeFunctionData('transferTo', [firstRecipient]),
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
// 中间钱包转账
|
|
489
|
+
for (let i = 0; i < hopWallets.length - 1; i++) {
|
|
490
|
+
calls.push({
|
|
491
|
+
target: hopWallets[i].address,
|
|
492
|
+
allowFailure: false,
|
|
493
|
+
value: 0n,
|
|
494
|
+
callData: delegateInterface.encodeFunctionData('transferTo', [hopWallets[i + 1].address]),
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
// 最后一个中间钱包转给第一个买家
|
|
498
|
+
if (hopWallets.length > 0) {
|
|
499
|
+
calls.push({
|
|
500
|
+
target: hopWallets[hopWallets.length - 1].address,
|
|
501
|
+
allowFailure: false,
|
|
502
|
+
value: 0n,
|
|
503
|
+
callData: delegateInterface.encodeFunctionData('transferTo', [buyerWallets[0].address]),
|
|
504
|
+
});
|
|
505
|
+
}
|
|
475
506
|
}
|
|
476
|
-
// ✅
|
|
477
|
-
const portalInterface = new ethers.Interface(FLAP_PORTAL_ABI);
|
|
507
|
+
// ✅ 2. 资金分配计算
|
|
478
508
|
const okbForBuyers = estimatedOkbOut - profitAmount; // 扣除利润后可用于买入的 OKB
|
|
479
|
-
// ✅ 计算每个买家的分配金额(按比例或平均)
|
|
480
509
|
let buyAmountsPerBuyer;
|
|
481
510
|
if (buyerRatios && buyerRatios.length === buyerWallets.length) {
|
|
482
|
-
// 按前端传入的比例分配
|
|
483
511
|
buyAmountsPerBuyer = buyerRatios.map(ratio => {
|
|
484
512
|
return (okbForBuyers * BigInt(Math.round(ratio * 10000))) / 10000n;
|
|
485
513
|
});
|
|
486
|
-
console.log(`[Bundle Batch Swap] 使用比例分配:`, buyerRatios);
|
|
487
514
|
}
|
|
488
515
|
else {
|
|
489
|
-
// 平均分配(向后兼容)
|
|
490
516
|
const avgAmount = okbForBuyers / BigInt(buyerWallets.length);
|
|
491
517
|
buyAmountsPerBuyer = buyerWallets.map(() => avgAmount);
|
|
492
|
-
console.log(`[Bundle Batch Swap] 使用平均分配: 每个买家 ${ethers.formatEther(avgAmount)} OKB`);
|
|
493
518
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
});
|
|
519
|
+
// ✅ 3. 资金分发 (仅资金利用率模式)
|
|
520
|
+
if (hopCount > 0 && buyerWallets.length > 1) {
|
|
521
|
+
for (let i = 1; i < buyerWallets.length; i++) {
|
|
522
|
+
calls.push({
|
|
523
|
+
target: buyerWallets[0].address, // 第一个买家分发
|
|
524
|
+
allowFailure: false,
|
|
525
|
+
value: 0n,
|
|
526
|
+
callData: delegateInterface.encodeFunctionData('transferAmount', [buyerWallets[i].address, buyAmountsPerBuyer[i]]),
|
|
527
|
+
});
|
|
528
|
+
}
|
|
505
529
|
}
|
|
506
|
-
// ✅
|
|
530
|
+
// ✅ 4. 所有买家买入
|
|
507
531
|
for (let i = 0; i < buyerWallets.length; i++) {
|
|
508
|
-
const buyCall = buildBuyCallInternal(buyerWallets[i], buyAmountsPerBuyer[i],
|
|
509
|
-
tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
532
|
+
const buyCall = buildBuyCallInternal(buyerWallets[i], buyAmountsPerBuyer[i], tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
510
533
|
calls.push(buyCall);
|
|
511
534
|
}
|
|
512
|
-
// ✅
|
|
535
|
+
// ✅ 5. 利润刮取(放在最后)
|
|
513
536
|
if (profitAmount > 0n) {
|
|
514
537
|
calls.push({
|
|
515
538
|
target: sellerWallet.address,
|
|
516
539
|
allowFailure: false,
|
|
517
|
-
value:
|
|
518
|
-
callData: delegateInterface.encodeFunctionData('
|
|
540
|
+
value: 0n,
|
|
541
|
+
callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
|
|
519
542
|
});
|
|
520
543
|
}
|
|
521
544
|
// ========================================
|
|
@@ -536,7 +559,7 @@ export async function bundleBatchSwap(params) {
|
|
|
536
559
|
// ========================================
|
|
537
560
|
// 同步构建交易
|
|
538
561
|
// ========================================
|
|
539
|
-
const signedTransaction = buildEIP7702TransactionSync(sellerWallet, authorizations, calls,
|
|
562
|
+
const signedTransaction = buildEIP7702TransactionSync(sellerWallet, authorizations, calls, 0n, // ✅ 利润使用 transferAmount 从卖家余额转账,不需要通过 msg.value 传递
|
|
540
563
|
sellerNonce, feeData, config);
|
|
541
564
|
return {
|
|
542
565
|
signedTransaction,
|
|
@@ -647,15 +670,6 @@ export async function bundleMultiSwap(params) {
|
|
|
647
670
|
// 构建调用
|
|
648
671
|
// ========================================
|
|
649
672
|
const calls = [];
|
|
650
|
-
// 利润刮取(从 Payer 钱包)
|
|
651
|
-
if (profitAmount > 0n) {
|
|
652
|
-
calls.push({
|
|
653
|
-
target: mainWallet.address,
|
|
654
|
-
allowFailure: false,
|
|
655
|
-
value: profitAmount,
|
|
656
|
-
callData: delegateInterface.encodeFunctionData('transferTo', [profitRecipient]),
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
673
|
// 卖出调用
|
|
660
674
|
for (let i = 0; i < sellerWallets.length; i++) {
|
|
661
675
|
if (sellAmountsWei[i] <= 0n)
|
|
@@ -663,6 +677,15 @@ export async function bundleMultiSwap(params) {
|
|
|
663
677
|
const sellCall = buildSellCallWithAmountInternal(sellerWallets[i], sellAmountsWei[i], tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
664
678
|
calls.push(sellCall);
|
|
665
679
|
}
|
|
680
|
+
// ✅ 修复:利润刮取放在卖出之后、买入之前(从第一个卖家余额转账)
|
|
681
|
+
if (profitAmount > 0n && sellerWallets.length > 0) {
|
|
682
|
+
calls.push({
|
|
683
|
+
target: sellerWallets[0].address, // 从第一个卖家扣利润
|
|
684
|
+
allowFailure: false,
|
|
685
|
+
value: 0n,
|
|
686
|
+
callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
|
|
687
|
+
});
|
|
688
|
+
}
|
|
666
689
|
// 买入调用
|
|
667
690
|
for (let i = 0; i < buyerWallets.length; i++) {
|
|
668
691
|
const buyCall = buildBuyCallInternal(buyerWallets[i], buyAmountPerWallet, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
@@ -671,8 +694,8 @@ export async function bundleMultiSwap(params) {
|
|
|
671
694
|
// ========================================
|
|
672
695
|
// 构建交易
|
|
673
696
|
// ========================================
|
|
674
|
-
// ✅
|
|
675
|
-
const totalValue =
|
|
697
|
+
// ✅ 修复:利润使用 transferAmount 从卖家余额转账,不需要通过 msg.value 传递
|
|
698
|
+
const totalValue = 0n;
|
|
676
699
|
const signedTransaction = buildEIP7702TransactionSync(mainWallet, authorizations, calls, totalValue, nonces[mainWalletIndex], feeData, config);
|
|
677
700
|
return {
|
|
678
701
|
signedTransaction,
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { ethers, Contract } from 'ethers';
|
|
16
16
|
import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByTxCount, getProfitRecipient, } from './utils.js';
|
|
17
|
-
import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, FLAP_PORTAL_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, WOKB_ADDRESS, V3_FEE_TIERS, ERC20_ABI, } from './constants.js';
|
|
17
|
+
import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, FLAP_PORTAL_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, WOKB_ADDRESS, V3_FEE_TIERS, ERC20_ABI, V2_ROUTER_ABI, } from './constants.js';
|
|
18
18
|
import { bundleBuy } from './bundle-buy.js';
|
|
19
19
|
import { bundleSell } from './bundle-sell.js';
|
|
20
20
|
import { bundleSwap } from './bundle-swap.js';
|
|
@@ -222,20 +222,21 @@ export async function washVolume(params) {
|
|
|
222
222
|
: buildSellCall(wallet, tokenAddress, sellPercent, tokenDecimals, poolType, actualRouter, fee, delegateInterface, portalInterface);
|
|
223
223
|
calls.push(sellCall);
|
|
224
224
|
}
|
|
225
|
-
//
|
|
226
|
-
|
|
225
|
+
// ✅ 修复:利润刮取从第一个参与刷量的钱包扣取(卖出后有OKB)
|
|
226
|
+
// 使用 transferAmount 从钱包余额转账,不需要通过 msg.value
|
|
227
|
+
if (profitAmount > 0n && wallets.length > 0) {
|
|
227
228
|
calls.push({
|
|
228
|
-
target:
|
|
229
|
+
target: wallets[0].address, // 从第一个刷量钱包扣利润
|
|
229
230
|
allowFailure: false,
|
|
230
|
-
value:
|
|
231
|
-
callData: delegateInterface.encodeFunctionData('
|
|
231
|
+
value: 0n,
|
|
232
|
+
callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
|
|
232
233
|
});
|
|
233
234
|
}
|
|
234
235
|
// ========================================
|
|
235
236
|
// 构建交易
|
|
236
237
|
// ========================================
|
|
237
|
-
// ✅
|
|
238
|
-
const totalValue =
|
|
238
|
+
// ✅ 修复:利润使用 transferAmount 从钱包余额转账,不需要通过 msg.value
|
|
239
|
+
const totalValue = 0n;
|
|
239
240
|
// ✅ 使用 mainWallet 的 nonce(mainWallet 是交易发起者和 Gas 支付者)
|
|
240
241
|
const signedTransaction = buildEIP7702TransactionSync(mainWallet, authorizations, calls, totalValue, nonces[mainWalletIndex], feeData, config);
|
|
241
242
|
return {
|
|
@@ -349,12 +350,93 @@ export async function bundleVolume(params) {
|
|
|
349
350
|
// 计算卖出金额
|
|
350
351
|
let sellAmountsWei;
|
|
351
352
|
if (sellAmounts && sellAmounts.length === sellerWallets.length) {
|
|
353
|
+
// ✅ 如果有指定卖出金额,直接使用
|
|
352
354
|
sellAmountsWei = sellAmounts.map(amt => ethers.parseUnits(truncateDecimals(amt, tokenDecimals), tokenDecimals));
|
|
353
355
|
}
|
|
354
356
|
else if (needBalanceQuery && results[2]) {
|
|
357
|
+
// ✅ 没有指定卖出金额,需要根据买入金额预估应卖出的代币数量
|
|
355
358
|
const balances = results[2].map(b => typeof b === 'bigint' ? b : BigInt(b.toString()));
|
|
356
|
-
const
|
|
357
|
-
|
|
359
|
+
const totalSellerBalance = balances.reduce((sum, b) => sum + b, 0n);
|
|
360
|
+
// ✅ 根据 poolType 预估买入能获得的代币数量
|
|
361
|
+
let estimatedTokenAmount = 0n;
|
|
362
|
+
try {
|
|
363
|
+
if (poolType === 'FLAP') {
|
|
364
|
+
// FLAP 内盘:使用 previewBuy,降级到 quoteExactInput
|
|
365
|
+
const portalContract = new ethers.Contract(FLAP_PORTAL_ADDRESS, FLAP_PORTAL_ABI, provider);
|
|
366
|
+
try {
|
|
367
|
+
estimatedTokenAmount = await portalContract.previewBuy(tokenAddress, totalBuyAmount);
|
|
368
|
+
console.log(`[bundleVolume] FLAP previewBuy: ${totalBuyAmount} wei OKB -> ${estimatedTokenAmount} wei 代币`);
|
|
369
|
+
}
|
|
370
|
+
catch (previewErr) {
|
|
371
|
+
console.warn(`[bundleVolume] previewBuy 失败,尝试 quoteExactInput:`, previewErr);
|
|
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 代币`);
|
|
379
|
+
}
|
|
380
|
+
catch (quoteErr) {
|
|
381
|
+
console.warn(`[bundleVolume] quoteExactInput 也失败:`, quoteErr);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
else if (poolType === 'V2' || poolType === 'V3') {
|
|
386
|
+
// V2/V3 外盘:使用 Router 报价
|
|
387
|
+
const v2Router = new ethers.Contract(POTATOSWAP_V2_ROUTER, V2_ROUTER_ABI, provider);
|
|
388
|
+
const path = [WOKB_ADDRESS, tokenAddress];
|
|
389
|
+
try {
|
|
390
|
+
if (poolType === 'V2') {
|
|
391
|
+
const amounts = await v2Router.getAmountsOut(totalBuyAmount, path);
|
|
392
|
+
estimatedTokenAmount = amounts[amounts.length - 1];
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
// V3: 使用 V3 Quoter
|
|
396
|
+
const v3Quoter = new ethers.Contract('0x27d52F6D9Ed2eBb3E6a4c0Af6b1f5dc85A4d5b2C', [
|
|
397
|
+
'function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96)) external returns (uint256 amountOut)'
|
|
398
|
+
], provider);
|
|
399
|
+
estimatedTokenAmount = await v3Quoter.quoteExactInputSingle.staticCall({
|
|
400
|
+
tokenIn: WOKB_ADDRESS,
|
|
401
|
+
tokenOut: tokenAddress,
|
|
402
|
+
amountIn: totalBuyAmount,
|
|
403
|
+
fee: fee,
|
|
404
|
+
sqrtPriceLimitX96: 0n,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
console.log(`[bundleVolume] ${poolType} quote: ${totalBuyAmount} wei OKB -> ${estimatedTokenAmount} wei 代币`);
|
|
408
|
+
}
|
|
409
|
+
catch (quoteErr) {
|
|
410
|
+
console.warn(`[bundleVolume] ${poolType} 报价失败:`, quoteErr);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
catch (e) {
|
|
415
|
+
console.warn(`[bundleVolume] 报价预估失败:`, e);
|
|
416
|
+
}
|
|
417
|
+
// ✅ 修正卖出数量计算逻辑:每个钱包应卖出的代币 = (原有余额 + 本次买入预估数量) * sellPercent / 100
|
|
418
|
+
const percentBigInt = BigInt(Math.min(100, Math.max(0, sellPercent)));
|
|
419
|
+
// 1. 初始化每个钱包的预期持仓
|
|
420
|
+
const walletTotalExpectedBalances = balances.map(b => b);
|
|
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
|
+
}
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
console.log(`[bundleVolume] 每个卖家预期总持仓 (原有+本次买入):`, walletTotalExpectedBalances.map(a => a.toString()));
|
|
436
|
+
}
|
|
437
|
+
// 3. 计算最终卖出数量
|
|
438
|
+
sellAmountsWei = walletTotalExpectedBalances.map(total => (total * percentBigInt) / 100n);
|
|
439
|
+
console.log(`[bundleVolume] 最终卖出数量 (sellPercent=${sellPercent}%):`, sellAmountsWei.map(a => a.toString()));
|
|
358
440
|
}
|
|
359
441
|
else {
|
|
360
442
|
sellAmountsWei = sellerWallets.map(() => 0n);
|
|
@@ -381,20 +463,21 @@ export async function bundleVolume(params) {
|
|
|
381
463
|
const sellCall = buildSellCallWithAmount(sellerWallets[i], sellAmountsWei[i], tokenAddress, poolType, actualRouter, fee, delegateInterface, portalInterface);
|
|
382
464
|
calls.push(sellCall);
|
|
383
465
|
}
|
|
384
|
-
//
|
|
385
|
-
|
|
466
|
+
// ✅ 修复:利润刮取从第一个卖方钱包扣取(卖出后有OKB)
|
|
467
|
+
// 使用 transferAmount 从钱包余额转账,不需要通过 msg.value
|
|
468
|
+
if (profitAmount > 0n && sellerWallets.length > 0) {
|
|
386
469
|
calls.push({
|
|
387
|
-
target:
|
|
470
|
+
target: sellerWallets[0].address, // 从第一个卖方钱包扣利润
|
|
388
471
|
allowFailure: false,
|
|
389
|
-
value:
|
|
390
|
-
callData: delegateInterface.encodeFunctionData('
|
|
472
|
+
value: 0n,
|
|
473
|
+
callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
|
|
391
474
|
});
|
|
392
475
|
}
|
|
393
476
|
// ========================================
|
|
394
477
|
// 构建交易
|
|
395
478
|
// ========================================
|
|
396
|
-
// ✅
|
|
397
|
-
const totalValue =
|
|
479
|
+
// ✅ 修复:利润使用 transferAmount 从钱包余额转账,不需要通过 msg.value
|
|
480
|
+
const totalValue = 0n;
|
|
398
481
|
const signedTransaction = buildEIP7702TransactionSync(mainWallet, authorizations, calls, totalValue, nonces[mainWalletIndex], feeData, config);
|
|
399
482
|
return {
|
|
400
483
|
signedTransaction,
|
|
@@ -580,7 +663,7 @@ function buildSellCall(wallet, tokenAddress, sellPercent, tokenDecimals, poolTyp
|
|
|
580
663
|
const portalCallData = portalInterface.encodeFunctionData('swapExactInput', [{
|
|
581
664
|
inputToken: tokenAddress,
|
|
582
665
|
outputToken: ethers.ZeroAddress, // 0x0 = OKB
|
|
583
|
-
inputAmount:
|
|
666
|
+
inputAmount: 1n, // 占位符,如果调用 buildSellCall 时不指定 amount,通常意味逻辑上有问题
|
|
584
667
|
minOutputAmount: 0n,
|
|
585
668
|
permitData: '0x',
|
|
586
669
|
}]);
|