four-flap-meme-sdk 2.2.13 → 2.2.15
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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/shared/flap/graduated-dex.d.ts +1 -1
- package/dist/shared/flap/graduated-dex.js +1 -1
- package/dist/utils/lp-inspect.js +46 -35
- package/dist/utils/pcs-infinity-cl.d.ts +25 -22
- package/dist/utils/pcs-infinity-cl.js +208 -80
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export { TM2, type FourChainV2 } from './contracts/tm2.js';
|
|
|
15
15
|
export { Helper3, Helper3Writer } from './contracts/helper3.js';
|
|
16
16
|
export { CDPV2 } from './shared/flap/curve.js';
|
|
17
17
|
export { FlapPortal, FlapPortalWriter, type FlapChain, type PortalConfig, type TokenStateV2, type TokenStateV3, type TokenStateV4, type TokenStateV5, type TokenStateV7, type QuoteExactInputParams, type ExactInputParams, type ExactInputV3Params, type NewTokenV3Params, type NewTokenV4Params, type NewTokenV5Params, type TaxDistributionConfig, validateTaxDistribution, TokenStatus, TokenVersion, FeeType, DexThreshType, MigratorType, V3LPFeeProfile, lpFeeProfileToV3Fee, DEXId } from './shared/flap/portal.js';
|
|
18
|
-
export { getFlapGraduatedDexMetrics, buildFlapInfinityPoolKey, computeInfinityPoolId, isInfinityCLPoolManagerAddress, PCS_INFINITY_CL_POOL_MANAGER_BSC, FLAP_INFINITY_HOOKS_BSC, type GraduatedDexMetrics, type InfinityPoolKey, } from './shared/flap/graduated-dex.js';
|
|
18
|
+
export { getFlapGraduatedDexMetrics, buildFlapInfinityPoolKey, computeInfinityPoolId, isInfinityCLPoolManagerAddress, isFlapInfinityCLGraduated, resolvePortalQuoteInputToken, PCS_INFINITY_CL_POOL_MANAGER_BSC, PCS_INFINITY_VAULT_BSC, FLAP_INFINITY_HOOKS_BSC, type GraduatedDexMetrics, type InfinityPoolKey, } from './shared/flap/graduated-dex.js';
|
|
19
19
|
export { uploadTokenMeta, type TokenMetaInput } from './shared/flap/ipfs.js';
|
|
20
20
|
export { buildPermitPiggybackAuto } from './shared/flap/permit.js';
|
|
21
21
|
export { predictVanityTokenAddressByChain, findSaltEndingByChain } from './shared/flap/vanity.js';
|
package/dist/index.js
CHANGED
|
@@ -48,7 +48,7 @@ export { TM2 } from './contracts/tm2.js';
|
|
|
48
48
|
export { Helper3, Helper3Writer } from './contracts/helper3.js';
|
|
49
49
|
export { CDPV2 } from './shared/flap/curve.js';
|
|
50
50
|
export { FlapPortal, FlapPortalWriter, validateTaxDistribution, TokenStatus, TokenVersion, FeeType, DexThreshType, MigratorType, V3LPFeeProfile, lpFeeProfileToV3Fee, DEXId } from './shared/flap/portal.js';
|
|
51
|
-
export { getFlapGraduatedDexMetrics, buildFlapInfinityPoolKey, computeInfinityPoolId, isInfinityCLPoolManagerAddress, PCS_INFINITY_CL_POOL_MANAGER_BSC, FLAP_INFINITY_HOOKS_BSC, } from './shared/flap/graduated-dex.js';
|
|
51
|
+
export { getFlapGraduatedDexMetrics, buildFlapInfinityPoolKey, computeInfinityPoolId, isInfinityCLPoolManagerAddress, isFlapInfinityCLGraduated, resolvePortalQuoteInputToken, PCS_INFINITY_CL_POOL_MANAGER_BSC, PCS_INFINITY_VAULT_BSC, FLAP_INFINITY_HOOKS_BSC, } from './shared/flap/graduated-dex.js';
|
|
52
52
|
export { uploadTokenMeta } from './shared/flap/ipfs.js';
|
|
53
53
|
export { buildPermitPiggybackAuto } from './shared/flap/permit.js';
|
|
54
54
|
export { predictVanityTokenAddressByChain, findSaltEndingByChain } from './shared/flap/vanity.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { getFlapGraduatedDexMetrics, buildFlapInfinityPoolKey, computeInfinityPoolId, isInfinityCLPoolManagerAddress, PCS_INFINITY_CL_POOL_MANAGER_BSC, FLAP_INFINITY_HOOKS_BSC, type GraduatedDexMetrics, type InfinityPoolKey, } from '../../utils/pcs-infinity-cl.js';
|
|
1
|
+
export { getFlapGraduatedDexMetrics, buildFlapInfinityPoolKey, computeInfinityPoolId, isInfinityCLPoolManagerAddress, isFlapInfinityCLGraduated, resolvePortalQuoteInputToken, PCS_INFINITY_CL_POOL_MANAGER_BSC, PCS_INFINITY_VAULT_BSC, FLAP_INFINITY_HOOKS_BSC, type GraduatedDexMetrics, type InfinityPoolKey, } from '../../utils/pcs-infinity-cl.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { getFlapGraduatedDexMetrics, buildFlapInfinityPoolKey, computeInfinityPoolId, isInfinityCLPoolManagerAddress, PCS_INFINITY_CL_POOL_MANAGER_BSC, FLAP_INFINITY_HOOKS_BSC, } from '../../utils/pcs-infinity-cl.js';
|
|
1
|
+
export { getFlapGraduatedDexMetrics, buildFlapInfinityPoolKey, computeInfinityPoolId, isInfinityCLPoolManagerAddress, isFlapInfinityCLGraduated, resolvePortalQuoteInputToken, PCS_INFINITY_CL_POOL_MANAGER_BSC, PCS_INFINITY_VAULT_BSC, FLAP_INFINITY_HOOKS_BSC, } from '../../utils/pcs-infinity-cl.js';
|
package/dist/utils/lp-inspect.js
CHANGED
|
@@ -3,7 +3,7 @@ import { ADDRESSES, ZERO_ADDRESS } from './constants.js';
|
|
|
3
3
|
import { MULTICALL3_ABI, V2_FACTORY_ABI, V2_PAIR_ABI, V3_FACTORY_ABI, ERC20_ABI } from '../shared/abis/common.js';
|
|
4
4
|
import { Helper3 } from '../contracts/helper3.js';
|
|
5
5
|
import { FlapPortal } from '../shared/flap/portal.js';
|
|
6
|
-
import { getFlapGraduatedDexMetrics, PCS_INFINITY_CL_POOL_MANAGER_BSC, } from './pcs-infinity-cl.js';
|
|
6
|
+
import { getFlapGraduatedDexMetrics, PCS_INFINITY_CL_POOL_MANAGER_BSC, isFlapInfinityCLGraduated, } from './pcs-infinity-cl.js';
|
|
7
7
|
// ============================================================================
|
|
8
8
|
// 链配置
|
|
9
9
|
// ============================================================================
|
|
@@ -363,7 +363,7 @@ export async function inspectTokenLP(token, opts) {
|
|
|
363
363
|
}
|
|
364
364
|
if (st && st.status !== undefined) {
|
|
365
365
|
const status = Number(st.status);
|
|
366
|
-
// ✅
|
|
366
|
+
// ✅ 已毕业:按池类型分流(Infinity CL / Pancake V3 pair / 继续扫外盘)
|
|
367
367
|
if (status === 4) {
|
|
368
368
|
const quoteAddr = st.quoteTokenAddress && st.quoteTokenAddress.toLowerCase() !== ZERO_ADDRESS
|
|
369
369
|
? st.quoteTokenAddress
|
|
@@ -381,40 +381,51 @@ export async function inspectTokenLP(token, opts) {
|
|
|
381
381
|
state: st,
|
|
382
382
|
rpcUrl: opts.rpcUrl,
|
|
383
383
|
wrappedNativeAddress: chainConfig.wrappedNative,
|
|
384
|
+
chain: opts.chain,
|
|
384
385
|
});
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
386
|
+
const hasMetrics = (metrics.price && metrics.price !== '0') ||
|
|
387
|
+
parseFloat(metrics.poolBNBAmount || '0') > 0;
|
|
388
|
+
if (hasMetrics) {
|
|
389
|
+
result.platform = 'FLAP';
|
|
390
|
+
result.flap = {
|
|
391
|
+
quoteToken: quoteAddr,
|
|
392
|
+
quoteSymbol,
|
|
393
|
+
quoteDecimals,
|
|
394
|
+
reserveNative: metrics.poolBNBAmount,
|
|
395
|
+
circulatingSupply: formatBalance(st.circulatingSupply, shouldFormat, tokenDecimals ?? 18),
|
|
396
|
+
price: metrics.price,
|
|
397
|
+
taxRate,
|
|
398
|
+
};
|
|
399
|
+
if (taxRate && taxRate > 0)
|
|
400
|
+
result.taxRate = taxRate;
|
|
401
|
+
const liqRaw = metrics.poolBNBAmount
|
|
402
|
+
? BigInt(Math.floor(parseFloat(metrics.poolBNBAmount) * 1e18))
|
|
403
|
+
: 0n;
|
|
404
|
+
const tokRaw = metrics.poolTokenAmount
|
|
405
|
+
? BigInt(Math.floor(parseFloat(metrics.poolTokenAmount) * 1e18))
|
|
406
|
+
: 0n;
|
|
407
|
+
const isInfinity = isFlapInfinityCLGraduated(st, opts.chain);
|
|
408
|
+
result.bestPools.push({
|
|
409
|
+
dex: isInfinity ? 'PancakeSwap Infinity CL' : 'PancakeSwap V3',
|
|
410
|
+
dexKey: isInfinity ? 'PCS_INFINITY_CL' : 'PANCAKESWAP_V3',
|
|
411
|
+
version: 'v3',
|
|
412
|
+
fee: metrics.infinityFee,
|
|
413
|
+
quoteToken: quoteAddr,
|
|
414
|
+
quoteSymbol,
|
|
415
|
+
quoteDecimals,
|
|
416
|
+
pairAddress: isInfinity
|
|
417
|
+
? (metrics.poolId ?? PCS_INFINITY_CL_POOL_MANAGER_BSC)
|
|
418
|
+
: (st.pool || ''),
|
|
419
|
+
liquidity: metrics.poolBNBAmount,
|
|
420
|
+
liquidityRaw: liqRaw,
|
|
421
|
+
reserveToken: metrics.poolTokenAmount,
|
|
422
|
+
reserveTokenRaw: tokRaw,
|
|
423
|
+
});
|
|
424
|
+
return result;
|
|
425
|
+
}
|
|
426
|
+
if (opts.debug) {
|
|
427
|
+
console.log('[LP Inspect] Flap graduated metrics empty, fallback to DEX scan');
|
|
428
|
+
}
|
|
418
429
|
}
|
|
419
430
|
result.platform = 'FLAP';
|
|
420
431
|
let quoteDecimals = 18;
|
|
@@ -2,6 +2,8 @@ import type { FlapPortal } from '../shared/flap/portal.js';
|
|
|
2
2
|
import type { TokenStateV7 } from '../shared/flap/portal.js';
|
|
3
3
|
/** PancakeSwap Infinity CL PoolManager(BSC / Base 同址) */
|
|
4
4
|
export declare const PCS_INFINITY_CL_POOL_MANAGER_BSC = "0xa0FfB9c1CE1Fe56963B0321B32E7A0302114058b";
|
|
5
|
+
/** PancakeSwap Infinity Vault(全池代币记账;BSC / Base 同址) */
|
|
6
|
+
export declare const PCS_INFINITY_VAULT_BSC = "0x238a358808379702088667322f80aC48bAd5e6c4";
|
|
5
7
|
/** Flap Portal 毕业到 Infinity CL 时使用的 hooks(BSC) */
|
|
6
8
|
export declare const FLAP_INFINITY_HOOKS_BSC = "0xF1A9aA042454b8553bE3896597ff11a0f011c1c1";
|
|
7
9
|
export type InfinityPoolKey = {
|
|
@@ -12,6 +14,15 @@ export type InfinityPoolKey = {
|
|
|
12
14
|
fee: number;
|
|
13
15
|
parameters: string;
|
|
14
16
|
};
|
|
17
|
+
export type GraduatedDexMetrics = {
|
|
18
|
+
price: string;
|
|
19
|
+
progress: string;
|
|
20
|
+
poolBNBAmount: string;
|
|
21
|
+
poolTokenAmount: string;
|
|
22
|
+
poolId?: string;
|
|
23
|
+
infinityFee?: number;
|
|
24
|
+
dexKind?: 'PCS_INFINITY_CL' | 'PANCAKE_V3' | 'PORTAL_QUOTE';
|
|
25
|
+
};
|
|
15
26
|
export declare function sortInfinityCurrencies(a: string, b: string): [string, string];
|
|
16
27
|
export declare function computeInfinityPoolId(key: InfinityPoolKey): string;
|
|
17
28
|
export declare function buildFlapInfinityPoolKey(params: {
|
|
@@ -20,7 +31,15 @@ export declare function buildFlapInfinityPoolKey(params: {
|
|
|
20
31
|
chain: string;
|
|
21
32
|
lpFeeProfile?: number;
|
|
22
33
|
}): InfinityPoolKey | null;
|
|
23
|
-
/**
|
|
34
|
+
/** 判断 getTokenV7.pool 是否为 Infinity CL PoolManager(而非 V3 pair) */
|
|
35
|
+
export declare function isInfinityCLPoolManagerAddress(pool: string, chain: string): boolean;
|
|
36
|
+
/** 仅 V7 + Infinity CL 才走 Infinity 专用逻辑 */
|
|
37
|
+
export declare function isFlapInfinityCLGraduated(state: {
|
|
38
|
+
pool?: string;
|
|
39
|
+
tokenVersion?: bigint | number;
|
|
40
|
+
}, chain: string): boolean;
|
|
41
|
+
/** Portal quoteExactInput:链上 quoteToken 为 0 时必须用 native(0),不能用 WBNB */
|
|
42
|
+
export declare function resolvePortalQuoteInputToken(quoteTokenAddress: string, wrappedNativeAddress: string): string;
|
|
24
43
|
export declare function infinitySpotPriceQuotePerToken(params: {
|
|
25
44
|
sqrtPriceX96: bigint;
|
|
26
45
|
tokenAddress: string;
|
|
@@ -30,26 +49,11 @@ export declare function infinitySpotPriceQuotePerToken(params: {
|
|
|
30
49
|
tokenDecimals?: number;
|
|
31
50
|
quoteDecimals?: number;
|
|
32
51
|
}): string | null;
|
|
33
|
-
/** 用活跃流动性 L 与 sqrtPrice 估算两侧虚拟储备(UI 展示用) */
|
|
34
|
-
export declare function estimateInfinityReserves(params: {
|
|
35
|
-
liquidity: bigint;
|
|
36
|
-
sqrtPriceX96: bigint;
|
|
37
|
-
tokenIsCurrency0: boolean;
|
|
38
|
-
}): {
|
|
39
|
-
reserveToken: string;
|
|
40
|
-
reserveQuote: string;
|
|
41
|
-
};
|
|
42
|
-
export type GraduatedDexMetrics = {
|
|
43
|
-
price: string;
|
|
44
|
-
progress: string;
|
|
45
|
-
poolBNBAmount: string;
|
|
46
|
-
poolTokenAmount: string;
|
|
47
|
-
poolId?: string;
|
|
48
|
-
infinityFee?: number;
|
|
49
|
-
};
|
|
50
52
|
/**
|
|
51
|
-
* Flap 毕业后(status=4
|
|
52
|
-
*
|
|
53
|
+
* Flap 毕业后(status=4)外盘统一入口:
|
|
54
|
+
* - V7 / Infinity CL → Infinity + Portal quote 深度
|
|
55
|
+
* - 旧版 V3/V2(pool 为 pair 地址)→ 读 pair 余额 + slot0
|
|
56
|
+
* - 否则仅 Portal quote
|
|
53
57
|
*/
|
|
54
58
|
export declare function getFlapGraduatedDexMetrics(params: {
|
|
55
59
|
portal: FlapPortal;
|
|
@@ -57,6 +61,5 @@ export declare function getFlapGraduatedDexMetrics(params: {
|
|
|
57
61
|
state: TokenStateV7;
|
|
58
62
|
rpcUrl: string;
|
|
59
63
|
wrappedNativeAddress?: string;
|
|
64
|
+
chain?: string;
|
|
60
65
|
}): Promise<GraduatedDexMetrics>;
|
|
61
|
-
/** 判断 getTokenV7.pool 是否为 Infinity CL PoolManager(而非 V3 pair) */
|
|
62
|
-
export declare function isInfinityCLPoolManagerAddress(pool: string, chain: string): boolean;
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { AbiCoder, Contract, JsonRpcProvider, formatEther, formatUnits, keccak256 } from 'ethers';
|
|
2
|
-
import {
|
|
2
|
+
import { ZERO_ADDRESS } from '../shared/flap/constants.js';
|
|
3
|
+
import { ERC20_ABI } from '../shared/abis/common.js';
|
|
3
4
|
/** PancakeSwap Infinity CL PoolManager(BSC / Base 同址) */
|
|
4
5
|
export const PCS_INFINITY_CL_POOL_MANAGER_BSC = '0xa0FfB9c1CE1Fe56963B0321B32E7A0302114058b';
|
|
6
|
+
/** PancakeSwap Infinity Vault(全池代币记账;BSC / Base 同址) */
|
|
7
|
+
export const PCS_INFINITY_VAULT_BSC = '0x238a358808379702088667322f80aC48bAd5e6c4';
|
|
5
8
|
/** Flap Portal 毕业到 Infinity CL 时使用的 hooks(BSC) */
|
|
6
9
|
export const FLAP_INFINITY_HOOKS_BSC = '0xF1A9aA042454b8553bE3896597ff11a0f011c1c1';
|
|
7
10
|
const CL_POOL_MANAGER_ABI = [
|
|
8
11
|
'function getSlot0(bytes32 id) view returns (uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 lpFee)',
|
|
9
12
|
'function getLiquidity(bytes32 id) view returns (uint128 liquidity)',
|
|
10
13
|
];
|
|
14
|
+
const V3_PAIR_ABI = [
|
|
15
|
+
'function slot0() view returns (uint160 sqrtPriceX96,int24 tick,uint16 observationIndex,uint16 observationCardinality,uint16 observationCardinalityNext,uint8 feeProtocol,bool unlocked)',
|
|
16
|
+
'function token0() view returns (address)',
|
|
17
|
+
'function token1() view returns (address)',
|
|
18
|
+
];
|
|
11
19
|
/** BSC:lpFeeProfile → Infinity PoolKey 的 fee / parameters(与链上 Initialize 一致) */
|
|
12
20
|
const BSC_INFINITY_POOL_KEY_BY_PROFILE = {
|
|
13
21
|
0: {
|
|
@@ -31,7 +39,7 @@ export function buildFlapInfinityPoolKey(params) {
|
|
|
31
39
|
const quote = params.quoteTokenAddress &&
|
|
32
40
|
params.quoteTokenAddress.toLowerCase() !== ZERO_ADDRESS
|
|
33
41
|
? params.quoteTokenAddress
|
|
34
|
-
: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
42
|
+
: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
35
43
|
const [currency0, currency1] = sortInfinityCurrencies(params.tokenAddress, quote);
|
|
36
44
|
return {
|
|
37
45
|
currency0,
|
|
@@ -42,6 +50,32 @@ export function buildFlapInfinityPoolKey(params) {
|
|
|
42
50
|
parameters: meta.parameters,
|
|
43
51
|
};
|
|
44
52
|
}
|
|
53
|
+
/** 判断 getTokenV7.pool 是否为 Infinity CL PoolManager(而非 V3 pair) */
|
|
54
|
+
export function isInfinityCLPoolManagerAddress(pool, chain) {
|
|
55
|
+
if (!pool)
|
|
56
|
+
return false;
|
|
57
|
+
const c = String(chain || '').toUpperCase();
|
|
58
|
+
if (c === 'BSC' || c === 'BASE') {
|
|
59
|
+
return pool.toLowerCase() === PCS_INFINITY_CL_POOL_MANAGER_BSC.toLowerCase();
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
/** 仅 V7 + Infinity CL 才走 Infinity 专用逻辑 */
|
|
64
|
+
export function isFlapInfinityCLGraduated(state, chain) {
|
|
65
|
+
if (isInfinityCLPoolManagerAddress(state.pool ?? '', chain))
|
|
66
|
+
return true;
|
|
67
|
+
return Number(state.tokenVersion ?? 0) === 7;
|
|
68
|
+
}
|
|
69
|
+
/** Portal quoteExactInput:链上 quoteToken 为 0 时必须用 native(0),不能用 WBNB */
|
|
70
|
+
export function resolvePortalQuoteInputToken(quoteTokenAddress, wrappedNativeAddress) {
|
|
71
|
+
if (!quoteTokenAddress || quoteTokenAddress.toLowerCase() === ZERO_ADDRESS) {
|
|
72
|
+
return ZERO_ADDRESS;
|
|
73
|
+
}
|
|
74
|
+
if (quoteTokenAddress.toLowerCase() === wrappedNativeAddress.toLowerCase()) {
|
|
75
|
+
return ZERO_ADDRESS;
|
|
76
|
+
}
|
|
77
|
+
return quoteTokenAddress;
|
|
78
|
+
}
|
|
45
79
|
function formatRatio(n, d, precision = 18) {
|
|
46
80
|
if (d === 0n)
|
|
47
81
|
return '0';
|
|
@@ -52,7 +86,6 @@ function formatRatio(n, d, precision = 18) {
|
|
|
52
86
|
const frac = s.slice(-precision).replace(/0+$/, '');
|
|
53
87
|
return frac ? `${int}.${frac}` : int;
|
|
54
88
|
}
|
|
55
|
-
/** 由 slot0 计算 quote per token(与 memeweb V3 spot 公式一致) */
|
|
56
89
|
export function infinitySpotPriceQuotePerToken(params) {
|
|
57
90
|
const tokenLower = params.tokenAddress.toLowerCase();
|
|
58
91
|
const quoteLower = params.quoteTokenAddress.toLowerCase();
|
|
@@ -80,32 +113,58 @@ export function infinitySpotPriceQuotePerToken(params) {
|
|
|
80
113
|
}
|
|
81
114
|
return formatRatio(n, d, 18);
|
|
82
115
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
116
|
+
function v3SpotPriceQuotePerToken(params) {
|
|
117
|
+
return infinitySpotPriceQuotePerToken({
|
|
118
|
+
sqrtPriceX96: params.sqrtPriceX96,
|
|
119
|
+
tokenAddress: params.tokenAddress,
|
|
120
|
+
currency0: params.token0,
|
|
121
|
+
currency1: params.token1,
|
|
122
|
+
quoteTokenAddress: params.quoteTokenAddress,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/** Infinity CL:用 quote 估算当前可卖出的 WBNB 深度(贴近 GMGN,勿把探测代币量当池子持仓) */
|
|
126
|
+
async function estimateInfinityPoolQuoteBnbDepth(portal, tokenAddress) {
|
|
127
|
+
const inputQuote = ZERO_ADDRESS;
|
|
128
|
+
const probeTokens = [110000000n, 100000000n, 50000000n, 10000000n].map((m) => m * 10n ** 18n);
|
|
129
|
+
for (const amt of probeTokens) {
|
|
130
|
+
try {
|
|
131
|
+
const bnbOut = await portal.quoteExactInput({
|
|
132
|
+
inputToken: tokenAddress,
|
|
133
|
+
outputToken: inputQuote,
|
|
134
|
+
inputAmount: amt,
|
|
135
|
+
});
|
|
136
|
+
if (bnbOut > 0n)
|
|
137
|
+
return formatEther(bnbOut);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
/* try smaller */
|
|
141
|
+
}
|
|
88
142
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
143
|
+
return '0';
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Infinity CL 池内代币量:读 Vault 中该 token 余额(与 GMGN 池子代币列一致)
|
|
147
|
+
* 注意:Vault 为全协议记账层,对 Flap 毕业主池通常即该交易对持仓。
|
|
148
|
+
*/
|
|
149
|
+
async function getInfinityCLPoolTokenReserve(params) {
|
|
150
|
+
const chain = String(params.chain || '').toUpperCase();
|
|
151
|
+
const vault = chain === 'BSC' || chain === 'BASE' ? PCS_INFINITY_VAULT_BSC : '';
|
|
152
|
+
if (!vault)
|
|
153
|
+
return '0';
|
|
154
|
+
try {
|
|
155
|
+
const provider = new JsonRpcProvider(params.rpcUrl);
|
|
156
|
+
const bal = (await new Contract(params.tokenAddress, ERC20_ABI, provider).balanceOf(vault));
|
|
157
|
+
return formatEther(bal);
|
|
94
158
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (params.tokenIsCurrency0) {
|
|
98
|
-
return { reserveToken: String(amount0), reserveQuote: String(amount1) };
|
|
159
|
+
catch {
|
|
160
|
+
return '0';
|
|
99
161
|
}
|
|
100
|
-
return { reserveToken: String(amount1), reserveQuote: String(amount0) };
|
|
101
162
|
}
|
|
102
|
-
async function quotePortalPrice(portal, tokenAddress, quoteTokenAddress) {
|
|
163
|
+
async function quotePortalPrice(portal, tokenAddress, quoteTokenAddress, wrappedNativeAddress) {
|
|
103
164
|
try {
|
|
104
|
-
const buyAmt = 10n ** 16n;
|
|
105
|
-
const sellAmt = 10n ** 21n;
|
|
106
|
-
const inputQuote = quoteTokenAddress
|
|
107
|
-
? ZERO_ADDRESS
|
|
108
|
-
: quoteTokenAddress;
|
|
165
|
+
const buyAmt = 10n ** 16n;
|
|
166
|
+
const sellAmt = 10n ** 21n;
|
|
167
|
+
const inputQuote = resolvePortalQuoteInputToken(quoteTokenAddress, wrappedNativeAddress);
|
|
109
168
|
const [tokensOut, quoteOut] = await Promise.all([
|
|
110
169
|
portal.quoteExactInput({
|
|
111
170
|
inputToken: inputQuote,
|
|
@@ -116,11 +175,13 @@ async function quotePortalPrice(portal, tokenAddress, quoteTokenAddress) {
|
|
|
116
175
|
inputToken: tokenAddress,
|
|
117
176
|
outputToken: inputQuote,
|
|
118
177
|
inputAmount: sellAmt,
|
|
119
|
-
}),
|
|
178
|
+
}).catch(() => 0n),
|
|
120
179
|
]);
|
|
121
|
-
if (tokensOut <= 0n
|
|
180
|
+
if (tokensOut <= 0n)
|
|
122
181
|
return null;
|
|
123
182
|
const buyPrice = Number(formatEther(buyAmt)) / Number(formatEther(tokensOut));
|
|
183
|
+
if (quoteOut <= 0n)
|
|
184
|
+
return String(buyPrice);
|
|
124
185
|
const sellPrice = Number(formatEther(quoteOut)) / Number(formatEther(sellAmt));
|
|
125
186
|
return String((buyPrice + sellPrice) / 2);
|
|
126
187
|
}
|
|
@@ -128,31 +189,67 @@ async function quotePortalPrice(portal, tokenAddress, quoteTokenAddress) {
|
|
|
128
189
|
return null;
|
|
129
190
|
}
|
|
130
191
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
192
|
+
function formatProgress(state) {
|
|
193
|
+
const progressRaw = state.progress ?? 0n;
|
|
194
|
+
if (progressRaw > 10n ** 18n)
|
|
195
|
+
return formatUnits(progressRaw, 18);
|
|
196
|
+
if (progressRaw >= 100n)
|
|
197
|
+
return '100';
|
|
198
|
+
return String(progressRaw);
|
|
199
|
+
}
|
|
200
|
+
async function getGraduatedV3PairMetrics(params) {
|
|
201
|
+
try {
|
|
202
|
+
const provider = new JsonRpcProvider(params.rpcUrl);
|
|
203
|
+
const pool = new Contract(params.poolAddress, V3_PAIR_ABI, provider);
|
|
204
|
+
const [t0, t1, slot0] = await Promise.all([
|
|
205
|
+
pool.token0(),
|
|
206
|
+
pool.token1(),
|
|
207
|
+
pool.slot0(),
|
|
208
|
+
]);
|
|
209
|
+
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
|
+
const spot = v3SpotPriceQuotePerToken({
|
|
215
|
+
sqrtPriceX96,
|
|
216
|
+
tokenAddress: params.tokenAddress,
|
|
217
|
+
token0: t0,
|
|
218
|
+
token1: t1,
|
|
219
|
+
quoteTokenAddress: quoteAddr,
|
|
220
|
+
});
|
|
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
|
+
const tokenIs0 = t0.toLowerCase() === tokenLower;
|
|
226
|
+
const poolTokenAmount = formatEther(tokenIs0 ? bal0 : bal1);
|
|
227
|
+
const poolBNBAmount = formatEther(tokenIs0 ? bal1 : bal0);
|
|
228
|
+
if (!price && poolTokenAmount === '0' && poolBNBAmount === '0')
|
|
229
|
+
return null;
|
|
230
|
+
return {
|
|
231
|
+
price: price ?? '0',
|
|
232
|
+
progress: '100',
|
|
233
|
+
poolBNBAmount,
|
|
234
|
+
poolTokenAmount,
|
|
235
|
+
dexKind: 'PANCAKE_V3',
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async function getInfinityCLGraduatedMetrics(params) {
|
|
243
|
+
const progress = formatProgress(params.state);
|
|
244
|
+
let price = await quotePortalPrice(params.portal, params.tokenAddress, params.quoteTokenAddress, params.wrappedNativeAddress);
|
|
148
245
|
let poolBNBAmount = '0';
|
|
149
246
|
let poolTokenAmount = '0';
|
|
150
247
|
let poolId;
|
|
151
248
|
let infinityFee;
|
|
152
249
|
const poolKey = buildFlapInfinityPoolKey({
|
|
153
|
-
tokenAddress,
|
|
154
|
-
quoteTokenAddress:
|
|
155
|
-
chain:
|
|
250
|
+
tokenAddress: params.tokenAddress,
|
|
251
|
+
quoteTokenAddress: params.quoteTokenAddress,
|
|
252
|
+
chain: params.chain,
|
|
156
253
|
lpFeeProfile: params.state.lpFeeProfile,
|
|
157
254
|
});
|
|
158
255
|
if (poolKey) {
|
|
@@ -161,48 +258,41 @@ export async function getFlapGraduatedDexMetrics(params) {
|
|
|
161
258
|
try {
|
|
162
259
|
const provider = new JsonRpcProvider(params.rpcUrl);
|
|
163
260
|
const manager = new Contract(poolKey.poolManager, CL_POOL_MANAGER_ABI, provider);
|
|
164
|
-
const [slot0
|
|
165
|
-
manager.getSlot0(poolId),
|
|
166
|
-
manager.getLiquidity(poolId),
|
|
167
|
-
]);
|
|
261
|
+
const [slot0] = await Promise.all([manager.getSlot0(poolId)]);
|
|
168
262
|
const sqrtPriceX96 = BigInt(slot0[0]);
|
|
169
|
-
const liq = BigInt(liquidity);
|
|
170
263
|
if (sqrtPriceX96 > 0n) {
|
|
171
|
-
const
|
|
264
|
+
const quoteAddr = params.quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
|
|
265
|
+
? params.wrappedNativeAddress
|
|
266
|
+
: params.quoteTokenAddress;
|
|
172
267
|
const spot = infinitySpotPriceQuotePerToken({
|
|
173
268
|
sqrtPriceX96,
|
|
174
|
-
tokenAddress,
|
|
269
|
+
tokenAddress: params.tokenAddress,
|
|
175
270
|
currency0: poolKey.currency0,
|
|
176
271
|
currency1: poolKey.currency1,
|
|
177
|
-
quoteTokenAddress:
|
|
272
|
+
quoteTokenAddress: quoteAddr,
|
|
178
273
|
});
|
|
179
274
|
if (spot)
|
|
180
275
|
price = spot;
|
|
181
|
-
const reserves = estimateInfinityReserves({
|
|
182
|
-
liquidity: liq,
|
|
183
|
-
sqrtPriceX96,
|
|
184
|
-
tokenIsCurrency0: tokenIs0,
|
|
185
|
-
});
|
|
186
|
-
poolBNBAmount = reserves.reserveQuote;
|
|
187
|
-
poolTokenAmount = reserves.reserveToken;
|
|
188
276
|
}
|
|
189
277
|
}
|
|
190
278
|
catch {
|
|
191
|
-
|
|
279
|
+
/* keep portal quote */
|
|
192
280
|
}
|
|
193
281
|
}
|
|
282
|
+
const [bnbDepth, tokenReserve] = await Promise.all([
|
|
283
|
+
estimateInfinityPoolQuoteBnbDepth(params.portal, params.tokenAddress),
|
|
284
|
+
getInfinityCLPoolTokenReserve({
|
|
285
|
+
tokenAddress: params.tokenAddress,
|
|
286
|
+
rpcUrl: params.rpcUrl,
|
|
287
|
+
chain: params.chain,
|
|
288
|
+
}),
|
|
289
|
+
]);
|
|
290
|
+
if (parseFloat(bnbDepth) > 0)
|
|
291
|
+
poolBNBAmount = bnbDepth;
|
|
292
|
+
if (parseFloat(tokenReserve) > 0)
|
|
293
|
+
poolTokenAmount = tokenReserve;
|
|
194
294
|
if (!price)
|
|
195
295
|
price = '0';
|
|
196
|
-
if (poolTokenAmount === '0' || poolTokenAmount === '0.0') {
|
|
197
|
-
try {
|
|
198
|
-
const circ = formatEther(params.state.circulatingSupply ?? 0n);
|
|
199
|
-
const total = formatEther(FLAP_TOTAL_SUPPLY);
|
|
200
|
-
poolTokenAmount = String(Math.max(0, Number(total) - Number(circ)));
|
|
201
|
-
}
|
|
202
|
-
catch {
|
|
203
|
-
poolTokenAmount = '0';
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
296
|
return {
|
|
207
297
|
price,
|
|
208
298
|
progress,
|
|
@@ -210,15 +300,53 @@ export async function getFlapGraduatedDexMetrics(params) {
|
|
|
210
300
|
poolTokenAmount,
|
|
211
301
|
poolId,
|
|
212
302
|
infinityFee,
|
|
303
|
+
dexKind: 'PCS_INFINITY_CL',
|
|
213
304
|
};
|
|
214
305
|
}
|
|
215
|
-
/**
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
306
|
+
/**
|
|
307
|
+
* Flap 毕业后(status=4)外盘统一入口:
|
|
308
|
+
* - V7 / Infinity CL → Infinity + Portal quote 深度
|
|
309
|
+
* - 旧版 V3/V2(pool 为 pair 地址)→ 读 pair 余额 + slot0
|
|
310
|
+
* - 否则仅 Portal quote
|
|
311
|
+
*/
|
|
312
|
+
export async function getFlapGraduatedDexMetrics(params) {
|
|
313
|
+
const chain = String(params.chain || 'BSC').toUpperCase();
|
|
314
|
+
const wrappedNative = params.wrappedNativeAddress ?? '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
315
|
+
const quoteToken = params.state.quoteTokenAddress &&
|
|
316
|
+
params.state.quoteTokenAddress.toLowerCase() !== ZERO_ADDRESS
|
|
317
|
+
? params.state.quoteTokenAddress
|
|
318
|
+
: wrappedNative;
|
|
319
|
+
const progress = formatProgress(params.state);
|
|
320
|
+
if (isFlapInfinityCLGraduated(params.state, chain)) {
|
|
321
|
+
return getInfinityCLGraduatedMetrics({
|
|
322
|
+
portal: params.portal,
|
|
323
|
+
tokenAddress: params.tokenAddress,
|
|
324
|
+
state: params.state,
|
|
325
|
+
rpcUrl: params.rpcUrl,
|
|
326
|
+
quoteTokenAddress: quoteToken,
|
|
327
|
+
wrappedNativeAddress: wrappedNative,
|
|
328
|
+
chain,
|
|
329
|
+
});
|
|
222
330
|
}
|
|
223
|
-
|
|
331
|
+
const pool = params.state.pool;
|
|
332
|
+
if (pool && pool.toLowerCase() !== ZERO_ADDRESS && !isInfinityCLPoolManagerAddress(pool, chain)) {
|
|
333
|
+
const v3 = await getGraduatedV3PairMetrics({
|
|
334
|
+
rpcUrl: params.rpcUrl,
|
|
335
|
+
tokenAddress: params.tokenAddress,
|
|
336
|
+
poolAddress: pool,
|
|
337
|
+
quoteTokenAddress: quoteToken,
|
|
338
|
+
wrappedNativeAddress: wrappedNative,
|
|
339
|
+
portal: params.portal,
|
|
340
|
+
});
|
|
341
|
+
if (v3)
|
|
342
|
+
return v3;
|
|
343
|
+
}
|
|
344
|
+
const price = await quotePortalPrice(params.portal, params.tokenAddress, quoteToken, wrappedNative);
|
|
345
|
+
return {
|
|
346
|
+
price: price ?? '0',
|
|
347
|
+
progress,
|
|
348
|
+
poolBNBAmount: '0',
|
|
349
|
+
poolTokenAmount: '0',
|
|
350
|
+
dexKind: 'PORTAL_QUOTE',
|
|
351
|
+
};
|
|
224
352
|
}
|