@strkfarm/sdk 1.1.25 → 1.1.27

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.
@@ -0,0 +1,140 @@
1
+ import { ContractAddr } from "@/dataTypes";
2
+ import { logger } from "@/utils";
3
+
4
+ export interface LSTStats {
5
+ asset: string;
6
+ assetAddress: string;
7
+ lstAddress: string;
8
+ tvlUsd: number;
9
+ tvlAsset: number;
10
+ apy: number;
11
+ apyInPercentage: string;
12
+ exchangeRate: number;
13
+ preciseExchangeRate: string;
14
+ }
15
+
16
+ export class LSTAPRService {
17
+ private static readonly ENDUR_API_URL = 'https://app.endur.fi/api/lst/stats';
18
+ private static cache: LSTStats[] | null = null;
19
+ private static cacheTimestamp: number = 0;
20
+ private static readonly CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
21
+
22
+ /**
23
+ * Fetches LST stats from Endur API with caching
24
+ * @returns Promise<LSTStats[]> Array of LST statistics
25
+ */
26
+ static async getLSTStats(): Promise<LSTStats[]> {
27
+ const now = Date.now();
28
+
29
+ // Return cached data if still valid
30
+ if (this.cache && (now - this.cacheTimestamp) < this.CACHE_DURATION) {
31
+ logger.verbose(`LSTAPRService: Returning cached LST stats`);
32
+ return this.cache;
33
+ }
34
+
35
+ try {
36
+ logger.verbose(`LSTAPRService: Fetching LST stats from Endur API`);
37
+ const response = await fetch(this.ENDUR_API_URL);
38
+
39
+ if (!response.ok) {
40
+ throw new Error(`Failed to fetch LST stats: ${response.status} ${response.statusText}`);
41
+ }
42
+
43
+ const data: LSTStats[] = await response.json();
44
+
45
+ // Validate the response structure
46
+ if (!Array.isArray(data)) {
47
+ throw new Error('Invalid response format: expected array');
48
+ }
49
+
50
+ // Cache the data
51
+ this.cache = data;
52
+ this.cacheTimestamp = now;
53
+
54
+ logger.verbose(`LSTAPRService: Successfully fetched ${data.length} LST stats`);
55
+ return data;
56
+
57
+ } catch (error) {
58
+ logger.error(`LSTAPRService: Error fetching LST stats: ${error}`);
59
+
60
+ // Return cached data if available, even if expired
61
+ if (this.cache) {
62
+ logger.warn(`LSTAPRService: Returning stale cached data due to API error`);
63
+ return this.cache;
64
+ }
65
+
66
+ throw error;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Gets LST APR for a specific asset address
72
+ * @param assetAddress - The contract address of the underlying asset
73
+ * @returns Promise<number> The LST APR (not divided by 1e18)
74
+ */
75
+ static async getLSTAPR(assetAddress: ContractAddr): Promise<number> {
76
+ const stats = await this.getLSTStats();
77
+
78
+ const lstStat = stats.find(stat =>
79
+ ContractAddr.eqString(stat.assetAddress, assetAddress.address)
80
+ );
81
+
82
+ if (!lstStat) {
83
+ logger.warn(`LSTAPRService: No LST stats found for asset address ${assetAddress.address}`);
84
+ return 0;
85
+ }
86
+
87
+ logger.verbose(`LSTAPRService: Found LST APR for ${lstStat.asset}: ${lstStat.apy} (${lstStat.apyInPercentage})`);
88
+ return lstStat.apy;
89
+ }
90
+
91
+ /**
92
+ * Gets LST APR for multiple asset addresses
93
+ * @param assetAddresses - Array of contract addresses
94
+ * @returns Promise<Map<string, number>> Map of asset address to LST APR
95
+ */
96
+ static async getLSTAPRs(assetAddresses: ContractAddr[]): Promise<Map<string, number>> {
97
+ const stats = await this.getLSTStats();
98
+ const result = new Map<string, number>();
99
+
100
+ for (const assetAddress of assetAddresses) {
101
+ const lstStat = stats.find(stat =>
102
+ ContractAddr.eqString(stat.assetAddress, assetAddress.address)
103
+ );
104
+
105
+ if (lstStat) {
106
+ result.set(assetAddress.address, lstStat.apy);
107
+ logger.verbose(`LSTAPRService: Found LST APR for ${lstStat.asset}: ${lstStat.apy}`);
108
+ } else {
109
+ result.set(assetAddress.address, 0);
110
+ logger.warn(`LSTAPRService: No LST stats found for asset address ${assetAddress.address}`);
111
+ }
112
+ }
113
+
114
+ return result;
115
+ }
116
+
117
+ /**
118
+ * Gets all available LST assets and their APRs
119
+ * @returns Promise<Map<string, LSTStats>> Map of asset address to LST stats
120
+ */
121
+ static async getAllLSTStats(): Promise<Map<string, LSTStats>> {
122
+ const stats = await this.getLSTStats();
123
+ const result = new Map<string, LSTStats>();
124
+
125
+ for (const stat of stats) {
126
+ result.set(stat.assetAddress, stat);
127
+ }
128
+
129
+ return result;
130
+ }
131
+
132
+ /**
133
+ * Clears the cache (useful for testing or forcing refresh)
134
+ */
135
+ static clearCache(): void {
136
+ this.cache = null;
137
+ this.cacheTimestamp = 0;
138
+ logger.verbose(`LSTAPRService: Cache cleared`);
139
+ }
140
+ }
@@ -9,7 +9,7 @@ import UniversalVaultAbi from '../data/universal-vault.abi.json';
9
9
  import ManagerAbi from '../data/vault-manager.abi.json';
10
10
  import { ApproveCallParams, AvnuSwapCallParams, BaseAdapter, CommonAdapter, FlashloanCallParams, GenerateCallFn, LeafAdapterFn, ManageCall, VesuAdapter, VesuDefiSpringRewardsCallParams, VesuModifyPositionCallParams, VesuPools } from "./universal-adapters";
11
11
  import { Global } from "@/global";
12
- import { AvnuWrapper, ERC20 } from "@/modules";
12
+ import { AvnuWrapper, ERC20, LSTAPRService } from "@/modules";
13
13
  import { AVNU_MIDDLEWARE, VESU_SINGLETON } from "./universal-adapters/adapter-utils";
14
14
  import { VesuHarvests } from "@/modules/harvests";
15
15
 
@@ -227,12 +227,28 @@ export class UniversalStrategy<
227
227
  logger.verbose(`${this.metadata.name}::netAPY: positions: ${JSON.stringify(positions)}`);
228
228
  const baseAPYs: number[] = [];
229
229
  const rewardAPYs: number[] = [];
230
+
231
+ // Get LST APRs for all collateral assets with fallback
232
+ const underlyingAddresses = vesuAdapters.map(adapter => adapter.config.debt.address);
233
+ let lstAPRs: Map<string, number>;
234
+ try {
235
+ lstAPRs = await LSTAPRService.getLSTAPRs(underlyingAddresses);
236
+ } catch (error) {
237
+ logger.warn(`${this.metadata.name}::netAPY: Failed to fetch LST APRs from Endur API, using fallback: ${error}`);
238
+ // Fallback: create empty map (will result in 0 LST APR)
239
+ lstAPRs = new Map();
240
+ }
241
+
230
242
  for (const [index, pool] of pools.entries()) {
231
243
  const vesuAdapter = vesuAdapters[index];
232
244
  const collateralAsset = pool.assets.find((a: any) => a.symbol.toLowerCase() === vesuAdapter.config.collateral.symbol.toLowerCase())?.stats!;
233
245
  const debtAsset = pool.assets.find((a: any) => a.symbol.toLowerCase() === vesuAdapter.config.debt.symbol.toLowerCase())?.stats!;
234
246
  const supplyApy = Number(collateralAsset.supplyApy.value || 0) / 1e18;
235
- const lstAPY = Number(collateralAsset.lstApr?.value || 0) / 1e18;
247
+
248
+ // Use LST APR from Endur API instead of Vesu's lstApr
249
+ const lstAPY = lstAPRs.get(vesuAdapter.config.debt.address.address) || 0;
250
+ logger.verbose(`${this.metadata.name}::netAPY: ${vesuAdapter.config.collateral.symbol} LST APR from Endur: ${lstAPY}`);
251
+
236
252
  baseAPYs.push(...[supplyApy + lstAPY, Number(debtAsset.borrowApr.value) / 1e18]);
237
253
  rewardAPYs.push(...[Number(collateralAsset.defiSpringSupplyApr?.value || "0") / 1e18, 0]);
238
254
  }
@@ -501,6 +517,19 @@ export class UniversalStrategy<
501
517
  return `${UniversalStrategy.name}:${this.metadata.name}`;
502
518
  }
503
519
 
520
+ /**
521
+ * Gets LST APR for the strategy's underlying asset from Endur API
522
+ * @returns Promise<number> The LST APR (not divided by 1e18)
523
+ */
524
+ async getLSTAPR(): Promise<number> {
525
+ try {
526
+ return await LSTAPRService.getLSTAPR(this.asset().address);
527
+ } catch (error) {
528
+ logger.warn(`${this.getTag()}: Failed to get LST APR: ${error}`);
529
+ return 0;
530
+ }
531
+ }
532
+
504
533
  async getVesuHealthFactors() {
505
534
  return await Promise.all(this.getVesuAdapters().map(v => v.getHealthFactor()));
506
535
  }