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.
- package/dist/xlayer/dex-bundle-swap.js +122 -96
- package/dist/xlayer/index.d.ts +1 -0
- package/dist/xlayer/index.js +4 -0
- package/dist/xlayer/paymaster.d.ts +202 -0
- package/dist/xlayer/paymaster.js +279 -0
- package/dist/xlayer/portal-bundle-swap.js +136 -105
- package/dist/xlayer/wash-ops.js +80 -26
- package/package.json +1 -1
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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' :
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
// ✅
|
|
714
|
-
//
|
|
715
|
-
|
|
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},
|
|
719
|
-
// ✅
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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
|
-
|
|
733
|
-
|
|
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
|
-
// -
|
|
747
|
-
// -
|
|
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,
|
|
755
|
-
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
|
|
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
|
|
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
|
-
// ✅
|
|
870
|
-
//
|
|
871
|
-
//
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
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
|
-
|
|
882
|
-
console.
|
|
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
|
|
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' :
|
|
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
|
-
|
|
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
|
-
// -
|
|
920
|
-
// -
|
|
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
|
|
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
|
-
//
|
|
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
|
-
// 传递金额
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1092
|
+
const signedBuy = await aaManager.signUserOp(buyUserOp, buyerOwners[i]);
|
|
1093
1093
|
outOps.push(signedBuy.userOp);
|
|
1094
1094
|
}
|
|
1095
|
-
// ✅ 获取 Payer 的 nonce
|
|
1095
|
+
// ✅ 获取 Payer 的 nonce
|
|
1096
1096
|
const handleOpsNonce = payerStartNonce ?? await provider.getTransactionCount(payerWallet.address, 'pending');
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
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
|
|
1108
|
-
|
|
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
|
|
1111
|
-
gasLimit
|
|
1126
|
+
nonce: handleOpsNonce,
|
|
1127
|
+
gasLimit,
|
|
1112
1128
|
gasPrice: fixedGasPrice,
|
|
1113
1129
|
chainId: this.config.chainId ?? 196,
|
|
1114
1130
|
type: 0,
|
|
1115
1131
|
});
|
|
1116
|
-
signedTransactions
|
|
1117
|
-
console.log(`[AA DEX 批量换手] ✅
|
|
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 || [];
|
package/dist/xlayer/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/xlayer/index.js
CHANGED
|
@@ -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';
|