four-flap-meme-sdk 2.2.15 → 2.2.17
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/utils/pcs-infinity-cl.js +124 -86
- package/package.json +1 -1
|
@@ -122,25 +122,39 @@ function v3SpotPriceQuotePerToken(params) {
|
|
|
122
122
|
quoteTokenAddress: params.quoteTokenAddress,
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
|
-
|
|
125
|
+
const providerByRpc = new Map();
|
|
126
|
+
const gradMetricsCache = new Map();
|
|
127
|
+
const GRAD_METRICS_TTL_MS = 2500;
|
|
128
|
+
function getCachedRpcProvider(rpcUrl) {
|
|
129
|
+
let p = providerByRpc.get(rpcUrl);
|
|
130
|
+
if (!p) {
|
|
131
|
+
p = new JsonRpcProvider(rpcUrl);
|
|
132
|
+
providerByRpc.set(rpcUrl, p);
|
|
133
|
+
}
|
|
134
|
+
return p;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Infinity CL 池子 WBNB:取多档卖出 quote 的最大值(贴近 GMGN「池内 WBNB」)
|
|
138
|
+
* 说明:不是 Vault 总 WBNB(全协议),也不是 L*sqrtP 虚拟储备(会高估到 ~5.8)。
|
|
139
|
+
*/
|
|
126
140
|
async function estimateInfinityPoolQuoteBnbDepth(portal, tokenAddress) {
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
for (const amt of
|
|
141
|
+
const probes = [107000000n, 105000000n, 100000000n, 90000000n].map((m) => m * 10n ** 18n);
|
|
142
|
+
let best = 0n;
|
|
143
|
+
for (const amt of probes) {
|
|
130
144
|
try {
|
|
131
145
|
const bnbOut = await portal.quoteExactInput({
|
|
132
146
|
inputToken: tokenAddress,
|
|
133
|
-
outputToken:
|
|
147
|
+
outputToken: ZERO_ADDRESS,
|
|
134
148
|
inputAmount: amt,
|
|
135
149
|
});
|
|
136
|
-
if (bnbOut >
|
|
137
|
-
|
|
150
|
+
if (bnbOut > best)
|
|
151
|
+
best = bnbOut;
|
|
138
152
|
}
|
|
139
153
|
catch {
|
|
140
|
-
/*
|
|
154
|
+
/* 超过可成交上限会 revert,继续试更小档位 */
|
|
141
155
|
}
|
|
142
156
|
}
|
|
143
|
-
return '0';
|
|
157
|
+
return best > 0n ? formatEther(best) : '0';
|
|
144
158
|
}
|
|
145
159
|
/**
|
|
146
160
|
* Infinity CL 池内代币量:读 Vault 中该 token 余额(与 GMGN 池子代币列一致)
|
|
@@ -152,7 +166,7 @@ async function getInfinityCLPoolTokenReserve(params) {
|
|
|
152
166
|
if (!vault)
|
|
153
167
|
return '0';
|
|
154
168
|
try {
|
|
155
|
-
const provider =
|
|
169
|
+
const provider = params.provider ?? getCachedRpcProvider(params.rpcUrl);
|
|
156
170
|
const bal = (await new Contract(params.tokenAddress, ERC20_ABI, provider).balanceOf(vault));
|
|
157
171
|
return formatEther(bal);
|
|
158
172
|
}
|
|
@@ -160,30 +174,19 @@ async function getInfinityCLPoolTokenReserve(params) {
|
|
|
160
174
|
return '0';
|
|
161
175
|
}
|
|
162
176
|
}
|
|
177
|
+
/** 单笔 buy quote 估价(比买卖双 quote 少一次 RPC) */
|
|
163
178
|
async function quotePortalPrice(portal, tokenAddress, quoteTokenAddress, wrappedNativeAddress) {
|
|
164
179
|
try {
|
|
165
180
|
const buyAmt = 10n ** 16n;
|
|
166
|
-
const sellAmt = 10n ** 21n;
|
|
167
181
|
const inputQuote = resolvePortalQuoteInputToken(quoteTokenAddress, wrappedNativeAddress);
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}),
|
|
174
|
-
portal.quoteExactInput({
|
|
175
|
-
inputToken: tokenAddress,
|
|
176
|
-
outputToken: inputQuote,
|
|
177
|
-
inputAmount: sellAmt,
|
|
178
|
-
}).catch(() => 0n),
|
|
179
|
-
]);
|
|
182
|
+
const tokensOut = await portal.quoteExactInput({
|
|
183
|
+
inputToken: inputQuote,
|
|
184
|
+
outputToken: tokenAddress,
|
|
185
|
+
inputAmount: buyAmt,
|
|
186
|
+
});
|
|
180
187
|
if (tokensOut <= 0n)
|
|
181
188
|
return null;
|
|
182
|
-
|
|
183
|
-
if (quoteOut <= 0n)
|
|
184
|
-
return String(buyPrice);
|
|
185
|
-
const sellPrice = Number(formatEther(quoteOut)) / Number(formatEther(sellAmt));
|
|
186
|
-
return String((buyPrice + sellPrice) / 2);
|
|
189
|
+
return String(Number(formatEther(buyAmt)) / Number(formatEther(tokensOut)));
|
|
187
190
|
}
|
|
188
191
|
catch {
|
|
189
192
|
return null;
|
|
@@ -199,18 +202,22 @@ function formatProgress(state) {
|
|
|
199
202
|
}
|
|
200
203
|
async function getGraduatedV3PairMetrics(params) {
|
|
201
204
|
try {
|
|
202
|
-
const provider =
|
|
205
|
+
const provider = getCachedRpcProvider(params.rpcUrl);
|
|
203
206
|
const pool = new Contract(params.poolAddress, V3_PAIR_ABI, provider);
|
|
207
|
+
const tokenLower = params.tokenAddress.toLowerCase();
|
|
208
|
+
const quoteAddr = params.quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
|
|
209
|
+
? params.wrappedNativeAddress
|
|
210
|
+
: params.quoteTokenAddress;
|
|
204
211
|
const [t0, t1, slot0] = await Promise.all([
|
|
205
212
|
pool.token0(),
|
|
206
213
|
pool.token1(),
|
|
207
214
|
pool.slot0(),
|
|
208
215
|
]);
|
|
216
|
+
const [bal0, bal1] = await Promise.all([
|
|
217
|
+
new Contract(t0, ERC20_ABI, provider).balanceOf(params.poolAddress),
|
|
218
|
+
new Contract(t1, ERC20_ABI, provider).balanceOf(params.poolAddress),
|
|
219
|
+
]);
|
|
209
220
|
const sqrtPriceX96 = BigInt(Array.isArray(slot0) ? slot0[0] : slot0.sqrtPriceX96);
|
|
210
|
-
const tokenLower = params.tokenAddress.toLowerCase();
|
|
211
|
-
const quoteAddr = params.quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
|
|
212
|
-
? params.wrappedNativeAddress
|
|
213
|
-
: params.quoteTokenAddress;
|
|
214
221
|
const spot = v3SpotPriceQuotePerToken({
|
|
215
222
|
sqrtPriceX96,
|
|
216
223
|
tokenAddress: params.tokenAddress,
|
|
@@ -218,13 +225,16 @@ async function getGraduatedV3PairMetrics(params) {
|
|
|
218
225
|
token1: t1,
|
|
219
226
|
quoteTokenAddress: quoteAddr,
|
|
220
227
|
});
|
|
221
|
-
const price = spot ??
|
|
222
|
-
(await quotePortalPrice(params.portal, params.tokenAddress, params.quoteTokenAddress, params.wrappedNativeAddress));
|
|
223
|
-
const bal0 = (await new Contract(t0, ERC20_ABI, provider).balanceOf(params.poolAddress));
|
|
224
|
-
const bal1 = (await new Contract(t1, ERC20_ABI, provider).balanceOf(params.poolAddress));
|
|
225
228
|
const tokenIs0 = t0.toLowerCase() === tokenLower;
|
|
226
229
|
const poolTokenAmount = formatEther(tokenIs0 ? bal0 : bal1);
|
|
227
230
|
const poolBNBAmount = formatEther(tokenIs0 ? bal1 : bal0);
|
|
231
|
+
let price = spot;
|
|
232
|
+
if (!price && parseFloat(poolTokenAmount) > 0) {
|
|
233
|
+
price = String(parseFloat(poolBNBAmount) / parseFloat(poolTokenAmount));
|
|
234
|
+
}
|
|
235
|
+
if (!price) {
|
|
236
|
+
price = await quotePortalPrice(params.portal, params.tokenAddress, params.quoteTokenAddress, params.wrappedNativeAddress);
|
|
237
|
+
}
|
|
228
238
|
if (!price && poolTokenAmount === '0' && poolBNBAmount === '0')
|
|
229
239
|
return null;
|
|
230
240
|
return {
|
|
@@ -241,60 +251,65 @@ async function getGraduatedV3PairMetrics(params) {
|
|
|
241
251
|
}
|
|
242
252
|
async function getInfinityCLGraduatedMetrics(params) {
|
|
243
253
|
const progress = formatProgress(params.state);
|
|
244
|
-
|
|
254
|
+
const provider = getCachedRpcProvider(params.rpcUrl);
|
|
245
255
|
let poolBNBAmount = '0';
|
|
246
256
|
let poolTokenAmount = '0';
|
|
247
257
|
let poolId;
|
|
248
258
|
let infinityFee;
|
|
259
|
+
let price = null;
|
|
249
260
|
const poolKey = buildFlapInfinityPoolKey({
|
|
250
261
|
tokenAddress: params.tokenAddress,
|
|
251
262
|
quoteTokenAddress: params.quoteTokenAddress,
|
|
252
263
|
chain: params.chain,
|
|
253
264
|
lpFeeProfile: params.state.lpFeeProfile,
|
|
254
265
|
});
|
|
255
|
-
|
|
266
|
+
const quoteAddr = params.quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
|
|
267
|
+
? params.wrappedNativeAddress
|
|
268
|
+
: params.quoteTokenAddress;
|
|
269
|
+
const slot0Promise = (async () => {
|
|
270
|
+
if (!poolKey)
|
|
271
|
+
return null;
|
|
256
272
|
poolId = computeInfinityPoolId(poolKey);
|
|
257
273
|
infinityFee = poolKey.fee;
|
|
258
274
|
try {
|
|
259
|
-
const provider = new JsonRpcProvider(params.rpcUrl);
|
|
260
275
|
const manager = new Contract(poolKey.poolManager, CL_POOL_MANAGER_ABI, provider);
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
if (sqrtPriceX96 > 0n) {
|
|
264
|
-
const quoteAddr = params.quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
|
|
265
|
-
? params.wrappedNativeAddress
|
|
266
|
-
: params.quoteTokenAddress;
|
|
267
|
-
const spot = infinitySpotPriceQuotePerToken({
|
|
268
|
-
sqrtPriceX96,
|
|
269
|
-
tokenAddress: params.tokenAddress,
|
|
270
|
-
currency0: poolKey.currency0,
|
|
271
|
-
currency1: poolKey.currency1,
|
|
272
|
-
quoteTokenAddress: quoteAddr,
|
|
273
|
-
});
|
|
274
|
-
if (spot)
|
|
275
|
-
price = spot;
|
|
276
|
-
}
|
|
276
|
+
const slot0 = await manager.getSlot0(poolId);
|
|
277
|
+
return BigInt(slot0[0]);
|
|
277
278
|
}
|
|
278
279
|
catch {
|
|
279
|
-
|
|
280
|
+
return null;
|
|
280
281
|
}
|
|
281
|
-
}
|
|
282
|
-
const [
|
|
283
|
-
|
|
282
|
+
})();
|
|
283
|
+
const [sqrtPriceX96, tokenReserve, bnbDepth] = await Promise.all([
|
|
284
|
+
slot0Promise,
|
|
284
285
|
getInfinityCLPoolTokenReserve({
|
|
285
286
|
tokenAddress: params.tokenAddress,
|
|
286
287
|
rpcUrl: params.rpcUrl,
|
|
287
288
|
chain: params.chain,
|
|
289
|
+
provider,
|
|
288
290
|
}),
|
|
291
|
+
estimateInfinityPoolQuoteBnbDepth(params.portal, params.tokenAddress),
|
|
289
292
|
]);
|
|
290
|
-
if (
|
|
291
|
-
|
|
293
|
+
if (sqrtPriceX96 && sqrtPriceX96 > 0n && poolKey) {
|
|
294
|
+
const spot = infinitySpotPriceQuotePerToken({
|
|
295
|
+
sqrtPriceX96,
|
|
296
|
+
tokenAddress: params.tokenAddress,
|
|
297
|
+
currency0: poolKey.currency0,
|
|
298
|
+
currency1: poolKey.currency1,
|
|
299
|
+
quoteTokenAddress: quoteAddr,
|
|
300
|
+
});
|
|
301
|
+
if (spot)
|
|
302
|
+
price = spot;
|
|
303
|
+
}
|
|
292
304
|
if (parseFloat(tokenReserve) > 0)
|
|
293
305
|
poolTokenAmount = tokenReserve;
|
|
294
|
-
if (
|
|
295
|
-
|
|
306
|
+
if (parseFloat(bnbDepth) > 0)
|
|
307
|
+
poolBNBAmount = bnbDepth;
|
|
308
|
+
if (!price) {
|
|
309
|
+
price = await quotePortalPrice(params.portal, params.tokenAddress, params.quoteTokenAddress, params.wrappedNativeAddress);
|
|
310
|
+
}
|
|
296
311
|
return {
|
|
297
|
-
price,
|
|
312
|
+
price: price ?? '0',
|
|
298
313
|
progress,
|
|
299
314
|
poolBNBAmount,
|
|
300
315
|
poolTokenAmount,
|
|
@@ -311,14 +326,20 @@ async function getInfinityCLGraduatedMetrics(params) {
|
|
|
311
326
|
*/
|
|
312
327
|
export async function getFlapGraduatedDexMetrics(params) {
|
|
313
328
|
const chain = String(params.chain || 'BSC').toUpperCase();
|
|
329
|
+
const tokenKey = `${chain}:${params.tokenAddress.toLowerCase()}`;
|
|
330
|
+
const cached = gradMetricsCache.get(tokenKey);
|
|
331
|
+
if (cached && Date.now() - cached.at < GRAD_METRICS_TTL_MS) {
|
|
332
|
+
return cached.data;
|
|
333
|
+
}
|
|
314
334
|
const wrappedNative = params.wrappedNativeAddress ?? '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
315
335
|
const quoteToken = params.state.quoteTokenAddress &&
|
|
316
336
|
params.state.quoteTokenAddress.toLowerCase() !== ZERO_ADDRESS
|
|
317
337
|
? params.state.quoteTokenAddress
|
|
318
338
|
: wrappedNative;
|
|
319
339
|
const progress = formatProgress(params.state);
|
|
340
|
+
let data;
|
|
320
341
|
if (isFlapInfinityCLGraduated(params.state, chain)) {
|
|
321
|
-
|
|
342
|
+
data = await getInfinityCLGraduatedMetrics({
|
|
322
343
|
portal: params.portal,
|
|
323
344
|
tokenAddress: params.tokenAddress,
|
|
324
345
|
state: params.state,
|
|
@@ -328,25 +349,42 @@ export async function getFlapGraduatedDexMetrics(params) {
|
|
|
328
349
|
chain,
|
|
329
350
|
});
|
|
330
351
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
352
|
+
else {
|
|
353
|
+
const pool = params.state.pool;
|
|
354
|
+
if (pool && pool.toLowerCase() !== ZERO_ADDRESS && !isInfinityCLPoolManagerAddress(pool, chain)) {
|
|
355
|
+
const v3 = await getGraduatedV3PairMetrics({
|
|
356
|
+
rpcUrl: params.rpcUrl,
|
|
357
|
+
tokenAddress: params.tokenAddress,
|
|
358
|
+
poolAddress: pool,
|
|
359
|
+
quoteTokenAddress: quoteToken,
|
|
360
|
+
wrappedNativeAddress: wrappedNative,
|
|
361
|
+
portal: params.portal,
|
|
362
|
+
});
|
|
363
|
+
if (v3) {
|
|
364
|
+
data = v3;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
const price = await quotePortalPrice(params.portal, params.tokenAddress, quoteToken, wrappedNative);
|
|
368
|
+
data = {
|
|
369
|
+
price: price ?? '0',
|
|
370
|
+
progress,
|
|
371
|
+
poolBNBAmount: '0',
|
|
372
|
+
poolTokenAmount: '0',
|
|
373
|
+
dexKind: 'PORTAL_QUOTE',
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
const price = await quotePortalPrice(params.portal, params.tokenAddress, quoteToken, wrappedNative);
|
|
379
|
+
data = {
|
|
380
|
+
price: price ?? '0',
|
|
381
|
+
progress,
|
|
382
|
+
poolBNBAmount: '0',
|
|
383
|
+
poolTokenAmount: '0',
|
|
384
|
+
dexKind: 'PORTAL_QUOTE',
|
|
385
|
+
};
|
|
386
|
+
}
|
|
343
387
|
}
|
|
344
|
-
|
|
345
|
-
return
|
|
346
|
-
price: price ?? '0',
|
|
347
|
-
progress,
|
|
348
|
-
poolBNBAmount: '0',
|
|
349
|
-
poolTokenAmount: '0',
|
|
350
|
-
dexKind: 'PORTAL_QUOTE',
|
|
351
|
-
};
|
|
388
|
+
gradMetricsCache.set(tokenKey, { at: Date.now(), data });
|
|
389
|
+
return data;
|
|
352
390
|
}
|