four-flap-meme-sdk 2.2.12 → 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 CHANGED
@@ -15,6 +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, isFlapInfinityCLGraduated, resolvePortalQuoteInputToken, PCS_INFINITY_CL_POOL_MANAGER_BSC, FLAP_INFINITY_HOOKS_BSC, type GraduatedDexMetrics, type InfinityPoolKey, } from './shared/flap/graduated-dex.js';
18
19
  export { uploadTokenMeta, type TokenMetaInput } from './shared/flap/ipfs.js';
19
20
  export { buildPermitPiggybackAuto } from './shared/flap/permit.js';
20
21
  export { predictVanityTokenAddressByChain, findSaltEndingByChain } from './shared/flap/vanity.js';
package/dist/index.js CHANGED
@@ -48,6 +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, isFlapInfinityCLGraduated, resolvePortalQuoteInputToken, PCS_INFINITY_CL_POOL_MANAGER_BSC, FLAP_INFINITY_HOOKS_BSC, } from './shared/flap/graduated-dex.js';
51
52
  export { uploadTokenMeta } from './shared/flap/ipfs.js';
52
53
  export { buildPermitPiggybackAuto } from './shared/flap/permit.js';
53
54
  export { predictVanityTokenAddressByChain, findSaltEndingByChain } from './shared/flap/vanity.js';
@@ -0,0 +1 @@
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';
@@ -0,0 +1 @@
1
+ export { getFlapGraduatedDexMetrics, buildFlapInfinityPoolKey, computeInfinityPoolId, isInfinityCLPoolManagerAddress, isFlapInfinityCLGraduated, resolvePortalQuoteInputToken, PCS_INFINITY_CL_POOL_MANAGER_BSC, FLAP_INFINITY_HOOKS_BSC, } from '../../utils/pcs-infinity-cl.js';
@@ -3,6 +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, isFlapInfinityCLGraduated, } from './pcs-infinity-cl.js';
6
7
  // ============================================================================
7
8
  // 链配置
8
9
  // ============================================================================
@@ -360,7 +361,72 @@ export async function inspectTokenLP(token, opts) {
360
361
  if (opts.debug)
361
362
  console.log('[LP Inspect] Flap V7 failed, using V5');
362
363
  }
363
- if (st && st.status !== undefined && Number(st.status) !== 4) {
364
+ if (st && st.status !== undefined) {
365
+ const status = Number(st.status);
366
+ // ✅ 已毕业:按池类型分流(Infinity CL / Pancake V3 pair / 继续扫外盘)
367
+ if (status === 4) {
368
+ const quoteAddr = st.quoteTokenAddress && st.quoteTokenAddress.toLowerCase() !== ZERO_ADDRESS
369
+ ? st.quoteTokenAddress
370
+ : chainConfig.wrappedNative;
371
+ let quoteDecimals = 18;
372
+ let quoteSymbol = chainConfig.wrappedNativeSymbol;
373
+ if (quoteAddr.toLowerCase() !== chainConfig.wrappedNative.toLowerCase()) {
374
+ quoteDecimals = await getTokenDecimals(quoteAddr, provider);
375
+ const stableCoin = chainConfig.stableCoins.find(c => c.address.toLowerCase() === quoteAddr.toLowerCase());
376
+ quoteSymbol = stableCoin?.symbol || 'TOKEN';
377
+ }
378
+ const metrics = await getFlapGraduatedDexMetrics({
379
+ portal: flap,
380
+ tokenAddress: token,
381
+ state: st,
382
+ rpcUrl: opts.rpcUrl,
383
+ wrappedNativeAddress: chainConfig.wrappedNative,
384
+ chain: opts.chain,
385
+ });
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
+ }
429
+ }
364
430
  result.platform = 'FLAP';
365
431
  let quoteDecimals = 18;
366
432
  let quoteSymbol = chainConfig.wrappedNativeSymbol;
@@ -0,0 +1,63 @@
1
+ import type { FlapPortal } from '../shared/flap/portal.js';
2
+ import type { TokenStateV7 } from '../shared/flap/portal.js';
3
+ /** PancakeSwap Infinity CL PoolManager(BSC / Base 同址) */
4
+ export declare const PCS_INFINITY_CL_POOL_MANAGER_BSC = "0xa0FfB9c1CE1Fe56963B0321B32E7A0302114058b";
5
+ /** Flap Portal 毕业到 Infinity CL 时使用的 hooks(BSC) */
6
+ export declare const FLAP_INFINITY_HOOKS_BSC = "0xF1A9aA042454b8553bE3896597ff11a0f011c1c1";
7
+ export type InfinityPoolKey = {
8
+ currency0: string;
9
+ currency1: string;
10
+ hooks: string;
11
+ poolManager: string;
12
+ fee: number;
13
+ parameters: string;
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
+ };
24
+ export declare function sortInfinityCurrencies(a: string, b: string): [string, string];
25
+ export declare function computeInfinityPoolId(key: InfinityPoolKey): string;
26
+ export declare function buildFlapInfinityPoolKey(params: {
27
+ tokenAddress: string;
28
+ quoteTokenAddress: string;
29
+ chain: string;
30
+ lpFeeProfile?: number;
31
+ }): InfinityPoolKey | null;
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;
41
+ export declare function infinitySpotPriceQuotePerToken(params: {
42
+ sqrtPriceX96: bigint;
43
+ tokenAddress: string;
44
+ currency0: string;
45
+ currency1: string;
46
+ quoteTokenAddress: string;
47
+ tokenDecimals?: number;
48
+ quoteDecimals?: number;
49
+ }): string | null;
50
+ /**
51
+ * Flap 毕业后(status=4)外盘统一入口:
52
+ * - V7 / Infinity CL → Infinity + Portal quote 深度
53
+ * - 旧版 V3/V2(pool 为 pair 地址)→ 读 pair 余额 + slot0
54
+ * - 否则仅 Portal quote
55
+ */
56
+ export declare function getFlapGraduatedDexMetrics(params: {
57
+ portal: FlapPortal;
58
+ tokenAddress: string;
59
+ state: TokenStateV7;
60
+ rpcUrl: string;
61
+ wrappedNativeAddress?: string;
62
+ chain?: string;
63
+ }): Promise<GraduatedDexMetrics>;
@@ -0,0 +1,329 @@
1
+ import { AbiCoder, Contract, JsonRpcProvider, formatEther, formatUnits, keccak256 } from 'ethers';
2
+ import { ZERO_ADDRESS } from '../shared/flap/constants.js';
3
+ import { ERC20_ABI } from '../shared/abis/common.js';
4
+ /** PancakeSwap Infinity CL PoolManager(BSC / Base 同址) */
5
+ export const PCS_INFINITY_CL_POOL_MANAGER_BSC = '0xa0FfB9c1CE1Fe56963B0321B32E7A0302114058b';
6
+ /** Flap Portal 毕业到 Infinity CL 时使用的 hooks(BSC) */
7
+ export const FLAP_INFINITY_HOOKS_BSC = '0xF1A9aA042454b8553bE3896597ff11a0f011c1c1';
8
+ const CL_POOL_MANAGER_ABI = [
9
+ 'function getSlot0(bytes32 id) view returns (uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 lpFee)',
10
+ 'function getLiquidity(bytes32 id) view returns (uint128 liquidity)',
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
+ ];
17
+ /** BSC:lpFeeProfile → Infinity PoolKey 的 fee / parameters(与链上 Initialize 一致) */
18
+ const BSC_INFINITY_POOL_KEY_BY_PROFILE = {
19
+ 0: {
20
+ fee: 8534,
21
+ parameters: '0x0000000000000000000000000000000000000000000000000000000000fa00d5',
22
+ },
23
+ };
24
+ export function sortInfinityCurrencies(a, b) {
25
+ return a.toLowerCase() < b.toLowerCase() ? [a, b] : [b, a];
26
+ }
27
+ export function computeInfinityPoolId(key) {
28
+ const coder = AbiCoder.defaultAbiCoder();
29
+ return keccak256(coder.encode(['address', 'address', 'address', 'address', 'uint24', 'bytes32'], [key.currency0, key.currency1, key.hooks, key.poolManager, key.fee, key.parameters]));
30
+ }
31
+ export function buildFlapInfinityPoolKey(params) {
32
+ const chain = String(params.chain || '').toUpperCase();
33
+ if (chain !== 'BSC')
34
+ return null;
35
+ const meta = BSC_INFINITY_POOL_KEY_BY_PROFILE[Number(params.lpFeeProfile ?? 0)] ??
36
+ BSC_INFINITY_POOL_KEY_BY_PROFILE[0];
37
+ const quote = params.quoteTokenAddress &&
38
+ params.quoteTokenAddress.toLowerCase() !== ZERO_ADDRESS
39
+ ? params.quoteTokenAddress
40
+ : '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
41
+ const [currency0, currency1] = sortInfinityCurrencies(params.tokenAddress, quote);
42
+ return {
43
+ currency0,
44
+ currency1,
45
+ hooks: FLAP_INFINITY_HOOKS_BSC,
46
+ poolManager: PCS_INFINITY_CL_POOL_MANAGER_BSC,
47
+ fee: meta.fee,
48
+ parameters: meta.parameters,
49
+ };
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
+ }
77
+ function formatRatio(n, d, precision = 18) {
78
+ if (d === 0n)
79
+ return '0';
80
+ const scale = 10n ** BigInt(precision);
81
+ const x = (n * scale) / d;
82
+ const s = x.toString().padStart(precision + 1, '0');
83
+ const int = s.slice(0, -precision);
84
+ const frac = s.slice(-precision).replace(/0+$/, '');
85
+ return frac ? `${int}.${frac}` : int;
86
+ }
87
+ export function infinitySpotPriceQuotePerToken(params) {
88
+ const tokenLower = params.tokenAddress.toLowerCase();
89
+ const quoteLower = params.quoteTokenAddress.toLowerCase();
90
+ const tokenIs0 = params.currency0.toLowerCase() === tokenLower;
91
+ const tokenIs1 = params.currency1.toLowerCase() === tokenLower;
92
+ const quoteIs0 = params.currency0.toLowerCase() === quoteLower;
93
+ const quoteIs1 = params.currency1.toLowerCase() === quoteLower;
94
+ if ((!tokenIs0 && !tokenIs1) || (!quoteIs0 && !quoteIs1))
95
+ return null;
96
+ const dec0 = params.tokenDecimals ?? 18;
97
+ const dec1 = params.quoteDecimals ?? 18;
98
+ const tokenDec = tokenIs0 ? dec0 : dec1;
99
+ const quoteDec = quoteIs0 ? dec0 : dec1;
100
+ const Q192 = 2n ** 192n;
101
+ const num = params.sqrtPriceX96 * params.sqrtPriceX96;
102
+ let n;
103
+ let d;
104
+ if (tokenIs0) {
105
+ n = num * 10n ** BigInt(tokenDec);
106
+ d = Q192 * 10n ** BigInt(quoteDec);
107
+ }
108
+ else {
109
+ n = Q192 * 10n ** BigInt(quoteDec);
110
+ d = num * 10n ** BigInt(tokenDec);
111
+ }
112
+ return formatRatio(n, d, 18);
113
+ }
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
+ }
144
+ }
145
+ return { poolBNBAmount: '0', poolTokenAmount: '0' };
146
+ }
147
+ async function quotePortalPrice(portal, tokenAddress, quoteTokenAddress, wrappedNativeAddress) {
148
+ try {
149
+ const buyAmt = 10n ** 16n;
150
+ const sellAmt = 10n ** 21n;
151
+ const inputQuote = resolvePortalQuoteInputToken(quoteTokenAddress, wrappedNativeAddress);
152
+ const [tokensOut, quoteOut] = await Promise.all([
153
+ portal.quoteExactInput({
154
+ inputToken: inputQuote,
155
+ outputToken: tokenAddress,
156
+ inputAmount: buyAmt,
157
+ }),
158
+ portal.quoteExactInput({
159
+ inputToken: tokenAddress,
160
+ outputToken: inputQuote,
161
+ inputAmount: sellAmt,
162
+ }).catch(() => 0n),
163
+ ]);
164
+ if (tokensOut <= 0n)
165
+ return null;
166
+ const buyPrice = Number(formatEther(buyAmt)) / Number(formatEther(tokensOut));
167
+ if (quoteOut <= 0n)
168
+ return String(buyPrice);
169
+ const sellPrice = Number(formatEther(quoteOut)) / Number(formatEther(sellAmt));
170
+ return String((buyPrice + sellPrice) / 2);
171
+ }
172
+ catch {
173
+ return null;
174
+ }
175
+ }
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);
229
+ let poolBNBAmount = '0';
230
+ let poolTokenAmount = '0';
231
+ let poolId;
232
+ let infinityFee;
233
+ const poolKey = buildFlapInfinityPoolKey({
234
+ tokenAddress: params.tokenAddress,
235
+ quoteTokenAddress: params.quoteTokenAddress,
236
+ chain: params.chain,
237
+ lpFeeProfile: params.state.lpFeeProfile,
238
+ });
239
+ if (poolKey) {
240
+ poolId = computeInfinityPoolId(poolKey);
241
+ infinityFee = poolKey.fee;
242
+ try {
243
+ const provider = new JsonRpcProvider(params.rpcUrl);
244
+ const manager = new Contract(poolKey.poolManager, CL_POOL_MANAGER_ABI, provider);
245
+ const [slot0] = await Promise.all([manager.getSlot0(poolId)]);
246
+ const sqrtPriceX96 = BigInt(slot0[0]);
247
+ if (sqrtPriceX96 > 0n) {
248
+ const quoteAddr = params.quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
249
+ ? params.wrappedNativeAddress
250
+ : params.quoteTokenAddress;
251
+ const spot = infinitySpotPriceQuotePerToken({
252
+ sqrtPriceX96,
253
+ tokenAddress: params.tokenAddress,
254
+ currency0: poolKey.currency0,
255
+ currency1: poolKey.currency1,
256
+ quoteTokenAddress: quoteAddr,
257
+ });
258
+ if (spot)
259
+ price = spot;
260
+ }
261
+ }
262
+ catch {
263
+ /* keep portal quote */
264
+ }
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
+ }
271
+ if (!price)
272
+ price = '0';
273
+ return {
274
+ price,
275
+ progress,
276
+ poolBNBAmount,
277
+ poolTokenAmount,
278
+ poolId,
279
+ infinityFee,
280
+ dexKind: 'PCS_INFINITY_CL',
281
+ };
282
+ }
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
+ });
307
+ }
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
+ };
329
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "2.2.12",
3
+ "version": "2.2.14",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",