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 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';
@@ -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
- // ✅ 已毕业:PCS Infinity CL 外盘(Portal pool 字段为 CLPoolManager)
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
- result.platform = 'FLAP';
386
- result.flap = {
387
- quoteToken: quoteAddr,
388
- quoteSymbol,
389
- quoteDecimals,
390
- reserveNative: metrics.poolBNBAmount,
391
- circulatingSupply: formatBalance(st.circulatingSupply, shouldFormat, tokenDecimals ?? 18),
392
- price: metrics.price,
393
- taxRate,
394
- };
395
- if (taxRate && taxRate > 0)
396
- result.taxRate = taxRate;
397
- const liqRaw = metrics.poolBNBAmount
398
- ? BigInt(Math.floor(parseFloat(metrics.poolBNBAmount) * 1e18))
399
- : 0n;
400
- const tokRaw = metrics.poolTokenAmount
401
- ? BigInt(Math.floor(parseFloat(metrics.poolTokenAmount) * 1e18))
402
- : 0n;
403
- result.bestPools.push({
404
- dex: 'PancakeSwap Infinity CL',
405
- dexKey: 'PCS_INFINITY_CL',
406
- version: 'v3',
407
- fee: metrics.infinityFee,
408
- quoteToken: quoteAddr,
409
- quoteSymbol,
410
- quoteDecimals,
411
- pairAddress: metrics.poolId ?? PCS_INFINITY_CL_POOL_MANAGER_BSC,
412
- liquidity: metrics.poolBNBAmount,
413
- liquidityRaw: liqRaw,
414
- reserveToken: metrics.poolTokenAmount,
415
- reserveTokenRaw: tokRaw,
416
- });
417
- return result;
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
- /** slot0 计算 quote per token(与 memeweb V3 spot 公式一致) */
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)外盘:PCS Infinity CL + Portal quote
52
- * getTokenV7().pool V7 标准币上为 CLPoolManager 合约地址,非 V3 pair。
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 { FLAP_TOTAL_SUPPLY, ZERO_ADDRESS } from '../shared/flap/constants.js';
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'; // WBNB
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
- /** 用活跃流动性 L 与 sqrtPrice 估算两侧虚拟储备(UI 展示用) */
84
- export function estimateInfinityReserves(params) {
85
- const Q96 = 2n ** 96n;
86
- if (params.liquidity === 0n || params.sqrtPriceX96 === 0n) {
87
- return { reserveToken: '0', reserveQuote: '0' };
88
- }
89
- // float 估算即可满足面板展示
90
- const sqrtP = Number(params.sqrtPriceX96) / Number(Q96);
91
- const L = Number(params.liquidity);
92
- if (!Number.isFinite(sqrtP) || sqrtP <= 0) {
93
- return { reserveToken: '0', reserveQuote: '0' };
94
- }
95
- const amount1 = (L * sqrtP) / 1e18;
96
- const amount0 = L / sqrtP / 1e18;
97
- if (params.tokenIsCurrency0) {
98
- return { reserveToken: String(amount0), reserveQuote: String(amount1) };
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 { reserveToken: String(amount1), reserveQuote: String(amount0) };
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; // 0.01 native
105
- const sellAmt = 10n ** 21n; // 1000 tokens
106
- const inputQuote = quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
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 || quoteOut <= 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
- * Flap 毕业后(status=4)外盘:PCS Infinity CL + Portal quote
133
- * getTokenV7().pool V7 标准币上为 CLPoolManager 合约地址,非 V3 pair。
134
- */
135
- export async function getFlapGraduatedDexMetrics(params) {
136
- const tokenAddress = params.tokenAddress;
137
- const quoteToken = params.state.quoteTokenAddress &&
138
- params.state.quoteTokenAddress.toLowerCase() !== ZERO_ADDRESS
139
- ? params.state.quoteTokenAddress
140
- : params.wrappedNativeAddress ?? '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
141
- const progressRaw = params.state.progress ?? 0n;
142
- const progress = progressRaw > 10n ** 18n
143
- ? formatUnits(progressRaw, 18)
144
- : progressRaw >= 100n
145
- ? '100'
146
- : String(progressRaw);
147
- let price = await quotePortalPrice(params.portal, tokenAddress, quoteToken);
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: quoteToken,
155
- chain: 'BSC',
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, liquidity] = await Promise.all([
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 tokenIs0 = poolKey.currency0.toLowerCase() === tokenAddress.toLowerCase();
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: quoteToken,
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
- // slot0 失败时保留 portal quote 价格
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
- /** 判断 getTokenV7.pool 是否为 Infinity CL PoolManager(而非 V3 pair) */
216
- export function isInfinityCLPoolManagerAddress(pool, chain) {
217
- if (!pool)
218
- return false;
219
- const c = String(chain || '').toUpperCase();
220
- if (c === 'BSC' || c === 'BASE') {
221
- return pool.toLowerCase() === PCS_INFINITY_CL_POOL_MANAGER_BSC.toLowerCase();
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
- return false;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "2.2.13",
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",