four-flap-meme-sdk 1.6.22 → 1.6.24
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/bundle.js
CHANGED
|
@@ -1561,9 +1561,14 @@ export class BundleExecutor {
|
|
|
1561
1561
|
ops1.push(signedCreateOp.userOp);
|
|
1562
1562
|
// --- 3. 构建内盘买入 Ops(并行优化)---
|
|
1563
1563
|
// ✅ 预计算所有买入金额
|
|
1564
|
+
// Gas Limit 策略:
|
|
1565
|
+
// - 最后一笔内盘买入(触发毕业):800W
|
|
1566
|
+
// - 其他内盘买入:50W
|
|
1567
|
+
const lastCurveIndex = curveBuyerWallets.length - 1;
|
|
1564
1568
|
const curveBuyData = curveBuyerWallets.map((wallet, i) => {
|
|
1565
1569
|
const buyWei = parseOkb(curveBuyAmounts[i]);
|
|
1566
|
-
const
|
|
1570
|
+
const isGraduationTrigger = (i === lastCurveIndex);
|
|
1571
|
+
const gasLimit = isGraduationTrigger ? 8000000n : 500000n;
|
|
1567
1572
|
return { wallet, info: curveBuyerInfos[i], buyWei, gasLimit };
|
|
1568
1573
|
});
|
|
1569
1574
|
totalCurveBuyWei = curveBuyData.reduce((sum, d) => sum + d.buyWei, 0n);
|
|
@@ -1636,7 +1641,7 @@ export class BundleExecutor {
|
|
|
1636
1641
|
nonce: nonceMap.next(info.sender),
|
|
1637
1642
|
initCode: info.deployed ? '0x' : (await aaManager.generateInitCode(wallet.address)),
|
|
1638
1643
|
deployed: info.deployed,
|
|
1639
|
-
fixedGas: { callGasLimit:
|
|
1644
|
+
fixedGas: { callGasLimit: 500000n } // 外盘买入:50W
|
|
1640
1645
|
});
|
|
1641
1646
|
const signedDexBuyOp = await aaManager.signUserOp(dexBuyOpRes.userOp, wallet);
|
|
1642
1647
|
return signedDexBuyOp.userOp;
|
|
@@ -1660,10 +1665,7 @@ export class BundleExecutor {
|
|
|
1660
1665
|
initCode: '0x', // Payer 已在发币时部署
|
|
1661
1666
|
callData: profitCallData,
|
|
1662
1667
|
deployed: true,
|
|
1663
|
-
fixedGas: {
|
|
1664
|
-
...(effConfig.fixedGas ?? {}),
|
|
1665
|
-
callGasLimit: effConfig.fixedGas?.callGasLimit ?? DEFAULT_CALL_GAS_LIMIT_WITHDRAW,
|
|
1666
|
-
},
|
|
1668
|
+
fixedGas: { callGasLimit: DEFAULT_CALL_GAS_LIMIT_WITHDRAW }, // 利润转账:12W(固定值)
|
|
1667
1669
|
});
|
|
1668
1670
|
const signedProfitOp = await aaManager.signUserOp(profitOp, payerWallet);
|
|
1669
1671
|
allOps.push(signedProfitOp.userOp);
|
|
@@ -607,15 +607,55 @@ export class AADexSwapExecutor {
|
|
|
607
607
|
// ✅ 多跳模式:在第一跳时同时将利润刮取到 profitRecipient
|
|
608
608
|
const hopSenders = buyerSenders.slice(0, hopCount);
|
|
609
609
|
const hopOwners = buyerOwners.slice(0, hopCount);
|
|
610
|
+
const hopAis = buyerAis.slice(0, hopCount);
|
|
611
|
+
// ✅ 关键修复:计算每个 hop 钱包执行 UserOp 需要的 prefund(gas 费用)
|
|
612
|
+
// EntryPoint 在 validation 阶段会扣除 prefund,此时 hop 钱包还没收到 seller 的转账
|
|
613
|
+
// 因此需要在 seller 转账时额外包含 hop 的 gas 费用
|
|
614
|
+
const feeData = await this.aaManager.getFeeData();
|
|
615
|
+
const gasPrice = feeData.gasPrice ?? feeData.maxFeePerGas ?? 5000000000n; // 5 gwei fallback
|
|
616
|
+
// 每个 hop 钱包的预估 prefund:(callGasLimit + verificationGasLimit + preVerificationGas) * gasPrice
|
|
617
|
+
// 未部署账户需要更高的 verificationGasLimit(约 800,000),已部署约 250,000
|
|
618
|
+
// callGasLimit 约 150,000(native 转账),preVerificationGas 约 21,000
|
|
619
|
+
const estimateHopPrefund = (deployed) => {
|
|
620
|
+
const callGas = 150000n;
|
|
621
|
+
const verifyGas = deployed ? 250000n : 800000n;
|
|
622
|
+
const preVerifyGas = 21000n;
|
|
623
|
+
const totalGas = callGas + verifyGas + preVerifyGas;
|
|
624
|
+
// 加 20% buffer 以确保足够
|
|
625
|
+
return (totalGas * gasPrice * 120n) / 100n;
|
|
626
|
+
};
|
|
627
|
+
// 计算所有 hop 钱包需要的总 prefund
|
|
628
|
+
let totalHopPrefund = 0n;
|
|
629
|
+
const hopPrefunds = [];
|
|
630
|
+
for (let j = 0; j < hopSenders.length; j++) {
|
|
631
|
+
const isLastHop = j === hopSenders.length - 1;
|
|
632
|
+
const hopAi = hopAis[j];
|
|
633
|
+
const hopDeployed = hopAi.deployed;
|
|
634
|
+
if (isLastHop) {
|
|
635
|
+
const rest = buyerSenders.slice(hopSenders.length);
|
|
636
|
+
const disperseOpCount = Math.ceil(rest.length / maxPerOp) || 1;
|
|
637
|
+
const prefund = estimateHopPrefund(hopDeployed) * BigInt(disperseOpCount);
|
|
638
|
+
hopPrefunds.push(prefund);
|
|
639
|
+
totalHopPrefund += prefund;
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
const prefund = estimateHopPrefund(hopDeployed);
|
|
643
|
+
hopPrefunds.push(prefund);
|
|
644
|
+
totalHopPrefund += prefund;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
console.log(`[AA DEX 多跳] hop 数量: ${hopSenders.length}, 总 prefund 预估: ${ethers.formatEther(totalHopPrefund)} OKB`);
|
|
610
648
|
const hop0 = hopSenders[0];
|
|
611
649
|
let callData0;
|
|
650
|
+
// ✅ 关键修复:seller 转给 hop0 时,额外包含所有 hop 的 gas 费用
|
|
651
|
+
const amountToHop0 = totalBuyWei + totalHopPrefund;
|
|
612
652
|
if (useNativeToken) {
|
|
613
653
|
// ✅ 原生代币模式
|
|
614
654
|
if (extractProfit && profitWei > 0n) {
|
|
615
|
-
callData0 = encodeExecuteBatch([hop0, profitRecipient], [
|
|
655
|
+
callData0 = encodeExecuteBatch([hop0, profitRecipient], [amountToHop0, profitWei], ['0x', '0x']);
|
|
616
656
|
}
|
|
617
657
|
else {
|
|
618
|
-
callData0 = encodeExecute(hop0,
|
|
658
|
+
callData0 = encodeExecute(hop0, amountToHop0, '0x');
|
|
619
659
|
}
|
|
620
660
|
}
|
|
621
661
|
else {
|
|
@@ -639,21 +679,27 @@ export class AADexSwapExecutor {
|
|
|
639
679
|
signOnly: true,
|
|
640
680
|
});
|
|
641
681
|
outOps.push(signedToHop0.userOp);
|
|
682
|
+
// ✅ 修复:每个 hop 在转发时,需要把后续 hop 的 prefund 一起转走
|
|
642
683
|
let prefixKept = 0n;
|
|
684
|
+
let prefixPrefundUsed = hopPrefunds[0] ?? 0n; // hop0 自己用掉的 prefund
|
|
643
685
|
for (let j = 0; j < hopSenders.length - 1; j++) {
|
|
644
686
|
const sender = hopSenders[j];
|
|
645
687
|
const next = hopSenders[j + 1];
|
|
646
688
|
const keep = buyAmountsWei[j] ?? 0n;
|
|
647
689
|
prefixKept += keep;
|
|
648
|
-
|
|
649
|
-
|
|
690
|
+
// 剩余需要转给后续 hop 的金额 = (totalBuyWei - 已保留的买入金额) + (剩余 hop 的 prefund)
|
|
691
|
+
const remainingPrefund = totalHopPrefund - prefixPrefundUsed;
|
|
692
|
+
const remainingBuyWei = totalBuyWei - prefixKept;
|
|
693
|
+
const amountToTransfer = remainingBuyWei + remainingPrefund;
|
|
694
|
+
if (amountToTransfer <= 0n)
|
|
650
695
|
break;
|
|
651
696
|
let callData;
|
|
652
697
|
if (useNativeToken) {
|
|
653
|
-
callData = encodeExecute(next,
|
|
698
|
+
callData = encodeExecute(next, amountToTransfer, '0x');
|
|
654
699
|
}
|
|
655
700
|
else {
|
|
656
|
-
|
|
701
|
+
// ERC20 模式:仍使用原来的 remainingBuyWei(不含 prefund,因为 ERC20 模式 prefund 仍为 OKB)
|
|
702
|
+
const transferData = erc20Iface.encodeFunctionData('transfer', [next, remainingBuyWei]);
|
|
657
703
|
callData = encodeExecute(quoteToken, 0n, transferData);
|
|
658
704
|
}
|
|
659
705
|
const signedHop = await this.aaManager.buildUserOpWithState({
|
|
@@ -665,6 +711,8 @@ export class AADexSwapExecutor {
|
|
|
665
711
|
signOnly: true,
|
|
666
712
|
});
|
|
667
713
|
outOps.push(signedHop.userOp);
|
|
714
|
+
// ✅ 更新已使用的 prefund(当前 hop 的 prefund 已被扣除)
|
|
715
|
+
prefixPrefundUsed += hopPrefunds[j + 1] ?? 0n;
|
|
668
716
|
}
|
|
669
717
|
const lastHopIdx = hopSenders.length - 1;
|
|
670
718
|
const lastHopSender = hopSenders[lastHopIdx];
|
|
@@ -769,8 +817,21 @@ export class AADexSwapExecutor {
|
|
|
769
817
|
});
|
|
770
818
|
// ✅ 利润已在 AA 内部刮取(capitalMode=true 在分发阶段;capitalMode=false 单独转账),不需要 Tail Tx
|
|
771
819
|
const signedTransactions = [signedHandleOps];
|
|
820
|
+
// ✅ 收集多跳钱包信息(用作资金中转的买方钱包),用于交易失败时找回资金
|
|
821
|
+
const hopWallets = [];
|
|
822
|
+
if (capitalMode && hopCount > 0) {
|
|
823
|
+
const hopSenders = buyerAis.slice(0, hopCount).map(ai => ai.sender);
|
|
824
|
+
const hopOwnerWallets = buyerOwners.slice(0, hopCount);
|
|
825
|
+
for (let i = 0; i < hopCount; i++) {
|
|
826
|
+
hopWallets.push({
|
|
827
|
+
address: hopSenders[i],
|
|
828
|
+
privateKey: hopOwnerWallets[i].privateKey,
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
}
|
|
772
832
|
return {
|
|
773
833
|
signedTransactions,
|
|
834
|
+
hopWallets: hopWallets.length > 0 ? hopWallets : undefined,
|
|
774
835
|
metadata: {
|
|
775
836
|
payer: payerWallet.address,
|
|
776
837
|
beneficiary,
|
|
@@ -414,14 +414,58 @@ export class AAPortalSwapExecutor {
|
|
|
414
414
|
// ✅ 多跳模式:在第一跳时同时将利润刮取到 profitRecipient
|
|
415
415
|
const hopSenders = buyerSenders.slice(0, hopCount);
|
|
416
416
|
const hopOwners = buyerOwners.slice(0, hopCount);
|
|
417
|
+
const hopAis = buyerAis.slice(0, hopCount);
|
|
418
|
+
// ✅ 关键修复:计算每个 hop 钱包执行 UserOp 需要的 prefund(gas 费用)
|
|
419
|
+
// EntryPoint 在 validation 阶段会扣除 prefund,此时 hop 钱包还没收到 seller 的转账
|
|
420
|
+
// 因此需要在 seller 转账时额外包含 hop 的 gas 费用
|
|
421
|
+
const feeData = await this.aaManager.getFeeData();
|
|
422
|
+
const gasPrice = feeData.gasPrice ?? feeData.maxFeePerGas ?? 5000000000n; // 5 gwei fallback
|
|
423
|
+
// 每个 hop 钱包的预估 prefund:(callGasLimit + verificationGasLimit + preVerificationGas) * gasPrice
|
|
424
|
+
// 未部署账户需要更高的 verificationGasLimit(约 800,000),已部署约 250,000
|
|
425
|
+
// callGasLimit 约 150,000(native 转账),preVerificationGas 约 21,000
|
|
426
|
+
const estimateHopPrefund = (deployed) => {
|
|
427
|
+
const callGas = 150000n;
|
|
428
|
+
const verifyGas = deployed ? 250000n : 800000n;
|
|
429
|
+
const preVerifyGas = 21000n;
|
|
430
|
+
const totalGas = callGas + verifyGas + preVerifyGas;
|
|
431
|
+
// 加 20% buffer 以确保足够
|
|
432
|
+
return (totalGas * gasPrice * 120n) / 100n;
|
|
433
|
+
};
|
|
434
|
+
// 计算所有 hop 钱包需要的总 prefund
|
|
435
|
+
let totalHopPrefund = 0n;
|
|
436
|
+
const hopPrefunds = [];
|
|
437
|
+
for (let j = 0; j < hopSenders.length; j++) {
|
|
438
|
+
// hop[j] 需要执行的 UserOp 数量:
|
|
439
|
+
// - 中间 hop (j < length-1):1 个转账 UserOp
|
|
440
|
+
// - 最后一个 hop:可能有多个分发 UserOp
|
|
441
|
+
const isLastHop = j === hopSenders.length - 1;
|
|
442
|
+
const hopAi = hopAis[j];
|
|
443
|
+
const hopDeployed = hopAi.deployed;
|
|
444
|
+
if (isLastHop) {
|
|
445
|
+
// 最后一个 hop 需要分发给剩余买方,可能需要多个 UserOp
|
|
446
|
+
const rest = buyerSenders.slice(hopSenders.length);
|
|
447
|
+
const disperseOpCount = Math.ceil(rest.length / maxPerOp) || 1;
|
|
448
|
+
const prefund = estimateHopPrefund(hopDeployed) * BigInt(disperseOpCount);
|
|
449
|
+
hopPrefunds.push(prefund);
|
|
450
|
+
totalHopPrefund += prefund;
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
const prefund = estimateHopPrefund(hopDeployed);
|
|
454
|
+
hopPrefunds.push(prefund);
|
|
455
|
+
totalHopPrefund += prefund;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
console.log(`[AA 多跳] hop 数量: ${hopSenders.length}, 总 prefund 预估: ${ethers.formatEther(totalHopPrefund)} OKB`);
|
|
417
459
|
const hop0 = hopSenders[0];
|
|
418
460
|
let callData0;
|
|
461
|
+
// ✅ 关键修复:seller 转给 hop0 时,额外包含所有 hop 的 gas 费用
|
|
462
|
+
const amountToHop0 = totalBuyWei + totalHopPrefund;
|
|
419
463
|
if (useNativeToken) {
|
|
420
464
|
if (extractProfit && profitWei > 0n) {
|
|
421
|
-
callData0 = encodeExecuteBatch([hop0, profitRecipient], [
|
|
465
|
+
callData0 = encodeExecuteBatch([hop0, profitRecipient], [amountToHop0, profitWei], ['0x', '0x']);
|
|
422
466
|
}
|
|
423
467
|
else {
|
|
424
|
-
callData0 = encodeExecute(hop0,
|
|
468
|
+
callData0 = encodeExecute(hop0, amountToHop0, '0x');
|
|
425
469
|
}
|
|
426
470
|
}
|
|
427
471
|
else {
|
|
@@ -445,21 +489,27 @@ export class AAPortalSwapExecutor {
|
|
|
445
489
|
});
|
|
446
490
|
outOps.push(signedToHop0.userOp);
|
|
447
491
|
// 2) hop j -> hop j+1
|
|
492
|
+
// ✅ 修复:每个 hop 在转发时,需要把后续 hop 的 prefund 一起转走
|
|
448
493
|
let prefixKept = 0n;
|
|
494
|
+
let prefixPrefundUsed = hopPrefunds[0] ?? 0n; // hop0 自己用掉的 prefund
|
|
449
495
|
for (let j = 0; j < hopSenders.length - 1; j++) {
|
|
450
496
|
const sender = hopSenders[j];
|
|
451
497
|
const next = hopSenders[j + 1];
|
|
452
498
|
const keep = buyAmountsWei[j] ?? 0n;
|
|
453
499
|
prefixKept += keep;
|
|
454
|
-
|
|
455
|
-
|
|
500
|
+
// 剩余需要转给后续 hop 的金额 = (totalBuyWei - 已保留的买入金额) + (剩余 hop 的 prefund)
|
|
501
|
+
const remainingPrefund = totalHopPrefund - prefixPrefundUsed;
|
|
502
|
+
const remainingBuyWei = totalBuyWei - prefixKept;
|
|
503
|
+
const amountToTransfer = remainingBuyWei + remainingPrefund;
|
|
504
|
+
if (amountToTransfer <= 0n)
|
|
456
505
|
break;
|
|
457
506
|
let callData;
|
|
458
507
|
if (useNativeToken) {
|
|
459
|
-
callData = encodeExecute(next,
|
|
508
|
+
callData = encodeExecute(next, amountToTransfer, '0x');
|
|
460
509
|
}
|
|
461
510
|
else {
|
|
462
|
-
|
|
511
|
+
// ERC20 模式:仍使用原来的 remainingBuyWei(不含 prefund,因为 ERC20 模式 prefund 仍为 OKB)
|
|
512
|
+
const transferData = erc20Iface.encodeFunctionData('transfer', [next, remainingBuyWei]);
|
|
463
513
|
callData = encodeExecute(quoteToken, 0n, transferData);
|
|
464
514
|
}
|
|
465
515
|
const signedHop = await this.aaManager.buildUserOpWithState({
|
|
@@ -471,6 +521,8 @@ export class AAPortalSwapExecutor {
|
|
|
471
521
|
signOnly: true,
|
|
472
522
|
});
|
|
473
523
|
outOps.push(signedHop.userOp);
|
|
524
|
+
// ✅ 更新已使用的 prefund(当前 hop 的 prefund 已被扣除)
|
|
525
|
+
prefixPrefundUsed += hopPrefunds[j + 1] ?? 0n;
|
|
474
526
|
}
|
|
475
527
|
// 3) lastHop 分发给剩余买方
|
|
476
528
|
const lastHopIdx = hopSenders.length - 1;
|
|
@@ -560,8 +612,21 @@ export class AAPortalSwapExecutor {
|
|
|
560
612
|
});
|
|
561
613
|
// ✅ 利润已在 AA 内部刮取(capitalMode=true 在分发阶段;capitalMode=false 单独转账),不需要 Tail Tx
|
|
562
614
|
const signedTransactions = [signedHandleOps];
|
|
615
|
+
// ✅ 收集多跳钱包信息(用作资金中转的买方钱包),用于交易失败时找回资金
|
|
616
|
+
const hopWallets = [];
|
|
617
|
+
if (capitalMode && hopCount > 0) {
|
|
618
|
+
const hopSenders = buyerAis.slice(0, hopCount).map(ai => ai.sender);
|
|
619
|
+
const hopOwnerWallets = buyerOwners.slice(0, hopCount);
|
|
620
|
+
for (let i = 0; i < hopCount; i++) {
|
|
621
|
+
hopWallets.push({
|
|
622
|
+
address: hopSenders[i],
|
|
623
|
+
privateKey: hopOwnerWallets[i].privateKey,
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
}
|
|
563
627
|
return {
|
|
564
628
|
signedTransactions,
|
|
629
|
+
hopWallets: hopWallets.length > 0 ? hopWallets : undefined,
|
|
565
630
|
metadata: {
|
|
566
631
|
payer: payerWallet.address,
|
|
567
632
|
beneficiary,
|
package/dist/xlayer/types.d.ts
CHANGED
|
@@ -555,6 +555,11 @@ export interface BundleBatchSwapSignParams extends BundleBatchSwapParams {
|
|
|
555
555
|
}
|
|
556
556
|
export interface BundleBatchSwapSignResult {
|
|
557
557
|
signedTransactions: string[];
|
|
558
|
+
/** ✅ 多跳中转钱包列表(用作资金中转的买方钱包,交易失败时可找回资金) */
|
|
559
|
+
hopWallets?: Array<{
|
|
560
|
+
address: string;
|
|
561
|
+
privateKey: string;
|
|
562
|
+
}>;
|
|
558
563
|
metadata: {
|
|
559
564
|
payer: string;
|
|
560
565
|
beneficiary: string;
|