four-flap-meme-sdk 2.2.15 → 2.2.16
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 +126 -86
- package/package.json +1 -1
|
@@ -122,23 +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
|
+
/** 单次 quote 估 WBNB 深度(市值场景优先速度) */
|
|
126
137
|
async function estimateInfinityPoolQuoteBnbDepth(portal, tokenAddress) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
try {
|
|
139
|
+
const bnbOut = await portal.quoteExactInput({
|
|
140
|
+
inputToken: tokenAddress,
|
|
141
|
+
outputToken: ZERO_ADDRESS,
|
|
142
|
+
inputAmount: 110000000n * 10n ** 18n,
|
|
143
|
+
});
|
|
144
|
+
if (bnbOut > 0n)
|
|
145
|
+
return formatEther(bnbOut);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
130
148
|
try {
|
|
131
149
|
const bnbOut = await portal.quoteExactInput({
|
|
132
150
|
inputToken: tokenAddress,
|
|
133
|
-
outputToken:
|
|
134
|
-
inputAmount:
|
|
151
|
+
outputToken: ZERO_ADDRESS,
|
|
152
|
+
inputAmount: 100000000n * 10n ** 18n,
|
|
135
153
|
});
|
|
136
154
|
if (bnbOut > 0n)
|
|
137
155
|
return formatEther(bnbOut);
|
|
138
156
|
}
|
|
139
|
-
catch {
|
|
140
|
-
/* try smaller */
|
|
141
|
-
}
|
|
157
|
+
catch { /* ignore */ }
|
|
142
158
|
}
|
|
143
159
|
return '0';
|
|
144
160
|
}
|
|
@@ -152,7 +168,7 @@ async function getInfinityCLPoolTokenReserve(params) {
|
|
|
152
168
|
if (!vault)
|
|
153
169
|
return '0';
|
|
154
170
|
try {
|
|
155
|
-
const provider =
|
|
171
|
+
const provider = params.provider ?? getCachedRpcProvider(params.rpcUrl);
|
|
156
172
|
const bal = (await new Contract(params.tokenAddress, ERC20_ABI, provider).balanceOf(vault));
|
|
157
173
|
return formatEther(bal);
|
|
158
174
|
}
|
|
@@ -160,30 +176,19 @@ async function getInfinityCLPoolTokenReserve(params) {
|
|
|
160
176
|
return '0';
|
|
161
177
|
}
|
|
162
178
|
}
|
|
179
|
+
/** 单笔 buy quote 估价(比买卖双 quote 少一次 RPC) */
|
|
163
180
|
async function quotePortalPrice(portal, tokenAddress, quoteTokenAddress, wrappedNativeAddress) {
|
|
164
181
|
try {
|
|
165
182
|
const buyAmt = 10n ** 16n;
|
|
166
|
-
const sellAmt = 10n ** 21n;
|
|
167
183
|
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
|
-
]);
|
|
184
|
+
const tokensOut = await portal.quoteExactInput({
|
|
185
|
+
inputToken: inputQuote,
|
|
186
|
+
outputToken: tokenAddress,
|
|
187
|
+
inputAmount: buyAmt,
|
|
188
|
+
});
|
|
180
189
|
if (tokensOut <= 0n)
|
|
181
190
|
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);
|
|
191
|
+
return String(Number(formatEther(buyAmt)) / Number(formatEther(tokensOut)));
|
|
187
192
|
}
|
|
188
193
|
catch {
|
|
189
194
|
return null;
|
|
@@ -199,18 +204,22 @@ function formatProgress(state) {
|
|
|
199
204
|
}
|
|
200
205
|
async function getGraduatedV3PairMetrics(params) {
|
|
201
206
|
try {
|
|
202
|
-
const provider =
|
|
207
|
+
const provider = getCachedRpcProvider(params.rpcUrl);
|
|
203
208
|
const pool = new Contract(params.poolAddress, V3_PAIR_ABI, provider);
|
|
209
|
+
const tokenLower = params.tokenAddress.toLowerCase();
|
|
210
|
+
const quoteAddr = params.quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
|
|
211
|
+
? params.wrappedNativeAddress
|
|
212
|
+
: params.quoteTokenAddress;
|
|
204
213
|
const [t0, t1, slot0] = await Promise.all([
|
|
205
214
|
pool.token0(),
|
|
206
215
|
pool.token1(),
|
|
207
216
|
pool.slot0(),
|
|
208
217
|
]);
|
|
218
|
+
const [bal0, bal1] = await Promise.all([
|
|
219
|
+
new Contract(t0, ERC20_ABI, provider).balanceOf(params.poolAddress),
|
|
220
|
+
new Contract(t1, ERC20_ABI, provider).balanceOf(params.poolAddress),
|
|
221
|
+
]);
|
|
209
222
|
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
223
|
const spot = v3SpotPriceQuotePerToken({
|
|
215
224
|
sqrtPriceX96,
|
|
216
225
|
tokenAddress: params.tokenAddress,
|
|
@@ -218,13 +227,16 @@ async function getGraduatedV3PairMetrics(params) {
|
|
|
218
227
|
token1: t1,
|
|
219
228
|
quoteTokenAddress: quoteAddr,
|
|
220
229
|
});
|
|
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
230
|
const tokenIs0 = t0.toLowerCase() === tokenLower;
|
|
226
231
|
const poolTokenAmount = formatEther(tokenIs0 ? bal0 : bal1);
|
|
227
232
|
const poolBNBAmount = formatEther(tokenIs0 ? bal1 : bal0);
|
|
233
|
+
let price = spot;
|
|
234
|
+
if (!price && parseFloat(poolTokenAmount) > 0) {
|
|
235
|
+
price = String(parseFloat(poolBNBAmount) / parseFloat(poolTokenAmount));
|
|
236
|
+
}
|
|
237
|
+
if (!price) {
|
|
238
|
+
price = await quotePortalPrice(params.portal, params.tokenAddress, params.quoteTokenAddress, params.wrappedNativeAddress);
|
|
239
|
+
}
|
|
228
240
|
if (!price && poolTokenAmount === '0' && poolBNBAmount === '0')
|
|
229
241
|
return null;
|
|
230
242
|
return {
|
|
@@ -241,60 +253,65 @@ async function getGraduatedV3PairMetrics(params) {
|
|
|
241
253
|
}
|
|
242
254
|
async function getInfinityCLGraduatedMetrics(params) {
|
|
243
255
|
const progress = formatProgress(params.state);
|
|
244
|
-
|
|
256
|
+
const provider = getCachedRpcProvider(params.rpcUrl);
|
|
245
257
|
let poolBNBAmount = '0';
|
|
246
258
|
let poolTokenAmount = '0';
|
|
247
259
|
let poolId;
|
|
248
260
|
let infinityFee;
|
|
261
|
+
let price = null;
|
|
249
262
|
const poolKey = buildFlapInfinityPoolKey({
|
|
250
263
|
tokenAddress: params.tokenAddress,
|
|
251
264
|
quoteTokenAddress: params.quoteTokenAddress,
|
|
252
265
|
chain: params.chain,
|
|
253
266
|
lpFeeProfile: params.state.lpFeeProfile,
|
|
254
267
|
});
|
|
255
|
-
|
|
268
|
+
const quoteAddr = params.quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
|
|
269
|
+
? params.wrappedNativeAddress
|
|
270
|
+
: params.quoteTokenAddress;
|
|
271
|
+
const slot0Promise = (async () => {
|
|
272
|
+
if (!poolKey)
|
|
273
|
+
return null;
|
|
256
274
|
poolId = computeInfinityPoolId(poolKey);
|
|
257
275
|
infinityFee = poolKey.fee;
|
|
258
276
|
try {
|
|
259
|
-
const provider = new JsonRpcProvider(params.rpcUrl);
|
|
260
277
|
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
|
-
}
|
|
278
|
+
const slot0 = await manager.getSlot0(poolId);
|
|
279
|
+
return BigInt(slot0[0]);
|
|
277
280
|
}
|
|
278
281
|
catch {
|
|
279
|
-
|
|
282
|
+
return null;
|
|
280
283
|
}
|
|
281
|
-
}
|
|
282
|
-
const [
|
|
283
|
-
|
|
284
|
+
})();
|
|
285
|
+
const [sqrtPriceX96, tokenReserve, bnbDepth] = await Promise.all([
|
|
286
|
+
slot0Promise,
|
|
284
287
|
getInfinityCLPoolTokenReserve({
|
|
285
288
|
tokenAddress: params.tokenAddress,
|
|
286
289
|
rpcUrl: params.rpcUrl,
|
|
287
290
|
chain: params.chain,
|
|
291
|
+
provider,
|
|
288
292
|
}),
|
|
293
|
+
estimateInfinityPoolQuoteBnbDepth(params.portal, params.tokenAddress),
|
|
289
294
|
]);
|
|
290
|
-
if (
|
|
291
|
-
|
|
295
|
+
if (sqrtPriceX96 && sqrtPriceX96 > 0n && poolKey) {
|
|
296
|
+
const spot = infinitySpotPriceQuotePerToken({
|
|
297
|
+
sqrtPriceX96,
|
|
298
|
+
tokenAddress: params.tokenAddress,
|
|
299
|
+
currency0: poolKey.currency0,
|
|
300
|
+
currency1: poolKey.currency1,
|
|
301
|
+
quoteTokenAddress: quoteAddr,
|
|
302
|
+
});
|
|
303
|
+
if (spot)
|
|
304
|
+
price = spot;
|
|
305
|
+
}
|
|
292
306
|
if (parseFloat(tokenReserve) > 0)
|
|
293
307
|
poolTokenAmount = tokenReserve;
|
|
294
|
-
if (
|
|
295
|
-
|
|
308
|
+
if (parseFloat(bnbDepth) > 0)
|
|
309
|
+
poolBNBAmount = bnbDepth;
|
|
310
|
+
if (!price) {
|
|
311
|
+
price = await quotePortalPrice(params.portal, params.tokenAddress, params.quoteTokenAddress, params.wrappedNativeAddress);
|
|
312
|
+
}
|
|
296
313
|
return {
|
|
297
|
-
price,
|
|
314
|
+
price: price ?? '0',
|
|
298
315
|
progress,
|
|
299
316
|
poolBNBAmount,
|
|
300
317
|
poolTokenAmount,
|
|
@@ -311,14 +328,20 @@ async function getInfinityCLGraduatedMetrics(params) {
|
|
|
311
328
|
*/
|
|
312
329
|
export async function getFlapGraduatedDexMetrics(params) {
|
|
313
330
|
const chain = String(params.chain || 'BSC').toUpperCase();
|
|
331
|
+
const tokenKey = `${chain}:${params.tokenAddress.toLowerCase()}`;
|
|
332
|
+
const cached = gradMetricsCache.get(tokenKey);
|
|
333
|
+
if (cached && Date.now() - cached.at < GRAD_METRICS_TTL_MS) {
|
|
334
|
+
return cached.data;
|
|
335
|
+
}
|
|
314
336
|
const wrappedNative = params.wrappedNativeAddress ?? '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
315
337
|
const quoteToken = params.state.quoteTokenAddress &&
|
|
316
338
|
params.state.quoteTokenAddress.toLowerCase() !== ZERO_ADDRESS
|
|
317
339
|
? params.state.quoteTokenAddress
|
|
318
340
|
: wrappedNative;
|
|
319
341
|
const progress = formatProgress(params.state);
|
|
342
|
+
let data;
|
|
320
343
|
if (isFlapInfinityCLGraduated(params.state, chain)) {
|
|
321
|
-
|
|
344
|
+
data = await getInfinityCLGraduatedMetrics({
|
|
322
345
|
portal: params.portal,
|
|
323
346
|
tokenAddress: params.tokenAddress,
|
|
324
347
|
state: params.state,
|
|
@@ -328,25 +351,42 @@ export async function getFlapGraduatedDexMetrics(params) {
|
|
|
328
351
|
chain,
|
|
329
352
|
});
|
|
330
353
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
354
|
+
else {
|
|
355
|
+
const pool = params.state.pool;
|
|
356
|
+
if (pool && pool.toLowerCase() !== ZERO_ADDRESS && !isInfinityCLPoolManagerAddress(pool, chain)) {
|
|
357
|
+
const v3 = await getGraduatedV3PairMetrics({
|
|
358
|
+
rpcUrl: params.rpcUrl,
|
|
359
|
+
tokenAddress: params.tokenAddress,
|
|
360
|
+
poolAddress: pool,
|
|
361
|
+
quoteTokenAddress: quoteToken,
|
|
362
|
+
wrappedNativeAddress: wrappedNative,
|
|
363
|
+
portal: params.portal,
|
|
364
|
+
});
|
|
365
|
+
if (v3) {
|
|
366
|
+
data = v3;
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
const price = await quotePortalPrice(params.portal, params.tokenAddress, quoteToken, wrappedNative);
|
|
370
|
+
data = {
|
|
371
|
+
price: price ?? '0',
|
|
372
|
+
progress,
|
|
373
|
+
poolBNBAmount: '0',
|
|
374
|
+
poolTokenAmount: '0',
|
|
375
|
+
dexKind: 'PORTAL_QUOTE',
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
const price = await quotePortalPrice(params.portal, params.tokenAddress, quoteToken, wrappedNative);
|
|
381
|
+
data = {
|
|
382
|
+
price: price ?? '0',
|
|
383
|
+
progress,
|
|
384
|
+
poolBNBAmount: '0',
|
|
385
|
+
poolTokenAmount: '0',
|
|
386
|
+
dexKind: 'PORTAL_QUOTE',
|
|
387
|
+
};
|
|
388
|
+
}
|
|
343
389
|
}
|
|
344
|
-
|
|
345
|
-
return
|
|
346
|
-
price: price ?? '0',
|
|
347
|
-
progress,
|
|
348
|
-
poolBNBAmount: '0',
|
|
349
|
-
poolTokenAmount: '0',
|
|
350
|
-
dexKind: 'PORTAL_QUOTE',
|
|
351
|
-
};
|
|
390
|
+
gradMetricsCache.set(tokenKey, { at: Date.now(), data });
|
|
391
|
+
return data;
|
|
352
392
|
}
|