four-flap-meme-sdk 1.5.98 → 1.5.99
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 +3 -1
- package/dist/xlayer/constants.d.ts +2 -2
- package/dist/xlayer/constants.js +8 -1
- package/dist/xlayer/dex-bundle-swap.js +104 -5
- package/dist/xlayer/dex-bundle.js +2 -0
- package/dist/xlayer/dex.d.ts +34 -13
- package/dist/xlayer/dex.js +51 -4
- package/package.json +1 -1
package/dist/xlayer/bundle.js
CHANGED
|
@@ -808,6 +808,7 @@ export class BundleExecutor {
|
|
|
808
808
|
if (dexType === 'V3') {
|
|
809
809
|
routerAddress = POTATOSWAP_V3_ROUTER;
|
|
810
810
|
const v3Fee = lpFeeProfileToV3Fee(tokenState.lpFeeProfile);
|
|
811
|
+
// ✅ V3 卖出需要 unwrap WOKB 到 OKB
|
|
811
812
|
swapData = encodeSwapExactTokensForETHV3({
|
|
812
813
|
tokenIn: tokenAddress,
|
|
813
814
|
tokenOut: WOKB,
|
|
@@ -816,7 +817,8 @@ export class BundleExecutor {
|
|
|
816
817
|
deadline,
|
|
817
818
|
amountIn: it.sellAmount,
|
|
818
819
|
amountOutMinimum: 0n,
|
|
819
|
-
sqrtPriceLimitX96: 0n
|
|
820
|
+
sqrtPriceLimitX96: 0n,
|
|
821
|
+
unwrapRecipient: it.sender, // ✅ 添加 unwrap 接收者
|
|
820
822
|
});
|
|
821
823
|
}
|
|
822
824
|
else {
|
|
@@ -74,6 +74,6 @@ export declare const PORTAL_ABI: readonly ["function swapExactInput((address inp
|
|
|
74
74
|
export declare const ERC20_ABI: readonly ["function balanceOf(address account) view returns (uint256)", "function allowance(address owner, address spender) view returns (uint256)", "function approve(address spender, uint256 amount) returns (bool)", "function transfer(address to, uint256 amount) returns (bool)", "function decimals() view returns (uint8)", "function symbol() view returns (string)", "function name() view returns (string)"];
|
|
75
75
|
/** PotatoSwap V2 Router ABI */
|
|
76
76
|
export declare const POTATOSWAP_V2_ROUTER_ABI: readonly ["function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts)", "function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)", "function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)", "function swapExactETHForTokensSupportingFeeOnTransferTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable", "function swapExactTokensForETHSupportingFeeOnTransferTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external", "function swapExactTokensForTokensSupportingFeeOnTransferTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external", "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)", "function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts)", "function WETH() external pure returns (address)"];
|
|
77
|
-
/** PotatoSwap V3 Router ABI (
|
|
78
|
-
export declare const POTATOSWAP_V3_ROUTER_ABI: readonly ["function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountOut)", "function exactOutputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountOut, uint256 amountInMaximum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountIn)"];
|
|
77
|
+
/** PotatoSwap V3 Router ABI (SwapRouter - Legacy 版本,deadline 在 struct 内部) */
|
|
78
|
+
export declare const POTATOSWAP_V3_ROUTER_ABI: readonly ["function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountOut)", "function exactOutputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountOut, uint256 amountInMaximum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountIn)", "function multicall(bytes[] calldata data) external payable returns (bytes[] memory results)", "function unwrapWETH9(uint256 amountMinimum, address recipient) external payable", "function refundETH() external payable"];
|
|
79
79
|
export type HexString = `0x${string}`;
|
package/dist/xlayer/constants.js
CHANGED
|
@@ -146,8 +146,15 @@ export const POTATOSWAP_V2_ROUTER_ABI = [
|
|
|
146
146
|
'function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts)',
|
|
147
147
|
'function WETH() external pure returns (address)',
|
|
148
148
|
];
|
|
149
|
-
/** PotatoSwap V3 Router ABI (
|
|
149
|
+
/** PotatoSwap V3 Router ABI (SwapRouter - Legacy 版本,deadline 在 struct 内部) */
|
|
150
150
|
export const POTATOSWAP_V3_ROUTER_ABI = [
|
|
151
|
+
// exactInputSingle - 单跳交换,deadline 在 params 内部(Legacy 版本)
|
|
151
152
|
'function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountOut)',
|
|
152
153
|
'function exactOutputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountOut, uint256 amountInMaximum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountIn)',
|
|
154
|
+
// multicall - 打包多个调用(Legacy 版本不含 deadline 参数)
|
|
155
|
+
'function multicall(bytes[] calldata data) external payable returns (bytes[] memory results)',
|
|
156
|
+
// unwrapWETH9 - 将 WOKB 转换为 OKB(用于卖出后获取原生币)
|
|
157
|
+
'function unwrapWETH9(uint256 amountMinimum, address recipient) external payable',
|
|
158
|
+
// refundETH - 退还多余的 ETH(用于买入时的找零)
|
|
159
|
+
'function refundETH() external payable',
|
|
153
160
|
];
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { Wallet, ethers } from 'ethers';
|
|
5
5
|
import { AANonceMap, } from './types.js';
|
|
6
|
-
import { POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, WOKB, MULTICALL3, } from './constants.js';
|
|
6
|
+
import { POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, POTATOSWAP_V3_FACTORY, WOKB, MULTICALL3, } from './constants.js';
|
|
7
7
|
import { AAAccountManager, encodeExecute } from './aa-account.js';
|
|
8
8
|
import { encodeApproveCall, lpFeeProfileToV3Fee, } from './portal-ops.js';
|
|
9
9
|
import { DexQuery, encodeSwapExactETHForTokensSupportingFee, encodeSwapExactTokensForETHSupportingFee, encodeSwapExactETHForTokensV3, encodeSwapExactTokensForETHV3, } from './dex.js';
|
|
@@ -12,6 +12,64 @@ import { PROFIT_CONFIG } from '../utils/constants.js';
|
|
|
12
12
|
const multicallIface = new ethers.Interface([
|
|
13
13
|
'function aggregate3Value((address target,bool allowFailure,uint256 value,bytes callData)[] calls) payable returns ((bool success,bytes returnData)[] returnData)',
|
|
14
14
|
]);
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// V3 Slot0 报价(用于 XLayer 没有 V3 Quoter 的情况)
|
|
17
|
+
// ============================================================================
|
|
18
|
+
const V3_FACTORY_ABI = [
|
|
19
|
+
'function getPool(address tokenA, address tokenB, uint24 fee) view returns (address pool)',
|
|
20
|
+
];
|
|
21
|
+
const V3_POOL_ABI = [
|
|
22
|
+
'function slot0() view returns (uint160 sqrtPriceX96,int24 tick,uint16 observationIndex,uint16 observationCardinality,uint16 observationCardinalityNext,uint8 feeProtocol,bool unlocked)',
|
|
23
|
+
'function token0() view returns (address)',
|
|
24
|
+
'function token1() view returns (address)',
|
|
25
|
+
];
|
|
26
|
+
const V3_FEE_DENOMINATOR = 1000000n;
|
|
27
|
+
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
28
|
+
/**
|
|
29
|
+
* 使用 V3 Pool 的 slot0 获取 Token → WOKB 的报价
|
|
30
|
+
* 这是一个现货价计算,比 V3 Quoter 更简单但精度稍低
|
|
31
|
+
*/
|
|
32
|
+
async function quoteV3ViaSlot0(params) {
|
|
33
|
+
try {
|
|
34
|
+
const { provider, tokenIn, amountIn, fee } = params;
|
|
35
|
+
if (!tokenIn || amountIn <= 0n)
|
|
36
|
+
return 0n;
|
|
37
|
+
const tokenInLower = tokenIn.toLowerCase();
|
|
38
|
+
const wokbLower = WOKB.toLowerCase();
|
|
39
|
+
if (tokenInLower === wokbLower)
|
|
40
|
+
return amountIn;
|
|
41
|
+
const factory = new ethers.Contract(POTATOSWAP_V3_FACTORY, V3_FACTORY_ABI, provider);
|
|
42
|
+
const poolAddr = await factory.getPool(tokenIn, WOKB, fee);
|
|
43
|
+
if (!poolAddr || poolAddr.toLowerCase() === ZERO_ADDRESS.toLowerCase())
|
|
44
|
+
return 0n;
|
|
45
|
+
const pool = new ethers.Contract(poolAddr, V3_POOL_ABI, provider);
|
|
46
|
+
const [t0, t1, slot0] = await Promise.all([pool.token0(), pool.token1(), pool.slot0()]);
|
|
47
|
+
if (!t0 || !t1 || !slot0)
|
|
48
|
+
return 0n;
|
|
49
|
+
const sqrtPriceX96 = BigInt(slot0[0]);
|
|
50
|
+
if (sqrtPriceX96 <= 0n)
|
|
51
|
+
return 0n;
|
|
52
|
+
// 扣除手续费
|
|
53
|
+
const amountInLessFee = (amountIn * (V3_FEE_DENOMINATOR - BigInt(fee))) / V3_FEE_DENOMINATOR;
|
|
54
|
+
if (amountInLessFee <= 0n)
|
|
55
|
+
return 0n;
|
|
56
|
+
const Q192 = 2n ** 192n;
|
|
57
|
+
const num = sqrtPriceX96 * sqrtPriceX96;
|
|
58
|
+
const t0Lower = String(t0).toLowerCase();
|
|
59
|
+
const t1Lower = String(t1).toLowerCase();
|
|
60
|
+
// sqrtPriceX96 表示 token1/token0 的现货价
|
|
61
|
+
if (tokenInLower === t0Lower && wokbLower === t1Lower) {
|
|
62
|
+
return (amountInLessFee * num) / Q192;
|
|
63
|
+
}
|
|
64
|
+
if (tokenInLower === t1Lower && wokbLower === t0Lower) {
|
|
65
|
+
return (amountInLessFee * Q192) / num;
|
|
66
|
+
}
|
|
67
|
+
return 0n;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return 0n;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
15
73
|
function chunkArray(arr, size) {
|
|
16
74
|
const out = [];
|
|
17
75
|
const n = Math.max(1, Math.floor(size));
|
|
@@ -140,15 +198,37 @@ export class AADexSwapExecutor {
|
|
|
140
198
|
needApprove = allowance < sellAmountWei;
|
|
141
199
|
}
|
|
142
200
|
// 估算卖出输出(用于利润提取与 buy 预算)
|
|
201
|
+
// ✅ V3 模式:V2 报价可能失败,需要使用 slot0 报价
|
|
143
202
|
const quotedSellOutWei = await (async () => {
|
|
203
|
+
// 如果是 V3 模式,优先使用 slot0 报价
|
|
204
|
+
if (isV3Trade) {
|
|
205
|
+
const v3Fee = lpFeeProfileToV3Fee(lpFeeProfile);
|
|
206
|
+
const slot0Quote = await quoteV3ViaSlot0({
|
|
207
|
+
provider,
|
|
208
|
+
tokenIn: tokenAddress,
|
|
209
|
+
amountIn: sellAmountWei,
|
|
210
|
+
fee: v3Fee,
|
|
211
|
+
});
|
|
212
|
+
if (slot0Quote > 0n) {
|
|
213
|
+
console.log(`[AA V3 捆绑换手] slot0 报价成功: ${ethers.formatEther(slot0Quote)} OKB`);
|
|
214
|
+
return slot0Quote;
|
|
215
|
+
}
|
|
216
|
+
// slot0 报价失败,尝试 V2 fallback
|
|
217
|
+
}
|
|
144
218
|
try {
|
|
219
|
+
// V2 报价(大多数代币同时有 V2/V3 池子)
|
|
145
220
|
return await this.dexQuery.quoteTokenToOkb(sellAmountWei, tokenAddress);
|
|
146
221
|
}
|
|
147
222
|
catch {
|
|
223
|
+
// V2 报价失败时返回 0
|
|
148
224
|
return 0n;
|
|
149
225
|
}
|
|
150
226
|
})();
|
|
151
227
|
// buyAmount:若未传则按 quotedSellOut 计算;利润从 quotedSellOut 中刮取,但要保证买入资金充足
|
|
228
|
+
// ✅ 修复:如果报价失败且未传入 buyAmountOkb,抛出明确错误
|
|
229
|
+
if (!buyAmountOkb && quotedSellOutWei === 0n) {
|
|
230
|
+
throw new Error('AA 捆绑换手:无法获取报价(V3 池子不存在或流动性不足),请明确传入 buyAmountOkb 参数');
|
|
231
|
+
}
|
|
152
232
|
const requestedBuyWei = buyAmountOkb
|
|
153
233
|
? ethers.parseEther(String(buyAmountOkb))
|
|
154
234
|
: (quotedSellOutWei * BigInt(10000 - slippageBps)) / 10000n;
|
|
@@ -177,16 +257,18 @@ export class AADexSwapExecutor {
|
|
|
177
257
|
const deadline = this.getDexDeadline();
|
|
178
258
|
let sellSwapData;
|
|
179
259
|
if (isV3Trade) {
|
|
180
|
-
// V3: 使用 exactInputSingle
|
|
260
|
+
// V3: 使用 multicall(exactInputSingle + unwrapWETH9)
|
|
261
|
+
// 注意:V3 卖出得到的是 WOKB,需要 unwrap 成 OKB
|
|
181
262
|
sellSwapData = encodeSwapExactTokensForETHV3({
|
|
182
263
|
tokenIn: tokenAddress,
|
|
183
264
|
tokenOut: WOKB,
|
|
184
265
|
fee: lpFeeProfileToV3Fee(lpFeeProfile),
|
|
185
|
-
recipient: sellerSender,
|
|
266
|
+
recipient: sellerSender, // 会被函数内部替换为 ADDRESS_THIS
|
|
186
267
|
deadline,
|
|
187
268
|
amountIn: sellAmountWei,
|
|
188
269
|
amountOutMinimum: 0n,
|
|
189
270
|
sqrtPriceLimitX96: 0n,
|
|
271
|
+
unwrapRecipient: sellerSender, // ✅ unwrap 后发送到 sellerSender
|
|
190
272
|
});
|
|
191
273
|
}
|
|
192
274
|
else {
|
|
@@ -368,16 +450,18 @@ export class AADexSwapExecutor {
|
|
|
368
450
|
const deadline = this.getDexDeadline();
|
|
369
451
|
let sellSwapData;
|
|
370
452
|
if (isV3Trade) {
|
|
371
|
-
// V3: 使用 exactInputSingle
|
|
453
|
+
// V3: 使用 multicall(exactInputSingle + unwrapWETH9)
|
|
454
|
+
// 注意:V3 卖出得到的是 WOKB,需要 unwrap 成 OKB
|
|
372
455
|
sellSwapData = encodeSwapExactTokensForETHV3({
|
|
373
456
|
tokenIn: tokenAddress,
|
|
374
457
|
tokenOut: WOKB,
|
|
375
458
|
fee: lpFeeProfileToV3Fee(lpFeeProfile),
|
|
376
|
-
recipient: sellerAi.sender,
|
|
459
|
+
recipient: sellerAi.sender, // 会被函数内部替换为 ADDRESS_THIS
|
|
377
460
|
deadline,
|
|
378
461
|
amountIn: sellAmountWei,
|
|
379
462
|
amountOutMinimum: 0n,
|
|
380
463
|
sqrtPriceLimitX96: 0n,
|
|
464
|
+
unwrapRecipient: sellerAi.sender, // ✅ unwrap 后发送到 sellerAi.sender
|
|
381
465
|
});
|
|
382
466
|
}
|
|
383
467
|
else {
|
|
@@ -398,7 +482,22 @@ export class AADexSwapExecutor {
|
|
|
398
482
|
const buyAmountsWei = buyAmountsOkb.map(a => ethers.parseEther(a));
|
|
399
483
|
const totalBuyWei = buyAmountsWei.reduce((a, b) => a + (b ?? 0n), 0n);
|
|
400
484
|
// Profit op:估算卖出输出,按比例刮取(但必须保证分发/买入资金充足)
|
|
485
|
+
// ✅ V3 模式:优先使用 slot0 报价
|
|
401
486
|
const quotedSellOutWei = await (async () => {
|
|
487
|
+
// 如果是 V3 模式,优先使用 slot0 报价
|
|
488
|
+
if (isV3Trade) {
|
|
489
|
+
const v3Fee = lpFeeProfileToV3Fee(lpFeeProfile);
|
|
490
|
+
const slot0Quote = await quoteV3ViaSlot0({
|
|
491
|
+
provider,
|
|
492
|
+
tokenIn: tokenAddress,
|
|
493
|
+
amountIn: sellAmountWei,
|
|
494
|
+
fee: v3Fee,
|
|
495
|
+
});
|
|
496
|
+
if (slot0Quote > 0n) {
|
|
497
|
+
console.log(`[AA V3 批量换手] slot0 报价成功: ${ethers.formatEther(slot0Quote)} OKB`);
|
|
498
|
+
return slot0Quote;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
402
501
|
try {
|
|
403
502
|
return await this.dexQuery.quoteTokenToOkb(sellAmountWei, tokenAddress);
|
|
404
503
|
}
|
|
@@ -306,6 +306,7 @@ export class DexBundleExecutor {
|
|
|
306
306
|
const sellNonce = nonceMap.next(sender);
|
|
307
307
|
let sellSwapData;
|
|
308
308
|
if (isV3) {
|
|
309
|
+
// ✅ V3 卖出需要 unwrap WOKB 到 OKB
|
|
309
310
|
sellSwapData = encodeSwapExactTokensForETHV3({
|
|
310
311
|
tokenIn: tokenAddress,
|
|
311
312
|
tokenOut: WOKB,
|
|
@@ -315,6 +316,7 @@ export class DexBundleExecutor {
|
|
|
315
316
|
amountIn: sellAmount,
|
|
316
317
|
amountOutMinimum: 0n,
|
|
317
318
|
sqrtPriceLimitX96: 0n,
|
|
319
|
+
unwrapRecipient: sender, // ✅ 添加 unwrap 接收者
|
|
318
320
|
});
|
|
319
321
|
}
|
|
320
322
|
else {
|
package/dist/xlayer/dex.d.ts
CHANGED
|
@@ -7,21 +7,10 @@
|
|
|
7
7
|
* - 通过 AA 账户执行
|
|
8
8
|
*/
|
|
9
9
|
import type { XLayerConfig, DexSwapResult } from './types.js';
|
|
10
|
-
export declare function encodeSwapExactETHForTokensV3(params: {
|
|
11
|
-
tokenIn: string;
|
|
12
|
-
tokenOut: string;
|
|
13
|
-
fee: number;
|
|
14
|
-
recipient: string;
|
|
15
|
-
deadline: number;
|
|
16
|
-
amountIn: bigint;
|
|
17
|
-
amountOutMinimum: bigint;
|
|
18
|
-
sqrtPriceLimitX96: bigint;
|
|
19
|
-
}): string;
|
|
20
10
|
/**
|
|
21
|
-
*
|
|
22
|
-
* 对齐 Uniswap V3 的 exactInputSingle 逻辑
|
|
11
|
+
* V3 exactInputSingle 参数结构(Legacy 版本,包含 deadline)
|
|
23
12
|
*/
|
|
24
|
-
|
|
13
|
+
interface V3ExactInputSingleParams {
|
|
25
14
|
tokenIn: string;
|
|
26
15
|
tokenOut: string;
|
|
27
16
|
fee: number;
|
|
@@ -30,6 +19,37 @@ export declare function encodeSwapExactTokensForETHV3(params: {
|
|
|
30
19
|
amountIn: bigint;
|
|
31
20
|
amountOutMinimum: bigint;
|
|
32
21
|
sqrtPriceLimitX96: bigint;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 编码 unwrapWETH9 调用(将 WOKB 转换为 OKB)
|
|
25
|
+
*/
|
|
26
|
+
export declare function encodeUnwrapWETH9(amountMinimum: bigint, recipient: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* 编码 refundETH 调用(退还多余的 ETH)
|
|
29
|
+
*/
|
|
30
|
+
export declare function encodeRefundETH(): string;
|
|
31
|
+
/**
|
|
32
|
+
* 编码 V3 multicall 调用(Legacy 版本,不含 deadline)
|
|
33
|
+
*/
|
|
34
|
+
export declare function encodeV3Multicall(data: string[]): string;
|
|
35
|
+
/**
|
|
36
|
+
* ✅ 编码 V3 买入调用(OKB → Token)
|
|
37
|
+
*
|
|
38
|
+
* 对于 AA 模式:直接返回 exactInputSingle 编码(不需要 multicall 包装)
|
|
39
|
+
* - AA 的 execute 会正确传递 msg.value
|
|
40
|
+
* - V3 Router 会自动将原生币包装为 WOKB
|
|
41
|
+
*
|
|
42
|
+
* 对于 EOA 模式:调用者需要自己用 multicall 包装
|
|
43
|
+
*/
|
|
44
|
+
export declare function encodeSwapExactETHForTokensV3(params: V3ExactInputSingleParams): string;
|
|
45
|
+
/**
|
|
46
|
+
* ✅ 编码 V3 卖出调用(Token → OKB)
|
|
47
|
+
* 使用 multicall 包装 exactInputSingle + unwrapWETH9
|
|
48
|
+
*
|
|
49
|
+
* 注意:V3 卖出返回的是 WOKB(ERC20),需要调用 unwrapWETH9 转换为 OKB
|
|
50
|
+
*/
|
|
51
|
+
export declare function encodeSwapExactTokensForETHV3(params: V3ExactInputSingleParams & {
|
|
52
|
+
unwrapRecipient?: string;
|
|
33
53
|
}): string;
|
|
34
54
|
/**
|
|
35
55
|
* 编码 swapExactETHForTokens 调用
|
|
@@ -190,3 +210,4 @@ export declare function quoteOkbToToken(okbAmount: bigint, tokenAddress: string,
|
|
|
190
210
|
* 快速获取 Token -> OKB 报价
|
|
191
211
|
*/
|
|
192
212
|
export declare function quoteTokenToOkb(tokenAmount: bigint, tokenAddress: string, config?: DexConfig): Promise<bigint>;
|
|
213
|
+
export {};
|
package/dist/xlayer/dex.js
CHANGED
|
@@ -15,15 +15,62 @@ import { encodeApproveCall, parseOkb, formatOkb } from './portal-ops.js';
|
|
|
15
15
|
// ============================================================================
|
|
16
16
|
const routerIface = new Interface(POTATOSWAP_V2_ROUTER_ABI);
|
|
17
17
|
const v3RouterIface = new Interface(POTATOSWAP_V3_ROUTER_ABI);
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* 编码 exactInputSingle 调用(内部使用)
|
|
20
|
+
*/
|
|
21
|
+
function encodeExactInputSingle(params) {
|
|
19
22
|
return v3RouterIface.encodeFunctionData('exactInputSingle', [params]);
|
|
20
23
|
}
|
|
21
24
|
/**
|
|
22
|
-
* 编码
|
|
23
|
-
|
|
25
|
+
* 编码 unwrapWETH9 调用(将 WOKB 转换为 OKB)
|
|
26
|
+
*/
|
|
27
|
+
export function encodeUnwrapWETH9(amountMinimum, recipient) {
|
|
28
|
+
return v3RouterIface.encodeFunctionData('unwrapWETH9', [amountMinimum, recipient]);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 编码 refundETH 调用(退还多余的 ETH)
|
|
32
|
+
*/
|
|
33
|
+
export function encodeRefundETH() {
|
|
34
|
+
return v3RouterIface.encodeFunctionData('refundETH', []);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 编码 V3 multicall 调用(Legacy 版本,不含 deadline)
|
|
38
|
+
*/
|
|
39
|
+
export function encodeV3Multicall(data) {
|
|
40
|
+
return v3RouterIface.encodeFunctionData('multicall', [data]);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* ✅ 编码 V3 买入调用(OKB → Token)
|
|
44
|
+
*
|
|
45
|
+
* 对于 AA 模式:直接返回 exactInputSingle 编码(不需要 multicall 包装)
|
|
46
|
+
* - AA 的 execute 会正确传递 msg.value
|
|
47
|
+
* - V3 Router 会自动将原生币包装为 WOKB
|
|
48
|
+
*
|
|
49
|
+
* 对于 EOA 模式:调用者需要自己用 multicall 包装
|
|
50
|
+
*/
|
|
51
|
+
export function encodeSwapExactETHForTokensV3(params) {
|
|
52
|
+
// 直接返回 exactInputSingle 编码
|
|
53
|
+
// V3 Router 会自动处理原生币 → WOKB 的转换
|
|
54
|
+
return encodeExactInputSingle(params);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* ✅ 编码 V3 卖出调用(Token → OKB)
|
|
58
|
+
* 使用 multicall 包装 exactInputSingle + unwrapWETH9
|
|
59
|
+
*
|
|
60
|
+
* 注意:V3 卖出返回的是 WOKB(ERC20),需要调用 unwrapWETH9 转换为 OKB
|
|
24
61
|
*/
|
|
25
62
|
export function encodeSwapExactTokensForETHV3(params) {
|
|
26
|
-
|
|
63
|
+
// V3 卖出需要:1) 先 swap 到 Router 地址;2) 再 unwrap 到最终接收者
|
|
64
|
+
const ADDRESS_THIS = '0x0000000000000000000000000000000000000002'; // V3 Router 的 ADDRESS_THIS
|
|
65
|
+
const swapParams = {
|
|
66
|
+
...params,
|
|
67
|
+
recipient: ADDRESS_THIS, // 先发送到 Router 内部
|
|
68
|
+
};
|
|
69
|
+
const swapData = encodeExactInputSingle(swapParams);
|
|
70
|
+
// unwrap 到最终接收者
|
|
71
|
+
const finalRecipient = params.unwrapRecipient || params.recipient;
|
|
72
|
+
const unwrapData = encodeUnwrapWETH9(0n, finalRecipient); // amountMinimum = 0,不做最小值检查
|
|
73
|
+
return encodeV3Multicall([swapData, unwrapData]);
|
|
27
74
|
}
|
|
28
75
|
/**
|
|
29
76
|
* 编码 swapExactETHForTokens 调用
|