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.
@@ -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 gasLimit = 8000000n; // 统一给 800W,不再区分是否最后一笔
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: 600000n }
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], [totalBuyWei, profitWei], ['0x', '0x']);
655
+ callData0 = encodeExecuteBatch([hop0, profitRecipient], [amountToHop0, profitWei], ['0x', '0x']);
616
656
  }
617
657
  else {
618
- callData0 = encodeExecute(hop0, totalBuyWei, '0x');
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
- const remaining = totalBuyWei - prefixKept;
649
- if (remaining <= 0n)
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, remaining, '0x');
698
+ callData = encodeExecute(next, amountToTransfer, '0x');
654
699
  }
655
700
  else {
656
- const transferData = erc20Iface.encodeFunctionData('transfer', [next, remaining]);
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], [totalBuyWei, profitWei], ['0x', '0x']);
465
+ callData0 = encodeExecuteBatch([hop0, profitRecipient], [amountToHop0, profitWei], ['0x', '0x']);
422
466
  }
423
467
  else {
424
- callData0 = encodeExecute(hop0, totalBuyWei, '0x');
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
- const remaining = totalBuyWei - prefixKept;
455
- if (remaining <= 0n)
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, remaining, '0x');
508
+ callData = encodeExecute(next, amountToTransfer, '0x');
460
509
  }
461
510
  else {
462
- const transferData = erc20Iface.encodeFunctionData('transfer', [next, remaining]);
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,
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.6.22",
3
+ "version": "1.6.24",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",