four-flap-meme-sdk 1.6.83 → 1.6.85

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.
@@ -183,7 +183,7 @@ export class AADexSwapExecutor {
183
183
  const nonceMap = new AANonceMap();
184
184
  nonceMap.init(sellerSender, sellerAi.nonce);
185
185
  nonceMap.init(buyerSender, buyerAi.nonce);
186
- // initCode:确保“同一 sender”只在第一笔 op 携带
186
+ // initCode:确保"同一 sender"只在第一笔 op 携带
187
187
  const initCodeBySenderLower = new Map();
188
188
  const initIfNeeded = (sender, deployed, ownerAddress) => {
189
189
  const k = sender.toLowerCase();
@@ -451,7 +451,12 @@ export class AADexSwapExecutor {
451
451
  const useNativeToken = isNativeToken(quoteToken);
452
452
  const isV3Trade = this.isV3(tradeType);
453
453
  const effectiveRouter = this.getEffectiveRouter({ dexKey, routerAddress: routerAddressIn, tradeType });
454
- const provider = this.aaManager.getProvider();
454
+ // 关键修复:如果 effectiveConfig 有 paymaster,使用新的 AAAccountManager
455
+ // 否则使用构造函数中的 aaManager(性能优化:避免每次都创建新实例)
456
+ const aaManager = effectiveConfig.paymaster
457
+ ? new AAAccountManager(effectiveConfig)
458
+ : this.aaManager;
459
+ const provider = aaManager.getProvider();
455
460
  const sellerOwner = new Wallet(sellerPrivateKey, provider);
456
461
  const buyerOwners = buyerPrivateKeys.map(pk => new Wallet(pk, provider));
457
462
  const payerWallet = new Wallet(payerPrivateKey || sellerPrivateKey, provider);
@@ -459,7 +464,7 @@ export class AADexSwapExecutor {
459
464
  // ✅ Gas 配置:优先使用前端传入的值,否则使用默认值
460
465
  const HOP_CALL_GAS_LIMIT = hopCallGasLimit ?? DEFAULT_HOP_CALL_GAS_LIMIT;
461
466
  const DEX_BUY_CALL_GAS_LIMIT = buyerCallGasLimit ?? DEFAULT_DEX_BUY_CALL_GAS_LIMIT;
462
- const accountInfos = await this.aaManager.getMultipleAccountInfo([sellerOwner.address, ...buyerOwners.map(w => w.address)]);
467
+ const accountInfos = await aaManager.getMultipleAccountInfo([sellerOwner.address, ...buyerOwners.map(w => w.address)]);
463
468
  const sellerAi = accountInfos[0];
464
469
  const buyerAis = accountInfos.slice(1);
465
470
  const nonceMap = new AANonceMap();
@@ -471,7 +476,7 @@ export class AADexSwapExecutor {
471
476
  const k = sender.toLowerCase();
472
477
  if (initCodeBySenderLower.has(k))
473
478
  return;
474
- initCodeBySenderLower.set(k, deployed ? '0x' : this.aaManager.generateInitCode(ownerAddress));
479
+ initCodeBySenderLower.set(k, deployed ? '0x' : aaManager.generateInitCode(ownerAddress));
475
480
  };
476
481
  const consumeInitCode = (sender) => {
477
482
  const k = sender.toLowerCase();
@@ -487,13 +492,13 @@ export class AADexSwapExecutor {
487
492
  initIfNeeded(buyerAis[i].sender, buyerAis[i].deployed, buyerOwners[i].address);
488
493
  }
489
494
  const decimals = await this.bundleExecutor['getErc20Decimals'](tokenAddress);
490
- const sellerTokenBal = await this.aaManager.getErc20Balance(tokenAddress, sellerAi.sender);
495
+ const sellerTokenBal = await aaManager.getErc20Balance(tokenAddress, sellerAi.sender);
491
496
  const sellAmountWei = sellAmount
492
497
  ? ethers.parseUnits(String(sellAmount), decimals)
493
498
  : (sellerTokenBal * BigInt(sellPercent)) / 100n;
494
499
  let needApprove = false;
495
500
  if (!skipApprovalCheck) {
496
- const allowance = await this.aaManager.getErc20Allowance(tokenAddress, sellerAi.sender, effectiveRouter);
501
+ const allowance = await aaManager.getErc20Allowance(tokenAddress, sellerAi.sender, effectiveRouter);
497
502
  needApprove = allowance < sellAmountWei;
498
503
  }
499
504
  // ✅ Gas 价格:优先使用前端传入的值,否则从链上获取
@@ -501,13 +506,13 @@ export class AADexSwapExecutor {
501
506
  const fixedGasPrice = gasPriceGwei
502
507
  ? BigInt(Math.floor(gasPriceGwei * 1e9)) // Gwei → wei
503
508
  : await (async () => {
504
- const feeData = await this.aaManager.getFeeData();
509
+ const feeData = await aaManager.getFeeData();
505
510
  return feeData.gasPrice ?? feeData.maxFeePerGas ?? 5000000000n;
506
511
  })();
507
512
  console.log(`[AA DEX 批量换手] Gas 配置: gasPrice=${fixedGasPrice.toString()} wei, buyerCallGasLimit=${DEX_BUY_CALL_GAS_LIMIT}, hopCallGasLimit=${HOP_CALL_GAS_LIMIT}`);
508
513
  const outOps = [];
509
514
  if (needApprove) {
510
- const { userOp } = await this.aaManager.buildUserOpWithFixedGas({
515
+ const { userOp } = await aaManager.buildUserOpWithFixedGas({
511
516
  ownerWallet: sellerOwner,
512
517
  sender: sellerAi.sender,
513
518
  nonce: nonceMap.next(sellerAi.sender),
@@ -516,7 +521,7 @@ export class AADexSwapExecutor {
516
521
  deployed: sellerAi.deployed,
517
522
  fixedGas: { gasPrice: fixedGasPrice }, // ✅ 使用统一的 gasPrice
518
523
  });
519
- const signedApprove = await this.aaManager.signUserOp(userOp, sellerOwner);
524
+ const signedApprove = await aaManager.signUserOp(userOp, sellerOwner);
520
525
  outOps.push(signedApprove.userOp);
521
526
  }
522
527
  // Sell op - ✅ 支持 V2/V3,支持 ERC20 稳定币
@@ -546,7 +551,7 @@ export class AADexSwapExecutor {
546
551
  sellSwapData = encodeSwapExactTokensForTokensSupportingFee(sellAmountWei, 0n, [tokenAddress, quoteToken], sellerAi.sender, deadline);
547
552
  }
548
553
  const sellCallData = encodeExecute(effectiveRouter, 0n, sellSwapData);
549
- const signedSell = await this.aaManager.buildUserOpWithState({
554
+ const signedSell = await aaManager.buildUserOpWithState({
550
555
  ownerWallet: sellerOwner,
551
556
  sender: sellerAi.sender,
552
557
  nonce: nonceMap.next(sellerAi.sender),
@@ -689,7 +694,7 @@ export class AADexSwapExecutor {
689
694
  // 多跳可用的条件:
690
695
  // 1. 配置了 Paymaster(prefund = 0n,hop 钱包不需要余额)
691
696
  // 2. 传入了预充值好的 hop 钱包(hop 钱包已有余额支付 prefund)
692
- const hasPaymaster = this.aaManager.hasPaymaster();
697
+ const hasPaymaster = aaManager.hasPaymaster();
693
698
  const hasPrefundedHops = Array.isArray(prefundedHopWallets) && prefundedHopWallets.length > 0;
694
699
  const canDoMultiHop = hasPaymaster || hasPrefundedHops;
695
700
  const effectiveHopCount = canDoMultiHop ? hopCount : 0;
@@ -710,50 +715,40 @@ export class AADexSwapExecutor {
710
715
  if (capitalMode && buyerSenders.length > 0 && totalBuyWei > 0n) {
711
716
  if (effectiveHopCount <= 0) {
712
717
  console.log('[AA DEX 批量换手] 进入直接分发模式 (effectiveHopCount <= 0)');
713
- // ✅ 关键修复:使用开始时获取的 fixedGasPrice 计算 prefund
714
- // 这样确保检查、分发和 UserOp 构建使用同一个 gasPrice,避免不一致
715
- // 计算每个买方需要的 prefund(带 buffer,用于余额检查和分发)
716
- const buyerPrefunds = buyerAis.map(ai => estimateBuyerPrefundWithBuffer(ai.deployed));
718
+ // ✅ Paymaster 模式:不需要 prefund,buyer 钱包不需要预先持有 OKB
719
+ // Paymaster 模式:需要检查 buyer 余额
720
+ const buyerPrefunds = buyerAis.map(ai => hasPaymaster ? 0n : estimateBuyerPrefundWithBuffer(ai.deployed));
717
721
  const totalBuyerPrefund = buyerPrefunds.reduce((a, b) => a + b, 0n);
718
- console.log(`[AA DEX 直接分发] 买方数量: ${buyerSenders.length}, prefund 预估: ${ethers.formatEther(totalBuyerPrefund)} OKB, fixedGasPrice: ${fixedGasPrice.toString()}`);
719
- // ✅ 关键修复:检查 buyer 钱包是否有足够的 OKB 支付 prefund
720
- // ERC-4337 handleOps 执行顺序:先验证所有 UserOps(需要 prefund),再执行所有 UserOps
721
- // Validation 阶段,Disperse UserOp 还没执行,所以 Buyer 还没收到资金
722
- // 因此 Buyer 必须预先持有足够的 OKB 来支付 prefund
723
- const buyerBalances = await Promise.all(buyerSenders.map(s => provider.getBalance(s)));
724
- const insufficientBuyers = [];
725
- const sufficientBuyers = [];
726
- for (let i = 0; i < buyerSenders.length; i++) {
727
- const prefundNeeded = buyerPrefunds[i];
728
- const balance = buyerBalances[i];
729
- if (balance < prefundNeeded) {
730
- insufficientBuyers.push(`${buyerSenders[i]} (余额: ${ethers.formatEther(balance)} OKB, 需要: ${ethers.formatEther(prefundNeeded)} OKB, 缺口: ${ethers.formatEther(prefundNeeded - balance)} OKB)`);
722
+ console.log(`[AA DEX 直接分发] 买方数量: ${buyerSenders.length}, Paymaster: ${hasPaymaster ? '✅ 启用' : '❌ 未启用'}, prefund: ${ethers.formatEther(totalBuyerPrefund)} OKB`);
723
+ // ✅ 只有无 Paymaster 时才检查 buyer 余额
724
+ if (!hasPaymaster) {
725
+ const buyerBalances = await Promise.all(buyerSenders.map(s => provider.getBalance(s)));
726
+ const insufficientBuyers = [];
727
+ for (let i = 0; i < buyerSenders.length; i++) {
728
+ const prefundNeeded = buyerPrefunds[i];
729
+ const balance = buyerBalances[i];
730
+ if (balance < prefundNeeded) {
731
+ insufficientBuyers.push(`${buyerSenders[i]} (余额: ${ethers.formatEther(balance)} OKB, 需要: ${ethers.formatEther(prefundNeeded)} OKB)`);
732
+ }
731
733
  }
732
- else {
733
- sufficientBuyers.push(buyerSenders[i]);
734
+ if (insufficientBuyers.length > 0) {
735
+ console.error('[AA DEX 直接分发] ❌ Buyer 钱包 OKB 余额不足:', insufficientBuyers);
736
+ throw new Error(`${insufficientBuyers.length}/${buyerSenders.length} 个 Buyer 钱包 OKB 余额不足!` +
737
+ `请配置 Paymaster(全局设置 → AA Paymaster)或给 buyer 钱包充值 OKB。`);
734
738
  }
739
+ console.log('[AA DEX 直接分发] ✅ Buyer 钱包 prefund 检查通过');
735
740
  }
736
- if (insufficientBuyers.length > 0) {
737
- console.error('[AA DEX 直接分发] ❌ 以下 buyer 钱包 OKB 余额不足以支付 prefund:', insufficientBuyers);
738
- console.error('[AA DEX 直接分发] ⚠️ ERC-4337 限制:handleOps 在 Validation 阶段就需要 prefund,此时分发还没执行');
739
- console.error('[AA DEX 直接分发] 💡 解决方案:请先给这些 buyer 钱包充值少量 OKB (约 0.001-0.002 OKB/钱包) 用于支付 gas 费用');
740
- throw new Error(`${insufficientBuyers.length}/${buyerSenders.length} 个 Buyer 钱包 OKB 余额不足!` +
741
- `ERC-4337 限制:所有 UserOps 在 Validation 阶段就需要支付 prefund(gas 费用),此时卖出和分发还没执行。` +
742
- `请先给 buyer 钱包充值少量 OKB (约 0.001-0.002 OKB/钱包)。`);
743
- }
744
- console.log('[AA DEX 直接分发] ✅ Buyer 钱包 prefund 检查通过,所有钱包余额充足');
745
741
  // ✅ 直接分发模式:
746
- // - 原生代币模式:分发金额 = 买入金额 + 买方 prefund
747
- // - ERC20 模式:只分发 ERC20 代币,prefund 需要 buyer 自己有 OKB
748
- // ✅ 利润由 Payer EOA 独立交易刮取,不参与 AA 分发
742
+ // - Paymaster 模式:分发金额 = 买入金额(不含 prefund,gas Paymaster 代付)
743
+ // - Paymaster 模式:分发金额 = 买入金额 + prefund
749
744
  const items = buyerSenders.map((to, i) => {
750
745
  const buyAmount = buyAmountsWei[i] ?? 0n;
751
746
  const prefund = buyerPrefunds[i] ?? 0n;
752
747
  return {
753
748
  to,
754
- buyAmount, // ERC20 代币金额
755
- prefund, // OKB prefund(仅原生代币模式使用)
756
- totalValue: buyAmount + prefund // 原生代币模式的总金额
749
+ buyAmount,
750
+ prefund,
751
+ totalValue: buyAmount + prefund
757
752
  };
758
753
  }).filter(x => x.buyAmount > 0n);
759
754
  const chunks = chunkArray(items, maxPerOp);
@@ -775,7 +770,7 @@ export class AADexSwapExecutor {
775
770
  const datas = ch.map(x => erc20Iface.encodeFunctionData('transfer', [x.to, x.buyAmount]));
776
771
  callData = encodeExecuteBatch(targets, values, datas);
777
772
  }
778
- const signedDisperse = await this.aaManager.buildUserOpWithState({
773
+ const signedDisperse = await aaManager.buildUserOpWithState({
779
774
  ownerWallet: sellerOwner,
780
775
  sender: sellerAi.sender,
781
776
  nonce: nonceMap.next(sellerAi.sender),
@@ -814,7 +809,7 @@ export class AADexSwapExecutor {
814
809
  }
815
810
  // ✅ 关键修复:从链上获取所有 hop 钱包的真实 nonce 和部署状态
816
811
  const hopSenders = prefundedHopWallets.slice(0, totalHopsNeeded).map(h => h.senderAddress);
817
- const hopNonces = await this.aaManager.batchGetNonces(hopSenders);
812
+ const hopNonces = await aaManager.batchGetNonces(hopSenders);
818
813
  // ✅ 直接检查链上 code 判断是否已部署(比 nonce 更可靠)
819
814
  const hopCodes = await Promise.all(hopSenders.map(s => provider.getCode(s)));
820
815
  const hopDeployed = hopCodes.map(code => code !== null && code !== '0x' && code.length > 2);
@@ -866,24 +861,28 @@ export class AADexSwapExecutor {
866
861
  allGeneratedHopWallets.push(chainHops);
867
862
  }
868
863
  console.log(`[AA DEX 多跳] ✅ 使用预充值 hop 钱包: ${totalHopsNeeded} 个 (${buyerSenders.length} 条链 × ${effectiveHopCount} 跳)`);
869
- // ✅ 关键修复:检查 buyer 钱包是否有足够的 OKB 支付 prefund
870
- // ERC-4337 handleOps 会先验证所有 UserOps(需要 prefund),然后才执行
871
- // 所以 buyer 需要在交易前就有足够的 OKB,不能依赖 hop 链在 execution 阶段转入的资金
872
- const buyerBalances = await Promise.all(buyerSenders.map(s => provider.getBalance(s)));
873
- const insufficientBuyers = [];
874
- for (let i = 0; i < buyerSenders.length; i++) {
875
- const buyerAi = buyerAis[i];
876
- const buyerPrefundNeeded = estimatePrefund(DEX_BUY_CALL_GAS_LIMIT, buyerAi.deployed);
877
- if (buyerBalances[i] < buyerPrefundNeeded) {
878
- insufficientBuyers.push(`${buyerSenders[i]} (余额: ${ethers.formatEther(buyerBalances[i])} OKB, 需要: ${ethers.formatEther(buyerPrefundNeeded)} OKB, 缺口: ${ethers.formatEther(buyerPrefundNeeded - buyerBalances[i])} OKB)`);
864
+ // ✅ 检查 buyer 钱包余额(仅预充值模式需要,Paymaster 模式不需要)
865
+ // 预充值模式下,hop 会向 buyer 转入资金(用于买入),但 buyer 仍需要自己的 OKB 支付 prefund
866
+ // 因为 ERC-4337 handleOps 会先验证所有 UserOps(需要 prefund),然后才执行
867
+ if (!hasPaymaster) {
868
+ const buyerBalances = await Promise.all(buyerSenders.map(s => provider.getBalance(s)));
869
+ const insufficientBuyers = [];
870
+ for (let i = 0; i < buyerSenders.length; i++) {
871
+ const buyerAi = buyerAis[i];
872
+ const buyerPrefundNeeded = estimatePrefund(DEX_BUY_CALL_GAS_LIMIT, buyerAi.deployed);
873
+ if (buyerBalances[i] < buyerPrefundNeeded) {
874
+ insufficientBuyers.push(`${buyerSenders[i]} (余额: ${ethers.formatEther(buyerBalances[i])} OKB, 需要: ${ethers.formatEther(buyerPrefundNeeded)} OKB, 缺口: ${ethers.formatEther(buyerPrefundNeeded - buyerBalances[i])} OKB)`);
875
+ }
876
+ }
877
+ if (insufficientBuyers.length > 0) {
878
+ console.error('[AA DEX 多跳] ❌ 以下 buyer 钱包 OKB 余额不足以支付 prefund:', insufficientBuyers);
879
+ throw new Error(`Buyer 钱包 OKB 余额不足!请配置 Paymaster(全局设置 → AA Paymaster)或给 buyer 钱包充值 OKB。不足的钱包: ${insufficientBuyers.length} 个`);
879
880
  }
881
+ console.log('[AA DEX 多跳] ✅ Buyer 钱包 prefund 检查通过');
880
882
  }
881
- if (insufficientBuyers.length > 0) {
882
- console.error('[AA DEX 多跳] 以下 buyer 钱包 OKB 余额不足以支付 prefund:', insufficientBuyers);
883
- throw new Error(`Buyer 钱包 OKB 余额不足!在 ERC-4337 的 handleOps 中,所有 UserOps 在 Validation 阶段就需要支付 prefund,此时 hop 链还没有执行。` +
884
- `请先给 buyer 钱包充值 OKB。不足的钱包: ${insufficientBuyers.length} 个`);
883
+ else {
884
+ console.log('[AA DEX 多跳] Paymaster 模式,跳过 buyer 余额检查');
885
885
  }
886
- console.log('[AA DEX 多跳] ✅ Buyer 钱包 prefund 检查通过');
887
886
  }
888
887
  else {
889
888
  // Paymaster 模式:生成新钱包(不需要预充值,prefund=0)
@@ -892,10 +891,10 @@ export class AADexSwapExecutor {
892
891
  for (let h = 0; h < effectiveHopCount; h++) {
893
892
  const randomWallet = ethers.Wallet.createRandom();
894
893
  const wallet = new ethers.Wallet(randomWallet.privateKey, provider);
895
- const sender = await this.aaManager.predictSenderAddress(wallet.address);
894
+ const sender = await aaManager.predictSenderAddress(wallet.address);
896
895
  const code = await provider.getCode(sender);
897
896
  const deployed = code !== null && code !== '0x';
898
- const initCode = deployed ? '0x' : this.aaManager.generateInitCode(wallet.address);
897
+ const initCode = deployed ? '0x' : aaManager.generateInitCode(wallet.address);
899
898
  chainHops.push({ wallet, sender, deployed, initCode });
900
899
  // 初始化 nonce
901
900
  nonceMap.init(sender, 0n);
@@ -913,13 +912,13 @@ export class AADexSwapExecutor {
913
912
  const buyAmount = buyAmountsWei[buyerIdx] ?? 0n;
914
913
  if (buyAmount <= 0n)
915
914
  continue;
916
- // ✅ 计算 buyer 的 prefund(用于买入,使用常量)
917
- const buyerPrefund = estimatePrefund(DEX_BUY_CALL_GAS_LIMIT, buyerAi.deployed);
915
+ // ✅ 计算 buyer 的 prefund(用于买入)
916
+ // Paymaster 模式:prefund = 0(gas 由 Paymaster 代付)
917
+ // 非 Paymaster 模式:需要预充值
918
+ const buyerPrefund = hasPaymaster ? 0n : estimatePrefund(DEX_BUY_CALL_GAS_LIMIT, buyerAi.deployed);
918
919
  // ✅ 计算分发金额:
919
- // - 预充值模式:hop 已有 prefund,分发金额 = 买入金额 + buyer prefund
920
- // - Paymaster 模式:prefund = 0,分发金额 = 买入金额 + buyer prefund
921
- // 注意:无论哪种模式,都不需要在分发中包含 hop 的 prefund
922
- //(预充值模式下 hop 已有余额,Paymaster 模式下 prefund=0)
920
+ // - Paymaster 模式:分发金额 = 买入金额(不含 prefund,gas Paymaster 代付)
921
+ // - 预充值模式:分发金额 = 买入金额 + buyer prefund
923
922
  const chainTotalAmount = buyAmount + buyerPrefund;
924
923
  // 1) seller → hop0(不包含利润,利润由 Payer EOA 单独处理)
925
924
  const hop0 = chainHops[0];
@@ -931,7 +930,7 @@ export class AADexSwapExecutor {
931
930
  const transferData = erc20Iface.encodeFunctionData('transfer', [hop0.sender, buyAmount]);
932
931
  callData0 = encodeExecute(quoteToken, 0n, transferData);
933
932
  }
934
- const signedSellerToHop0 = await this.aaManager.buildUserOpWithState({
933
+ const signedSellerToHop0 = await aaManager.buildUserOpWithState({
935
934
  ownerWallet: sellerOwner,
936
935
  sender: sellerAi.sender,
937
936
  nonce: nonceMap.next(sellerAi.sender),
@@ -941,11 +940,12 @@ export class AADexSwapExecutor {
941
940
  });
942
941
  outOps.push(signedSellerToHop0.userOp);
943
942
  // 2) hop[j] → hop[j+1] (中间跳)
944
- // 预充值模式下,hop 钱包自己有 prefund,只需要传递买入金额 + buyer prefund
943
+ // Paymaster 模式:只传递买入金额(不含 prefund
944
+ // 预充值模式:传递买入金额 + buyer prefund(hop 自己的 gas 从预充值中支付)
945
945
  for (let j = 0; j < chainHops.length - 1; j++) {
946
946
  const currentHop = chainHops[j];
947
947
  const nextHop = chainHops[j + 1];
948
- // 传递金额 = 买入金额 + buyer prefund(hop 自己的 gas 从预充值中支付)
948
+ // 传递金额
949
949
  const amountToTransfer = buyAmount + buyerPrefund;
950
950
  let callData;
951
951
  if (useNativeToken) {
@@ -958,7 +958,7 @@ export class AADexSwapExecutor {
958
958
  // ✅ 关键修复:使用 buildUserOpWithFixedGas,确保 callGasLimit 与预充值时一致
959
959
  // 预充值时使用 HOP_CALL_GAS_LIMIT (150,000),这里也必须使用相同的值
960
960
  // 否则 buildUserOpWithState 会动态估算,可能得到 500,000,导致 prefund 不足
961
- const { userOp: hopUserOp } = await this.aaManager.buildUserOpWithFixedGas({
961
+ const { userOp: hopUserOp } = await aaManager.buildUserOpWithFixedGas({
962
962
  ownerWallet: currentHop.wallet,
963
963
  sender: currentHop.sender,
964
964
  nonce: nonceMap.next(currentHop.sender),
@@ -970,7 +970,7 @@ export class AADexSwapExecutor {
970
970
  gasPrice: fixedGasPrice, // ✅ 使用统一的 gasPrice
971
971
  },
972
972
  });
973
- const signedHop = await this.aaManager.signUserOp(hopUserOp, currentHop.wallet);
973
+ const signedHop = await aaManager.signUserOp(hopUserOp, currentHop.wallet);
974
974
  outOps.push(signedHop.userOp);
975
975
  }
976
976
  // 3) lastHop → buyer
@@ -985,7 +985,7 @@ export class AADexSwapExecutor {
985
985
  callDataLast = encodeExecute(quoteToken, 0n, transferData);
986
986
  }
987
987
  // ✅ 关键修复:同样使用 buildUserOpWithFixedGas
988
- const { userOp: lastHopUserOp } = await this.aaManager.buildUserOpWithFixedGas({
988
+ const { userOp: lastHopUserOp } = await aaManager.buildUserOpWithFixedGas({
989
989
  ownerWallet: lastHop.wallet,
990
990
  sender: lastHop.sender,
991
991
  nonce: nonceMap.next(lastHop.sender),
@@ -997,7 +997,7 @@ export class AADexSwapExecutor {
997
997
  gasPrice: fixedGasPrice, // ✅ 使用统一的 gasPrice
998
998
  },
999
999
  });
1000
- const signedLastHop = await this.aaManager.signUserOp(lastHopUserOp, lastHop.wallet);
1000
+ const signedLastHop = await aaManager.signUserOp(lastHopUserOp, lastHop.wallet);
1001
1001
  outOps.push(signedLastHop.userOp);
1002
1002
  }
1003
1003
  // ✅ 保存所有动态生成的多跳钱包信息,用于后续导出
@@ -1023,7 +1023,7 @@ export class AADexSwapExecutor {
1023
1023
  const transferData = erc20Iface.encodeFunctionData('transfer', [profitRecipient, profitWei]);
1024
1024
  profitCallData = encodeExecute(quoteToken, 0n, transferData);
1025
1025
  }
1026
- const signedProfitTransfer = await this.aaManager.buildUserOpWithState({
1026
+ const signedProfitTransfer = await aaManager.buildUserOpWithState({
1027
1027
  ownerWallet: sellerOwner,
1028
1028
  sender: sellerAi.sender,
1029
1029
  nonce: nonceMap.next(sellerAi.sender),
@@ -1077,7 +1077,7 @@ export class AADexSwapExecutor {
1077
1077
  // ✅ 关键修复:使用 buildUserOpWithFixedGas,确保 callGasLimit 和 gasPrice 与预检查时一致
1078
1078
  // 预检查使用 DEX_BUY_CALL_GAS_LIMIT (650,000) 和 fixedGasPrice
1079
1079
  // 这里必须使用相同的值,避免 gasPrice 不一致导致 prefund 不足
1080
- const { userOp: buyUserOp } = await this.aaManager.buildUserOpWithFixedGas({
1080
+ const { userOp: buyUserOp } = await aaManager.buildUserOpWithFixedGas({
1081
1081
  ownerWallet: buyerOwners[i],
1082
1082
  sender: ai.sender,
1083
1083
  nonce: nonceMap.next(ai.sender),
@@ -1089,32 +1089,58 @@ export class AADexSwapExecutor {
1089
1089
  gasPrice: fixedGasPrice, // ✅ 使用统一的 gasPrice
1090
1090
  },
1091
1091
  });
1092
- const signedBuy = await this.aaManager.signUserOp(buyUserOp, buyerOwners[i]);
1092
+ const signedBuy = await aaManager.signUserOp(buyUserOp, buyerOwners[i]);
1093
1093
  outOps.push(signedBuy.userOp);
1094
1094
  }
1095
- // ✅ 获取 Payer 的 nonce(handleOps 使用 payerStartNonce,利润交易使用 +1)
1095
+ // ✅ 获取 Payer 的 nonce
1096
1096
  const handleOpsNonce = payerStartNonce ?? await provider.getTransactionCount(payerWallet.address, 'pending');
1097
- const signedHandleOps = await this.bundleExecutor['signHandleOpsTx']({
1098
- ops: outOps,
1099
- payerWallet,
1100
- beneficiary,
1101
- nonce: handleOpsNonce,
1102
- });
1103
- const signedTransactions = [signedHandleOps];
1104
- // ✅ 利润由 Payer EOA 独立交易刮取(capitalMode=true 时)
1105
- // 不参与 AA 分发,避免价格下跌导致买方资金不足
1097
+ let signedTransactions;
1098
+ // ✅ 利润刮取优化:使用 Multicall3 在同一个交易里执行 handleOps + 利润转账
1099
+ // 好处:原子性执行,只需要一个交易,节省 gas
1106
1100
  if (capitalMode && extractProfit && profitWei > 0n) {
1107
- const profitTx = await payerWallet.signTransaction({
1108
- to: profitRecipient,
1101
+ const entryPointAddress = aaManager.getEntryPointAddress();
1102
+ const epIface = new ethers.Interface([
1103
+ 'function handleOps((address sender,uint256 nonce,bytes initCode,bytes callData,uint256 callGasLimit,uint256 verificationGasLimit,uint256 preVerificationGas,uint256 maxFeePerGas,uint256 maxPriorityFeePerGas,bytes paymasterAndData,bytes signature)[] ops, address payable beneficiary) external',
1104
+ ]);
1105
+ const handleOpsData = epIface.encodeFunctionData('handleOps', [outOps, beneficiary]);
1106
+ // 使用 Multicall3 包装 handleOps + 利润转账
1107
+ const multicallIface = new ethers.Interface([
1108
+ 'function aggregate3Value((address target, bool allowFailure, uint256 value, bytes callData)[] calls) payable returns ((bool success, bytes returnData)[] returnData)',
1109
+ ]);
1110
+ const calls = [
1111
+ { target: entryPointAddress, allowFailure: false, value: 0n, callData: handleOpsData },
1112
+ { target: profitRecipient, allowFailure: false, value: profitWei, callData: '0x' },
1113
+ ];
1114
+ const multicallData = multicallIface.encodeFunctionData('aggregate3Value', [calls]);
1115
+ // 计算 handleOps 的 gasLimit
1116
+ const sumOpsGasLimit = outOps.reduce((sum, op) => {
1117
+ return sum + BigInt(op.callGasLimit) + BigInt(op.verificationGasLimit) + BigInt(op.preVerificationGas);
1118
+ }, 0n);
1119
+ const handleOpsOverhead = BigInt(outOps.length * 50000) + 100000n;
1120
+ const minRequiredGas = sumOpsGasLimit + handleOpsOverhead + 50000n; // 额外 50k 给 Multicall3
1121
+ const gasLimit = BigInt(Math.ceil(Number(minRequiredGas) * 1.1));
1122
+ const signedMulticallTx = await payerWallet.signTransaction({
1123
+ to: MULTICALL3,
1124
+ data: multicallData,
1109
1125
  value: profitWei,
1110
- nonce: handleOpsNonce + 1,
1111
- gasLimit: 21000n,
1126
+ nonce: handleOpsNonce,
1127
+ gasLimit,
1112
1128
  gasPrice: fixedGasPrice,
1113
1129
  chainId: this.config.chainId ?? 196,
1114
1130
  type: 0,
1115
1131
  });
1116
- signedTransactions.push(profitTx);
1117
- console.log(`[AA DEX 批量换手] ✅ 利润由 Payer EOA 独立刮取: ${ethers.formatEther(profitWei)} OKB → ${profitRecipient}`);
1132
+ signedTransactions = [signedMulticallTx];
1133
+ console.log(`[AA DEX 批量换手] ✅ 利润刮取已合并到 handleOps(通过 Multicall3): ${ethers.formatEther(profitWei)} OKB → ${profitRecipient}`);
1134
+ }
1135
+ else {
1136
+ // 无利润刮取:直接签名 handleOps
1137
+ const signedHandleOps = await this.bundleExecutor['signHandleOpsTx']({
1138
+ ops: outOps,
1139
+ payerWallet,
1140
+ beneficiary,
1141
+ nonce: handleOpsNonce,
1142
+ });
1143
+ signedTransactions = [signedHandleOps];
1118
1144
  }
1119
1145
  // ✅ 收集动态生成的多跳钱包信息,用于交易失败时找回资金
1120
1146
  const hopWallets = this._generatedHopWallets || [];
@@ -67,6 +67,7 @@ export { DexBundleExecutor, createDexBundleExecutor, dexBundleBuySell, type DexB
67
67
  export { AAPortalSwapExecutor, } from './portal-bundle-swap.js';
68
68
  export { AADexSwapExecutor, } from './dex-bundle-swap.js';
69
69
  export { HopWalletManager, createHopWalletManager, type HopWalletInfo, type PrefundProgressCallback, type PrefundConfig, type PrefundResult, type RefundResult, } from './hop-wallet-manager.js';
70
+ export { PaymasterManager, createPaymasterManager, SIMPLE_PAYMASTER_ABI, SIMPLE_PAYMASTER_BYTECODE, type PaymasterConfig, type PaymasterDeployResult, type PaymasterDepositResult, } from './paymaster.js';
70
71
  export { AAPortalBuyFirstExecutor, createAAPortalBuyFirstExecutor, } from './portal-buy-first.js';
71
72
  export { AADexBuyFirstExecutor, createAADexBuyFirstExecutor, } from './dex-buy-first.js';
72
73
  export { BuyFirstVolumeExecutor, createBuyFirstVolumeExecutor, makeBuyFirstVolume, } from './buy-first-volume.js';
@@ -98,6 +98,10 @@ export { AADexSwapExecutor, } from './dex-bundle-swap.js';
98
98
  // ============================================================================
99
99
  export { HopWalletManager, createHopWalletManager, } from './hop-wallet-manager.js';
100
100
  // ============================================================================
101
+ // Paymaster 管理器(免预充值)
102
+ // ============================================================================
103
+ export { PaymasterManager, createPaymasterManager, SIMPLE_PAYMASTER_ABI, SIMPLE_PAYMASTER_BYTECODE, } from './paymaster.js';
104
+ // ============================================================================
101
105
  // AA Buy-First(刷量专用)
102
106
  // ============================================================================
103
107
  export { AAPortalBuyFirstExecutor, createAAPortalBuyFirstExecutor, } from './portal-buy-first.js';