@strkfarm/sdk 1.1.22 → 1.1.23

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.
@@ -37,6 +37,8 @@ import { EkuboHarvests } from "@/modules/harvests";
37
37
  import { logger } from "@/utils/logger";
38
38
  import { COMMON_CONTRACTS } from "./constants";
39
39
  import { DepegRiskLevel, ImpermanentLossLevel, MarketRiskLevel, SmartContractRiskLevel } from "@/interfaces/risks";
40
+ import { gql } from "@apollo/client";
41
+ import apolloClient from "@/modules/apollo-client";
40
42
 
41
43
  export interface EkuboPoolKey {
42
44
  token0: ContractAddr;
@@ -51,6 +53,12 @@ export interface EkuboBounds {
51
53
  upperTick: bigint;
52
54
  }
53
55
 
56
+ interface FeeHistory {
57
+ date: string;
58
+ tokenInfo: TokenInfo;
59
+ amount: Web3Number;
60
+ }
61
+
54
62
  /**
55
63
  * Settings for the CLVaultStrategy
56
64
  *
@@ -285,11 +293,97 @@ export class EkuboCLVault extends BaseStrategy<
285
293
  return [this.contract.populate("handle_fees", [])];
286
294
  }
287
295
 
288
- /**
289
- * Calculates assets before and now in a given token of TVL per share to observe growth
290
- * @returns {Promise<number>} The weighted average APY across all pools
291
- */
292
- async netAPY(
296
+ async getFeeHistory(timePeriod: '24h' | '30d' | '3m' = '24h'): Promise<{
297
+ summary: DualTokenInfo,
298
+ history: FeeHistory[]
299
+ }> {
300
+ const { data } = await apolloClient.query({
301
+ query: gql`
302
+ query ContractFeeEarnings(
303
+ $timeframe: String!
304
+ $contract: String!
305
+ ) {
306
+ contractFeeEarnings(timeframe: $timeframe, contract: $contract) {
307
+ contract
308
+ dailyEarnings {
309
+ date
310
+ tokenAddress
311
+ amount
312
+ }
313
+ totalCollections
314
+ }
315
+ }
316
+ `,
317
+ variables: {
318
+ timeframe: timePeriod,
319
+ contract: this.address.address
320
+ },
321
+ fetchPolicy: 'no-cache',
322
+ });
323
+
324
+ // Sample response type:
325
+ // {
326
+ // contractFeeEarnings: {
327
+ // contract: '0x2bcaef2eb7706875a5fdc6853dd961a0590f850bc3a031c59887189b5e84ba1',
328
+ // dailyEarnings: [ [Object], [Object], [Object], [Object] ],
329
+ // totalCollections: 3,
330
+ // __typename: 'FeeSummary'
331
+ // }
332
+ // }
333
+ // dailyEarnings: {
334
+ // date: '2025-09-27',
335
+ // tokenAddress: '0x3fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac',
336
+ // amount: '15404',
337
+ // __typename: 'DailyFeeEarnings'
338
+ // }[]
339
+
340
+ // get pool key and token info
341
+ const poolKey = await this.getPoolKey();
342
+ const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
343
+ const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
344
+ const price0 = await this.pricer.getPrice(token0Info.symbol);
345
+ const price1 = await this.pricer.getPrice(token1Info.symbol);
346
+ let totalToken0Amount = Web3Number.fromWei(0, token0Info.decimals);
347
+ let totalToken1Amount = Web3Number.fromWei(0, token1Info.decimals);
348
+ let totalToken0Usd = 0;
349
+ let totalToken1Usd = 0;
350
+ const parsedFeeInfo: FeeHistory[] = [];
351
+ const feeInfo = data.contractFeeEarnings.dailyEarnings;
352
+ for (const d of feeInfo) {
353
+ const tokenInfo = await Global.getTokenInfoFromAddr(ContractAddr.from(d.tokenAddress));
354
+ const amount = Web3Number.fromWei(d.amount, tokenInfo.decimals);
355
+ if (tokenInfo.address.eq(poolKey.token0)) {
356
+ totalToken0Amount = totalToken0Amount.plus(amount);
357
+ totalToken0Usd = totalToken0Usd + amount.multipliedBy(price0.price).toNumber();
358
+ } else {
359
+ totalToken1Amount = totalToken1Amount.plus(amount);
360
+ totalToken1Usd = totalToken1Usd + amount.multipliedBy(price1.price).toNumber();
361
+ }
362
+ parsedFeeInfo.push({
363
+ date: d.date,
364
+ tokenInfo,
365
+ amount: Web3Number.fromWei(d.amount, tokenInfo.decimals)
366
+ });
367
+ }
368
+ return {
369
+ summary: {
370
+ usdValue: totalToken0Usd + totalToken1Usd,
371
+ token0: {
372
+ tokenInfo: token0Info,
373
+ amount: totalToken0Amount,
374
+ usdValue: totalToken0Usd
375
+ },
376
+ token1: {
377
+ tokenInfo: token1Info,
378
+ amount: totalToken1Amount,
379
+ usdValue: totalToken1Usd
380
+ }
381
+ },
382
+ history: parsedFeeInfo
383
+ }
384
+ }
385
+
386
+ async netSharesBasedTrueAPY(
293
387
  blockIdentifier: BlockIdentifier = "latest",
294
388
  sinceBlocks = 600000
295
389
  ): Promise<number> {
@@ -354,6 +448,33 @@ export class EkuboCLVault extends BaseStrategy<
354
448
  return (apyForGivenBlocks * (365 * 24 * 3600)) / timeDiffSeconds;
355
449
  }
356
450
 
451
+ async feeBasedAPY(
452
+ blockIdentifier: BlockIdentifier = "latest",
453
+ sinceBlocks = 600000
454
+ ): Promise<number> {
455
+ const feeInfo = await this.getFeeHistory('24h');
456
+ const tvlNow = await this.getTVL('latest');
457
+ return feeInfo.summary.usdValue * 365 / tvlNow.usdValue;
458
+ }
459
+
460
+ /**
461
+ * Calculates assets before and now in a given token of TVL per share to observe growth
462
+ * @returns {Promise<number>} The weighted average APY across all pools
463
+ */
464
+ async netAPY(
465
+ blockIdentifier: BlockIdentifier = "latest",
466
+ sinceBlocks = 600000
467
+ ): Promise<number> {
468
+ const isUSDCQouteToken = this.metadata.additionalInfo.quoteAsset.symbol === "USDC";
469
+ if (!isUSDCQouteToken) {
470
+ // good for LSTs and stables
471
+ return this.netSharesBasedTrueAPY(blockIdentifier, sinceBlocks);
472
+ } else {
473
+ // good for non-stables
474
+ return this.feeBasedAPY(blockIdentifier, sinceBlocks);
475
+ }
476
+ }
477
+
357
478
  async getHarvestRewardShares(fromBlock: number, toBlock: number) {
358
479
  const len = Number(await this.contract.call("get_total_rewards"));
359
480
  let shares = Web3Number.fromWei(0, 18);
@@ -1893,7 +2014,7 @@ const ETHUSDCRe7Strategy: IStrategyMetadata<CLVaultStrategySettings> = {
1893
2014
  Global.getDefaultTokens().find((t) => t.symbol === "USDC")!
1894
2015
  ],
1895
2016
  apyMethodology:
1896
- "APY based on 7-day historical performance, including fees and rewards.",
2017
+ "Annualized fee APY, calculated as fees earned in the last 24h divided by TVL",
1897
2018
  additionalInfo: {
1898
2019
  newBounds: "Managed by Re7",
1899
2020
  truePrice: 1,
@@ -1913,6 +2034,11 @@ const ETHUSDCRe7Strategy: IStrategyMetadata<CLVaultStrategySettings> = {
1913
2034
  answer:
1914
2035
  <div>Re7 Labs is the curator of this strategy. Re7 Labs is a well-known Web3 asset management firm. This strategy is completely managed by them, including ownership of the vault. Troves is developer of the smart contracts and maintains infrastructure to help users access these strategies. You can find more information about them on their website <a href='https://www.re7labs.xyz' style={{textDecoration: "underline", marginLeft: "2px"}} target="_blank">here</a>.</div>
1915
2036
  },
2037
+ {
2038
+ question: "How is the APY calculated?",
2039
+ answer:
2040
+ <div>It's an annualized fee APY, calculated as fees earned in the last 24h divided by TVL. Factors like impermanent loss are not considered.</div>
2041
+ },
1916
2042
  ],
1917
2043
  risk: highRisk,
1918
2044
  points: [],