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.
- package/dist/xlayer/bundle.js +23 -14
- package/dist/xlayer/dex-bundle-swap.d.ts +12 -0
- package/dist/xlayer/dex-bundle-swap.js +106 -18
- package/dist/xlayer/types.d.ts +4 -0
- package/package.json +1 -1
package/dist/xlayer/bundle.js
CHANGED
|
@@ -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
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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],
|
package/dist/xlayer/types.d.ts
CHANGED
|
@@ -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 私钥 */
|