four-flap-meme-sdk 2.2.13 → 2.2.14
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 +23 -22
- package/dist/utils/pcs-infinity-cl.js +187 -82
- 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, 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, 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, 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, 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;
|
|
@@ -12,6 +12,15 @@ export type InfinityPoolKey = {
|
|
|
12
12
|
fee: number;
|
|
13
13
|
parameters: string;
|
|
14
14
|
};
|
|
15
|
+
export type GraduatedDexMetrics = {
|
|
16
|
+
price: string;
|
|
17
|
+
progress: string;
|
|
18
|
+
poolBNBAmount: string;
|
|
19
|
+
poolTokenAmount: string;
|
|
20
|
+
poolId?: string;
|
|
21
|
+
infinityFee?: number;
|
|
22
|
+
dexKind?: 'PCS_INFINITY_CL' | 'PANCAKE_V3' | 'PORTAL_QUOTE';
|
|
23
|
+
};
|
|
15
24
|
export declare function sortInfinityCurrencies(a: string, b: string): [string, string];
|
|
16
25
|
export declare function computeInfinityPoolId(key: InfinityPoolKey): string;
|
|
17
26
|
export declare function buildFlapInfinityPoolKey(params: {
|
|
@@ -20,7 +29,15 @@ export declare function buildFlapInfinityPoolKey(params: {
|
|
|
20
29
|
chain: string;
|
|
21
30
|
lpFeeProfile?: number;
|
|
22
31
|
}): InfinityPoolKey | null;
|
|
23
|
-
/**
|
|
32
|
+
/** 判断 getTokenV7.pool 是否为 Infinity CL PoolManager(而非 V3 pair) */
|
|
33
|
+
export declare function isInfinityCLPoolManagerAddress(pool: string, chain: string): boolean;
|
|
34
|
+
/** 仅 V7 + Infinity CL 才走 Infinity 专用逻辑 */
|
|
35
|
+
export declare function isFlapInfinityCLGraduated(state: {
|
|
36
|
+
pool?: string;
|
|
37
|
+
tokenVersion?: bigint | number;
|
|
38
|
+
}, chain: string): boolean;
|
|
39
|
+
/** Portal quoteExactInput:链上 quoteToken 为 0 时必须用 native(0),不能用 WBNB */
|
|
40
|
+
export declare function resolvePortalQuoteInputToken(quoteTokenAddress: string, wrappedNativeAddress: string): string;
|
|
24
41
|
export declare function infinitySpotPriceQuotePerToken(params: {
|
|
25
42
|
sqrtPriceX96: bigint;
|
|
26
43
|
tokenAddress: string;
|
|
@@ -30,26 +47,11 @@ export declare function infinitySpotPriceQuotePerToken(params: {
|
|
|
30
47
|
tokenDecimals?: number;
|
|
31
48
|
quoteDecimals?: number;
|
|
32
49
|
}): 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
50
|
/**
|
|
51
|
-
* Flap 毕业后(status=4
|
|
52
|
-
*
|
|
51
|
+
* Flap 毕业后(status=4)外盘统一入口:
|
|
52
|
+
* - V7 / Infinity CL → Infinity + Portal quote 深度
|
|
53
|
+
* - 旧版 V3/V2(pool 为 pair 地址)→ 读 pair 余额 + slot0
|
|
54
|
+
* - 否则仅 Portal quote
|
|
53
55
|
*/
|
|
54
56
|
export declare function getFlapGraduatedDexMetrics(params: {
|
|
55
57
|
portal: FlapPortal;
|
|
@@ -57,6 +59,5 @@ export declare function getFlapGraduatedDexMetrics(params: {
|
|
|
57
59
|
state: TokenStateV7;
|
|
58
60
|
rpcUrl: string;
|
|
59
61
|
wrappedNativeAddress?: string;
|
|
62
|
+
chain?: string;
|
|
60
63
|
}): Promise<GraduatedDexMetrics>;
|
|
61
|
-
/** 判断 getTokenV7.pool 是否为 Infinity CL PoolManager(而非 V3 pair) */
|
|
62
|
-
export declare function isInfinityCLPoolManagerAddress(pool: string, chain: string): boolean;
|
|
@@ -1,5 +1,6 @@
|
|
|
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';
|
|
5
6
|
/** Flap Portal 毕业到 Infinity CL 时使用的 hooks(BSC) */
|
|
@@ -8,6 +9,11 @@ const CL_POOL_MANAGER_ABI = [
|
|
|
8
9
|
'function getSlot0(bytes32 id) view returns (uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 lpFee)',
|
|
9
10
|
'function getLiquidity(bytes32 id) view returns (uint128 liquidity)',
|
|
10
11
|
];
|
|
12
|
+
const V3_PAIR_ABI = [
|
|
13
|
+
'function slot0() view returns (uint160 sqrtPriceX96,int24 tick,uint16 observationIndex,uint16 observationCardinality,uint16 observationCardinalityNext,uint8 feeProtocol,bool unlocked)',
|
|
14
|
+
'function token0() view returns (address)',
|
|
15
|
+
'function token1() view returns (address)',
|
|
16
|
+
];
|
|
11
17
|
/** BSC:lpFeeProfile → Infinity PoolKey 的 fee / parameters(与链上 Initialize 一致) */
|
|
12
18
|
const BSC_INFINITY_POOL_KEY_BY_PROFILE = {
|
|
13
19
|
0: {
|
|
@@ -31,7 +37,7 @@ export function buildFlapInfinityPoolKey(params) {
|
|
|
31
37
|
const quote = params.quoteTokenAddress &&
|
|
32
38
|
params.quoteTokenAddress.toLowerCase() !== ZERO_ADDRESS
|
|
33
39
|
? params.quoteTokenAddress
|
|
34
|
-
: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
40
|
+
: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
35
41
|
const [currency0, currency1] = sortInfinityCurrencies(params.tokenAddress, quote);
|
|
36
42
|
return {
|
|
37
43
|
currency0,
|
|
@@ -42,6 +48,32 @@ export function buildFlapInfinityPoolKey(params) {
|
|
|
42
48
|
parameters: meta.parameters,
|
|
43
49
|
};
|
|
44
50
|
}
|
|
51
|
+
/** 判断 getTokenV7.pool 是否为 Infinity CL PoolManager(而非 V3 pair) */
|
|
52
|
+
export function isInfinityCLPoolManagerAddress(pool, chain) {
|
|
53
|
+
if (!pool)
|
|
54
|
+
return false;
|
|
55
|
+
const c = String(chain || '').toUpperCase();
|
|
56
|
+
if (c === 'BSC' || c === 'BASE') {
|
|
57
|
+
return pool.toLowerCase() === PCS_INFINITY_CL_POOL_MANAGER_BSC.toLowerCase();
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
/** 仅 V7 + Infinity CL 才走 Infinity 专用逻辑 */
|
|
62
|
+
export function isFlapInfinityCLGraduated(state, chain) {
|
|
63
|
+
if (isInfinityCLPoolManagerAddress(state.pool ?? '', chain))
|
|
64
|
+
return true;
|
|
65
|
+
return Number(state.tokenVersion ?? 0) === 7;
|
|
66
|
+
}
|
|
67
|
+
/** Portal quoteExactInput:链上 quoteToken 为 0 时必须用 native(0),不能用 WBNB */
|
|
68
|
+
export function resolvePortalQuoteInputToken(quoteTokenAddress, wrappedNativeAddress) {
|
|
69
|
+
if (!quoteTokenAddress || quoteTokenAddress.toLowerCase() === ZERO_ADDRESS) {
|
|
70
|
+
return ZERO_ADDRESS;
|
|
71
|
+
}
|
|
72
|
+
if (quoteTokenAddress.toLowerCase() === wrappedNativeAddress.toLowerCase()) {
|
|
73
|
+
return ZERO_ADDRESS;
|
|
74
|
+
}
|
|
75
|
+
return quoteTokenAddress;
|
|
76
|
+
}
|
|
45
77
|
function formatRatio(n, d, precision = 18) {
|
|
46
78
|
if (d === 0n)
|
|
47
79
|
return '0';
|
|
@@ -52,7 +84,6 @@ function formatRatio(n, d, precision = 18) {
|
|
|
52
84
|
const frac = s.slice(-precision).replace(/0+$/, '');
|
|
53
85
|
return frac ? `${int}.${frac}` : int;
|
|
54
86
|
}
|
|
55
|
-
/** 由 slot0 计算 quote per token(与 memeweb V3 spot 公式一致) */
|
|
56
87
|
export function infinitySpotPriceQuotePerToken(params) {
|
|
57
88
|
const tokenLower = params.tokenAddress.toLowerCase();
|
|
58
89
|
const quoteLower = params.quoteTokenAddress.toLowerCase();
|
|
@@ -80,32 +111,44 @@ export function infinitySpotPriceQuotePerToken(params) {
|
|
|
80
111
|
}
|
|
81
112
|
return formatRatio(n, d, 18);
|
|
82
113
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
114
|
+
function v3SpotPriceQuotePerToken(params) {
|
|
115
|
+
return infinitySpotPriceQuotePerToken({
|
|
116
|
+
sqrtPriceX96: params.sqrtPriceX96,
|
|
117
|
+
tokenAddress: params.tokenAddress,
|
|
118
|
+
currency0: params.token0,
|
|
119
|
+
currency1: params.token1,
|
|
120
|
+
quoteTokenAddress: params.quoteTokenAddress,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/** Infinity CL:用 quote 估算当前可卖出的 WBNB 深度(贴近 GMGN,避免 L*sqrtP 高估) */
|
|
124
|
+
async function estimateInfinityPoolQuoteDepth(portal, tokenAddress, wrappedNative) {
|
|
125
|
+
const inputQuote = ZERO_ADDRESS;
|
|
126
|
+
const probeTokens = [110000000n, 100000000n, 50000000n, 10000000n].map((m) => m * 10n ** 18n);
|
|
127
|
+
for (const amt of probeTokens) {
|
|
128
|
+
try {
|
|
129
|
+
const bnbOut = await portal.quoteExactInput({
|
|
130
|
+
inputToken: tokenAddress,
|
|
131
|
+
outputToken: inputQuote,
|
|
132
|
+
inputAmount: amt,
|
|
133
|
+
});
|
|
134
|
+
if (bnbOut > 0n) {
|
|
135
|
+
return {
|
|
136
|
+
poolBNBAmount: formatEther(bnbOut),
|
|
137
|
+
poolTokenAmount: formatEther(amt),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
/* try smaller */
|
|
143
|
+
}
|
|
99
144
|
}
|
|
100
|
-
return {
|
|
145
|
+
return { poolBNBAmount: '0', poolTokenAmount: '0' };
|
|
101
146
|
}
|
|
102
|
-
async function quotePortalPrice(portal, tokenAddress, quoteTokenAddress) {
|
|
147
|
+
async function quotePortalPrice(portal, tokenAddress, quoteTokenAddress, wrappedNativeAddress) {
|
|
103
148
|
try {
|
|
104
|
-
const buyAmt = 10n ** 16n;
|
|
105
|
-
const sellAmt = 10n ** 21n;
|
|
106
|
-
const inputQuote = quoteTokenAddress
|
|
107
|
-
? ZERO_ADDRESS
|
|
108
|
-
: quoteTokenAddress;
|
|
149
|
+
const buyAmt = 10n ** 16n;
|
|
150
|
+
const sellAmt = 10n ** 21n;
|
|
151
|
+
const inputQuote = resolvePortalQuoteInputToken(quoteTokenAddress, wrappedNativeAddress);
|
|
109
152
|
const [tokensOut, quoteOut] = await Promise.all([
|
|
110
153
|
portal.quoteExactInput({
|
|
111
154
|
inputToken: inputQuote,
|
|
@@ -116,11 +159,13 @@ async function quotePortalPrice(portal, tokenAddress, quoteTokenAddress) {
|
|
|
116
159
|
inputToken: tokenAddress,
|
|
117
160
|
outputToken: inputQuote,
|
|
118
161
|
inputAmount: sellAmt,
|
|
119
|
-
}),
|
|
162
|
+
}).catch(() => 0n),
|
|
120
163
|
]);
|
|
121
|
-
if (tokensOut <= 0n
|
|
164
|
+
if (tokensOut <= 0n)
|
|
122
165
|
return null;
|
|
123
166
|
const buyPrice = Number(formatEther(buyAmt)) / Number(formatEther(tokensOut));
|
|
167
|
+
if (quoteOut <= 0n)
|
|
168
|
+
return String(buyPrice);
|
|
124
169
|
const sellPrice = Number(formatEther(quoteOut)) / Number(formatEther(sellAmt));
|
|
125
170
|
return String((buyPrice + sellPrice) / 2);
|
|
126
171
|
}
|
|
@@ -128,31 +173,67 @@ async function quotePortalPrice(portal, tokenAddress, quoteTokenAddress) {
|
|
|
128
173
|
return null;
|
|
129
174
|
}
|
|
130
175
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
176
|
+
function formatProgress(state) {
|
|
177
|
+
const progressRaw = state.progress ?? 0n;
|
|
178
|
+
if (progressRaw > 10n ** 18n)
|
|
179
|
+
return formatUnits(progressRaw, 18);
|
|
180
|
+
if (progressRaw >= 100n)
|
|
181
|
+
return '100';
|
|
182
|
+
return String(progressRaw);
|
|
183
|
+
}
|
|
184
|
+
async function getGraduatedV3PairMetrics(params) {
|
|
185
|
+
try {
|
|
186
|
+
const provider = new JsonRpcProvider(params.rpcUrl);
|
|
187
|
+
const pool = new Contract(params.poolAddress, V3_PAIR_ABI, provider);
|
|
188
|
+
const [t0, t1, slot0] = await Promise.all([
|
|
189
|
+
pool.token0(),
|
|
190
|
+
pool.token1(),
|
|
191
|
+
pool.slot0(),
|
|
192
|
+
]);
|
|
193
|
+
const sqrtPriceX96 = BigInt(Array.isArray(slot0) ? slot0[0] : slot0.sqrtPriceX96);
|
|
194
|
+
const tokenLower = params.tokenAddress.toLowerCase();
|
|
195
|
+
const quoteAddr = params.quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
|
|
196
|
+
? params.wrappedNativeAddress
|
|
197
|
+
: params.quoteTokenAddress;
|
|
198
|
+
const spot = v3SpotPriceQuotePerToken({
|
|
199
|
+
sqrtPriceX96,
|
|
200
|
+
tokenAddress: params.tokenAddress,
|
|
201
|
+
token0: t0,
|
|
202
|
+
token1: t1,
|
|
203
|
+
quoteTokenAddress: quoteAddr,
|
|
204
|
+
});
|
|
205
|
+
const price = spot ??
|
|
206
|
+
(await quotePortalPrice(params.portal, params.tokenAddress, params.quoteTokenAddress, params.wrappedNativeAddress));
|
|
207
|
+
const bal0 = (await new Contract(t0, ERC20_ABI, provider).balanceOf(params.poolAddress));
|
|
208
|
+
const bal1 = (await new Contract(t1, ERC20_ABI, provider).balanceOf(params.poolAddress));
|
|
209
|
+
const tokenIs0 = t0.toLowerCase() === tokenLower;
|
|
210
|
+
const poolTokenAmount = formatEther(tokenIs0 ? bal0 : bal1);
|
|
211
|
+
const poolBNBAmount = formatEther(tokenIs0 ? bal1 : bal0);
|
|
212
|
+
if (!price && poolTokenAmount === '0' && poolBNBAmount === '0')
|
|
213
|
+
return null;
|
|
214
|
+
return {
|
|
215
|
+
price: price ?? '0',
|
|
216
|
+
progress: '100',
|
|
217
|
+
poolBNBAmount,
|
|
218
|
+
poolTokenAmount,
|
|
219
|
+
dexKind: 'PANCAKE_V3',
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async function getInfinityCLGraduatedMetrics(params) {
|
|
227
|
+
const progress = formatProgress(params.state);
|
|
228
|
+
let price = await quotePortalPrice(params.portal, params.tokenAddress, params.quoteTokenAddress, params.wrappedNativeAddress);
|
|
148
229
|
let poolBNBAmount = '0';
|
|
149
230
|
let poolTokenAmount = '0';
|
|
150
231
|
let poolId;
|
|
151
232
|
let infinityFee;
|
|
152
233
|
const poolKey = buildFlapInfinityPoolKey({
|
|
153
|
-
tokenAddress,
|
|
154
|
-
quoteTokenAddress:
|
|
155
|
-
chain:
|
|
234
|
+
tokenAddress: params.tokenAddress,
|
|
235
|
+
quoteTokenAddress: params.quoteTokenAddress,
|
|
236
|
+
chain: params.chain,
|
|
156
237
|
lpFeeProfile: params.state.lpFeeProfile,
|
|
157
238
|
});
|
|
158
239
|
if (poolKey) {
|
|
@@ -161,48 +242,34 @@ export async function getFlapGraduatedDexMetrics(params) {
|
|
|
161
242
|
try {
|
|
162
243
|
const provider = new JsonRpcProvider(params.rpcUrl);
|
|
163
244
|
const manager = new Contract(poolKey.poolManager, CL_POOL_MANAGER_ABI, provider);
|
|
164
|
-
const [slot0
|
|
165
|
-
manager.getSlot0(poolId),
|
|
166
|
-
manager.getLiquidity(poolId),
|
|
167
|
-
]);
|
|
245
|
+
const [slot0] = await Promise.all([manager.getSlot0(poolId)]);
|
|
168
246
|
const sqrtPriceX96 = BigInt(slot0[0]);
|
|
169
|
-
const liq = BigInt(liquidity);
|
|
170
247
|
if (sqrtPriceX96 > 0n) {
|
|
171
|
-
const
|
|
248
|
+
const quoteAddr = params.quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
|
|
249
|
+
? params.wrappedNativeAddress
|
|
250
|
+
: params.quoteTokenAddress;
|
|
172
251
|
const spot = infinitySpotPriceQuotePerToken({
|
|
173
252
|
sqrtPriceX96,
|
|
174
|
-
tokenAddress,
|
|
253
|
+
tokenAddress: params.tokenAddress,
|
|
175
254
|
currency0: poolKey.currency0,
|
|
176
255
|
currency1: poolKey.currency1,
|
|
177
|
-
quoteTokenAddress:
|
|
256
|
+
quoteTokenAddress: quoteAddr,
|
|
178
257
|
});
|
|
179
258
|
if (spot)
|
|
180
259
|
price = spot;
|
|
181
|
-
const reserves = estimateInfinityReserves({
|
|
182
|
-
liquidity: liq,
|
|
183
|
-
sqrtPriceX96,
|
|
184
|
-
tokenIsCurrency0: tokenIs0,
|
|
185
|
-
});
|
|
186
|
-
poolBNBAmount = reserves.reserveQuote;
|
|
187
|
-
poolTokenAmount = reserves.reserveToken;
|
|
188
260
|
}
|
|
189
261
|
}
|
|
190
262
|
catch {
|
|
191
|
-
|
|
263
|
+
/* keep portal quote */
|
|
192
264
|
}
|
|
193
265
|
}
|
|
266
|
+
const depth = await estimateInfinityPoolQuoteDepth(params.portal, params.tokenAddress, params.wrappedNativeAddress);
|
|
267
|
+
if (parseFloat(depth.poolBNBAmount) > 0) {
|
|
268
|
+
poolBNBAmount = depth.poolBNBAmount;
|
|
269
|
+
poolTokenAmount = depth.poolTokenAmount;
|
|
270
|
+
}
|
|
194
271
|
if (!price)
|
|
195
272
|
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
273
|
return {
|
|
207
274
|
price,
|
|
208
275
|
progress,
|
|
@@ -210,15 +277,53 @@ export async function getFlapGraduatedDexMetrics(params) {
|
|
|
210
277
|
poolTokenAmount,
|
|
211
278
|
poolId,
|
|
212
279
|
infinityFee,
|
|
280
|
+
dexKind: 'PCS_INFINITY_CL',
|
|
213
281
|
};
|
|
214
282
|
}
|
|
215
|
-
/**
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
283
|
+
/**
|
|
284
|
+
* Flap 毕业后(status=4)外盘统一入口:
|
|
285
|
+
* - V7 / Infinity CL → Infinity + Portal quote 深度
|
|
286
|
+
* - 旧版 V3/V2(pool 为 pair 地址)→ 读 pair 余额 + slot0
|
|
287
|
+
* - 否则仅 Portal quote
|
|
288
|
+
*/
|
|
289
|
+
export async function getFlapGraduatedDexMetrics(params) {
|
|
290
|
+
const chain = String(params.chain || 'BSC').toUpperCase();
|
|
291
|
+
const wrappedNative = params.wrappedNativeAddress ?? '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
292
|
+
const quoteToken = params.state.quoteTokenAddress &&
|
|
293
|
+
params.state.quoteTokenAddress.toLowerCase() !== ZERO_ADDRESS
|
|
294
|
+
? params.state.quoteTokenAddress
|
|
295
|
+
: wrappedNative;
|
|
296
|
+
const progress = formatProgress(params.state);
|
|
297
|
+
if (isFlapInfinityCLGraduated(params.state, chain)) {
|
|
298
|
+
return getInfinityCLGraduatedMetrics({
|
|
299
|
+
portal: params.portal,
|
|
300
|
+
tokenAddress: params.tokenAddress,
|
|
301
|
+
state: params.state,
|
|
302
|
+
rpcUrl: params.rpcUrl,
|
|
303
|
+
quoteTokenAddress: quoteToken,
|
|
304
|
+
wrappedNativeAddress: wrappedNative,
|
|
305
|
+
chain,
|
|
306
|
+
});
|
|
222
307
|
}
|
|
223
|
-
|
|
308
|
+
const pool = params.state.pool;
|
|
309
|
+
if (pool && pool.toLowerCase() !== ZERO_ADDRESS && !isInfinityCLPoolManagerAddress(pool, chain)) {
|
|
310
|
+
const v3 = await getGraduatedV3PairMetrics({
|
|
311
|
+
rpcUrl: params.rpcUrl,
|
|
312
|
+
tokenAddress: params.tokenAddress,
|
|
313
|
+
poolAddress: pool,
|
|
314
|
+
quoteTokenAddress: quoteToken,
|
|
315
|
+
wrappedNativeAddress: wrappedNative,
|
|
316
|
+
portal: params.portal,
|
|
317
|
+
});
|
|
318
|
+
if (v3)
|
|
319
|
+
return v3;
|
|
320
|
+
}
|
|
321
|
+
const price = await quotePortalPrice(params.portal, params.tokenAddress, quoteToken, wrappedNative);
|
|
322
|
+
return {
|
|
323
|
+
price: price ?? '0',
|
|
324
|
+
progress,
|
|
325
|
+
poolBNBAmount: '0',
|
|
326
|
+
poolTokenAmount: '0',
|
|
327
|
+
dexKind: 'PORTAL_QUOTE',
|
|
328
|
+
};
|
|
224
329
|
}
|