four-flap-meme-sdk 1.6.4 → 1.6.6

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.
@@ -742,14 +742,13 @@ export async function fourQuickBatchSwapMerkle(params) {
742
742
  });
743
743
  signedTransactions.push(...profitHopResult.signedTransactions);
744
744
  profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
745
- console.log(`[fourQuickBatchSwapMerkle] 利润多跳交易已签名: ${profitHopResult.signedTransactions.length} 笔`);
745
+ // 多跳交易已签名
746
746
  }
747
747
  console.log(`[fourQuickBatchSwapMerkle] 交易组装完成: ${signedTransactions.length} 笔`);
748
748
  console.log(` - 贿赂: ${bribeTx ? 1 : 0}`);
749
749
  console.log(` - 卖出: 1`);
750
750
  console.log(` - 转账: ${transferTxs.length}`);
751
751
  console.log(` - 买入: ${signedBuys.length}`);
752
- console.log(` - 利润多跳: ${profitAmount > 0n ? PROFIT_HOP_COUNT + 1 : 0}`);
753
752
  return {
754
753
  signedTransactions,
755
754
  disperseHopWallets: allHopWallets.length > 0 ? allHopWallets : undefined, // ✅ 返回转账多跳钱包
@@ -196,8 +196,7 @@ async function getTokenToNativeQuote(provider, tokenAddress, tokenAmount, chainI
196
196
  catch (error) {
197
197
  console.warn(`[getTokenToNativeQuote] 报价失败:`, error);
198
198
  }
199
- // ✅ 报价失败,返回最低利润(0.0001 BNB)
200
- console.log(`[getTokenToNativeQuote] 无法获取报价,使用最低利润: ${MIN_PROFIT_WEI}`);
199
+ // 报价失败,返回最低金额
201
200
  return MIN_PROFIT_WEI;
202
201
  }
203
202
  /**
@@ -1195,14 +1195,13 @@ export async function flapQuickBatchSwapMerkle(params) {
1195
1195
  signedTransactions.push(...profitResult.signedTransactions);
1196
1196
  profitHopWallets = profitResult.hopWallets; // ✅ 收集利润多跳钱包
1197
1197
  }
1198
- console.log(`[flapQuickBatchSwapMerkle] 利润多跳交易已签名: ${profitResult?.signedTransactions.length || 0} 笔`);
1198
+ // 多跳交易已签名
1199
1199
  }
1200
1200
  console.log(`[flapQuickBatchSwapMerkle] 交易组装完成: ${signedTransactions.length} 笔`);
1201
1201
  console.log(` - 贿赂: ${bribeTx ? 1 : 0}`);
1202
1202
  console.log(` - 卖出: 1`);
1203
1203
  console.log(` - 转账: ${transferTxs.length}`);
1204
1204
  console.log(` - 买入: ${signedBuys.length}`);
1205
- console.log(` - 利润多跳: ${nativeProfitAmount > 0n ? PROFIT_HOP_COUNT + 1 : 0}`);
1206
1205
  return {
1207
1206
  signedTransactions,
1208
1207
  disperseHopWallets: allHopWallets.length > 0 ? allHopWallets : undefined, // ✅ 返回转账多跳钱包
package/dist/index.d.ts CHANGED
@@ -54,4 +54,4 @@ submitDirectToRpcSequentialNoWait, // ✅ 顺序广播但不等待确认(用
54
54
  submitDirectToRpcParallel, type DirectSubmitConfig, type DirectSubmitResult, type DirectTxResult } from './contracts/tm-bundle-merkle/submit.js';
55
55
  export { directV2BatchBuy, directV2BatchSell, directV3BatchBuy, directV3BatchSell, getRouterAddress, DIRECT_ROUTERS, type DirectV2BuyParams, type DirectV2SellParams, type DirectV3BuyParams, type DirectV3SellParams, type DirectRouterResult, type DirectRouterSignConfig, type DexKey, type RouterVersion, } from './dex/index.js';
56
56
  export * as XLayer from './xlayer/index.js';
57
- export { bundleBuy as xlayerBundleBuy, bundleSell as xlayerBundleSell, bundleBuySell as xlayerBundleBuySell, bundleCreateBuySign as xlayerBundleCreateBuySign, bundleCreateToDexSign as xlayerBundleCreateToDexSign, bundleGraduateBuy as xlayerBundleGraduateBuy, bundleSwapSign as xlayerBundleSwapSign, bundleBatchSwapSign as xlayerBundleBatchSwapSign, createBundleExecutor as xlayerCreateBundleExecutor, BundleExecutor as XLayerBundleExecutor, makeVolume as xlayerMakeVolume, singleRoundVolume as xlayerSingleRoundVolume, createVolumeExecutor as xlayerCreateVolumeExecutor, VolumeExecutor as XLayerVolumeExecutor, makeBuyFirstVolume as xlayerMakeBuyFirstVolume, BuyFirstVolumeExecutor as XLayerBuyFirstVolumeExecutor, AAPortalBuyFirstExecutor as XLayerAAPortalBuyFirstExecutor, AADexBuyFirstExecutor as XLayerAADexBuyFirstExecutor, AAVolumeBuyFirstExecutor as XLayerAAVolumeBuyFirstExecutor, createAAVolumeBuyFirstExecutor as xlayerCreateAAVolumeBuyFirstExecutor, type VolumeBuyFirstParams as XLayerVolumeBuyFirstParams, type VolumeBuyFirstResult as XLayerVolumeBuyFirstResult, type VolumeContinuousParams as XLayerVolumeContinuousParams, type VolumeContinuousResult as XLayerVolumeContinuousResult, type VolumeProgress as XLayerVolumeProgress, createDexExecutor as xlayerCreateDexExecutor, createDexQuery as xlayerCreateDexQuery, quoteOkbToToken as xlayerQuoteOkbToToken, quoteTokenToOkb as xlayerQuoteTokenToOkb, DexExecutor as XLayerDexExecutor, DexQuery as XLayerDexQuery, createAAAccountManager as xlayerCreateAAAccountManager, predictSender as xlayerPredictSender, createWallet as xlayerCreateWallet, AAAccountManager as XLayerAAAccountManager, generateAAWallets as xlayerGenerateAAWallets, generateAAWalletsFromMnemonic as xlayerGenerateAAWalletsFromMnemonic, predictSendersFromPrivateKeys as xlayerPredictSendersFromPrivateKeys, createBundlerClient as xlayerCreateBundlerClient, BundlerClient as XLayerBundlerClient, createPortalQuery as xlayerCreatePortalQuery, PortalQuery as XLayerPortalQuery, encodeBuyCall as xlayerEncodeBuyCall, encodeSellCall as xlayerEncodeSellCall, encodeApproveCall as xlayerEncodeApproveCall, parseOkb as xlayerParseOkb, formatOkb as xlayerFormatOkb, XLAYER_CHAIN_ID, FLAP_PORTAL as XLAYER_FLAP_PORTAL, ENTRYPOINT_V06 as XLAYER_ENTRYPOINT, SIMPLE_ACCOUNT_FACTORY as XLAYER_FACTORY, PARTICLE_BUNDLER_URL as XLAYER_BUNDLER_URL, WOKB as XLAYER_WOKB, POTATOSWAP_V2_ROUTER as XLAYER_POTATOSWAP_ROUTER, type XLayerConfig, type BundleBuyParams as XLayerBundleBuyParams, type BundleSellParams as XLayerBundleSellParams, type BundleBuySellParams as XLayerBundleBuySellParams, type BundleSwapParams as XLayerBundleSwapParams, type BundleSwapSignParams as XLayerBundleSwapSignParams, type BundleBatchSwapParams as XLayerBundleBatchSwapParams, type BundleBatchSwapSignParams as XLayerBundleBatchSwapSignParams, type VolumeParams as XLayerVolumeParams, type BundleBuyResult as XLayerBundleBuyResult, type BundleSellResult as XLayerBundleSellResult, type BundleBuySellResult as XLayerBundleBuySellResult, type BundleCreateBuySignParams as XLayerBundleCreateBuySignParams, type BundleCreateBuySignResult as XLayerBundleCreateBuySignResult, type BundleCreateToDexSignParams as XLayerBundleCreateToDexSignParams, type BundleCreateToDexSignResult as XLayerBundleCreateToDexSignResult, type BundleGraduateBuyParams as XLayerBundleGraduateBuyParams, type BundleGraduateBuyResult as XLayerBundleGraduateBuyResult, type BundleSwapResult as XLayerBundleSwapResult, type BundleSwapSignResult as XLayerBundleSwapSignResult, type BundleBatchSwapResult as XLayerBundleBatchSwapResult, type BundleBatchSwapSignResult as XLayerBundleBatchSwapSignResult, type VolumeResult as XLayerVolumeResult, type BuyFirstParams as XLayerBuyFirstParams, type BuyFirstResult as XLayerBuyFirstResult, type BuyFirstVolumeParams as XLayerBuyFirstVolumeParams, type BuyFirstVolumeResult as XLayerBuyFirstVolumeResult, type HandleOpsResult as XLayerHandleOpsResult, type AAAccount as XLayerAAAccount, type UserOperation as XLayerUserOperation, type GeneratedAAWallet as XLayerGeneratedAAWallet, type GenerateAAWalletsParams as XLayerGenerateAAWalletsParams, type GenerateAAWalletsResult as XLayerGenerateAAWalletsResult, } from './xlayer/index.js';
57
+ export { bundleBuy as xlayerBundleBuy, bundleSell as xlayerBundleSell, bundleBuySell as xlayerBundleBuySell, bundleCreateBuySign as xlayerBundleCreateBuySign, bundleCreateToDexSign as xlayerBundleCreateToDexSign, bundleGraduateBuy as xlayerBundleGraduateBuy, bundleSwapSign as xlayerBundleSwapSign, bundleBatchSwapSign as xlayerBundleBatchSwapSign, createBundleExecutor as xlayerCreateBundleExecutor, BundleExecutor as XLayerBundleExecutor, makeVolume as xlayerMakeVolume, singleRoundVolume as xlayerSingleRoundVolume, createVolumeExecutor as xlayerCreateVolumeExecutor, VolumeExecutor as XLayerVolumeExecutor, makeBuyFirstVolume as xlayerMakeBuyFirstVolume, BuyFirstVolumeExecutor as XLayerBuyFirstVolumeExecutor, AAPortalBuyFirstExecutor as XLayerAAPortalBuyFirstExecutor, AADexBuyFirstExecutor as XLayerAADexBuyFirstExecutor, AAVolumeBuyFirstExecutor as XLayerAAVolumeBuyFirstExecutor, createAAVolumeBuyFirstExecutor as xlayerCreateAAVolumeBuyFirstExecutor, type VolumeBuyFirstParams as XLayerVolumeBuyFirstParams, type VolumeBuyFirstResult as XLayerVolumeBuyFirstResult, type VolumeContinuousParams as XLayerVolumeContinuousParams, type VolumeContinuousResult as XLayerVolumeContinuousResult, type VolumeProgress as XLayerVolumeProgress, createDexExecutor as xlayerCreateDexExecutor, createDexQuery as xlayerCreateDexQuery, quoteOkbToToken as xlayerQuoteOkbToToken, quoteTokenToOkb as xlayerQuoteTokenToOkb, DexExecutor as XLayerDexExecutor, DexQuery as XLayerDexQuery, buildDexBatchBuyOps, buildDexBatchBuyOpsV3, type DexBatchBuyParams, type DexBatchBuyV3Params, type DexBatchBuyResult, buildDexBatchSellOps, buildDexBatchSellOpsV3, type DexBatchSellParams, type DexBatchSellV3Params, type DexBatchSellResult, createAAAccountManager as xlayerCreateAAAccountManager, predictSender as xlayerPredictSender, createWallet as xlayerCreateWallet, AAAccountManager as XLayerAAAccountManager, generateAAWallets as xlayerGenerateAAWallets, generateAAWalletsFromMnemonic as xlayerGenerateAAWalletsFromMnemonic, predictSendersFromPrivateKeys as xlayerPredictSendersFromPrivateKeys, createBundlerClient as xlayerCreateBundlerClient, BundlerClient as XLayerBundlerClient, createPortalQuery as xlayerCreatePortalQuery, PortalQuery as XLayerPortalQuery, encodeBuyCall as xlayerEncodeBuyCall, encodeSellCall as xlayerEncodeSellCall, encodeApproveCall as xlayerEncodeApproveCall, parseOkb as xlayerParseOkb, formatOkb as xlayerFormatOkb, XLAYER_CHAIN_ID, FLAP_PORTAL as XLAYER_FLAP_PORTAL, ENTRYPOINT_V06 as XLAYER_ENTRYPOINT, SIMPLE_ACCOUNT_FACTORY as XLAYER_FACTORY, PARTICLE_BUNDLER_URL as XLAYER_BUNDLER_URL, WOKB as XLAYER_WOKB, POTATOSWAP_V2_ROUTER as XLAYER_POTATOSWAP_ROUTER, type XLayerConfig, type BundleBuyParams as XLayerBundleBuyParams, type BundleSellParams as XLayerBundleSellParams, type BundleBuySellParams as XLayerBundleBuySellParams, type BundleSwapParams as XLayerBundleSwapParams, type BundleSwapSignParams as XLayerBundleSwapSignParams, type BundleBatchSwapParams as XLayerBundleBatchSwapParams, type BundleBatchSwapSignParams as XLayerBundleBatchSwapSignParams, type VolumeParams as XLayerVolumeParams, type BundleBuyResult as XLayerBundleBuyResult, type BundleSellResult as XLayerBundleSellResult, type BundleBuySellResult as XLayerBundleBuySellResult, type BundleCreateBuySignParams as XLayerBundleCreateBuySignParams, type BundleCreateBuySignResult as XLayerBundleCreateBuySignResult, type BundleCreateToDexSignParams as XLayerBundleCreateToDexSignParams, type BundleCreateToDexSignResult as XLayerBundleCreateToDexSignResult, type BundleGraduateBuyParams as XLayerBundleGraduateBuyParams, type BundleGraduateBuyResult as XLayerBundleGraduateBuyResult, type BundleSwapResult as XLayerBundleSwapResult, type BundleSwapSignResult as XLayerBundleSwapSignResult, type BundleBatchSwapResult as XLayerBundleBatchSwapResult, type BundleBatchSwapSignResult as XLayerBundleBatchSwapSignResult, type VolumeResult as XLayerVolumeResult, type BuyFirstParams as XLayerBuyFirstParams, type BuyFirstResult as XLayerBuyFirstResult, type BuyFirstVolumeParams as XLayerBuyFirstVolumeParams, type BuyFirstVolumeResult as XLayerBuyFirstVolumeResult, type HandleOpsResult as XLayerHandleOpsResult, type AAAccount as XLayerAAAccount, type UserOperation as XLayerUserOperation, type GeneratedAAWallet as XLayerGeneratedAAWallet, type GenerateAAWalletsParams as XLayerGenerateAAWalletsParams, type GenerateAAWalletsResult as XLayerGenerateAAWalletsResult, } from './xlayer/index.js';
package/dist/index.js CHANGED
@@ -112,6 +112,10 @@ makeBuyFirstVolume as xlayerMakeBuyFirstVolume, BuyFirstVolumeExecutor as XLayer
112
112
  AAVolumeBuyFirstExecutor as XLayerAAVolumeBuyFirstExecutor, createAAVolumeBuyFirstExecutor as xlayerCreateAAVolumeBuyFirstExecutor,
113
113
  // DEX 交易
114
114
  createDexExecutor as xlayerCreateDexExecutor, createDexQuery as xlayerCreateDexQuery, quoteOkbToToken as xlayerQuoteOkbToToken, quoteTokenToOkb as xlayerQuoteTokenToOkb, DexExecutor as XLayerDexExecutor, DexQuery as XLayerDexQuery,
115
+ // ✅ AA 批量买入(安全版:利润配置硬编码在 SDK 内部)
116
+ buildDexBatchBuyOps, buildDexBatchBuyOpsV3,
117
+ // ✅ AA 批量卖出(安全版:利润配置硬编码在 SDK 内部)
118
+ buildDexBatchSellOps, buildDexBatchSellOpsV3,
115
119
  // AA 账户管理
116
120
  createAAAccountManager as xlayerCreateAAAccountManager, predictSender as xlayerPredictSender, createWallet as xlayerCreateWallet, AAAccountManager as XLayerAAAccountManager,
117
121
  // 批量生成钱包工具
@@ -194,7 +194,7 @@ export async function pancakeBundleBuyFirstMerkle(params) {
194
194
  const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
195
195
  const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
196
196
  profitAmount = await getTokenToNativeQuote(context.provider, quoteToken, tokenProfitAmount, 'BSC', version, fee);
197
- console.log(`[pancakeBundleBuyFirstMerkle] ERC20→BNB 利润转换: ${ethers.formatUnits(tokenProfitAmount, quoteTokenDecimals)} → ${ethers.formatEther(profitAmount)} BNB`);
197
+ // ERC20→BNB 转换完成
198
198
  }
199
199
  // ✅ 获取贿赂金额
200
200
  const bribeAmount = config.bribeAmount && config.bribeAmount > 0
@@ -656,7 +656,7 @@ async function pancakeBundleBuyFirstMultiWallet(params) {
656
656
  const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
657
657
  const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
658
658
  profitAmount = await getTokenToNativeQuote(context.provider, quoteToken, tokenProfitAmount, 'BSC', version, fee);
659
- console.log(`[pancakeBundleBuyFirstMultiWallet] ERC20→BNB 利润转换: ${ethers.formatUnits(tokenProfitAmount, quoteTokenDecimals)} → ${ethers.formatEther(profitAmount)} BNB`);
659
+ // ERC20→BNB 转换完成
660
660
  }
661
661
  // ✅ 第三批并行:构建并签名所有交易
662
662
  const allTransactions = [];
@@ -634,7 +634,7 @@ export async function pancakeBundleSwapMerkle(params) {
634
634
  if (useNativeToken) {
635
635
  // 输出是 BNB,直接计算利润(根据 userType 动态调整)
636
636
  profitAmount = calculateProfitAmount(quoteResult.estimatedBNBOut, config.userType);
637
- console.log(`[pancakeBundleSwapMerkle] 原生代币利润: ${ethers.formatEther(profitAmount)} BNB`);
637
+ // 利润计算完成
638
638
  }
639
639
  else {
640
640
  // 输出是 ERC20,需要先获取 ERC20 → BNB 的报价
@@ -643,7 +643,7 @@ export async function pancakeBundleSwapMerkle(params) {
643
643
  const estimatedBNBValue = await getERC20ToNativeQuote(context.provider, quoteToken, quoteResult.estimatedBNBOut, // 这实际上是 ERC20 数量
644
644
  version, fee);
645
645
  profitAmount = calculateProfitAmount(estimatedBNBValue, config.userType);
646
- console.log(`[pancakeBundleSwapMerkle] ERC20→BNB 报价: ${ethers.formatUnits(quoteResult.estimatedBNBOut, quoteTokenDecimals)} ${quoteToken?.slice(0, 10)}... → ${ethers.formatEther(estimatedBNBValue)} BNB, 利润: ${ethers.formatEther(profitAmount)} BNB`);
646
+ // ERC20→BNB 报价完成
647
647
  }
648
648
  // ✅ 获取贿赂金额
649
649
  const bribeAmount = config.bribeAmount && config.bribeAmount > 0
@@ -954,14 +954,14 @@ export async function pancakeBatchSwapMerkle(params) {
954
954
  let profitAmount;
955
955
  if (useNativeToken) {
956
956
  profitAmount = calculateProfitAmount(estimatedBNBOut, config.userType);
957
- console.log(`[pancakeBatchSwapMerkle] 原生代币利润: ${ethers.formatEther(profitAmount)} BNB`);
957
+ // 利润计算完成
958
958
  }
959
959
  else {
960
960
  const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
961
961
  const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
962
962
  const estimatedBNBValue = await getERC20ToNativeQuote(context.provider, quoteToken, estimatedBNBOut, version, fee);
963
963
  profitAmount = calculateProfitAmount(estimatedBNBValue, config.userType);
964
- console.log(`[pancakeBatchSwapMerkle] ERC20→BNB 报价: ${ethers.formatUnits(estimatedBNBOut, quoteTokenDecimals)} → ${ethers.formatEther(estimatedBNBValue)} BNB, 利润: ${ethers.formatEther(profitAmount)} BNB`);
964
+ // ERC20→BNB 报价完成
965
965
  }
966
966
  // 计算利润 nonce
967
967
  const profitNonce = profitAmount > 0n
@@ -1444,14 +1444,13 @@ export async function pancakeQuickBatchSwapMerkle(params) {
1444
1444
  });
1445
1445
  signedTransactions.push(...profitHopResult.signedTransactions);
1446
1446
  profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
1447
- console.log(`[pancakeQuickBatchSwapMerkle] 利润多跳交易已签名: ${profitHopResult.signedTransactions.length} 笔`);
1447
+ // 多跳交易已签名
1448
1448
  }
1449
1449
  console.log(`[pancakeQuickBatchSwapMerkle] 交易组装完成: ${signedTransactions.length} 笔`);
1450
1450
  console.log(` - 贿赂: ${bribeTx ? 1 : 0}`);
1451
1451
  console.log(` - 卖出: 1`);
1452
1452
  console.log(` - 转账: ${transferTxs.length}`);
1453
1453
  console.log(` - 买入: ${signedBuys.length}`);
1454
- console.log(` - 利润多跳: ${profitAmount > 0n ? PROFIT_HOP_COUNT + 1 : 0}`);
1455
1454
  const outputUnit = useNativeToken ? 'BNB' : 'ERC20';
1456
1455
  return {
1457
1456
  signedTransactions,
@@ -644,7 +644,7 @@ export async function holdersMaker(params) {
644
644
  const profitRateBps = PROFIT_CONFIG.RATE_BPS;
645
645
  const totalProfit = (totalBuyAmountForProfit * BigInt(profitRateBps)) / 10000n;
646
646
  const profitPerBatch = totalProfit / BigInt(walletBatches.length);
647
- console.log(`[HoldersMaker] 总利润: ${ethers.formatEther(totalProfit)} BNB`);
647
+ // 利润计算完成
648
648
  // 6. 生成分发多跳路径(如果启用)
649
649
  // XLayer 强制禁用分发多跳
650
650
  let allDisperseHopWallets = [];
@@ -36,9 +36,15 @@ export declare class AAAccountManager {
36
36
  private deployedSenderSet;
37
37
  private feeDataCache?;
38
38
  private readonly feeDataCacheTtlMs;
39
+ private entryPointSynced;
39
40
  private defaultGasPolicy?;
40
41
  private defaultFixedGas?;
41
42
  constructor(config?: XLayerConfig);
43
+ /**
44
+ * ✅ 同步 EntryPoint(带缓存,避免重复 RPC 调用)
45
+ *
46
+ * 优化:EntryPoint 地址在运行期间几乎不会变化,只需同步一次
47
+ */
42
48
  private syncEntryPointIfNeeded;
43
49
  /**
44
50
  * 获取 Provider
@@ -29,6 +29,8 @@ export class AAAccountManager {
29
29
  // PERF: 本文件的改造会尽量减少逐地址 RPC 调用
30
30
  this.deployedSenderSet = new Set(); // key: senderLower(只缓存 deployed=true)
31
31
  this.feeDataCacheTtlMs = 1200;
32
+ // ✅ 优化:缓存 EntryPoint 同步状态,避免重复 RPC 调用
33
+ this.entryPointSynced = false;
32
34
  this.chainId = config.chainId ?? XLAYER_CHAIN_ID;
33
35
  const rpcUrl = config.rpcUrl ?? DEFAULT_RPC_URL;
34
36
  this.rpcUrl = rpcUrl;
@@ -55,7 +57,15 @@ export class AAAccountManager {
55
57
  timeoutMs: config.timeoutMs,
56
58
  });
57
59
  }
60
+ /**
61
+ * ✅ 同步 EntryPoint(带缓存,避免重复 RPC 调用)
62
+ *
63
+ * 优化:EntryPoint 地址在运行期间几乎不会变化,只需同步一次
64
+ */
58
65
  async syncEntryPointIfNeeded() {
66
+ // ✅ 如果已同步过,直接返回(避免重复 RPC 调用)
67
+ if (this.entryPointSynced)
68
+ return;
59
69
  try {
60
70
  const eps = await this.bundler.getSupportedEntryPoints();
61
71
  if (Array.isArray(eps) && eps.length > 0) {
@@ -66,6 +76,8 @@ export class AAAccountManager {
66
76
  this.bundler.setEntryPoint(this.entryPointAddress);
67
77
  }
68
78
  }
79
+ // ✅ 标记为已同步
80
+ this.entryPointSynced = true;
69
81
  }
70
82
  catch { }
71
83
  }
@@ -74,6 +74,10 @@ class AANonceMap {
74
74
  const DEFAULT_CALL_GAS_LIMIT_BUY = DEFAULT_CALL_GAS_LIMIT_SELL; // buy 与 sell 共享一个保守值
75
75
  const DEFAULT_CALL_GAS_LIMIT_APPROVE = 200000n;
76
76
  const DEFAULT_CALL_GAS_LIMIT_WITHDRAW = 120000n;
77
+ /**
78
+ * ✅ 解析利润设置(捆绑换手/自动刷量模式)
79
+ * 默认费率:RATE_BPS_SWAP (6 bps = 0.06%)
80
+ */
77
81
  function resolveProfitSettings(config) {
78
82
  const extractProfit = config?.extractProfit !== false; // 默认 true
79
83
  // ✅ 对齐 bundle/刷量模式默认费率(与 BSC bundle-buy-first 语义一致)
@@ -82,6 +86,20 @@ function resolveProfitSettings(config) {
82
86
  const profitRecipient = config?.profitRecipient ?? PROFIT_CONFIG.RECIPIENT;
83
87
  return { extractProfit, profitBps, profitRecipient };
84
88
  }
89
+ /**
90
+ * ✅ 解析利润设置(代币发射/代币交易模式)
91
+ * 默认费率:RATE_BPS (40 bps = 0.4%)
92
+ *
93
+ * 与 BSC 链保持一致:代币发射使用 RATE_BPS,不是 RATE_BPS_SWAP
94
+ */
95
+ function resolveProfitSettingsForLaunch(config) {
96
+ const extractProfit = config?.extractProfit !== false; // 默认 true
97
+ // ✅ 代币发射使用 RATE_BPS (40 bps = 0.4%),与 BSC 保持一致
98
+ const profitBpsRaw = config?.profitBps ?? PROFIT_CONFIG.RATE_BPS;
99
+ const profitBps = Math.max(0, Math.min(10000, Number(profitBpsRaw)));
100
+ const profitRecipient = config?.profitRecipient ?? PROFIT_CONFIG.RECIPIENT;
101
+ return { extractProfit, profitBps, profitRecipient };
102
+ }
85
103
  function calculateProfitWei(amountWei, profitBps) {
86
104
  if (amountWei <= 0n)
87
105
  return 0n;
@@ -553,9 +571,7 @@ export class BundleExecutor {
553
571
  }
554
572
  }
555
573
  }
556
- if (profitSettings.extractProfit && nativeProfitAmount > 0n) {
557
- console.log(`[利润提取] 总利润: ${useNativeToken ? formatOkb(nativeProfitAmount) : `${formatOkb(totalProfitWei)} (ERC20) -> ${formatOkb(nativeProfitAmount)} (OKB)`} -> ${profitSettings.profitRecipient}`);
558
- }
574
+ // 利润提取已启用,不打印敏感信息
559
575
  // ✅ 获取代币状态,判断是走内盘 Portal 还是外盘 DEX
560
576
  const tokenState = await this.portalQuery.getTokenV7(tokenAddress);
561
577
  const isGraduated = tokenState.status === 4; // DEX = 4 表示已毕业
@@ -1008,9 +1024,7 @@ export class BundleExecutor {
1008
1024
  }
1009
1025
  }
1010
1026
  }
1011
- if (profitSettingsBuySell.extractProfit && nativeBuyProfitAmount > 0n) {
1012
- console.log(`[利润提取-买入] 总利润: ${useNativeToken ? formatOkb(nativeBuyProfitAmount) : `${formatOkb(totalBuyProfitWei)} (ERC20) -> ${formatOkb(nativeBuyProfitAmount)} (OKB)`} -> ${profitSettingsBuySell.profitRecipient}`);
1013
- }
1027
+ // 利润提取已启用,不打印敏感信息
1014
1028
  // 1. 买入(批量估算 + 并发补余额 + 并发签名)
1015
1029
  await mapWithConcurrency(accountInfos, 6, async (ai, i) => {
1016
1030
  const buyWei = buyWeis[i] ?? 0n;
@@ -1305,7 +1319,8 @@ export class BundleExecutor {
1305
1319
  });
1306
1320
  const signedDevOp = await this.aaManager.signUserOp(devCreateOp.userOp, payer);
1307
1321
  ops.push(signedDevOp.userOp);
1308
- const profitSettings = resolveProfitSettings(effConfig);
1322
+ // 代币发射使用 RATE_BPS (40 bps = 0.4%),与 BSC 保持一致
1323
+ const profitSettings = resolveProfitSettingsForLaunch(effConfig);
1309
1324
  const inputToken = params.quoteToken && params.quoteToken !== ZERO_ADDRESS ? params.quoteToken : ZERO_ADDRESS;
1310
1325
  const useNativeToken = inputToken === ZERO_ADDRESS;
1311
1326
  const quoteDecimals = 18;
@@ -1448,7 +1463,6 @@ export class BundleExecutor {
1448
1463
  currentNonce++;
1449
1464
  // === 2. 利润提取交易(独立的 EOA 转账)===
1450
1465
  if (nativeProfitAmount > 0n) {
1451
- console.log(`[利润提取-签名] 非 AA 转账: ${useNativeToken ? formatOkb(nativeProfitAmount) : `${formatOkb(totalBuyProfitWei)} (ERC20) -> ${formatOkb(nativeProfitAmount)} (OKB)`} -> ${profitSettings.profitRecipient}`);
1452
1466
  const profitTxRequest = {
1453
1467
  to: profitSettings.profitRecipient,
1454
1468
  value: nativeProfitAmount,
@@ -1501,7 +1515,8 @@ export class BundleExecutor {
1501
1515
  const nonceMap = new AANonceMap();
1502
1516
  nonceMap.init(payerAccount.sender, payerAccount.nonce);
1503
1517
  const effConfig = { ...(this.config ?? {}), ...(config ?? {}) };
1504
- const profitSettings = resolveProfitSettings(effConfig);
1518
+ // 代币发射使用 RATE_BPS (40 bps = 0.4%),与 BSC 保持一致
1519
+ const profitSettings = resolveProfitSettingsForLaunch(effConfig);
1505
1520
  const signedTransactions = [];
1506
1521
  let totalCurveBuyWei = 0n;
1507
1522
  let totalDexBuyWei = 0n;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * ✅ XLayer AA 外盘批量买入(安全版)
3
+ *
4
+ * 利润配置硬编码在 SDK 内部,前端无法篡改。
5
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
6
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
7
+ */
8
+ import type { XLayerConfig, UserOperation } from './types.js';
9
+ /**
10
+ * ✅ 批量买入参数
11
+ */
12
+ export interface DexBatchBuyParams {
13
+ /** 代币地址 */
14
+ tokenAddress: string;
15
+ /** 买入钱包私钥列表 */
16
+ ownerPrivateKeys: string[];
17
+ /** 买入金额列表(OKB,与 ownerPrivateKeys 一一对应) */
18
+ buyAmountsOkb: string[];
19
+ /** XLayer 配置 */
20
+ config?: Partial<XLayerConfig>;
21
+ /** V2 Router 地址 */
22
+ routerAddress: string;
23
+ /** Wrapped OKB 地址(默认 WOKB) */
24
+ wrappedOkbAddress?: string;
25
+ /** Deadline 分钟(默认 20) */
26
+ deadlineMinutes?: number;
27
+ /** 是否使用 WebWorker 签名(默认 true) */
28
+ useWebWorkerSign?: boolean;
29
+ }
30
+ /**
31
+ * ✅ 批量买入结果
32
+ */
33
+ export interface DexBatchBuyResult {
34
+ /** 已签名的 UserOps */
35
+ ops: UserOperation[];
36
+ /** 每个钱包的 AA(Sender) 地址 */
37
+ senders: string[];
38
+ /** 总利润金额(wei) */
39
+ profitWei: bigint;
40
+ }
41
+ /**
42
+ * ✅ V3 批量买入参数
43
+ */
44
+ export interface DexBatchBuyV3Params extends DexBatchBuyParams {
45
+ /** V3 pool fee (e.g., 100, 500, 2500, 10000) */
46
+ fee: number;
47
+ }
48
+ /**
49
+ * ✅ XLayer AA 外盘 V2 批量买入(安全版)
50
+ *
51
+ * 利润配置硬编码,前端无法篡改:
52
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
53
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
54
+ */
55
+ export declare function buildDexBatchBuyOps(params: DexBatchBuyParams): Promise<DexBatchBuyResult>;
56
+ /**
57
+ * ✅ XLayer AA 外盘 V3 批量买入(安全版)
58
+ *
59
+ * 利润配置硬编码,前端无法篡改:
60
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
61
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
62
+ */
63
+ export declare function buildDexBatchBuyOpsV3(params: DexBatchBuyV3Params): Promise<DexBatchBuyResult>;
@@ -0,0 +1,318 @@
1
+ /**
2
+ * ✅ XLayer AA 外盘批量买入(安全版)
3
+ *
4
+ * 利润配置硬编码在 SDK 内部,前端无法篡改。
5
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
6
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
7
+ */
8
+ import { ethers } from 'ethers';
9
+ import { PROFIT_CONFIG } from '../utils/constants.js';
10
+ import { WOKB as XLAYER_WOKB } from './constants.js';
11
+ import { createAAAccountManager, createWallet, encodeExecute } from './aa-account.js';
12
+ import { encodeSwapExactETHForTokensSupportingFee } from './dex.js';
13
+ import { parseOkb } from './portal-ops.js';
14
+ // V3 Router02 ABI
15
+ const V3_ROUTER02_ABI = [
16
+ 'function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountOut)',
17
+ 'function multicall(uint256 deadline, bytes[] data) external payable returns (bytes[] results)',
18
+ ];
19
+ function encodeExactInputSingleForV3(params) {
20
+ const iface = new ethers.Interface(V3_ROUTER02_ABI);
21
+ const exactInputSingleData = iface.encodeFunctionData('exactInputSingle', [{
22
+ tokenIn: params.tokenIn,
23
+ tokenOut: params.tokenOut,
24
+ fee: params.fee,
25
+ recipient: params.recipient,
26
+ amountIn: params.amountIn,
27
+ amountOutMinimum: params.amountOutMinimum,
28
+ sqrtPriceLimitX96: params.sqrtPriceLimitX96,
29
+ }]);
30
+ return iface.encodeFunctionData('multicall', [params.deadline, [exactInputSingleData]]);
31
+ }
32
+ function gasLimitFromConfig(v, fallback) {
33
+ const n = Number(v);
34
+ if (Number.isFinite(n) && n > 0)
35
+ return BigInt(Math.floor(n));
36
+ return fallback;
37
+ }
38
+ /**
39
+ * ✅ XLayer AA 外盘 V2 批量买入(安全版)
40
+ *
41
+ * 利润配置硬编码,前端无法篡改:
42
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
43
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
44
+ */
45
+ export async function buildDexBatchBuyOps(params) {
46
+ if (params.ownerPrivateKeys.length !== params.buyAmountsOkb.length) {
47
+ throw new Error('AA dex buy:私钥数量与买入金额数量不一致');
48
+ }
49
+ const config = {
50
+ rpcUrl: params.config?.rpcUrl || '',
51
+ bundlerUrl: params.config?.bundlerUrl,
52
+ chainId: params.config?.chainId,
53
+ entryPoint: params.config?.entryPoint,
54
+ factory: params.config?.factory,
55
+ salt: params.config?.salt ?? 0n,
56
+ paymaster: params.config?.paymaster,
57
+ paymasterData: params.config?.paymasterData,
58
+ timeoutMs: params.config?.timeoutMs,
59
+ gasLimitMultiplier: params.config?.gasLimitMultiplier,
60
+ };
61
+ // ✅ 利润配置:硬编码,不接受外部参数
62
+ const profitBps = PROFIT_CONFIG.RATE_BPS; // 40 bps = 0.4%
63
+ const profitRecipient = PROFIT_CONFIG.RECIPIENT;
64
+ const aaManager = createAAAccountManager(config);
65
+ const ownerWallets = params.ownerPrivateKeys.map((pk) => createWallet(String(pk), config));
66
+ const owners = ownerWallets.map((w) => w.address);
67
+ const accountInfos = await aaManager.getMultipleAccountInfo(owners);
68
+ const senders = accountInfos.map((ai) => String(ai?.sender || ''));
69
+ // nonceMap:同一 sender 连续分配 nonce
70
+ const nonceNext = new Map();
71
+ const nextNonce = (sender, startNonce) => {
72
+ const k = sender.toLowerCase();
73
+ if (!nonceNext.has(k))
74
+ nonceNext.set(k, startNonce);
75
+ const n = nonceNext.get(k);
76
+ nonceNext.set(k, n + 1n);
77
+ return n;
78
+ };
79
+ // initCode:同一 sender 只允许第一笔携带 initCode
80
+ const initCodeBySender = new Map();
81
+ const consumeInitCode = (sender, deployed, ownerAddress) => {
82
+ const k = sender.toLowerCase();
83
+ if (!initCodeBySender.has(k)) {
84
+ initCodeBySender.set(k, deployed ? '0x' : aaManager.generateInitCode(ownerAddress));
85
+ }
86
+ const cur = initCodeBySender.get(k);
87
+ if (cur !== '0x')
88
+ initCodeBySender.set(k, '0x');
89
+ return cur;
90
+ };
91
+ const router = params.routerAddress;
92
+ if (!/^0x[a-fA-F0-9]{40}$/.test(router)) {
93
+ throw new Error(`AA dex buy:routerAddress 不合法: ${router}`);
94
+ }
95
+ const wokb = params.wrappedOkbAddress || XLAYER_WOKB;
96
+ const deadline = Math.floor(Date.now() / 1000) + 60 * Math.max(1, Math.floor(Number(params.deadlineMinutes ?? 20)));
97
+ const callGasLimit = gasLimitFromConfig(params.config?.fixedGasBuyCallGasLimit, 650000n);
98
+ // ✅ 计算利润:从总买入金额中扣除(硬编码比例)
99
+ const originalBuyWeis = params.buyAmountsOkb.map((a) => parseOkb(String(a || '0')));
100
+ const buyWeis = [];
101
+ let totalProfitWei = 0n;
102
+ for (const original of originalBuyWeis) {
103
+ if (original > 0n) {
104
+ const profit = (original * BigInt(profitBps)) / 10000n;
105
+ buyWeis.push(original - profit);
106
+ totalProfitWei += profit;
107
+ }
108
+ else {
109
+ buyWeis.push(original);
110
+ }
111
+ }
112
+ const unsignedOps = [];
113
+ const opOwnerIndex = [];
114
+ for (let i = 0; i < ownerWallets.length; i++) {
115
+ const w = ownerWallets[i];
116
+ const ai = accountInfos[i];
117
+ const sender = String(ai?.sender || '');
118
+ if (!/^0x[a-fA-F0-9]{40}$/.test(sender))
119
+ continue;
120
+ const buyWei = buyWeis[i] ?? 0n;
121
+ if (buyWei <= 0n)
122
+ continue;
123
+ const swapData = encodeSwapExactETHForTokensSupportingFee(0n, // minAmountOut = 0(由外部控制滑点)
124
+ [wokb, params.tokenAddress], sender, deadline);
125
+ const callData = encodeExecute(router, buyWei, swapData);
126
+ const nonce = nextNonce(sender, BigInt(ai?.nonce ?? 0n));
127
+ const initCode = consumeInitCode(sender, !!ai?.deployed, w.address);
128
+ const deployedForOp = initCode === '0x';
129
+ const built = await aaManager.buildUserOpWithFixedGas({
130
+ ownerWallet: w,
131
+ sender,
132
+ callData,
133
+ nonce,
134
+ initCode,
135
+ deployed: deployedForOp,
136
+ fixedGas: { callGasLimit },
137
+ });
138
+ unsignedOps.push(built.userOp);
139
+ opOwnerIndex.push(i);
140
+ }
141
+ // ✅ 添加利润转账 UserOp(使用第一个钱包作为利润发送者)
142
+ if (totalProfitWei > 0n && ownerWallets.length > 0) {
143
+ const profitSenderIndex = 0;
144
+ const profitSender = senders[profitSenderIndex] || '';
145
+ const profitOwner = ownerWallets[profitSenderIndex];
146
+ const profitAi = accountInfos[profitSenderIndex];
147
+ if (profitSender && profitOwner) {
148
+ const profitCallData = encodeExecute(profitRecipient, totalProfitWei, '0x');
149
+ const profitNonce = nextNonce(profitSender, BigInt(profitAi?.nonce ?? 0n));
150
+ const profitInitCode = consumeInitCode(profitSender, !!profitAi?.deployed, profitOwner.address);
151
+ const profitBuilt = await aaManager.buildUserOpWithFixedGas({
152
+ ownerWallet: profitOwner,
153
+ sender: profitSender,
154
+ callData: profitCallData,
155
+ nonce: profitNonce,
156
+ initCode: profitInitCode,
157
+ deployed: profitInitCode === '0x',
158
+ fixedGas: { callGasLimit: 120000n },
159
+ });
160
+ unsignedOps.push(profitBuilt.userOp);
161
+ opOwnerIndex.push(profitSenderIndex);
162
+ }
163
+ }
164
+ // 签名
165
+ const signedOps = [];
166
+ for (let i = 0; i < unsignedOps.length; i++) {
167
+ const idx = opOwnerIndex[i];
168
+ const ownerWallet = ownerWallets[idx];
169
+ const signed = await aaManager.signUserOp(unsignedOps[i], ownerWallet);
170
+ signedOps.push(signed.userOp);
171
+ }
172
+ return { ops: signedOps, senders, profitWei: totalProfitWei };
173
+ }
174
+ /**
175
+ * ✅ XLayer AA 外盘 V3 批量买入(安全版)
176
+ *
177
+ * 利润配置硬编码,前端无法篡改:
178
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
179
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
180
+ */
181
+ export async function buildDexBatchBuyOpsV3(params) {
182
+ if (params.ownerPrivateKeys.length !== params.buyAmountsOkb.length) {
183
+ throw new Error('AA dex V3 buy:私钥数量与买入金额数量不一致');
184
+ }
185
+ const config = {
186
+ rpcUrl: params.config?.rpcUrl || '',
187
+ bundlerUrl: params.config?.bundlerUrl,
188
+ chainId: params.config?.chainId,
189
+ entryPoint: params.config?.entryPoint,
190
+ factory: params.config?.factory,
191
+ salt: params.config?.salt ?? 0n,
192
+ paymaster: params.config?.paymaster,
193
+ paymasterData: params.config?.paymasterData,
194
+ timeoutMs: params.config?.timeoutMs,
195
+ gasLimitMultiplier: params.config?.gasLimitMultiplier,
196
+ };
197
+ // ✅ 利润配置:硬编码,不接受外部参数
198
+ const profitBps = PROFIT_CONFIG.RATE_BPS; // 40 bps = 0.4%
199
+ const profitRecipient = PROFIT_CONFIG.RECIPIENT;
200
+ const aaManager = createAAAccountManager(config);
201
+ const ownerWallets = params.ownerPrivateKeys.map((pk) => createWallet(String(pk), config));
202
+ const owners = ownerWallets.map((w) => w.address);
203
+ const accountInfos = await aaManager.getMultipleAccountInfo(owners);
204
+ const senders = accountInfos.map((ai) => String(ai?.sender || ''));
205
+ // nonceMap
206
+ const nonceNext = new Map();
207
+ const nextNonce = (sender, startNonce) => {
208
+ const k = sender.toLowerCase();
209
+ if (!nonceNext.has(k))
210
+ nonceNext.set(k, startNonce);
211
+ const n = nonceNext.get(k);
212
+ nonceNext.set(k, n + 1n);
213
+ return n;
214
+ };
215
+ // initCode
216
+ const initCodeBySender = new Map();
217
+ const consumeInitCode = (sender, deployed, ownerAddress) => {
218
+ const k = sender.toLowerCase();
219
+ if (!initCodeBySender.has(k)) {
220
+ initCodeBySender.set(k, deployed ? '0x' : aaManager.generateInitCode(ownerAddress));
221
+ }
222
+ const cur = initCodeBySender.get(k);
223
+ if (cur !== '0x')
224
+ initCodeBySender.set(k, '0x');
225
+ return cur;
226
+ };
227
+ const router = params.routerAddress;
228
+ if (!/^0x[a-fA-F0-9]{40}$/.test(router)) {
229
+ throw new Error(`AA dex V3 buy:routerAddress 不合法: ${router}`);
230
+ }
231
+ const wokb = params.wrappedOkbAddress || XLAYER_WOKB;
232
+ const deadline = Math.floor(Date.now() / 1000) + 60 * Math.max(1, Math.floor(Number(params.deadlineMinutes ?? 20)));
233
+ const fee = Math.max(0, Math.floor(Number(params.fee || 2500)));
234
+ const callGasLimit = gasLimitFromConfig(params.config?.fixedGasBuyCallGasLimit, 800000n);
235
+ // ✅ 计算利润:从总买入金额中扣除(硬编码比例)
236
+ const originalBuyWeis = params.buyAmountsOkb.map((a) => parseOkb(String(a || '0')));
237
+ const buyWeis = [];
238
+ let totalProfitWei = 0n;
239
+ for (const original of originalBuyWeis) {
240
+ if (original > 0n) {
241
+ const profit = (original * BigInt(profitBps)) / 10000n;
242
+ buyWeis.push(original - profit);
243
+ totalProfitWei += profit;
244
+ }
245
+ else {
246
+ buyWeis.push(original);
247
+ }
248
+ }
249
+ const unsignedOps = [];
250
+ const opOwnerIndex = [];
251
+ for (let i = 0; i < ownerWallets.length; i++) {
252
+ const w = ownerWallets[i];
253
+ const ai = accountInfos[i];
254
+ const sender = String(ai?.sender || '');
255
+ if (!/^0x[a-fA-F0-9]{40}$/.test(sender))
256
+ continue;
257
+ const buyWei = buyWeis[i] ?? 0n;
258
+ if (buyWei <= 0n)
259
+ continue;
260
+ const swapData = encodeExactInputSingleForV3({
261
+ tokenIn: wokb,
262
+ tokenOut: params.tokenAddress,
263
+ fee,
264
+ recipient: sender,
265
+ deadline,
266
+ amountIn: buyWei,
267
+ amountOutMinimum: 0n,
268
+ sqrtPriceLimitX96: 0n,
269
+ });
270
+ const callData = encodeExecute(router, buyWei, swapData);
271
+ const nonce = nextNonce(sender, BigInt(ai?.nonce ?? 0n));
272
+ const initCode = consumeInitCode(sender, !!ai?.deployed, w.address);
273
+ const deployedForOp = initCode === '0x';
274
+ const built = await aaManager.buildUserOpWithFixedGas({
275
+ ownerWallet: w,
276
+ sender,
277
+ callData,
278
+ nonce,
279
+ initCode,
280
+ deployed: deployedForOp,
281
+ fixedGas: { callGasLimit },
282
+ });
283
+ unsignedOps.push(built.userOp);
284
+ opOwnerIndex.push(i);
285
+ }
286
+ // ✅ 添加利润转账 UserOp
287
+ if (totalProfitWei > 0n && ownerWallets.length > 0) {
288
+ const profitSenderIndex = 0;
289
+ const profitSender = senders[profitSenderIndex] || '';
290
+ const profitOwner = ownerWallets[profitSenderIndex];
291
+ const profitAi = accountInfos[profitSenderIndex];
292
+ if (profitSender && profitOwner) {
293
+ const profitCallData = encodeExecute(profitRecipient, totalProfitWei, '0x');
294
+ const profitNonce = nextNonce(profitSender, BigInt(profitAi?.nonce ?? 0n));
295
+ const profitInitCode = consumeInitCode(profitSender, !!profitAi?.deployed, profitOwner.address);
296
+ const profitBuilt = await aaManager.buildUserOpWithFixedGas({
297
+ ownerWallet: profitOwner,
298
+ sender: profitSender,
299
+ callData: profitCallData,
300
+ nonce: profitNonce,
301
+ initCode: profitInitCode,
302
+ deployed: profitInitCode === '0x',
303
+ fixedGas: { callGasLimit: 120000n },
304
+ });
305
+ unsignedOps.push(profitBuilt.userOp);
306
+ opOwnerIndex.push(profitSenderIndex);
307
+ }
308
+ }
309
+ // 签名
310
+ const signedOps = [];
311
+ for (let i = 0; i < unsignedOps.length; i++) {
312
+ const idx = opOwnerIndex[i];
313
+ const ownerWallet = ownerWallets[idx];
314
+ const signed = await aaManager.signUserOp(unsignedOps[i], ownerWallet);
315
+ signedOps.push(signed.userOp);
316
+ }
317
+ return { ops: signedOps, senders, profitWei: totalProfitWei };
318
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * ✅ XLayer AA 外盘批量卖出(安全版)
3
+ *
4
+ * 利润配置硬编码在 SDK 内部,前端无法篡改。
5
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
6
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
7
+ *
8
+ * 卖出利润逻辑:
9
+ * 1. 预估卖出输出(OKB 金额)
10
+ * 2. 计算利润(从预估输出中按比例扣除)
11
+ * 3. 构建卖出 UserOp + 利润转账 UserOp
12
+ */
13
+ import type { XLayerConfig, UserOperation } from './types.js';
14
+ /**
15
+ * ✅ 批量卖出参数
16
+ */
17
+ export interface DexBatchSellParams {
18
+ /** 代币地址 */
19
+ tokenAddress: string;
20
+ /** 代币精度 */
21
+ tokenDecimals: number;
22
+ /** 卖出钱包私钥列表 */
23
+ ownerPrivateKeys: string[];
24
+ /** 卖出数量列表(与 ownerPrivateKeys 一一对应,人类可读字符串) */
25
+ sellAmounts: string[];
26
+ /** XLayer 配置 */
27
+ config?: Partial<XLayerConfig>;
28
+ /** V2 Router 地址 */
29
+ routerAddress: string;
30
+ /** Wrapped OKB 地址(默认 WOKB) */
31
+ wrappedOkbAddress?: string;
32
+ /** Deadline 分钟(默认 20) */
33
+ deadlineMinutes?: number;
34
+ /** 是否跳过授权(默认 false) */
35
+ skipApprove?: boolean;
36
+ }
37
+ /**
38
+ * ✅ 批量卖出结果
39
+ */
40
+ export interface DexBatchSellResult {
41
+ /** 已签名的 UserOps */
42
+ ops: UserOperation[];
43
+ /** 每个钱包的 AA(Sender) 地址 */
44
+ senders: string[];
45
+ /** 总利润金额(wei) */
46
+ profitWei: bigint;
47
+ }
48
+ /**
49
+ * ✅ V3 批量卖出参数
50
+ */
51
+ export interface DexBatchSellV3Params extends DexBatchSellParams {
52
+ /** V3 pool fee (e.g., 100, 500, 2500, 10000) */
53
+ fee: number;
54
+ }
55
+ /**
56
+ * ✅ XLayer AA 外盘 V2 批量卖出(安全版)
57
+ *
58
+ * 利润配置硬编码,前端无法篡改:
59
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
60
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
61
+ */
62
+ export declare function buildDexBatchSellOps(params: DexBatchSellParams): Promise<DexBatchSellResult>;
63
+ /**
64
+ * ✅ XLayer AA 外盘 V3 批量卖出(安全版)
65
+ *
66
+ * 利润配置硬编码,前端无法篡改:
67
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
68
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
69
+ */
70
+ export declare function buildDexBatchSellOpsV3(params: DexBatchSellV3Params): Promise<DexBatchSellResult>;
@@ -0,0 +1,416 @@
1
+ /**
2
+ * ✅ XLayer AA 外盘批量卖出(安全版)
3
+ *
4
+ * 利润配置硬编码在 SDK 内部,前端无法篡改。
5
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
6
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
7
+ *
8
+ * 卖出利润逻辑:
9
+ * 1. 预估卖出输出(OKB 金额)
10
+ * 2. 计算利润(从预估输出中按比例扣除)
11
+ * 3. 构建卖出 UserOp + 利润转账 UserOp
12
+ */
13
+ import { ethers } from 'ethers';
14
+ import { PROFIT_CONFIG } from '../utils/constants.js';
15
+ import { WOKB as XLAYER_WOKB } from './constants.js';
16
+ import { createAAAccountManager, createWallet, encodeExecute } from './aa-account.js';
17
+ import { DexQuery, encodeSwapExactTokensForETH } from './dex.js';
18
+ import { PortalQuery } from './portal-ops.js';
19
+ // V3 Router02 ABI for sell
20
+ const V3_ROUTER02_SELL_ABI = [
21
+ 'function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountOut)',
22
+ 'function multicall(uint256 deadline, bytes[] data) external payable returns (bytes[] results)',
23
+ 'function unwrapWETH9(uint256 amountMinimum, address recipient) external payable',
24
+ ];
25
+ function encodeExactInputSingleForV3Sell(params) {
26
+ const iface = new ethers.Interface(V3_ROUTER02_SELL_ABI);
27
+ const exactInputSingleData = iface.encodeFunctionData('exactInputSingle', [{
28
+ tokenIn: params.tokenIn,
29
+ tokenOut: params.tokenOut,
30
+ fee: params.fee,
31
+ recipient: 2n, // address(2) = Router 自己(需要 unwrap)
32
+ amountIn: params.amountIn,
33
+ amountOutMinimum: params.amountOutMinimum,
34
+ sqrtPriceLimitX96: 0n,
35
+ }]);
36
+ const unwrapData = iface.encodeFunctionData('unwrapWETH9', [0n, params.recipient]);
37
+ return iface.encodeFunctionData('multicall', [params.deadline, [exactInputSingleData, unwrapData]]);
38
+ }
39
+ function gasLimitFromConfig(v, fallback) {
40
+ const n = Number(v);
41
+ if (Number.isFinite(n) && n > 0)
42
+ return BigInt(Math.floor(n));
43
+ return fallback;
44
+ }
45
+ const encodeApproveCall = (spender) => {
46
+ const iface = new ethers.Interface(['function approve(address spender, uint256 amount)']);
47
+ return iface.encodeFunctionData('approve', [spender, ethers.MaxUint256]);
48
+ };
49
+ /**
50
+ * ✅ XLayer AA 外盘 V2 批量卖出(安全版)
51
+ *
52
+ * 利润配置硬编码,前端无法篡改:
53
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
54
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
55
+ */
56
+ export async function buildDexBatchSellOps(params) {
57
+ if (params.ownerPrivateKeys.length !== params.sellAmounts.length) {
58
+ throw new Error('AA dex sell:私钥数量与卖出数量不一致');
59
+ }
60
+ const config = {
61
+ rpcUrl: params.config?.rpcUrl || '',
62
+ bundlerUrl: params.config?.bundlerUrl,
63
+ chainId: params.config?.chainId,
64
+ entryPoint: params.config?.entryPoint,
65
+ factory: params.config?.factory,
66
+ salt: params.config?.salt ?? 0n,
67
+ paymaster: params.config?.paymaster,
68
+ paymasterData: params.config?.paymasterData,
69
+ timeoutMs: params.config?.timeoutMs,
70
+ gasLimitMultiplier: params.config?.gasLimitMultiplier,
71
+ };
72
+ // ✅ 利润配置:硬编码,不接受外部参数
73
+ const profitBps = PROFIT_CONFIG.RATE_BPS; // 40 bps = 0.4%
74
+ const profitRecipient = PROFIT_CONFIG.RECIPIENT;
75
+ const aaManager = createAAAccountManager(config);
76
+ const dexQuery = new DexQuery(config);
77
+ const portalQuery = new PortalQuery({ rpcUrl: config.rpcUrl, chainId: config.chainId });
78
+ const ownerWallets = params.ownerPrivateKeys.map((pk) => createWallet(String(pk), config));
79
+ const owners = ownerWallets.map((w) => w.address);
80
+ const accountInfos = await aaManager.getMultipleAccountInfo(owners);
81
+ const senders = accountInfos.map((ai) => String(ai?.sender || ''));
82
+ // nonceMap:同一 sender 连续分配 nonce
83
+ const nonceNext = new Map();
84
+ const nextNonce = (sender, startNonce) => {
85
+ const k = sender.toLowerCase();
86
+ if (!nonceNext.has(k))
87
+ nonceNext.set(k, startNonce);
88
+ const n = nonceNext.get(k);
89
+ nonceNext.set(k, n + 1n);
90
+ return n;
91
+ };
92
+ // initCode:同一 sender 只允许第一笔携带 initCode
93
+ const initCodeBySender = new Map();
94
+ const consumeInitCode = (sender, deployed, ownerAddress) => {
95
+ const k = sender.toLowerCase();
96
+ if (!initCodeBySender.has(k)) {
97
+ initCodeBySender.set(k, deployed ? '0x' : aaManager.generateInitCode(ownerAddress));
98
+ }
99
+ const cur = initCodeBySender.get(k);
100
+ if (cur !== '0x')
101
+ initCodeBySender.set(k, '0x');
102
+ return cur;
103
+ };
104
+ const router = params.routerAddress;
105
+ if (!/^0x[a-fA-F0-9]{40}$/.test(router)) {
106
+ throw new Error(`AA dex sell:routerAddress 不合法: ${router}`);
107
+ }
108
+ const wokb = params.wrappedOkbAddress || XLAYER_WOKB;
109
+ const deadline = Math.floor(Date.now() / 1000) + 60 * Math.max(1, Math.floor(Number(params.deadlineMinutes ?? 20)));
110
+ const dec = Math.max(0, Math.floor(Number(params.tokenDecimals ?? 18)));
111
+ const approveCallGasLimit = gasLimitFromConfig(params.config?.fixedGasApproveCallGasLimit, 220000n);
112
+ const sellCallGasLimit = gasLimitFromConfig(params.config?.fixedGasSellCallGasLimit, 450000n);
113
+ // ✅ 获取授权状态
114
+ const allowanceMap = params.skipApprove
115
+ ? new Map()
116
+ : await portalQuery.getMultipleAllowances(params.tokenAddress, senders, router);
117
+ // ✅ 预估卖出输出,计算利润
118
+ // ⚡ 性能优化:先计算所有卖出金额,然后只调用一次报价(使用总量报价)
119
+ const sellWeis = params.sellAmounts.map((amtStr) => {
120
+ const str = String(amtStr || '0').trim();
121
+ if (!str || str === '0')
122
+ return 0n;
123
+ return ethers.parseUnits(str, dec);
124
+ });
125
+ const totalSellWei = sellWeis.reduce((sum, w) => sum + w, 0n);
126
+ // ⚡ 只调用一次报价(避免 N 次串行 RPC 调用)
127
+ let totalEstimatedOkbOut = 0n;
128
+ if (totalSellWei > 0n) {
129
+ try {
130
+ totalEstimatedOkbOut = await dexQuery.quoteTokenToOkb(totalSellWei, params.tokenAddress);
131
+ }
132
+ catch {
133
+ totalEstimatedOkbOut = 0n;
134
+ }
135
+ }
136
+ // ✅ 计算总利润
137
+ const totalProfitWei = totalEstimatedOkbOut > 0n
138
+ ? (totalEstimatedOkbOut * BigInt(profitBps)) / 10000n
139
+ : 0n;
140
+ const unsignedOps = [];
141
+ const opOwnerIndex = [];
142
+ for (let i = 0; i < ownerWallets.length; i++) {
143
+ const w = ownerWallets[i];
144
+ const ai = accountInfos[i];
145
+ const sender = String(ai?.sender || '');
146
+ if (!/^0x[a-fA-F0-9]{40}$/.test(sender))
147
+ continue;
148
+ const sellWei = sellWeis[i] ?? 0n;
149
+ if (sellWei <= 0n)
150
+ continue;
151
+ const allowance = allowanceMap.get(sender) ?? ethers.MaxUint256;
152
+ const needApprove = !params.skipApprove && allowance < sellWei;
153
+ // approve(可选)
154
+ if (needApprove) {
155
+ const nonce = nextNonce(sender, BigInt(ai?.nonce ?? 0n));
156
+ const initCode = consumeInitCode(sender, !!ai?.deployed, w.address);
157
+ const approveData = encodeApproveCall(router);
158
+ const approveCallData = encodeExecute(params.tokenAddress, 0n, approveData);
159
+ const built = await aaManager.buildUserOpWithFixedGas({
160
+ ownerWallet: w,
161
+ sender,
162
+ callData: approveCallData,
163
+ nonce,
164
+ initCode,
165
+ deployed: initCode === '0x',
166
+ fixedGas: { callGasLimit: approveCallGasLimit },
167
+ });
168
+ unsignedOps.push(built.userOp);
169
+ opOwnerIndex.push(i);
170
+ }
171
+ // sell (V2)
172
+ const nonce2 = nextNonce(sender, BigInt(ai?.nonce ?? 0n));
173
+ const initCode2 = consumeInitCode(sender, !!ai?.deployed, w.address);
174
+ const swapData = encodeSwapExactTokensForETH(sellWei, 0n, // minAmountOut = 0(由外部控制滑点)
175
+ [params.tokenAddress, wokb], sender, deadline);
176
+ const sellCallData = encodeExecute(router, 0n, swapData);
177
+ const builtSell = await aaManager.buildUserOpWithFixedGas({
178
+ ownerWallet: w,
179
+ sender,
180
+ callData: sellCallData,
181
+ nonce: nonce2,
182
+ initCode: initCode2,
183
+ deployed: initCode2 === '0x',
184
+ fixedGas: { callGasLimit: sellCallGasLimit },
185
+ });
186
+ unsignedOps.push(builtSell.userOp);
187
+ opOwnerIndex.push(i);
188
+ }
189
+ // ✅ 添加利润转账 UserOp(使用第一个有卖出的钱包)
190
+ if (totalProfitWei > 0n && ownerWallets.length > 0) {
191
+ // 找到第一个有卖出的钱包
192
+ let profitSenderIndex = 0;
193
+ for (let i = 0; i < sellWeis.length; i++) {
194
+ if (sellWeis[i] > 0n) {
195
+ profitSenderIndex = i;
196
+ break;
197
+ }
198
+ }
199
+ const profitSender = senders[profitSenderIndex] || '';
200
+ const profitOwner = ownerWallets[profitSenderIndex];
201
+ const profitAi = accountInfos[profitSenderIndex];
202
+ if (profitSender && profitOwner) {
203
+ const profitCallData = encodeExecute(profitRecipient, totalProfitWei, '0x');
204
+ const profitNonce = nextNonce(profitSender, BigInt(profitAi?.nonce ?? 0n));
205
+ const profitInitCode = consumeInitCode(profitSender, !!profitAi?.deployed, profitOwner.address);
206
+ const profitBuilt = await aaManager.buildUserOpWithFixedGas({
207
+ ownerWallet: profitOwner,
208
+ sender: profitSender,
209
+ callData: profitCallData,
210
+ nonce: profitNonce,
211
+ initCode: profitInitCode,
212
+ deployed: profitInitCode === '0x',
213
+ fixedGas: { callGasLimit: 120000n },
214
+ });
215
+ unsignedOps.push(profitBuilt.userOp);
216
+ opOwnerIndex.push(profitSenderIndex);
217
+ }
218
+ }
219
+ // 签名
220
+ const signedOps = [];
221
+ for (let i = 0; i < unsignedOps.length; i++) {
222
+ const idx = opOwnerIndex[i];
223
+ const ownerWallet = ownerWallets[idx];
224
+ const signed = await aaManager.signUserOp(unsignedOps[i], ownerWallet);
225
+ signedOps.push(signed.userOp);
226
+ }
227
+ return { ops: signedOps, senders, profitWei: totalProfitWei };
228
+ }
229
+ /**
230
+ * ✅ XLayer AA 外盘 V3 批量卖出(安全版)
231
+ *
232
+ * 利润配置硬编码,前端无法篡改:
233
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
234
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
235
+ */
236
+ export async function buildDexBatchSellOpsV3(params) {
237
+ if (params.ownerPrivateKeys.length !== params.sellAmounts.length) {
238
+ throw new Error('AA dex V3 sell:私钥数量与卖出数量不一致');
239
+ }
240
+ const config = {
241
+ rpcUrl: params.config?.rpcUrl || '',
242
+ bundlerUrl: params.config?.bundlerUrl,
243
+ chainId: params.config?.chainId,
244
+ entryPoint: params.config?.entryPoint,
245
+ factory: params.config?.factory,
246
+ salt: params.config?.salt ?? 0n,
247
+ paymaster: params.config?.paymaster,
248
+ paymasterData: params.config?.paymasterData,
249
+ timeoutMs: params.config?.timeoutMs,
250
+ gasLimitMultiplier: params.config?.gasLimitMultiplier,
251
+ };
252
+ // ✅ 利润配置:硬编码,不接受外部参数
253
+ const profitBps = PROFIT_CONFIG.RATE_BPS; // 40 bps = 0.4%
254
+ const profitRecipient = PROFIT_CONFIG.RECIPIENT;
255
+ const aaManager = createAAAccountManager(config);
256
+ const dexQuery = new DexQuery(config);
257
+ const portalQuery = new PortalQuery({ rpcUrl: config.rpcUrl, chainId: config.chainId });
258
+ const ownerWallets = params.ownerPrivateKeys.map((pk) => createWallet(String(pk), config));
259
+ const owners = ownerWallets.map((w) => w.address);
260
+ const accountInfos = await aaManager.getMultipleAccountInfo(owners);
261
+ const senders = accountInfos.map((ai) => String(ai?.sender || ''));
262
+ // nonceMap:同一 sender 连续分配 nonce
263
+ const nonceNext = new Map();
264
+ const nextNonce = (sender, startNonce) => {
265
+ const k = sender.toLowerCase();
266
+ if (!nonceNext.has(k))
267
+ nonceNext.set(k, startNonce);
268
+ const n = nonceNext.get(k);
269
+ nonceNext.set(k, n + 1n);
270
+ return n;
271
+ };
272
+ // initCode:同一 sender 只允许第一笔携带 initCode
273
+ const initCodeBySender = new Map();
274
+ const consumeInitCode = (sender, deployed, ownerAddress) => {
275
+ const k = sender.toLowerCase();
276
+ if (!initCodeBySender.has(k)) {
277
+ initCodeBySender.set(k, deployed ? '0x' : aaManager.generateInitCode(ownerAddress));
278
+ }
279
+ const cur = initCodeBySender.get(k);
280
+ if (cur !== '0x')
281
+ initCodeBySender.set(k, '0x');
282
+ return cur;
283
+ };
284
+ const router = params.routerAddress;
285
+ if (!/^0x[a-fA-F0-9]{40}$/.test(router)) {
286
+ throw new Error(`AA dex V3 sell:routerAddress 不合法: ${router}`);
287
+ }
288
+ const wokb = params.wrappedOkbAddress || XLAYER_WOKB;
289
+ const fee = Math.max(1, Math.floor(Number(params.fee ?? 100)));
290
+ const deadline = Math.floor(Date.now() / 1000) + 60 * Math.max(1, Math.floor(Number(params.deadlineMinutes ?? 20)));
291
+ const dec = Math.max(0, Math.floor(Number(params.tokenDecimals ?? 18)));
292
+ const approveCallGasLimit = gasLimitFromConfig(params.config?.fixedGasApproveCallGasLimit, 220000n);
293
+ const sellCallGasLimit = gasLimitFromConfig(params.config?.fixedGasSellCallGasLimit, 450000n);
294
+ // ✅ 获取授权状态
295
+ const allowanceMap = params.skipApprove
296
+ ? new Map()
297
+ : await portalQuery.getMultipleAllowances(params.tokenAddress, senders, router);
298
+ // ✅ 预估卖出输出,计算利润
299
+ // ⚡ 性能优化:先计算所有卖出金额,然后只调用一次报价(使用总量报价)
300
+ const sellWeis = params.sellAmounts.map((amtStr) => {
301
+ const str = String(amtStr || '0').trim();
302
+ if (!str || str === '0')
303
+ return 0n;
304
+ return ethers.parseUnits(str, dec);
305
+ });
306
+ const totalSellWei = sellWeis.reduce((sum, w) => sum + w, 0n);
307
+ // ⚡ 只调用一次报价(避免 N 次串行 RPC 调用)
308
+ let totalEstimatedOkbOut = 0n;
309
+ if (totalSellWei > 0n) {
310
+ try {
311
+ totalEstimatedOkbOut = await dexQuery.quoteTokenToOkb(totalSellWei, params.tokenAddress);
312
+ }
313
+ catch {
314
+ totalEstimatedOkbOut = 0n;
315
+ }
316
+ }
317
+ // ✅ 计算总利润
318
+ const totalProfitWei = totalEstimatedOkbOut > 0n
319
+ ? (totalEstimatedOkbOut * BigInt(profitBps)) / 10000n
320
+ : 0n;
321
+ const unsignedOps = [];
322
+ const opOwnerIndex = [];
323
+ for (let i = 0; i < ownerWallets.length; i++) {
324
+ const w = ownerWallets[i];
325
+ const ai = accountInfos[i];
326
+ const sender = String(ai?.sender || '');
327
+ if (!/^0x[a-fA-F0-9]{40}$/.test(sender))
328
+ continue;
329
+ const sellWei = sellWeis[i] ?? 0n;
330
+ if (sellWei <= 0n)
331
+ continue;
332
+ const allowance = allowanceMap.get(sender) ?? ethers.MaxUint256;
333
+ const needApprove = !params.skipApprove && allowance < sellWei;
334
+ // approve(可选)
335
+ if (needApprove) {
336
+ const nonce = nextNonce(sender, BigInt(ai?.nonce ?? 0n));
337
+ const initCode = consumeInitCode(sender, !!ai?.deployed, w.address);
338
+ const approveData = encodeApproveCall(router);
339
+ const approveCallData = encodeExecute(params.tokenAddress, 0n, approveData);
340
+ const built = await aaManager.buildUserOpWithFixedGas({
341
+ ownerWallet: w,
342
+ sender,
343
+ callData: approveCallData,
344
+ nonce,
345
+ initCode,
346
+ deployed: initCode === '0x',
347
+ fixedGas: { callGasLimit: approveCallGasLimit },
348
+ });
349
+ unsignedOps.push(built.userOp);
350
+ opOwnerIndex.push(i);
351
+ }
352
+ // sell (V3)
353
+ const nonce2 = nextNonce(sender, BigInt(ai?.nonce ?? 0n));
354
+ const initCode2 = consumeInitCode(sender, !!ai?.deployed, w.address);
355
+ const swapData = encodeExactInputSingleForV3Sell({
356
+ tokenIn: params.tokenAddress,
357
+ tokenOut: wokb,
358
+ fee,
359
+ recipient: sender,
360
+ deadline,
361
+ amountIn: sellWei,
362
+ amountOutMinimum: 0n, // 0 滑点,由外部控制
363
+ });
364
+ const sellCallData = encodeExecute(router, 0n, swapData);
365
+ const builtSell = await aaManager.buildUserOpWithFixedGas({
366
+ ownerWallet: w,
367
+ sender,
368
+ callData: sellCallData,
369
+ nonce: nonce2,
370
+ initCode: initCode2,
371
+ deployed: initCode2 === '0x',
372
+ fixedGas: { callGasLimit: sellCallGasLimit },
373
+ });
374
+ unsignedOps.push(builtSell.userOp);
375
+ opOwnerIndex.push(i);
376
+ }
377
+ // ✅ 添加利润转账 UserOp(使用第一个有卖出的钱包)
378
+ if (totalProfitWei > 0n && ownerWallets.length > 0) {
379
+ // 找到第一个有卖出的钱包
380
+ let profitSenderIndex = 0;
381
+ for (let i = 0; i < sellWeis.length; i++) {
382
+ if (sellWeis[i] > 0n) {
383
+ profitSenderIndex = i;
384
+ break;
385
+ }
386
+ }
387
+ const profitSender = senders[profitSenderIndex] || '';
388
+ const profitOwner = ownerWallets[profitSenderIndex];
389
+ const profitAi = accountInfos[profitSenderIndex];
390
+ if (profitSender && profitOwner) {
391
+ const profitCallData = encodeExecute(profitRecipient, totalProfitWei, '0x');
392
+ const profitNonce = nextNonce(profitSender, BigInt(profitAi?.nonce ?? 0n));
393
+ const profitInitCode = consumeInitCode(profitSender, !!profitAi?.deployed, profitOwner.address);
394
+ const profitBuilt = await aaManager.buildUserOpWithFixedGas({
395
+ ownerWallet: profitOwner,
396
+ sender: profitSender,
397
+ callData: profitCallData,
398
+ nonce: profitNonce,
399
+ initCode: profitInitCode,
400
+ deployed: profitInitCode === '0x',
401
+ fixedGas: { callGasLimit: 120000n },
402
+ });
403
+ unsignedOps.push(profitBuilt.userOp);
404
+ opOwnerIndex.push(profitSenderIndex);
405
+ }
406
+ }
407
+ // 签名
408
+ const signedOps = [];
409
+ for (let i = 0; i < unsignedOps.length; i++) {
410
+ const idx = opOwnerIndex[i];
411
+ const ownerWallet = ownerWallets[idx];
412
+ const signed = await aaManager.signUserOp(unsignedOps[i], ownerWallet);
413
+ signedOps.push(signed.userOp);
414
+ }
415
+ return { ops: signedOps, senders, profitWei: totalProfitWei };
416
+ }
@@ -9,7 +9,7 @@
9
9
  import { ethers, Contract, Interface, JsonRpcProvider } from 'ethers';
10
10
  import { POTATOSWAP_V2_ROUTER, POTATOSWAP_V2_ROUTER_ABI, POTATOSWAP_V3_ROUTER_ABI, WOKB, DEFAULT_RPC_URL, XLAYER_CHAIN_ID, ERC20_ABI, } from './constants.js';
11
11
  import { AAAccountManager, encodeExecute, createWallet } from './aa-account.js';
12
- import { encodeApproveCall, parseOkb, formatOkb } from './portal-ops.js';
12
+ import { encodeApproveCall, parseOkb } from './portal-ops.js';
13
13
  // ============================================================================
14
14
  // DEX 交易编码器
15
15
  // ============================================================================
@@ -474,7 +474,6 @@ export class DexExecutor {
474
474
  const ops = [signedBuyOp.userOp];
475
475
  // 3. 构建利润转账 UserOp (AA 内部刮取,不再使用 Tail Transaction)
476
476
  if (profitAmount > 0n && params.profitConfig?.profitRecipient) {
477
- console.log(`[DEX-Buy] 利润 (AA 内部): ${formatOkb(profitAmount)} OKB -> ${params.profitConfig.profitRecipient}`);
478
477
  const profitCallData = encodeExecute(params.profitConfig.profitRecipient, profitAmount, '0x');
479
478
  const { userOp: profitOp } = await this.aaManager.buildUserOpWithFixedGas({
480
479
  ownerWallet: wallet,
@@ -85,6 +85,8 @@ export declare function bundleBatchSwapSign(params: BundleBatchSwapSignParams &
85
85
  export { VolumeExecutor, createVolumeExecutor, makeVolume, singleRoundVolume, } from './volume.js';
86
86
  export { DexVolumeExecutor, createDexVolumeExecutor, makeDexVolume, type DexVolumeParams, } from './dex-volume.js';
87
87
  export { DexQuery, DexExecutor, createDexQuery, createDexExecutor, quoteOkbToToken, quoteTokenToOkb, encodeSwapExactETHForTokens, encodeSwapExactTokensForETH, encodeSwapExactTokensForTokens, encodeSwapExactETHForTokensSupportingFee, encodeSwapExactTokensForETHSupportingFee, encodeSwapExactTokensForTokensSupportingFee, type DexConfig, } from './dex.js';
88
+ export { buildDexBatchBuyOps, buildDexBatchBuyOpsV3, type DexBatchBuyParams, type DexBatchBuyV3Params, type DexBatchBuyResult, } from './dex-aa-buy.js';
89
+ export { buildDexBatchSellOps, buildDexBatchSellOpsV3, type DexBatchSellParams, type DexBatchSellV3Params, type DexBatchSellResult, } from './dex-aa-sell.js';
88
90
  export declare const xlayer: {
89
91
  bundleBuy: (params: import("./types.js").BundleBuyParams) => Promise<import("./types.js").BundleBuyResult>;
90
92
  bundleSell: (params: import("./types.js").BundleSellParams) => Promise<import("./types.js").BundleSellResult>;
@@ -144,6 +144,14 @@ encodeSwapExactETHForTokens, encodeSwapExactTokensForETH, encodeSwapExactTokensF
144
144
  // SupportingFeeOnTransferTokens 版本(推荐使用)
145
145
  encodeSwapExactETHForTokensSupportingFee, encodeSwapExactTokensForETHSupportingFee, encodeSwapExactTokensForTokensSupportingFee, } from './dex.js';
146
146
  // ============================================================================
147
+ // ✅ AA 批量买入(安全版:利润配置硬编码在 SDK 内部)
148
+ // ============================================================================
149
+ export { buildDexBatchBuyOps, buildDexBatchBuyOpsV3, } from './dex-aa-buy.js';
150
+ // ============================================================================
151
+ // ✅ AA 批量卖出(安全版:利润配置硬编码在 SDK 内部)
152
+ // ============================================================================
153
+ export { buildDexBatchSellOps, buildDexBatchSellOpsV3, } from './dex-aa-sell.js';
154
+ // ============================================================================
147
155
  // 便捷重导出
148
156
  // ============================================================================
149
157
  // 将最常用的函数放在默认导出对象中
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.6.4",
3
+ "version": "1.6.6",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",