four-flap-meme-sdk 2.2.14 → 2.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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, isFlapInfinityCLGraduated, resolvePortalQuoteInputToken, 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, isFlapInfinityCLGraduated, resolvePortalQuoteInputToken, 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, isFlapInfinityCLGraduated, resolvePortalQuoteInputToken, 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, isFlapInfinityCLGraduated, resolvePortalQuoteInputToken, 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';
@@ -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 = {
@@ -3,6 +3,8 @@ import { ZERO_ADDRESS } from '../shared/flap/constants.js';
3
3
  import { ERC20_ABI } from '../shared/abis/common.js';
4
4
  /** PancakeSwap Infinity CL PoolManager(BSC / Base 同址) */
5
5
  export const PCS_INFINITY_CL_POOL_MANAGER_BSC = '0xa0FfB9c1CE1Fe56963B0321B32E7A0302114058b';
6
+ /** PancakeSwap Infinity Vault(全池代币记账;BSC / Base 同址) */
7
+ export const PCS_INFINITY_VAULT_BSC = '0x238a358808379702088667322f80aC48bAd5e6c4';
6
8
  /** Flap Portal 毕业到 Infinity CL 时使用的 hooks(BSC) */
7
9
  export const FLAP_INFINITY_HOOKS_BSC = '0xF1A9aA042454b8553bE3896597ff11a0f011c1c1';
8
10
  const CL_POOL_MANAGER_ABI = [
@@ -120,54 +122,73 @@ function v3SpotPriceQuotePerToken(params) {
120
122
  quoteTokenAddress: params.quoteTokenAddress,
121
123
  });
122
124
  }
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) {
125
+ const providerByRpc = new Map();
126
+ const gradMetricsCache = new Map();
127
+ const GRAD_METRICS_TTL_MS = 2500;
128
+ function getCachedRpcProvider(rpcUrl) {
129
+ let p = providerByRpc.get(rpcUrl);
130
+ if (!p) {
131
+ p = new JsonRpcProvider(rpcUrl);
132
+ providerByRpc.set(rpcUrl, p);
133
+ }
134
+ return p;
135
+ }
136
+ /** 单次 quote 估 WBNB 深度(市值场景优先速度) */
137
+ async function estimateInfinityPoolQuoteBnbDepth(portal, tokenAddress) {
138
+ try {
139
+ const bnbOut = await portal.quoteExactInput({
140
+ inputToken: tokenAddress,
141
+ outputToken: ZERO_ADDRESS,
142
+ inputAmount: 110000000n * 10n ** 18n,
143
+ });
144
+ if (bnbOut > 0n)
145
+ return formatEther(bnbOut);
146
+ }
147
+ catch {
128
148
  try {
129
149
  const bnbOut = await portal.quoteExactInput({
130
150
  inputToken: tokenAddress,
131
- outputToken: inputQuote,
132
- inputAmount: amt,
151
+ outputToken: ZERO_ADDRESS,
152
+ inputAmount: 100000000n * 10n ** 18n,
133
153
  });
134
- if (bnbOut > 0n) {
135
- return {
136
- poolBNBAmount: formatEther(bnbOut),
137
- poolTokenAmount: formatEther(amt),
138
- };
139
- }
140
- }
141
- catch {
142
- /* try smaller */
154
+ if (bnbOut > 0n)
155
+ return formatEther(bnbOut);
143
156
  }
157
+ catch { /* ignore */ }
144
158
  }
145
- return { poolBNBAmount: '0', poolTokenAmount: '0' };
159
+ return '0';
146
160
  }
161
+ /**
162
+ * Infinity CL 池内代币量:读 Vault 中该 token 余额(与 GMGN 池子代币列一致)
163
+ * 注意:Vault 为全协议记账层,对 Flap 毕业主池通常即该交易对持仓。
164
+ */
165
+ async function getInfinityCLPoolTokenReserve(params) {
166
+ const chain = String(params.chain || '').toUpperCase();
167
+ const vault = chain === 'BSC' || chain === 'BASE' ? PCS_INFINITY_VAULT_BSC : '';
168
+ if (!vault)
169
+ return '0';
170
+ try {
171
+ const provider = params.provider ?? getCachedRpcProvider(params.rpcUrl);
172
+ const bal = (await new Contract(params.tokenAddress, ERC20_ABI, provider).balanceOf(vault));
173
+ return formatEther(bal);
174
+ }
175
+ catch {
176
+ return '0';
177
+ }
178
+ }
179
+ /** 单笔 buy quote 估价(比买卖双 quote 少一次 RPC) */
147
180
  async function quotePortalPrice(portal, tokenAddress, quoteTokenAddress, wrappedNativeAddress) {
148
181
  try {
149
182
  const buyAmt = 10n ** 16n;
150
- const sellAmt = 10n ** 21n;
151
183
  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
- ]);
184
+ const tokensOut = await portal.quoteExactInput({
185
+ inputToken: inputQuote,
186
+ outputToken: tokenAddress,
187
+ inputAmount: buyAmt,
188
+ });
164
189
  if (tokensOut <= 0n)
165
190
  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);
191
+ return String(Number(formatEther(buyAmt)) / Number(formatEther(tokensOut)));
171
192
  }
172
193
  catch {
173
194
  return null;
@@ -183,18 +204,22 @@ function formatProgress(state) {
183
204
  }
184
205
  async function getGraduatedV3PairMetrics(params) {
185
206
  try {
186
- const provider = new JsonRpcProvider(params.rpcUrl);
207
+ const provider = getCachedRpcProvider(params.rpcUrl);
187
208
  const pool = new Contract(params.poolAddress, V3_PAIR_ABI, provider);
209
+ const tokenLower = params.tokenAddress.toLowerCase();
210
+ const quoteAddr = params.quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
211
+ ? params.wrappedNativeAddress
212
+ : params.quoteTokenAddress;
188
213
  const [t0, t1, slot0] = await Promise.all([
189
214
  pool.token0(),
190
215
  pool.token1(),
191
216
  pool.slot0(),
192
217
  ]);
218
+ const [bal0, bal1] = await Promise.all([
219
+ new Contract(t0, ERC20_ABI, provider).balanceOf(params.poolAddress),
220
+ new Contract(t1, ERC20_ABI, provider).balanceOf(params.poolAddress),
221
+ ]);
193
222
  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
223
  const spot = v3SpotPriceQuotePerToken({
199
224
  sqrtPriceX96,
200
225
  tokenAddress: params.tokenAddress,
@@ -202,13 +227,16 @@ async function getGraduatedV3PairMetrics(params) {
202
227
  token1: t1,
203
228
  quoteTokenAddress: quoteAddr,
204
229
  });
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
230
  const tokenIs0 = t0.toLowerCase() === tokenLower;
210
231
  const poolTokenAmount = formatEther(tokenIs0 ? bal0 : bal1);
211
232
  const poolBNBAmount = formatEther(tokenIs0 ? bal1 : bal0);
233
+ let price = spot;
234
+ if (!price && parseFloat(poolTokenAmount) > 0) {
235
+ price = String(parseFloat(poolBNBAmount) / parseFloat(poolTokenAmount));
236
+ }
237
+ if (!price) {
238
+ price = await quotePortalPrice(params.portal, params.tokenAddress, params.quoteTokenAddress, params.wrappedNativeAddress);
239
+ }
212
240
  if (!price && poolTokenAmount === '0' && poolBNBAmount === '0')
213
241
  return null;
214
242
  return {
@@ -225,53 +253,65 @@ async function getGraduatedV3PairMetrics(params) {
225
253
  }
226
254
  async function getInfinityCLGraduatedMetrics(params) {
227
255
  const progress = formatProgress(params.state);
228
- let price = await quotePortalPrice(params.portal, params.tokenAddress, params.quoteTokenAddress, params.wrappedNativeAddress);
256
+ const provider = getCachedRpcProvider(params.rpcUrl);
229
257
  let poolBNBAmount = '0';
230
258
  let poolTokenAmount = '0';
231
259
  let poolId;
232
260
  let infinityFee;
261
+ let price = null;
233
262
  const poolKey = buildFlapInfinityPoolKey({
234
263
  tokenAddress: params.tokenAddress,
235
264
  quoteTokenAddress: params.quoteTokenAddress,
236
265
  chain: params.chain,
237
266
  lpFeeProfile: params.state.lpFeeProfile,
238
267
  });
239
- if (poolKey) {
268
+ const quoteAddr = params.quoteTokenAddress.toLowerCase() === ZERO_ADDRESS
269
+ ? params.wrappedNativeAddress
270
+ : params.quoteTokenAddress;
271
+ const slot0Promise = (async () => {
272
+ if (!poolKey)
273
+ return null;
240
274
  poolId = computeInfinityPoolId(poolKey);
241
275
  infinityFee = poolKey.fee;
242
276
  try {
243
- const provider = new JsonRpcProvider(params.rpcUrl);
244
277
  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
- }
278
+ const slot0 = await manager.getSlot0(poolId);
279
+ return BigInt(slot0[0]);
261
280
  }
262
281
  catch {
263
- /* keep portal quote */
282
+ return null;
264
283
  }
284
+ })();
285
+ const [sqrtPriceX96, tokenReserve, bnbDepth] = await Promise.all([
286
+ slot0Promise,
287
+ getInfinityCLPoolTokenReserve({
288
+ tokenAddress: params.tokenAddress,
289
+ rpcUrl: params.rpcUrl,
290
+ chain: params.chain,
291
+ provider,
292
+ }),
293
+ estimateInfinityPoolQuoteBnbDepth(params.portal, params.tokenAddress),
294
+ ]);
295
+ if (sqrtPriceX96 && sqrtPriceX96 > 0n && poolKey) {
296
+ const spot = infinitySpotPriceQuotePerToken({
297
+ sqrtPriceX96,
298
+ tokenAddress: params.tokenAddress,
299
+ currency0: poolKey.currency0,
300
+ currency1: poolKey.currency1,
301
+ quoteTokenAddress: quoteAddr,
302
+ });
303
+ if (spot)
304
+ price = spot;
265
305
  }
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;
306
+ if (parseFloat(tokenReserve) > 0)
307
+ poolTokenAmount = tokenReserve;
308
+ if (parseFloat(bnbDepth) > 0)
309
+ poolBNBAmount = bnbDepth;
310
+ if (!price) {
311
+ price = await quotePortalPrice(params.portal, params.tokenAddress, params.quoteTokenAddress, params.wrappedNativeAddress);
270
312
  }
271
- if (!price)
272
- price = '0';
273
313
  return {
274
- price,
314
+ price: price ?? '0',
275
315
  progress,
276
316
  poolBNBAmount,
277
317
  poolTokenAmount,
@@ -288,14 +328,20 @@ async function getInfinityCLGraduatedMetrics(params) {
288
328
  */
289
329
  export async function getFlapGraduatedDexMetrics(params) {
290
330
  const chain = String(params.chain || 'BSC').toUpperCase();
331
+ const tokenKey = `${chain}:${params.tokenAddress.toLowerCase()}`;
332
+ const cached = gradMetricsCache.get(tokenKey);
333
+ if (cached && Date.now() - cached.at < GRAD_METRICS_TTL_MS) {
334
+ return cached.data;
335
+ }
291
336
  const wrappedNative = params.wrappedNativeAddress ?? '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
292
337
  const quoteToken = params.state.quoteTokenAddress &&
293
338
  params.state.quoteTokenAddress.toLowerCase() !== ZERO_ADDRESS
294
339
  ? params.state.quoteTokenAddress
295
340
  : wrappedNative;
296
341
  const progress = formatProgress(params.state);
342
+ let data;
297
343
  if (isFlapInfinityCLGraduated(params.state, chain)) {
298
- return getInfinityCLGraduatedMetrics({
344
+ data = await getInfinityCLGraduatedMetrics({
299
345
  portal: params.portal,
300
346
  tokenAddress: params.tokenAddress,
301
347
  state: params.state,
@@ -305,25 +351,42 @@ export async function getFlapGraduatedDexMetrics(params) {
305
351
  chain,
306
352
  });
307
353
  }
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;
354
+ else {
355
+ const pool = params.state.pool;
356
+ if (pool && pool.toLowerCase() !== ZERO_ADDRESS && !isInfinityCLPoolManagerAddress(pool, chain)) {
357
+ const v3 = await getGraduatedV3PairMetrics({
358
+ rpcUrl: params.rpcUrl,
359
+ tokenAddress: params.tokenAddress,
360
+ poolAddress: pool,
361
+ quoteTokenAddress: quoteToken,
362
+ wrappedNativeAddress: wrappedNative,
363
+ portal: params.portal,
364
+ });
365
+ if (v3) {
366
+ data = v3;
367
+ }
368
+ else {
369
+ const price = await quotePortalPrice(params.portal, params.tokenAddress, quoteToken, wrappedNative);
370
+ data = {
371
+ price: price ?? '0',
372
+ progress,
373
+ poolBNBAmount: '0',
374
+ poolTokenAmount: '0',
375
+ dexKind: 'PORTAL_QUOTE',
376
+ };
377
+ }
378
+ }
379
+ else {
380
+ const price = await quotePortalPrice(params.portal, params.tokenAddress, quoteToken, wrappedNative);
381
+ data = {
382
+ price: price ?? '0',
383
+ progress,
384
+ poolBNBAmount: '0',
385
+ poolTokenAmount: '0',
386
+ dexKind: 'PORTAL_QUOTE',
387
+ };
388
+ }
320
389
  }
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
- };
390
+ gradMetricsCache.set(tokenKey, { at: Date.now(), data });
391
+ return data;
329
392
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "2.2.14",
3
+ "version": "2.2.16",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",