four-flap-meme-sdk 1.6.67 → 1.6.69
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/wash-ops.d.ts +2 -0
- package/dist/xlayer/wash-ops.js +76 -7
- package/package.json +1 -1
package/dist/xlayer/wash-ops.js
CHANGED
|
@@ -16,10 +16,73 @@ import { ethers } from 'ethers';
|
|
|
16
16
|
import { createAAAccountManager, encodeExecute, createWallet } from './aa-account.js';
|
|
17
17
|
import { encodeBuyCall, encodeSellCall, PortalQuery, lpFeeProfileToV3Fee } from './portal-ops.js';
|
|
18
18
|
import { encodeSwapExactETHForTokensSupportingFee, encodeSwapExactTokensForETHSupportingFee, encodeSwapExactETHForTokensV3, encodeSwapExactTokensForETHV3, DexQuery, } from './dex.js';
|
|
19
|
-
import { FLAP_PORTAL, WOKB, XLAYER_CHAIN_ID, DEFAULT_RPC_URL, ENTRYPOINT_V06, SIMPLE_ACCOUNT_FACTORY, PARTICLE_BUNDLER_URL, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, } from './constants.js';
|
|
20
|
-
import { PROFIT_CONFIG } from '../utils/constants.js';
|
|
19
|
+
import { FLAP_PORTAL, WOKB, XLAYER_CHAIN_ID, DEFAULT_RPC_URL, ENTRYPOINT_V06, SIMPLE_ACCOUNT_FACTORY, PARTICLE_BUNDLER_URL, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, POTATOSWAP_V3_FACTORY, } from './constants.js';
|
|
20
|
+
import { PROFIT_CONFIG, ZERO_ADDRESS } from '../utils/constants.js';
|
|
21
21
|
import { mapWithConcurrency } from '../utils/concurrency.js';
|
|
22
22
|
// ============================================================================
|
|
23
|
+
// V3 报价工具函数
|
|
24
|
+
// ============================================================================
|
|
25
|
+
const V3_FACTORY_ABI = [
|
|
26
|
+
'function getPool(address tokenA, address tokenB, uint24 fee) view returns (address pool)',
|
|
27
|
+
];
|
|
28
|
+
const V3_POOL_ABI = [
|
|
29
|
+
'function slot0() view returns (uint160 sqrtPriceX96,int24 tick,uint16 observationIndex,uint16 observationCardinality,uint16 observationCardinalityNext,uint8 feeProtocol,bool unlocked)',
|
|
30
|
+
'function token0() view returns (address)',
|
|
31
|
+
'function token1() view returns (address)',
|
|
32
|
+
];
|
|
33
|
+
const V3_FEE_DENOMINATOR = 1000000n;
|
|
34
|
+
/**
|
|
35
|
+
* 使用 V3 Pool 的 slot0 获取 WOKB → Token 的报价(买入方向)
|
|
36
|
+
*/
|
|
37
|
+
async function quoteV3BuyViaSlot0(params) {
|
|
38
|
+
try {
|
|
39
|
+
const { rpcUrl, tokenAddress, wokbAmount, fee } = params;
|
|
40
|
+
if (!tokenAddress || wokbAmount <= 0n)
|
|
41
|
+
return 0n;
|
|
42
|
+
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
|
43
|
+
const wokbLower = WOKB.toLowerCase();
|
|
44
|
+
const tokenLower = tokenAddress.toLowerCase();
|
|
45
|
+
if (wokbLower === tokenLower)
|
|
46
|
+
return wokbAmount;
|
|
47
|
+
const factory = new ethers.Contract(POTATOSWAP_V3_FACTORY, V3_FACTORY_ABI, provider);
|
|
48
|
+
const poolAddr = await factory.getPool(tokenAddress, WOKB, fee);
|
|
49
|
+
if (!poolAddr || poolAddr.toLowerCase() === ZERO_ADDRESS.toLowerCase())
|
|
50
|
+
return 0n;
|
|
51
|
+
const pool = new ethers.Contract(poolAddr, V3_POOL_ABI, provider);
|
|
52
|
+
const [t0, t1, slot0] = await Promise.all([pool.token0(), pool.token1(), pool.slot0()]);
|
|
53
|
+
if (!t0 || !t1 || !slot0)
|
|
54
|
+
return 0n;
|
|
55
|
+
const sqrtPriceX96 = BigInt(slot0[0]);
|
|
56
|
+
if (sqrtPriceX96 <= 0n)
|
|
57
|
+
return 0n;
|
|
58
|
+
// 扣除手续费
|
|
59
|
+
const amountInLessFee = (wokbAmount * (V3_FEE_DENOMINATOR - BigInt(fee))) / V3_FEE_DENOMINATOR;
|
|
60
|
+
if (amountInLessFee <= 0n)
|
|
61
|
+
return 0n;
|
|
62
|
+
const Q192 = 2n ** 192n;
|
|
63
|
+
const num = sqrtPriceX96 * sqrtPriceX96;
|
|
64
|
+
const t0Lower = String(t0).toLowerCase();
|
|
65
|
+
const t1Lower = String(t1).toLowerCase();
|
|
66
|
+
// sqrtPriceX96 表示 token1/token0 的现货价
|
|
67
|
+
// 买入方向:WOKB → Token
|
|
68
|
+
if (wokbLower === t0Lower && tokenLower === t1Lower) {
|
|
69
|
+
// WOKB 是 token0,Token 是 token1
|
|
70
|
+
// price = token1/token0, 所以 tokenOut = wokbIn * price = wokbIn * num / Q192
|
|
71
|
+
return (amountInLessFee * num) / Q192;
|
|
72
|
+
}
|
|
73
|
+
if (wokbLower === t1Lower && tokenLower === t0Lower) {
|
|
74
|
+
// WOKB 是 token1,Token 是 token0
|
|
75
|
+
// price = token1/token0 = WOKB/Token, 所以 tokenOut = wokbIn / price = wokbIn * Q192 / num
|
|
76
|
+
return (amountInLessFee * Q192) / num;
|
|
77
|
+
}
|
|
78
|
+
return 0n;
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
console.warn('[quoteV3BuyViaSlot0] 报价失败:', e);
|
|
82
|
+
return 0n;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// ============================================================================
|
|
23
86
|
// 工具函数
|
|
24
87
|
// ============================================================================
|
|
25
88
|
/**
|
|
@@ -203,13 +266,18 @@ export async function buildWashOps(params) {
|
|
|
203
266
|
});
|
|
204
267
|
}
|
|
205
268
|
else if (poolType === 'v3') {
|
|
206
|
-
// V3 使用
|
|
207
|
-
const portalQuery = new PortalQuery({ rpcUrl: config.rpcUrl, chainId: config.chainId });
|
|
269
|
+
// ✅ V3 使用 slot0 现货价计算预期代币输出(与 dex-bundle-swap 保持一致)
|
|
208
270
|
expectedTokenAmounts = await mapWithConcurrency(buyWeiList, 4, async (buyWei) => {
|
|
209
271
|
if (buyWei <= 0n)
|
|
210
272
|
return 0n;
|
|
211
273
|
try {
|
|
212
|
-
|
|
274
|
+
const quoted = await quoteV3BuyViaSlot0({
|
|
275
|
+
rpcUrl: config.rpcUrl,
|
|
276
|
+
tokenAddress: params.tokenAddress,
|
|
277
|
+
wokbAmount: buyWei,
|
|
278
|
+
fee: v3Fee,
|
|
279
|
+
});
|
|
280
|
+
return quoted;
|
|
213
281
|
}
|
|
214
282
|
catch {
|
|
215
283
|
return 0n;
|
|
@@ -276,6 +344,7 @@ export async function buildWashOps(params) {
|
|
|
276
344
|
fee: v3Fee,
|
|
277
345
|
recipient: sender, // ✅ 使用已验证的 sender 变量
|
|
278
346
|
unwrapRecipient: sender, // ✅ 使用已验证的 sender 变量
|
|
347
|
+
routerAddress, // ✅ 传递正确的 Router 地址
|
|
279
348
|
deadline,
|
|
280
349
|
amountIn: expectedTokenAmount,
|
|
281
350
|
amountOutMinimum: 0n,
|
|
@@ -314,9 +383,9 @@ export async function buildWashOps(params) {
|
|
|
314
383
|
opType: 'sell',
|
|
315
384
|
});
|
|
316
385
|
}
|
|
317
|
-
// 固定 Gas
|
|
386
|
+
// 固定 Gas(买卖操作支持前端传入)
|
|
318
387
|
const profitCallGasLimit = 120000n;
|
|
319
|
-
const tradeCallGasLimit = 400000n;
|
|
388
|
+
const tradeCallGasLimit = params.tradeGasLimit ?? 400000n;
|
|
320
389
|
// 构造 UserOps
|
|
321
390
|
const built = await mapWithConcurrency(allSkeletons, 20, async (sk) => {
|
|
322
391
|
const callGasLimit = sk.opType === 'profit' ? profitCallGasLimit : tradeCallGasLimit;
|