four-flap-meme-sdk 1.6.5 → 1.6.7

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/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, buildDexBatchBuyOps, buildDexBatchBuyOpsV3, type DexBatchBuyParams, type DexBatchBuyV3Params, type DexBatchBuyResult, 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
@@ -114,6 +114,8 @@ AAVolumeBuyFirstExecutor as XLayerAAVolumeBuyFirstExecutor, createAAVolumeBuyFir
114
114
  createDexExecutor as xlayerCreateDexExecutor, createDexQuery as xlayerCreateDexQuery, quoteOkbToToken as xlayerQuoteOkbToToken, quoteTokenToOkb as xlayerQuoteTokenToOkb, DexExecutor as XLayerDexExecutor, DexQuery as XLayerDexQuery,
115
115
  // ✅ AA 批量买入(安全版:利润配置硬编码在 SDK 内部)
116
116
  buildDexBatchBuyOps, buildDexBatchBuyOpsV3,
117
+ // ✅ AA 批量卖出(安全版:利润配置硬编码在 SDK 内部)
118
+ buildDexBatchSellOps, buildDexBatchSellOpsV3,
117
119
  // AA 账户管理
118
120
  createAAAccountManager as xlayerCreateAAAccountManager, predictSender as xlayerPredictSender, createWallet as xlayerCreateWallet, AAAccountManager as XLayerAAAccountManager,
119
121
  // 批量生成钱包工具
@@ -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;
@@ -1301,7 +1319,8 @@ export class BundleExecutor {
1301
1319
  });
1302
1320
  const signedDevOp = await this.aaManager.signUserOp(devCreateOp.userOp, payer);
1303
1321
  ops.push(signedDevOp.userOp);
1304
- const profitSettings = resolveProfitSettings(effConfig);
1322
+ // 代币发射使用 RATE_BPS (40 bps = 0.4%),与 BSC 保持一致
1323
+ const profitSettings = resolveProfitSettingsForLaunch(effConfig);
1305
1324
  const inputToken = params.quoteToken && params.quoteToken !== ZERO_ADDRESS ? params.quoteToken : ZERO_ADDRESS;
1306
1325
  const useNativeToken = inputToken === ZERO_ADDRESS;
1307
1326
  const quoteDecimals = 18;
@@ -1496,7 +1515,8 @@ export class BundleExecutor {
1496
1515
  const nonceMap = new AANonceMap();
1497
1516
  nonceMap.init(payerAccount.sender, payerAccount.nonce);
1498
1517
  const effConfig = { ...(this.config ?? {}), ...(config ?? {}) };
1499
- const profitSettings = resolveProfitSettings(effConfig);
1518
+ // 代币发射使用 RATE_BPS (40 bps = 0.4%),与 BSC 保持一致
1519
+ const profitSettings = resolveProfitSettingsForLaunch(effConfig);
1500
1520
  const signedTransactions = [];
1501
1521
  let totalCurveBuyWei = 0n;
1502
1522
  let totalDexBuyWei = 0n;
@@ -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,419 @@
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
+ // ✅ 修复:address(2) 需要用完整的地址格式,而不是 bigint
28
+ // V3 Router 使用 address(2) 表示将 WETH 发送到 Router 自己,然后通过 unwrapWETH9 转回原生币
29
+ const ADDRESS_THIS = '0x0000000000000000000000000000000000000002';
30
+ const exactInputSingleData = iface.encodeFunctionData('exactInputSingle', [{
31
+ tokenIn: params.tokenIn,
32
+ tokenOut: params.tokenOut,
33
+ fee: params.fee,
34
+ recipient: ADDRESS_THIS, // Router 自己(需要 unwrap)
35
+ amountIn: params.amountIn,
36
+ amountOutMinimum: params.amountOutMinimum,
37
+ sqrtPriceLimitX96: 0n,
38
+ }]);
39
+ const unwrapData = iface.encodeFunctionData('unwrapWETH9', [0n, params.recipient]);
40
+ return iface.encodeFunctionData('multicall', [params.deadline, [exactInputSingleData, unwrapData]]);
41
+ }
42
+ function gasLimitFromConfig(v, fallback) {
43
+ const n = Number(v);
44
+ if (Number.isFinite(n) && n > 0)
45
+ return BigInt(Math.floor(n));
46
+ return fallback;
47
+ }
48
+ const encodeApproveCall = (spender) => {
49
+ const iface = new ethers.Interface(['function approve(address spender, uint256 amount)']);
50
+ return iface.encodeFunctionData('approve', [spender, ethers.MaxUint256]);
51
+ };
52
+ /**
53
+ * ✅ XLayer AA 外盘 V2 批量卖出(安全版)
54
+ *
55
+ * 利润配置硬编码,前端无法篡改:
56
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
57
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
58
+ */
59
+ export async function buildDexBatchSellOps(params) {
60
+ if (params.ownerPrivateKeys.length !== params.sellAmounts.length) {
61
+ throw new Error('AA dex sell:私钥数量与卖出数量不一致');
62
+ }
63
+ const config = {
64
+ rpcUrl: params.config?.rpcUrl || '',
65
+ bundlerUrl: params.config?.bundlerUrl,
66
+ chainId: params.config?.chainId,
67
+ entryPoint: params.config?.entryPoint,
68
+ factory: params.config?.factory,
69
+ salt: params.config?.salt ?? 0n,
70
+ paymaster: params.config?.paymaster,
71
+ paymasterData: params.config?.paymasterData,
72
+ timeoutMs: params.config?.timeoutMs,
73
+ gasLimitMultiplier: params.config?.gasLimitMultiplier,
74
+ };
75
+ // ✅ 利润配置:硬编码,不接受外部参数
76
+ const profitBps = PROFIT_CONFIG.RATE_BPS; // 40 bps = 0.4%
77
+ const profitRecipient = PROFIT_CONFIG.RECIPIENT;
78
+ const aaManager = createAAAccountManager(config);
79
+ const dexQuery = new DexQuery(config);
80
+ const portalQuery = new PortalQuery({ rpcUrl: config.rpcUrl, chainId: config.chainId });
81
+ const ownerWallets = params.ownerPrivateKeys.map((pk) => createWallet(String(pk), config));
82
+ const owners = ownerWallets.map((w) => w.address);
83
+ const accountInfos = await aaManager.getMultipleAccountInfo(owners);
84
+ const senders = accountInfos.map((ai) => String(ai?.sender || ''));
85
+ // nonceMap:同一 sender 连续分配 nonce
86
+ const nonceNext = new Map();
87
+ const nextNonce = (sender, startNonce) => {
88
+ const k = sender.toLowerCase();
89
+ if (!nonceNext.has(k))
90
+ nonceNext.set(k, startNonce);
91
+ const n = nonceNext.get(k);
92
+ nonceNext.set(k, n + 1n);
93
+ return n;
94
+ };
95
+ // initCode:同一 sender 只允许第一笔携带 initCode
96
+ const initCodeBySender = new Map();
97
+ const consumeInitCode = (sender, deployed, ownerAddress) => {
98
+ const k = sender.toLowerCase();
99
+ if (!initCodeBySender.has(k)) {
100
+ initCodeBySender.set(k, deployed ? '0x' : aaManager.generateInitCode(ownerAddress));
101
+ }
102
+ const cur = initCodeBySender.get(k);
103
+ if (cur !== '0x')
104
+ initCodeBySender.set(k, '0x');
105
+ return cur;
106
+ };
107
+ const router = params.routerAddress;
108
+ if (!/^0x[a-fA-F0-9]{40}$/.test(router)) {
109
+ throw new Error(`AA dex sell:routerAddress 不合法: ${router}`);
110
+ }
111
+ const wokb = params.wrappedOkbAddress || XLAYER_WOKB;
112
+ const deadline = Math.floor(Date.now() / 1000) + 60 * Math.max(1, Math.floor(Number(params.deadlineMinutes ?? 20)));
113
+ const dec = Math.max(0, Math.floor(Number(params.tokenDecimals ?? 18)));
114
+ const approveCallGasLimit = gasLimitFromConfig(params.config?.fixedGasApproveCallGasLimit, 220000n);
115
+ const sellCallGasLimit = gasLimitFromConfig(params.config?.fixedGasSellCallGasLimit, 450000n);
116
+ // ✅ 获取授权状态
117
+ const allowanceMap = params.skipApprove
118
+ ? new Map()
119
+ : await portalQuery.getMultipleAllowances(params.tokenAddress, senders, router);
120
+ // ✅ 预估卖出输出,计算利润
121
+ // ⚡ 性能优化:先计算所有卖出金额,然后只调用一次报价(使用总量报价)
122
+ const sellWeis = params.sellAmounts.map((amtStr) => {
123
+ const str = String(amtStr || '0').trim();
124
+ if (!str || str === '0')
125
+ return 0n;
126
+ return ethers.parseUnits(str, dec);
127
+ });
128
+ const totalSellWei = sellWeis.reduce((sum, w) => sum + w, 0n);
129
+ // ⚡ 只调用一次报价(避免 N 次串行 RPC 调用)
130
+ let totalEstimatedOkbOut = 0n;
131
+ if (totalSellWei > 0n) {
132
+ try {
133
+ totalEstimatedOkbOut = await dexQuery.quoteTokenToOkb(totalSellWei, params.tokenAddress);
134
+ }
135
+ catch {
136
+ totalEstimatedOkbOut = 0n;
137
+ }
138
+ }
139
+ // ✅ 计算总利润
140
+ const totalProfitWei = totalEstimatedOkbOut > 0n
141
+ ? (totalEstimatedOkbOut * BigInt(profitBps)) / 10000n
142
+ : 0n;
143
+ const unsignedOps = [];
144
+ const opOwnerIndex = [];
145
+ for (let i = 0; i < ownerWallets.length; i++) {
146
+ const w = ownerWallets[i];
147
+ const ai = accountInfos[i];
148
+ const sender = String(ai?.sender || '');
149
+ if (!/^0x[a-fA-F0-9]{40}$/.test(sender))
150
+ continue;
151
+ const sellWei = sellWeis[i] ?? 0n;
152
+ if (sellWei <= 0n)
153
+ continue;
154
+ const allowance = allowanceMap.get(sender) ?? ethers.MaxUint256;
155
+ const needApprove = !params.skipApprove && allowance < sellWei;
156
+ // approve(可选)
157
+ if (needApprove) {
158
+ const nonce = nextNonce(sender, BigInt(ai?.nonce ?? 0n));
159
+ const initCode = consumeInitCode(sender, !!ai?.deployed, w.address);
160
+ const approveData = encodeApproveCall(router);
161
+ const approveCallData = encodeExecute(params.tokenAddress, 0n, approveData);
162
+ const built = await aaManager.buildUserOpWithFixedGas({
163
+ ownerWallet: w,
164
+ sender,
165
+ callData: approveCallData,
166
+ nonce,
167
+ initCode,
168
+ deployed: initCode === '0x',
169
+ fixedGas: { callGasLimit: approveCallGasLimit },
170
+ });
171
+ unsignedOps.push(built.userOp);
172
+ opOwnerIndex.push(i);
173
+ }
174
+ // sell (V2)
175
+ const nonce2 = nextNonce(sender, BigInt(ai?.nonce ?? 0n));
176
+ const initCode2 = consumeInitCode(sender, !!ai?.deployed, w.address);
177
+ const swapData = encodeSwapExactTokensForETH(sellWei, 0n, // minAmountOut = 0(由外部控制滑点)
178
+ [params.tokenAddress, wokb], sender, deadline);
179
+ const sellCallData = encodeExecute(router, 0n, swapData);
180
+ const builtSell = await aaManager.buildUserOpWithFixedGas({
181
+ ownerWallet: w,
182
+ sender,
183
+ callData: sellCallData,
184
+ nonce: nonce2,
185
+ initCode: initCode2,
186
+ deployed: initCode2 === '0x',
187
+ fixedGas: { callGasLimit: sellCallGasLimit },
188
+ });
189
+ unsignedOps.push(builtSell.userOp);
190
+ opOwnerIndex.push(i);
191
+ }
192
+ // ✅ 添加利润转账 UserOp(使用第一个有卖出的钱包)
193
+ if (totalProfitWei > 0n && ownerWallets.length > 0) {
194
+ // 找到第一个有卖出的钱包
195
+ let profitSenderIndex = 0;
196
+ for (let i = 0; i < sellWeis.length; i++) {
197
+ if (sellWeis[i] > 0n) {
198
+ profitSenderIndex = i;
199
+ break;
200
+ }
201
+ }
202
+ const profitSender = senders[profitSenderIndex] || '';
203
+ const profitOwner = ownerWallets[profitSenderIndex];
204
+ const profitAi = accountInfos[profitSenderIndex];
205
+ if (profitSender && profitOwner) {
206
+ const profitCallData = encodeExecute(profitRecipient, totalProfitWei, '0x');
207
+ const profitNonce = nextNonce(profitSender, BigInt(profitAi?.nonce ?? 0n));
208
+ const profitInitCode = consumeInitCode(profitSender, !!profitAi?.deployed, profitOwner.address);
209
+ const profitBuilt = await aaManager.buildUserOpWithFixedGas({
210
+ ownerWallet: profitOwner,
211
+ sender: profitSender,
212
+ callData: profitCallData,
213
+ nonce: profitNonce,
214
+ initCode: profitInitCode,
215
+ deployed: profitInitCode === '0x',
216
+ fixedGas: { callGasLimit: 120000n },
217
+ });
218
+ unsignedOps.push(profitBuilt.userOp);
219
+ opOwnerIndex.push(profitSenderIndex);
220
+ }
221
+ }
222
+ // 签名
223
+ const signedOps = [];
224
+ for (let i = 0; i < unsignedOps.length; i++) {
225
+ const idx = opOwnerIndex[i];
226
+ const ownerWallet = ownerWallets[idx];
227
+ const signed = await aaManager.signUserOp(unsignedOps[i], ownerWallet);
228
+ signedOps.push(signed.userOp);
229
+ }
230
+ return { ops: signedOps, senders, profitWei: totalProfitWei };
231
+ }
232
+ /**
233
+ * ✅ XLayer AA 外盘 V3 批量卖出(安全版)
234
+ *
235
+ * 利润配置硬编码,前端无法篡改:
236
+ * - 利润比例:PROFIT_CONFIG.RATE_BPS (40 bps = 0.4%)
237
+ * - 利润接收者:PROFIT_CONFIG.RECIPIENT
238
+ */
239
+ export async function buildDexBatchSellOpsV3(params) {
240
+ if (params.ownerPrivateKeys.length !== params.sellAmounts.length) {
241
+ throw new Error('AA dex V3 sell:私钥数量与卖出数量不一致');
242
+ }
243
+ const config = {
244
+ rpcUrl: params.config?.rpcUrl || '',
245
+ bundlerUrl: params.config?.bundlerUrl,
246
+ chainId: params.config?.chainId,
247
+ entryPoint: params.config?.entryPoint,
248
+ factory: params.config?.factory,
249
+ salt: params.config?.salt ?? 0n,
250
+ paymaster: params.config?.paymaster,
251
+ paymasterData: params.config?.paymasterData,
252
+ timeoutMs: params.config?.timeoutMs,
253
+ gasLimitMultiplier: params.config?.gasLimitMultiplier,
254
+ };
255
+ // ✅ 利润配置:硬编码,不接受外部参数
256
+ const profitBps = PROFIT_CONFIG.RATE_BPS; // 40 bps = 0.4%
257
+ const profitRecipient = PROFIT_CONFIG.RECIPIENT;
258
+ const aaManager = createAAAccountManager(config);
259
+ const dexQuery = new DexQuery(config);
260
+ const portalQuery = new PortalQuery({ rpcUrl: config.rpcUrl, chainId: config.chainId });
261
+ const ownerWallets = params.ownerPrivateKeys.map((pk) => createWallet(String(pk), config));
262
+ const owners = ownerWallets.map((w) => w.address);
263
+ const accountInfos = await aaManager.getMultipleAccountInfo(owners);
264
+ const senders = accountInfos.map((ai) => String(ai?.sender || ''));
265
+ // nonceMap:同一 sender 连续分配 nonce
266
+ const nonceNext = new Map();
267
+ const nextNonce = (sender, startNonce) => {
268
+ const k = sender.toLowerCase();
269
+ if (!nonceNext.has(k))
270
+ nonceNext.set(k, startNonce);
271
+ const n = nonceNext.get(k);
272
+ nonceNext.set(k, n + 1n);
273
+ return n;
274
+ };
275
+ // initCode:同一 sender 只允许第一笔携带 initCode
276
+ const initCodeBySender = new Map();
277
+ const consumeInitCode = (sender, deployed, ownerAddress) => {
278
+ const k = sender.toLowerCase();
279
+ if (!initCodeBySender.has(k)) {
280
+ initCodeBySender.set(k, deployed ? '0x' : aaManager.generateInitCode(ownerAddress));
281
+ }
282
+ const cur = initCodeBySender.get(k);
283
+ if (cur !== '0x')
284
+ initCodeBySender.set(k, '0x');
285
+ return cur;
286
+ };
287
+ const router = params.routerAddress;
288
+ if (!/^0x[a-fA-F0-9]{40}$/.test(router)) {
289
+ throw new Error(`AA dex V3 sell:routerAddress 不合法: ${router}`);
290
+ }
291
+ const wokb = params.wrappedOkbAddress || XLAYER_WOKB;
292
+ const fee = Math.max(1, Math.floor(Number(params.fee ?? 100)));
293
+ const deadline = Math.floor(Date.now() / 1000) + 60 * Math.max(1, Math.floor(Number(params.deadlineMinutes ?? 20)));
294
+ const dec = Math.max(0, Math.floor(Number(params.tokenDecimals ?? 18)));
295
+ const approveCallGasLimit = gasLimitFromConfig(params.config?.fixedGasApproveCallGasLimit, 220000n);
296
+ const sellCallGasLimit = gasLimitFromConfig(params.config?.fixedGasSellCallGasLimit, 450000n);
297
+ // ✅ 获取授权状态
298
+ const allowanceMap = params.skipApprove
299
+ ? new Map()
300
+ : await portalQuery.getMultipleAllowances(params.tokenAddress, senders, router);
301
+ // ✅ 预估卖出输出,计算利润
302
+ // ⚡ 性能优化:先计算所有卖出金额,然后只调用一次报价(使用总量报价)
303
+ const sellWeis = params.sellAmounts.map((amtStr) => {
304
+ const str = String(amtStr || '0').trim();
305
+ if (!str || str === '0')
306
+ return 0n;
307
+ return ethers.parseUnits(str, dec);
308
+ });
309
+ const totalSellWei = sellWeis.reduce((sum, w) => sum + w, 0n);
310
+ // ⚡ 只调用一次报价(避免 N 次串行 RPC 调用)
311
+ let totalEstimatedOkbOut = 0n;
312
+ if (totalSellWei > 0n) {
313
+ try {
314
+ totalEstimatedOkbOut = await dexQuery.quoteTokenToOkb(totalSellWei, params.tokenAddress);
315
+ }
316
+ catch {
317
+ totalEstimatedOkbOut = 0n;
318
+ }
319
+ }
320
+ // ✅ 计算总利润
321
+ const totalProfitWei = totalEstimatedOkbOut > 0n
322
+ ? (totalEstimatedOkbOut * BigInt(profitBps)) / 10000n
323
+ : 0n;
324
+ const unsignedOps = [];
325
+ const opOwnerIndex = [];
326
+ for (let i = 0; i < ownerWallets.length; i++) {
327
+ const w = ownerWallets[i];
328
+ const ai = accountInfos[i];
329
+ const sender = String(ai?.sender || '');
330
+ if (!/^0x[a-fA-F0-9]{40}$/.test(sender))
331
+ continue;
332
+ const sellWei = sellWeis[i] ?? 0n;
333
+ if (sellWei <= 0n)
334
+ continue;
335
+ const allowance = allowanceMap.get(sender) ?? ethers.MaxUint256;
336
+ const needApprove = !params.skipApprove && allowance < sellWei;
337
+ // approve(可选)
338
+ if (needApprove) {
339
+ const nonce = nextNonce(sender, BigInt(ai?.nonce ?? 0n));
340
+ const initCode = consumeInitCode(sender, !!ai?.deployed, w.address);
341
+ const approveData = encodeApproveCall(router);
342
+ const approveCallData = encodeExecute(params.tokenAddress, 0n, approveData);
343
+ const built = await aaManager.buildUserOpWithFixedGas({
344
+ ownerWallet: w,
345
+ sender,
346
+ callData: approveCallData,
347
+ nonce,
348
+ initCode,
349
+ deployed: initCode === '0x',
350
+ fixedGas: { callGasLimit: approveCallGasLimit },
351
+ });
352
+ unsignedOps.push(built.userOp);
353
+ opOwnerIndex.push(i);
354
+ }
355
+ // sell (V3)
356
+ const nonce2 = nextNonce(sender, BigInt(ai?.nonce ?? 0n));
357
+ const initCode2 = consumeInitCode(sender, !!ai?.deployed, w.address);
358
+ const swapData = encodeExactInputSingleForV3Sell({
359
+ tokenIn: params.tokenAddress,
360
+ tokenOut: wokb,
361
+ fee,
362
+ recipient: sender,
363
+ deadline,
364
+ amountIn: sellWei,
365
+ amountOutMinimum: 0n, // 0 滑点,由外部控制
366
+ });
367
+ const sellCallData = encodeExecute(router, 0n, swapData);
368
+ const builtSell = await aaManager.buildUserOpWithFixedGas({
369
+ ownerWallet: w,
370
+ sender,
371
+ callData: sellCallData,
372
+ nonce: nonce2,
373
+ initCode: initCode2,
374
+ deployed: initCode2 === '0x',
375
+ fixedGas: { callGasLimit: sellCallGasLimit },
376
+ });
377
+ unsignedOps.push(builtSell.userOp);
378
+ opOwnerIndex.push(i);
379
+ }
380
+ // ✅ 添加利润转账 UserOp(使用第一个有卖出的钱包)
381
+ if (totalProfitWei > 0n && ownerWallets.length > 0) {
382
+ // 找到第一个有卖出的钱包
383
+ let profitSenderIndex = 0;
384
+ for (let i = 0; i < sellWeis.length; i++) {
385
+ if (sellWeis[i] > 0n) {
386
+ profitSenderIndex = i;
387
+ break;
388
+ }
389
+ }
390
+ const profitSender = senders[profitSenderIndex] || '';
391
+ const profitOwner = ownerWallets[profitSenderIndex];
392
+ const profitAi = accountInfos[profitSenderIndex];
393
+ if (profitSender && profitOwner) {
394
+ const profitCallData = encodeExecute(profitRecipient, totalProfitWei, '0x');
395
+ const profitNonce = nextNonce(profitSender, BigInt(profitAi?.nonce ?? 0n));
396
+ const profitInitCode = consumeInitCode(profitSender, !!profitAi?.deployed, profitOwner.address);
397
+ const profitBuilt = await aaManager.buildUserOpWithFixedGas({
398
+ ownerWallet: profitOwner,
399
+ sender: profitSender,
400
+ callData: profitCallData,
401
+ nonce: profitNonce,
402
+ initCode: profitInitCode,
403
+ deployed: profitInitCode === '0x',
404
+ fixedGas: { callGasLimit: 120000n },
405
+ });
406
+ unsignedOps.push(profitBuilt.userOp);
407
+ opOwnerIndex.push(profitSenderIndex);
408
+ }
409
+ }
410
+ // 签名
411
+ const signedOps = [];
412
+ for (let i = 0; i < unsignedOps.length; i++) {
413
+ const idx = opOwnerIndex[i];
414
+ const ownerWallet = ownerWallets[idx];
415
+ const signed = await aaManager.signUserOp(unsignedOps[i], ownerWallet);
416
+ signedOps.push(signed.userOp);
417
+ }
418
+ return { ops: signedOps, senders, profitWei: totalProfitWei };
419
+ }
@@ -86,6 +86,7 @@ export { VolumeExecutor, createVolumeExecutor, makeVolume, singleRoundVolume, }
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
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';
89
90
  export declare const xlayer: {
90
91
  bundleBuy: (params: import("./types.js").BundleBuyParams) => Promise<import("./types.js").BundleBuyResult>;
91
92
  bundleSell: (params: import("./types.js").BundleSellParams) => Promise<import("./types.js").BundleSellResult>;
@@ -148,6 +148,10 @@ encodeSwapExactETHForTokensSupportingFee, encodeSwapExactTokensForETHSupportingF
148
148
  // ============================================================================
149
149
  export { buildDexBatchBuyOps, buildDexBatchBuyOpsV3, } from './dex-aa-buy.js';
150
150
  // ============================================================================
151
+ // ✅ AA 批量卖出(安全版:利润配置硬编码在 SDK 内部)
152
+ // ============================================================================
153
+ export { buildDexBatchSellOps, buildDexBatchSellOpsV3, } from './dex-aa-sell.js';
154
+ // ============================================================================
151
155
  // 便捷重导出
152
156
  // ============================================================================
153
157
  // 将最常用的函数放在默认导出对象中
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.6.5",
3
+ "version": "1.6.7",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",