four-flap-meme-sdk 1.5.93 → 1.5.95

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.
@@ -73,7 +73,6 @@ class AANonceMap {
73
73
  // 固定 gas(用于大规模减少 RPC);具体值允许通过 config.fixedGas 覆盖
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
- const DEFAULT_CALL_GAS_LIMIT_TRANSFER = 150000n;
77
76
  const DEFAULT_CALL_GAS_LIMIT_WITHDRAW = 120000n;
78
77
  function resolveProfitSettings(config) {
79
78
  const extractProfit = config?.extractProfit !== false; // 默认 true
@@ -1350,17 +1349,31 @@ export class BundleExecutor {
1350
1349
  const totalBuyWei = buyerData.reduce((sum, d) => sum + d.buyWei, 0n);
1351
1350
  const totalBuyProfitWei = buyerData.reduce((sum, d) => sum + d.profitWei, 0n);
1352
1351
  // ✅ 并行构建和签名所有买家的 UserOps
1352
+ // ✅ V3/V4 代币使用 swapExactInputV3(支持 extensionData),与 bundleGraduateBuy 保持一致
1353
+ const extensionData = params.extensionData ?? '0x';
1353
1354
  const signedBuyOps = await mapWithConcurrency(buyerData, 5, async (data) => {
1354
1355
  const { buyer, buyWei, accountInfo } = data;
1355
- const swapData = portalIface.encodeFunctionData('swapExactInput', [
1356
- {
1357
- inputToken,
1358
- outputToken: tokenAddress,
1359
- inputAmount: buyWei,
1360
- minOutputAmount: 0n,
1361
- permitData: '0x',
1362
- },
1363
- ]);
1356
+ // V3/V4 代币使用 swapExactInputV3,否则使用旧版 swapExactInput
1357
+ const swapData = (useV3 || useV4)
1358
+ ? portalIface.encodeFunctionData('swapExactInputV3', [
1359
+ {
1360
+ inputToken,
1361
+ outputToken: tokenAddress,
1362
+ inputAmount: buyWei,
1363
+ minOutputAmount: 0n,
1364
+ permitData: '0x',
1365
+ extensionData,
1366
+ },
1367
+ ])
1368
+ : portalIface.encodeFunctionData('swapExactInput', [
1369
+ {
1370
+ inputToken,
1371
+ outputToken: tokenAddress,
1372
+ inputAmount: buyWei,
1373
+ minOutputAmount: 0n,
1374
+ permitData: '0x',
1375
+ },
1376
+ ]);
1364
1377
  // ✅ ERC20 代币需要先 approve,使用 executeBatch 将 approve + swap 合并为一个 UserOp
1365
1378
  let buyCallData;
1366
1379
  if (useNativeToken) {
@@ -1666,7 +1679,6 @@ export class BundleExecutor {
1666
1679
  const tokenState = await this.portalQuery.getTokenV7(tokenAddress);
1667
1680
  // ✅ 内联曲线公式计算毕业容量
1668
1681
  // 公式:graduationReserve = h * k(曲线参数从合约读取)
1669
- const r = tokenState.r; // 衰减因子
1670
1682
  const h = tokenState.h; // 初始高度
1671
1683
  const k = tokenState.k; // 缩放系数
1672
1684
  // 毕业储备 = h * k (wei)
@@ -1677,9 +1689,6 @@ export class BundleExecutor {
1677
1689
  const graduateCapacity = graduationAmount ?? 73.38;
1678
1690
  const remainingToGraduate = Math.max(0, graduateCapacity - parseFloat(poolReserveOkb));
1679
1691
  // const graduateCapacity = parseFloat(graduationReserve);
1680
- // 剩余毕业金额 = 毕业阈值 - 当前储备(加 0.5% 手续费缓冲)
1681
- const remaining = Math.max(0, graduateCapacity - parseFloat(poolReserveOkb));
1682
- const remainingWithFee = remaining * 1.005; // 0.5% 手续费缓冲
1683
1692
  // const remainingToGraduate = remainingWithFee;
1684
1693
  const remainingToGraduateWei = ethers.parseEther(remainingToGraduate.toFixed(18));
1685
1694
  //
@@ -4,6 +4,7 @@
4
4
  import { XLayerConfig, BundleSwapSignParams, BundleSwapSignResult, BundleBatchSwapSignParams, BundleBatchSwapSignResult } from './types.js';
5
5
  /**
6
6
  * XLayer AA 外盘换手执行器
7
+ * ✅ 支持 V2 和 V3 交易
7
8
  */
8
9
  export declare class AADexSwapExecutor {
9
10
  private aaManager;
@@ -13,16 +14,27 @@ export declare class AADexSwapExecutor {
13
14
  constructor(config?: XLayerConfig);
14
15
  /**
15
16
  * 获取 Router 地址
17
+ * @param tradeType - 交易类型:'V2' 或 'V3'
16
18
  */
17
19
  private getEffectiveRouter;
20
+ /**
21
+ * 判断是否使用 V3
22
+ */
23
+ private isV3;
24
+ /**
25
+ * 获取 V3 swap 的 deadline
26
+ */
27
+ private getDexDeadline;
18
28
  /**
19
29
  * AA 外盘单钱包换手签名
30
+ * ✅ 支持 V2 和 V3 交易
20
31
  */
21
32
  bundleSwapSign(params: BundleSwapSignParams & {
22
33
  skipApprovalCheck?: boolean;
23
34
  }): Promise<BundleSwapSignResult>;
24
35
  /**
25
36
  * AA 外盘批量换手签名
37
+ * ✅ 支持 V2 和 V3 交易
26
38
  */
27
39
  bundleBatchSwapSign(params: BundleBatchSwapSignParams & {
28
40
  skipApprovalCheck?: boolean;
@@ -3,10 +3,10 @@
3
3
  */
4
4
  import { Wallet, ethers } from 'ethers';
5
5
  import { AANonceMap, } from './types.js';
6
- import { POTATOSWAP_V2_ROUTER, WOKB, MULTICALL3, } from './constants.js';
6
+ import { POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, WOKB, MULTICALL3, } from './constants.js';
7
7
  import { AAAccountManager, encodeExecute } from './aa-account.js';
8
- import { encodeApproveCall, } from './portal-ops.js';
9
- import { DexQuery, encodeSwapExactETHForTokensSupportingFee, encodeSwapExactTokensForETHSupportingFee, } from './dex.js';
8
+ import { encodeApproveCall, lpFeeProfileToV3Fee, } from './portal-ops.js';
9
+ import { DexQuery, encodeSwapExactETHForTokensSupportingFee, encodeSwapExactTokensForETHSupportingFee, encodeSwapExactETHForTokensV3, encodeSwapExactTokensForETHV3, } from './dex.js';
10
10
  import { BundleExecutor } from './bundle.js';
11
11
  import { PROFIT_CONFIG } from '../utils/constants.js';
12
12
  const multicallIface = new ethers.Interface([
@@ -45,11 +45,9 @@ function calculateProfitWei(amountWei, profitBps) {
45
45
  return 0n;
46
46
  return (amountWei * BigInt(profitBps)) / 10000n;
47
47
  }
48
- function getDexDeadline(minutes = 20) {
49
- return Math.floor(Date.now() / 1000) + minutes * 60;
50
- }
51
48
  /**
52
49
  * XLayer AA 外盘换手执行器
50
+ * ✅ 支持 V2 和 V3 交易
53
51
  */
54
52
  export class AADexSwapExecutor {
55
53
  constructor(config = {}) {
@@ -63,23 +61,41 @@ export class AADexSwapExecutor {
63
61
  }
64
62
  /**
65
63
  * 获取 Router 地址
64
+ * @param tradeType - 交易类型:'V2' 或 'V3'
66
65
  */
67
66
  getEffectiveRouter(params) {
68
67
  if (params.routerAddress)
69
68
  return params.routerAddress;
69
+ // ✅ 支持 V3 Router
70
+ if (params.tradeType === 'V3')
71
+ return POTATOSWAP_V3_ROUTER;
70
72
  if (params.dexKey === 'DYORSWAP') {
71
73
  return '0xfb001fbbace32f09cb6d3c449b935183de53ee96';
72
74
  }
73
75
  return POTATOSWAP_V2_ROUTER;
74
76
  }
77
+ /**
78
+ * 判断是否使用 V3
79
+ */
80
+ isV3(tradeType) {
81
+ return tradeType === 'V3';
82
+ }
83
+ /**
84
+ * 获取 V3 swap 的 deadline
85
+ */
86
+ getDexDeadline(minutes = 20) {
87
+ return Math.floor(Date.now() / 1000) + minutes * 60;
88
+ }
75
89
  /**
76
90
  * AA 外盘单钱包换手签名
91
+ * ✅ 支持 V2 和 V3 交易
77
92
  */
78
93
  async bundleSwapSign(params) {
79
- const { dexKey, routerAddress: routerAddressIn, tokenAddress, sellerPrivateKey, buyerPrivateKey, sellAmount, sellPercent = 100, buyAmountOkb, slippageBps = 100, disperseHopCount: disperseHopCountIn, payerPrivateKey, beneficiary: beneficiaryIn, payerStartNonce, routeAddress, skipApprovalCheck = false, } = params;
94
+ const { dexKey, routerAddress: routerAddressIn, tokenAddress, sellerPrivateKey, buyerPrivateKey, sellAmount, sellPercent = 100, buyAmountOkb, slippageBps = 100, disperseHopCount: disperseHopCountIn, payerPrivateKey, beneficiary: beneficiaryIn, payerStartNonce, routeAddress, skipApprovalCheck = false, tradeType, lpFeeProfile = 0, } = params;
80
95
  const effectiveConfig = { ...(this.config ?? {}), ...(params.config ?? {}) };
81
96
  const { extractProfit, profitBps, profitRecipient } = resolveProfitSettings(effectiveConfig);
82
- const effectiveRouter = this.getEffectiveRouter({ dexKey, routerAddress: routerAddressIn });
97
+ const isV3Trade = this.isV3(tradeType);
98
+ const effectiveRouter = this.getEffectiveRouter({ dexKey, routerAddress: routerAddressIn, tradeType });
83
99
  const provider = this.aaManager.getProvider();
84
100
  const sellerOwner = new Wallet(sellerPrivateKey, provider);
85
101
  const buyerOwner = new Wallet(buyerPrivateKey, provider);
@@ -157,8 +173,26 @@ export class AADexSwapExecutor {
157
173
  const signedApprove = await this.aaManager.signUserOp(userOp, sellerOwner);
158
174
  outOps.push(signedApprove.userOp);
159
175
  }
160
- // Sell op
161
- const sellSwapData = encodeSwapExactTokensForETHSupportingFee(sellAmountWei, 0n, [tokenAddress, WOKB], sellerSender, getDexDeadline());
176
+ // Sell op - ✅ 支持 V2 和 V3
177
+ const deadline = this.getDexDeadline();
178
+ let sellSwapData;
179
+ if (isV3Trade) {
180
+ // V3: 使用 exactInputSingle
181
+ sellSwapData = encodeSwapExactTokensForETHV3({
182
+ tokenIn: tokenAddress,
183
+ tokenOut: WOKB,
184
+ fee: lpFeeProfileToV3Fee(lpFeeProfile),
185
+ recipient: sellerSender,
186
+ deadline,
187
+ amountIn: sellAmountWei,
188
+ amountOutMinimum: 0n,
189
+ sqrtPriceLimitX96: 0n,
190
+ });
191
+ }
192
+ else {
193
+ // V2: 使用 swapExactTokensForETHSupportingFeeOnTransferTokens
194
+ sellSwapData = encodeSwapExactTokensForETHSupportingFee(sellAmountWei, 0n, [tokenAddress, WOKB], sellerSender, deadline);
195
+ }
162
196
  const sellCallData = encodeExecute(effectiveRouter, 0n, sellSwapData);
163
197
  const signedSell = await this.aaManager.buildUserOpWithState({
164
198
  ownerWallet: sellerOwner,
@@ -183,8 +217,25 @@ export class AADexSwapExecutor {
183
217
  });
184
218
  outOps.push(signedTransfer.userOp);
185
219
  }
186
- // Buy op
187
- const buySwapData = encodeSwapExactETHForTokensSupportingFee(0n, [WOKB, tokenAddress], buyerSender, getDexDeadline());
220
+ // Buy op - ✅ 支持 V2 和 V3
221
+ let buySwapData;
222
+ if (isV3Trade) {
223
+ // V3: 使用 exactInputSingle(注意:V3 买入需要用 WOKB 作为输入)
224
+ buySwapData = encodeSwapExactETHForTokensV3({
225
+ tokenIn: WOKB,
226
+ tokenOut: tokenAddress,
227
+ fee: lpFeeProfileToV3Fee(lpFeeProfile),
228
+ recipient: buyerSender,
229
+ deadline,
230
+ amountIn: finalBuyAmountWei,
231
+ amountOutMinimum: 0n,
232
+ sqrtPriceLimitX96: 0n,
233
+ });
234
+ }
235
+ else {
236
+ // V2: 使用 swapExactETHForTokensSupportingFeeOnTransferTokens
237
+ buySwapData = encodeSwapExactETHForTokensSupportingFee(0n, [WOKB, tokenAddress], buyerSender, deadline);
238
+ }
188
239
  const buyCallData = encodeExecute(effectiveRouter, finalBuyAmountWei, buySwapData);
189
240
  const signedBuy = await this.aaManager.buildUserOpWithState({
190
241
  ownerWallet: buyerOwner,
@@ -250,12 +301,14 @@ export class AADexSwapExecutor {
250
301
  }
251
302
  /**
252
303
  * AA 外盘批量换手签名
304
+ * ✅ 支持 V2 和 V3 交易
253
305
  */
254
306
  async bundleBatchSwapSign(params) {
255
- const { dexKey, routerAddress: routerAddressIn, tokenAddress, sellerPrivateKey, buyerPrivateKeys, buyAmountsOkb, sellAmount, sellPercent = 100, disperseHopCount: disperseHopCountIn, payerPrivateKey, beneficiary: beneficiaryIn, payerStartNonce, routeAddress, skipApprovalCheck = false, } = params;
307
+ const { dexKey, routerAddress: routerAddressIn, tokenAddress, sellerPrivateKey, buyerPrivateKeys, buyAmountsOkb, sellAmount, sellPercent = 100, disperseHopCount: disperseHopCountIn, payerPrivateKey, beneficiary: beneficiaryIn, payerStartNonce, routeAddress, skipApprovalCheck = false, tradeType, lpFeeProfile = 0, } = params;
256
308
  const effectiveConfig = { ...(this.config ?? {}), ...(params.config ?? {}) };
257
309
  const { extractProfit, profitBps, profitRecipient } = resolveProfitSettings(effectiveConfig);
258
- const effectiveRouter = this.getEffectiveRouter({ dexKey, routerAddress: routerAddressIn });
310
+ const isV3Trade = this.isV3(tradeType);
311
+ const effectiveRouter = this.getEffectiveRouter({ dexKey, routerAddress: routerAddressIn, tradeType });
259
312
  const provider = this.aaManager.getProvider();
260
313
  const sellerOwner = new Wallet(sellerPrivateKey, provider);
261
314
  const buyerOwners = buyerPrivateKeys.map(pk => new Wallet(pk, provider));
@@ -311,8 +364,26 @@ export class AADexSwapExecutor {
311
364
  const signedApprove = await this.aaManager.signUserOp(userOp, sellerOwner);
312
365
  outOps.push(signedApprove.userOp);
313
366
  }
314
- // Sell op
315
- const sellSwapData = encodeSwapExactTokensForETHSupportingFee(sellAmountWei, 0n, [tokenAddress, WOKB], sellerAi.sender, getDexDeadline());
367
+ // Sell op - ✅ 支持 V2 和 V3
368
+ const deadline = this.getDexDeadline();
369
+ let sellSwapData;
370
+ if (isV3Trade) {
371
+ // V3: 使用 exactInputSingle
372
+ sellSwapData = encodeSwapExactTokensForETHV3({
373
+ tokenIn: tokenAddress,
374
+ tokenOut: WOKB,
375
+ fee: lpFeeProfileToV3Fee(lpFeeProfile),
376
+ recipient: sellerAi.sender,
377
+ deadline,
378
+ amountIn: sellAmountWei,
379
+ amountOutMinimum: 0n,
380
+ sqrtPriceLimitX96: 0n,
381
+ });
382
+ }
383
+ else {
384
+ // V2: 使用 swapExactTokensForETHSupportingFeeOnTransferTokens
385
+ sellSwapData = encodeSwapExactTokensForETHSupportingFee(sellAmountWei, 0n, [tokenAddress, WOKB], sellerAi.sender, deadline);
386
+ }
316
387
  const sellCallData = encodeExecute(effectiveRouter, 0n, sellSwapData);
317
388
  const signedSell = await this.aaManager.buildUserOpWithState({
318
389
  ownerWallet: sellerOwner,
@@ -427,11 +498,28 @@ export class AADexSwapExecutor {
427
498
  }
428
499
  }
429
500
  }
430
- // Batch Buy ops(分发后再执行)
501
+ // Batch Buy ops(分发后再执行)- ✅ 支持 V2 和 V3
431
502
  for (let i = 0; i < buyerOwners.length; i++) {
432
503
  const ai = buyerAis[i];
433
504
  const buyWei = buyAmountsWei[i];
434
- const buySwapData = encodeSwapExactETHForTokensSupportingFee(0n, [WOKB, tokenAddress], ai.sender, getDexDeadline());
505
+ let buySwapData;
506
+ if (isV3Trade) {
507
+ // V3: 使用 exactInputSingle
508
+ buySwapData = encodeSwapExactETHForTokensV3({
509
+ tokenIn: WOKB,
510
+ tokenOut: tokenAddress,
511
+ fee: lpFeeProfileToV3Fee(lpFeeProfile),
512
+ recipient: ai.sender,
513
+ deadline,
514
+ amountIn: buyWei,
515
+ amountOutMinimum: 0n,
516
+ sqrtPriceLimitX96: 0n,
517
+ });
518
+ }
519
+ else {
520
+ // V2: 使用 swapExactETHForTokensSupportingFeeOnTransferTokens
521
+ buySwapData = encodeSwapExactETHForTokensSupportingFee(0n, [WOKB, tokenAddress], ai.sender, deadline);
522
+ }
435
523
  const buyCallData = encodeExecute(effectiveRouter, buyWei, buySwapData);
436
524
  const signedBuy = await this.aaManager.buildUserOpWithState({
437
525
  ownerWallet: buyerOwners[i],
@@ -361,6 +361,8 @@ export interface BundleSwapParams {
361
361
  dexKey?: string;
362
362
  /** 显式指定 Router 地址(若不传则由 SDK 根据 dexKey 自动决定) */
363
363
  routerAddress?: string;
364
+ /** LP 费率档位 (V3 专用): 0=STANDARD (0.25%), 1=LOW (0.01%), 2=HIGH (1%) */
365
+ lpFeeProfile?: number;
364
366
  /** 代币地址 */
365
367
  tokenAddress: string;
366
368
  /** 卖方 Owner 私钥 */
@@ -480,6 +482,8 @@ export interface BundleBatchSwapParams {
480
482
  dexKey?: string;
481
483
  /** 显式指定 Router 地址(若不传则由 SDK 根据 dexKey 自动决定) */
482
484
  routerAddress?: string;
485
+ /** LP 费率档位 (V3 专用): 0=STANDARD (0.25%), 1=LOW (0.01%), 2=HIGH (1%) */
486
+ lpFeeProfile?: number;
483
487
  /** 代币地址 */
484
488
  tokenAddress: string;
485
489
  /** 卖方 Owner 私钥 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.5.93",
3
+ "version": "1.5.95",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",