@tokemak/queries 0.0.9 → 0.0.11

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,2063 @@
1
+ import { sepolia, mainnet, sonic, base } from 'viem/chains';
2
+ import { TOKEMAK_SWAP_PRICING_URL, SUPPORTED_DEV_CHAINS, SUPPORTED_PROD_CHAINS, TOKEMAK_GENSTRAT_APRS_API_URL } from '@tokemak/constants';
3
+ import { ETH_TOKEN, PXETH_TOKEN, USDC_TOKEN, DOLA_TOKEN, S_TOKEN, EURC_TOKEN, USDT_TOKEN, USDT0_TOKEN, TOKE_TOKEN, SILO_TOKEN, XPL_TOKEN, WXPL_TOKEN, WETH_TOKEN, WS_TOKEN, BEETS_PROTOCOL, ALL_TOKENS, ALL_AUTOPOOLS } from '@tokemak/tokenlist';
4
+ import { formatEtherNum, formatUnitsNum, convertTimestampToDate, formatDateToReadable, getToken, formatPoolName, getProtocol, getNetwork } from '@tokemak/utils';
5
+ import { getAddress, hexToBigInt, formatUnits, erc20Abi } from 'viem';
6
+ import { getSdkByChainId } from '@tokemak/graph-cli';
7
+ import { AUTOPOOLS_WHITELIST_PROD, getMainnetConfig, getCoreConfig } from '@tokemak/config';
8
+ import { readContract, readContracts } from '@wagmi/core';
9
+ import { stakingV1Abi, rewardsV1Abi, accTokeV1Abi, autopoolEthAbi, rewardsV1HashAbi, systemRegistryAbi, lensAbi } from '@tokemak/abis';
10
+
11
+ // functions/getEthPrice.ts
12
+
13
+ // functions/getDefillamaPrice.ts
14
+ var getDefillamaPrice = async (tokenAddress) => {
15
+ const response = await fetch(
16
+ `https://coins.llama.fi/prices/current/ethereum:${tokenAddress}`
17
+ );
18
+ const data = await response.json();
19
+ return data.coins[`ethereum:${tokenAddress}`].price;
20
+ };
21
+
22
+ // functions/getTokenPrice.ts
23
+ var getTokenPrice = async ({
24
+ chainId = 1,
25
+ tokenAddress,
26
+ includedSources,
27
+ excludedSources
28
+ }) => {
29
+ try {
30
+ const params = new URLSearchParams({
31
+ chainId: chainId.toString(),
32
+ systemName: "gen3",
33
+ token: tokenAddress
34
+ });
35
+ if (includedSources) {
36
+ params.set("includedSources", includedSources.join(","));
37
+ }
38
+ if (excludedSources) {
39
+ params.set("excludedSources", excludedSources.join(","));
40
+ }
41
+ const response = await fetch(`${TOKEMAK_SWAP_PRICING_URL}?${params}`);
42
+ const data = await response.json();
43
+ return data.price;
44
+ } catch (error) {
45
+ try {
46
+ const defillamaPrice = await getDefillamaPrice(tokenAddress);
47
+ return defillamaPrice;
48
+ } catch (error2) {
49
+ console.log(error2);
50
+ return void 0;
51
+ }
52
+ }
53
+ };
54
+
55
+ // functions/getTokenPriceFallback.ts
56
+ async function getTokenPriceFallback(tokenSymbol) {
57
+ try {
58
+ const response = await fetch(
59
+ "https://tokemakmarketdata.s3.amazonaws.com/current.json"
60
+ );
61
+ if (!response.ok) {
62
+ console.error(
63
+ `Request failed with status: ${response.status} - ${response.statusText}. Returning fallback.`
64
+ );
65
+ return 0;
66
+ }
67
+ const data = await response.json();
68
+ if (!data.prices || !(tokenSymbol in data.prices)) {
69
+ console.warn(
70
+ `Token symbol "${tokenSymbol}" not found in price data. Returning fallback.`
71
+ );
72
+ return 0;
73
+ }
74
+ return data.prices[tokenSymbol];
75
+ } catch (error) {
76
+ console.error(`Error fetching or parsing price data:`, error);
77
+ return 0;
78
+ }
79
+ }
80
+
81
+ // functions/getEthPrice.ts
82
+ var getEthPrice = async () => {
83
+ try {
84
+ return await getTokenPrice({
85
+ chainId: sepolia.id,
86
+ tokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
87
+ });
88
+ } catch (error) {
89
+ console.warn("Primary price fetch failed. Attempting fallback...", error);
90
+ try {
91
+ const fallbackPrice = await getTokenPriceFallback("eth");
92
+ return fallbackPrice;
93
+ } catch (fallbackError) {
94
+ console.error("Fallback fetch also failed:", fallbackError);
95
+ return 0;
96
+ }
97
+ }
98
+ };
99
+ var getTokePrice = async () => {
100
+ try {
101
+ return await getTokenPrice({ tokenAddress: TOKE_TOKEN.address });
102
+ } catch (error) {
103
+ console.warn("Primary price fetch failed. Attempting fallback...", error);
104
+ try {
105
+ const fallbackPrice = await getTokenPriceFallback("toke");
106
+ return fallbackPrice;
107
+ } catch (fallbackError) {
108
+ console.error("Fallback fetch also failed:", fallbackError);
109
+ return 0;
110
+ }
111
+ }
112
+ };
113
+
114
+ // utils/fillMissingDates.ts
115
+ function fillMissingDates(data) {
116
+ data.sort((a, b) => a.date.getTime() - b.date.getTime());
117
+ let filledData = [];
118
+ let currentDate;
119
+ const today = /* @__PURE__ */ new Date();
120
+ data.forEach((entry, index) => {
121
+ currentDate = new Date(entry.date);
122
+ filledData.push(entry);
123
+ let nextDay = new Date(currentDate);
124
+ nextDay.setDate(nextDay.getDate() + 1);
125
+ let nextEntry = data[index + 1];
126
+ while (nextEntry && nextDay < nextEntry.date) {
127
+ let newTimestamp = Math.floor(nextDay.getTime() / 1e3).toString();
128
+ let newEntry = {
129
+ ...entry,
130
+ date: new Date(nextDay),
131
+ timestamp: newTimestamp
132
+ };
133
+ filledData.push(newEntry);
134
+ nextDay.setDate(nextDay.getDate() + 1);
135
+ }
136
+ if (!nextEntry) {
137
+ while (nextDay <= today) {
138
+ let newTimestamp = Math.floor(nextDay.getTime() / 1e3).toString();
139
+ let newEntry = {
140
+ ...entry,
141
+ date: new Date(nextDay),
142
+ timestamp: newTimestamp
143
+ };
144
+ filledData.push(newEntry);
145
+ nextDay.setDate(nextDay.getDate() + 1);
146
+ }
147
+ }
148
+ });
149
+ return filledData;
150
+ }
151
+
152
+ // utils/mergeArraysWithKey.ts
153
+ function mergeArraysWithKey(objectsArray, arraysArray, key) {
154
+ if (objectsArray.length !== arraysArray.length) {
155
+ throw new Error("Both arrays must have the same length.");
156
+ }
157
+ return objectsArray.map((obj, index) => ({
158
+ ...obj,
159
+ [key]: arraysArray[index]
160
+ }));
161
+ }
162
+
163
+ // utils/mergeArrays.ts
164
+ function mergeArrays(objectsArray, objectsToMergeArray) {
165
+ if (objectsArray.length !== objectsToMergeArray.length) {
166
+ throw new Error("Both arrays must have the same length.");
167
+ }
168
+ return objectsArray.map((obj, index) => ({
169
+ ...obj,
170
+ ...objectsToMergeArray[index]
171
+ }));
172
+ }
173
+ function getChainsForEnv({
174
+ includeTestnet = false
175
+ }) {
176
+ let chains;
177
+ chains = includeTestnet ? [...SUPPORTED_DEV_CHAINS] : [...SUPPORTED_PROD_CHAINS];
178
+ return chains;
179
+ }
180
+
181
+ // utils/getAutopoolCategory.ts
182
+ var ETH_BASE_ASSETS = ["ETH", "WETH"];
183
+ var USD_BASE_ASSETS = ["USDC", "DOLA", "USDT0"];
184
+ var EUR_BASE_ASSETS = ["EURC"];
185
+ var getAutopoolCategory = (baseAsset) => {
186
+ if (ETH_BASE_ASSETS.includes(baseAsset?.toUpperCase())) {
187
+ return "eth" /* ETH */;
188
+ } else if (USD_BASE_ASSETS.includes(baseAsset?.toUpperCase()) || EUR_BASE_ASSETS.includes(baseAsset?.toUpperCase())) {
189
+ return "stables" /* STABLES */;
190
+ }
191
+ return "crypto" /* CRYPTO */;
192
+ };
193
+
194
+ // utils/convertBaseAssetToTokenPrices.ts
195
+ var convertBaseAssetToTokenPrices = (baseAssetValue, baseAssetPrice, prices) => {
196
+ const baseAssetToTokenPrices = Object.fromEntries(
197
+ Object.entries(prices).map(([tokenSymbol, tokenPrice]) => [
198
+ tokenSymbol,
199
+ baseAssetPrice / tokenPrice
200
+ ])
201
+ );
202
+ return {
203
+ baseAsset: baseAssetValue,
204
+ USD: baseAssetValue * baseAssetPrice,
205
+ ...Object.fromEntries(
206
+ Object.entries(baseAssetToTokenPrices).map(([tokenSymbol, price]) => [
207
+ tokenSymbol.toUpperCase(),
208
+ baseAssetValue * price
209
+ ])
210
+ )
211
+ };
212
+ };
213
+
214
+ // utils/convertBaseAssetToTokenPricesAndDenom.ts
215
+ var convertBaseAssetToTokenPricesAndDenom = (baseAsset, baseAssetPrice, denomPrice, prices) => {
216
+ const baseAssetToDenom = baseAssetPrice / denomPrice;
217
+ return {
218
+ ...convertBaseAssetToTokenPrices(baseAsset, baseAssetPrice, prices),
219
+ denom: baseAsset * baseAssetToDenom
220
+ };
221
+ };
222
+ var getAutopoolInfo = (symbol) => {
223
+ const autopool = ALL_AUTOPOOLS.find((pool) => pool.symbol === symbol);
224
+ return autopool;
225
+ };
226
+
227
+ // utils/findClosestEntry.ts
228
+ function findClosestEntry(data, targetValue, propertyKey, maxDifference = Infinity, getValue) {
229
+ if (!data || data.length === 0)
230
+ return null;
231
+ let closestEntry = null;
232
+ let minDiff = Infinity;
233
+ for (const entry of data) {
234
+ const value = getValue ? getValue(entry) : entry[propertyKey];
235
+ if (typeof value !== "number" || isNaN(value))
236
+ continue;
237
+ const diff = Math.abs(value - targetValue);
238
+ if (diff < minDiff && diff <= maxDifference) {
239
+ minDiff = diff;
240
+ closestEntry = entry;
241
+ }
242
+ }
243
+ return closestEntry;
244
+ }
245
+ function findClosestTimestampEntry(data, targetTimestamp, maxDifferenceInSeconds = 3600 * 12) {
246
+ return findClosestEntry(
247
+ data,
248
+ targetTimestamp,
249
+ "timestamp",
250
+ maxDifferenceInSeconds
251
+ );
252
+ }
253
+ function findClosestDateEntry(data, targetDate, maxDifferenceInMs = 24 * 60 * 60 * 1e3) {
254
+ return findClosestEntry(
255
+ data,
256
+ targetDate.getTime(),
257
+ "date",
258
+ maxDifferenceInMs,
259
+ (item) => item.date.getTime()
260
+ );
261
+ }
262
+
263
+ // functions/getBlobData.ts
264
+ var getBlobData = async (blobName) => {
265
+ try {
266
+ const blobUrl = `https://kg4e5p7eknl8rdgg.public.blob.vercel-storage.com/${blobName}`;
267
+ const response = await fetch(blobUrl, { cache: "no-store" });
268
+ if (!response.ok) {
269
+ console.warn(
270
+ `Failed to fetch blob data for ${blobName}: ${response.status} ${response.statusText}`
271
+ );
272
+ return null;
273
+ }
274
+ const data = await response.text();
275
+ return JSON.parse(data);
276
+ } catch (error) {
277
+ console.warn(`Failed to fetch blob data for ${blobName}:`, error);
278
+ return null;
279
+ }
280
+ };
281
+
282
+ // functions/getPoolsAndDestinationsBackup.ts
283
+ var getPoolsAndDestinationsBackup = async (chainId) => {
284
+ try {
285
+ const networkName = getNetwork(chainId)?.name.toLowerCase();
286
+ const backupData = await getBlobData(
287
+ `${networkName}-getPoolsAndDestinations-latest-success.json`
288
+ );
289
+ return backupData?.data;
290
+ } catch (e) {
291
+ console.error(e);
292
+ }
293
+ };
294
+
295
+ // constants/tokenOrders.ts
296
+ var UITokenOrder = [
297
+ "ETH",
298
+ "WETH",
299
+ "STETH",
300
+ "WSTETH",
301
+ "RETH",
302
+ "SWETH",
303
+ "FRXETH",
304
+ "SFRXETH",
305
+ "EETH",
306
+ "CBETH",
307
+ "OSETH",
308
+ "PXETH",
309
+ "OETH",
310
+ "EZETH",
311
+ "RSETH",
312
+ "RSWETH",
313
+ "ETHX",
314
+ "RSWETH",
315
+ "PUFETH",
316
+ "USDC",
317
+ "DOLA",
318
+ "USDT",
319
+ "DAI"
320
+ ];
321
+ var protocolOrder = [
322
+ "Balancer",
323
+ "Curve",
324
+ "Aerodrome",
325
+ "Pendle",
326
+ "Aave",
327
+ "Morpho",
328
+ "Fluid"
329
+ ];
330
+ var getGenStratAprs = async ({
331
+ chainId = 1
332
+ }) => {
333
+ try {
334
+ const params = new URLSearchParams({
335
+ chainId: chainId.toString(),
336
+ systemName: "gen3"
337
+ });
338
+ const response = await fetch(`${TOKEMAK_GENSTRAT_APRS_API_URL}?${params}`);
339
+ const data = await response.json();
340
+ if (data && data.success) {
341
+ return data.aprs.reduce((p, c) => {
342
+ if (!p[c.autopoolAddress]) {
343
+ p[c.autopoolAddress] = {};
344
+ }
345
+ for (const d of c.destinations) {
346
+ p[c.autopoolAddress][d.address] = {
347
+ name: d.name,
348
+ apr: d.apr
349
+ };
350
+ }
351
+ return p;
352
+ }, {});
353
+ } else {
354
+ return void 0;
355
+ }
356
+ } catch (error) {
357
+ console.log(error);
358
+ return void 0;
359
+ }
360
+ };
361
+ var getPoolsAndDestinations = async (wagmiConfig, { chainId }) => {
362
+ try {
363
+ const { lens } = getCoreConfig(chainId);
364
+ const { autoPools, destinations } = await readContract(wagmiConfig, {
365
+ address: lens,
366
+ abi: lensAbi,
367
+ functionName: "getPoolsAndDestinations",
368
+ chainId
369
+ });
370
+ const autopoolsAndDestinations = mergeArraysWithKey(
371
+ autoPools,
372
+ destinations,
373
+ "destinations"
374
+ );
375
+ return autopoolsAndDestinations;
376
+ } catch (error) {
377
+ console.error(`Error on ${chainId} in getPoolsAndDestinations:`, error);
378
+ }
379
+ };
380
+ var getBackupApr = async (chainId, poolAddress) => {
381
+ try {
382
+ const networkName = getNetwork(chainId)?.name.toLowerCase();
383
+ const backupData = await getBlobData(
384
+ `${networkName}-getChainAutopools-latest-success.json`
385
+ );
386
+ if (backupData) {
387
+ const backupApr = backupData.data.find(
388
+ (item) => item.poolAddress.toLowerCase() === poolAddress.toLowerCase()
389
+ )?.apr.base;
390
+ return backupApr || 0;
391
+ }
392
+ } catch (e) {
393
+ console.error(e);
394
+ return 0;
395
+ }
396
+ return null;
397
+ };
398
+
399
+ // functions/getChainAutopools.ts
400
+ var getChainAutopools = async (wagmiConfig, {
401
+ chainId,
402
+ prices
403
+ }) => {
404
+ const { GetVaultAddeds, GetAutopoolsInactiveDestinations } = getSdkByChainId(chainId);
405
+ try {
406
+ const { vaultAddeds } = await GetVaultAddeds();
407
+ const { GetAutopoolsApr } = getSdkByChainId(chainId);
408
+ const { autopools: autopoolsApr } = await GetAutopoolsApr();
409
+ const genStratAprs = await getGenStratAprs({
410
+ chainId
411
+ });
412
+ let autopoolsAndDestinations = await getPoolsAndDestinations(wagmiConfig, {
413
+ chainId
414
+ });
415
+ if (!autopoolsAndDestinations) {
416
+ const backupData = await getPoolsAndDestinationsBackup(chainId);
417
+ if (backupData) {
418
+ autopoolsAndDestinations = backupData;
419
+ } else {
420
+ throw new Error(`No autopools and destinations found for ${chainId}`);
421
+ }
422
+ }
423
+ const vaultAddedMapping = vaultAddeds.reduce((acc, vaultAdded) => {
424
+ acc[vaultAdded.vault.toLowerCase()] = Number(vaultAdded.blockTimestamp);
425
+ return acc;
426
+ }, {});
427
+ const autopools = await Promise.all(
428
+ autopoolsAndDestinations.map(async (autopool) => {
429
+ let baseAsset = getToken(autopool.baseAsset);
430
+ if (!baseAsset?.symbol) {
431
+ console.error(
432
+ "FIX THIS BEFORE PROD: base asset not found",
433
+ autopool.baseAsset
434
+ );
435
+ }
436
+ let baseAssetPrice = prices[(baseAsset?.symbol || "").toUpperCase()];
437
+ if (!baseAssetPrice) {
438
+ baseAssetPrice = await getTokenPrice({
439
+ chainId: baseAsset.chainId,
440
+ tokenAddress: baseAsset.address
441
+ }) || 0;
442
+ }
443
+ const timestamp = vaultAddedMapping[autopool.poolAddress.toLowerCase()];
444
+ const totalAssets = formatUnitsNum(
445
+ autopool.totalAssets,
446
+ baseAsset.decimals
447
+ );
448
+ const tvl = totalAssets * baseAssetPrice;
449
+ const totalIdleAssets = formatUnitsNum(
450
+ autopool.totalIdle,
451
+ baseAsset.decimals
452
+ );
453
+ const exchangeValues = {};
454
+ const destinations = autopool.destinations.map((destination) => {
455
+ const debtValueHeldByVaultEth = formatUnitsNum(
456
+ destination.debtValueHeldByVault,
457
+ baseAsset.decimals
458
+ );
459
+ if (!exchangeValues[destination.exchangeName.toLowerCase()]) {
460
+ exchangeValues[destination.exchangeName.toLowerCase()] = 0;
461
+ }
462
+ exchangeValues[destination.exchangeName.toLowerCase()] += debtValueHeldByVaultEth;
463
+ const tokensWithValue = mergeArrays(
464
+ destination.underlyingTokens,
465
+ destination.underlyingTokenValueHeld
466
+ );
467
+ let underlyingTokens = tokensWithValue.map((token) => {
468
+ const tokenDetails = getToken(token.tokenAddress);
469
+ let value = formatUnitsNum(
470
+ token.valueHeldInEth,
471
+ baseAsset.decimals
472
+ );
473
+ let valueUsd = value * baseAssetPrice;
474
+ return {
475
+ ...tokenDetails,
476
+ valueUsd,
477
+ value
478
+ };
479
+ });
480
+ if (destination.underlyingTokenSymbols.length === 1) {
481
+ const underlyingTokenAddress = tokensWithValue[0].tokenAddress;
482
+ if (underlyingTokenAddress) {
483
+ underlyingTokens = [
484
+ {
485
+ ...getToken(underlyingTokenAddress, chainId),
486
+ valueUsd: debtValueHeldByVaultEth * baseAssetPrice,
487
+ value: debtValueHeldByVaultEth
488
+ }
489
+ ];
490
+ }
491
+ }
492
+ const isGenStrat = autopool.strategy.toLowerCase() === "0x000000000000000000000000000000000000dead" || autopool.strategy === "0x0000000000000000000000000000000000000000";
493
+ return {
494
+ ...destination,
495
+ debtValueHeldByVaultUsd: debtValueHeldByVaultEth * baseAssetPrice,
496
+ debtValueHeldByVaultEth,
497
+ compositeReturn: isGenStrat ? genStratAprs?.[autopool.poolAddress]?.[destination.vaultAddress]?.apr || 0 : formatEtherNum(destination.compositeReturn),
498
+ debtValueHeldByVaultAllocation: debtValueHeldByVaultEth / totalAssets,
499
+ underlyingTokens,
500
+ poolName: formatPoolName(destination.lpTokenName),
501
+ exchange: getProtocol(destination.exchangeName)
502
+ };
503
+ });
504
+ const uniqueExchanges = Array.from(
505
+ new Set(
506
+ destinations.map((d) => getProtocol(d.exchangeName)).filter(Boolean)
507
+ )
508
+ );
509
+ const uniqueExchangesWithValueHeld = uniqueExchanges.map((exchange) => {
510
+ const exchangeName = exchange.name.toLowerCase();
511
+ let exchangeInfo = exchange;
512
+ const value = exchangeValues[exchangeName];
513
+ if (chainId === sonic.id) {
514
+ if (exchangeName === "balancerv3" || exchangeName === "balancer") {
515
+ exchangeInfo = BEETS_PROTOCOL;
516
+ }
517
+ }
518
+ return {
519
+ ...exchangeInfo,
520
+ valueUsd: value * baseAssetPrice,
521
+ value,
522
+ allocation: value / totalAssets
523
+ };
524
+ });
525
+ const groupedExchanges = uniqueExchangesWithValueHeld.reduce(
526
+ (acc, exchange) => {
527
+ const exchangeName = exchange.name.toLowerCase();
528
+ const existingExchange = acc.find(
529
+ (e) => e.name.toLowerCase() === exchangeName
530
+ );
531
+ if (existingExchange) {
532
+ existingExchange.valueUsd += exchange.valueUsd;
533
+ existingExchange.value += exchange.value;
534
+ existingExchange.allocation += exchange.allocation;
535
+ } else {
536
+ acc.push(exchange);
537
+ }
538
+ return acc;
539
+ },
540
+ []
541
+ );
542
+ const uniqueTokensWithValueHeldMap = destinations.flatMap((d) => d.underlyingTokens).reduce((acc, token) => {
543
+ if (acc[token.address]) {
544
+ acc[token.address].valueUsd += token.valueUsd;
545
+ acc[token.address].value += token.value;
546
+ } else {
547
+ acc[token.address] = { ...token };
548
+ }
549
+ return acc;
550
+ }, {});
551
+ const uniqueTokens = Object.values(uniqueTokensWithValueHeldMap).map(
552
+ (token) => {
553
+ let valueUsd = token.valueUsd;
554
+ let value = token.value;
555
+ const allocation = value / totalAssets;
556
+ return {
557
+ ...token,
558
+ valueUsd,
559
+ value,
560
+ allocation
561
+ };
562
+ }
563
+ );
564
+ let UIBaseAsset = baseAsset;
565
+ if (baseAsset.symbol?.toLowerCase() === WETH_TOKEN.symbol.toLowerCase()) {
566
+ UIBaseAsset = ETH_TOKEN;
567
+ }
568
+ if (baseAsset.symbol?.toLowerCase() === WS_TOKEN.symbol.toLowerCase()) {
569
+ UIBaseAsset = S_TOKEN;
570
+ }
571
+ if (baseAsset.symbol?.toLowerCase() === WXPL_TOKEN.symbol.toLowerCase()) {
572
+ UIBaseAsset = XPL_TOKEN;
573
+ }
574
+ const isNew = (Date.now() / 1e3 - timestamp) / 60 / 60 / 24 < 45;
575
+ const aprs = autopoolsApr.find(
576
+ (autopoolApr) => getAddress(autopoolApr.id) === getAddress(autopool.poolAddress)
577
+ );
578
+ let baseApr = aprs?.currentApy;
579
+ let boostedApr = 0;
580
+ let extraApr = 0;
581
+ const formattedRewarder7DayMAApy = formatEtherNum(
582
+ BigInt(Number(aprs?.rewarder?.day7MAApy) || 0)
583
+ );
584
+ const formattedRewarder30DayMAApy = formatEtherNum(
585
+ BigInt(Number(aprs?.rewarder?.day30MAApy) || 0)
586
+ );
587
+ if (formattedRewarder7DayMAApy > 0) {
588
+ boostedApr = formattedRewarder7DayMAApy;
589
+ }
590
+ if (formattedRewarder30DayMAApy > 0) {
591
+ boostedApr = formattedRewarder30DayMAApy;
592
+ }
593
+ if (!baseApr) {
594
+ const weightedCrNum = destinations.reduce((acc, cur) => {
595
+ return acc + cur.debtValueHeldByVaultAllocation * cur.compositeReturn;
596
+ }, 0);
597
+ const periodicFeeBps = autopool.periodicFeeBps || 0n;
598
+ const streamingFeeBps = autopool.streamingFeeBps || 0n;
599
+ baseApr = (weightedCrNum / (1 - totalIdleAssets / totalAssets) - Number(formatUnits(periodicFeeBps, 4))) * (1 - Number(formatUnits(streamingFeeBps, 4)));
600
+ } else {
601
+ baseApr = Number(formatUnits(BigInt(baseApr), baseAsset.decimals));
602
+ }
603
+ if (!baseApr) {
604
+ baseApr = 0;
605
+ }
606
+ if (baseApr === 0) {
607
+ try {
608
+ const backupApr = await getBackupApr(chainId, autopool.poolAddress);
609
+ if (backupApr) {
610
+ baseApr = backupApr;
611
+ console.log("using backup apr", backupApr);
612
+ }
613
+ } catch (e) {
614
+ console.error(e);
615
+ console.log("unable to retrieve backup apr");
616
+ }
617
+ }
618
+ if (baseApr === 0 && autopool.symbol.toLowerCase() === "baseusd") {
619
+ baseApr = 0.0914;
620
+ }
621
+ let extraRewards = [];
622
+ if (aprs?.rewarder?.extraRewarders?.length && aprs?.rewarder?.extraRewarders?.length > 0) {
623
+ extraRewards = aprs?.rewarder?.extraRewarders.map((reward) => {
624
+ const token = getToken(reward?.rewardToken?.id, chainId);
625
+ return {
626
+ ...token,
627
+ apr: formatEtherNum(BigInt(reward.currentApy || 0))
628
+ };
629
+ });
630
+ }
631
+ if (autopool?.symbol?.toLowerCase() === "plasmausd") {
632
+ boostedApr = 0;
633
+ }
634
+ extraApr = extraRewards.reduce((acc, reward) => acc + reward.apr, 0);
635
+ const combinedApr = baseApr + boostedApr + extraApr;
636
+ let denominatedToken = ETH_TOKEN;
637
+ let useDenominatedValues = false;
638
+ const denominatedIn = aprs?.denominatedIn;
639
+ if (denominatedIn?.symbol?.toLowerCase() === "weth") {
640
+ denominatedToken = ETH_TOKEN;
641
+ } else {
642
+ denominatedToken = getToken(denominatedIn?.id);
643
+ if (denominatedToken) {
644
+ useDenominatedValues = true;
645
+ }
646
+ }
647
+ let denominatedTokenPrice = 0;
648
+ const tokenSymbol = denominatedToken.symbol?.toUpperCase();
649
+ if (tokenSymbol in prices) {
650
+ denominatedTokenPrice = prices[tokenSymbol];
651
+ } else {
652
+ denominatedTokenPrice = await getTokenPrice({
653
+ chainId: denominatedToken.chainId,
654
+ tokenAddress: denominatedToken.address
655
+ }) || 0;
656
+ }
657
+ const navPerShareBaseAsset = formatUnitsNum(
658
+ autopool.navPerShare,
659
+ baseAsset.decimals
660
+ );
661
+ const assets = convertBaseAssetToTokenPricesAndDenom(
662
+ formatUnitsNum(autopool.totalAssets, baseAsset.decimals),
663
+ baseAssetPrice,
664
+ denominatedTokenPrice,
665
+ prices
666
+ );
667
+ const navPerShare = convertBaseAssetToTokenPricesAndDenom(
668
+ navPerShareBaseAsset,
669
+ baseAssetPrice,
670
+ denominatedTokenPrice,
671
+ prices
672
+ );
673
+ const idle = convertBaseAssetToTokenPricesAndDenom(
674
+ totalIdleAssets,
675
+ baseAssetPrice,
676
+ denominatedTokenPrice,
677
+ prices
678
+ );
679
+ const idleAllocation = totalIdleAssets / totalAssets;
680
+ const combinedDailyEarnings = convertBaseAssetToTokenPricesAndDenom(
681
+ totalAssets * combinedApr / 365,
682
+ baseAssetPrice,
683
+ denominatedTokenPrice,
684
+ prices
685
+ );
686
+ const baseDailyEarnings = convertBaseAssetToTokenPricesAndDenom(
687
+ totalAssets * baseApr / 365,
688
+ baseAssetPrice,
689
+ denominatedTokenPrice,
690
+ prices
691
+ );
692
+ const tokenOrder = [UIBaseAsset.symbol, ...UITokenOrder].filter(
693
+ (value, index, array) => array.indexOf(value) === index
694
+ );
695
+ const UITokens = uniqueTokens.reduce((acc, token) => {
696
+ try {
697
+ const parentAsset = token.extensions?.parentAsset;
698
+ if (parentAsset) {
699
+ const parentToken = ALL_TOKENS.find(
700
+ (t) => t.symbol.toLowerCase() === parentAsset.toLowerCase()
701
+ );
702
+ if (parentToken && !acc.some(
703
+ (t) => t?.address?.toLowerCase() === parentToken?.address?.toLowerCase()
704
+ )) {
705
+ acc.push({
706
+ ...parentToken,
707
+ valueUsd: 0,
708
+ value: 0,
709
+ allocation: 0
710
+ });
711
+ }
712
+ } else if (!acc.some(
713
+ (t) => t?.address?.toLowerCase() === token?.address?.toLowerCase()
714
+ )) {
715
+ acc.push(token);
716
+ }
717
+ } catch (error) {
718
+ console.warn(
719
+ `Error processing token: ${token?.symbol || "unknown"}`,
720
+ error
721
+ );
722
+ }
723
+ return acc;
724
+ }, []).sort((a, b) => {
725
+ try {
726
+ const aIndex = tokenOrder.indexOf(a.symbol.toUpperCase());
727
+ const bIndex = tokenOrder.indexOf(b.symbol.toUpperCase());
728
+ if (aIndex !== -1 && bIndex !== -1) {
729
+ return aIndex - bIndex;
730
+ }
731
+ if (aIndex !== -1)
732
+ return -1;
733
+ if (bIndex !== -1)
734
+ return 1;
735
+ return 0;
736
+ } catch (error) {
737
+ console.warn("Error sorting tokens:", error);
738
+ return 0;
739
+ }
740
+ });
741
+ const UIExchanges = groupedExchanges.filter((exchange) => {
742
+ try {
743
+ return exchange.type === "Lending Market" || exchange.type === "DEX";
744
+ } catch (error) {
745
+ console.warn(
746
+ `Error filtering exchange: ${exchange?.name || "unknown"}`,
747
+ error
748
+ );
749
+ return false;
750
+ }
751
+ }).sort((a, b) => {
752
+ try {
753
+ const aIndex = protocolOrder.indexOf(a.name);
754
+ const bIndex = protocolOrder.indexOf(b.name);
755
+ return aIndex - bIndex;
756
+ } catch (error) {
757
+ console.warn("Error sorting exchanges:", error);
758
+ return 0;
759
+ }
760
+ });
761
+ const finalUIExchanges = UIExchanges.filter((exchange) => {
762
+ if (exchange.name === "BalancerV3") {
763
+ return !UIExchanges.some((e) => e.name === "Balancer");
764
+ }
765
+ return true;
766
+ });
767
+ const autopoolInfo = getAutopoolInfo(autopool.symbol);
768
+ if (autopoolInfo && !autopoolInfo.description) {
769
+ autopoolInfo.description = `Autopool featuring ${UIBaseAsset.symbol} deployed across integrated DEXs and lending protocols on ${getNetwork(chainId)?.name}.`;
770
+ }
771
+ return {
772
+ ...autopool,
773
+ tvl,
774
+ totalAssets: assets,
775
+ destinations,
776
+ exchanges: groupedExchanges,
777
+ timestamp,
778
+ isNew,
779
+ extraRewarders: aprs?.rewarder?.extraRewarders,
780
+ createdAt: new Date(timestamp * 1e3),
781
+ baseAsset: {
782
+ ...baseAsset,
783
+ price: baseAssetPrice
784
+ },
785
+ denomination: {
786
+ ...denominatedToken,
787
+ price: denominatedTokenPrice
788
+ },
789
+ useDenomination: useDenominatedValues,
790
+ UIBaseAsset,
791
+ UITokens,
792
+ UIExchanges: finalUIExchanges,
793
+ tokens: uniqueTokens,
794
+ chain: getNetwork(chainId),
795
+ apr: {
796
+ base: baseApr,
797
+ boosted: boostedApr,
798
+ extraAprs: extraRewards,
799
+ combined: combinedApr,
800
+ hasBoostedApr: boostedApr > 1e-4,
801
+ hasExtraAprs: extraRewards.length > 0
802
+ },
803
+ dailyEarnings: {
804
+ combined: combinedDailyEarnings,
805
+ base: baseDailyEarnings
806
+ },
807
+ navPerShare,
808
+ idle: {
809
+ ...idle,
810
+ allocation: idleAllocation,
811
+ token: UIBaseAsset
812
+ },
813
+ category: getAutopoolCategory(baseAsset.symbol),
814
+ ...autopoolInfo
815
+ };
816
+ })
817
+ );
818
+ return autopools;
819
+ } catch (e) {
820
+ console.error(e);
821
+ return [];
822
+ }
823
+ };
824
+ var getAutopools = async (wagmiConfig, {
825
+ prices,
826
+ includeTestnet = false
827
+ }) => {
828
+ try {
829
+ const chains = getChainsForEnv({ includeTestnet });
830
+ const autopools = (await Promise.all(
831
+ chains.map(
832
+ (chain) => getChainAutopools(wagmiConfig, {
833
+ prices,
834
+ chainId: chain?.chainId
835
+ })
836
+ )
837
+ )).flat();
838
+ const sortedAutopoolsByTimestamp = autopools.sort(
839
+ (a, b) => b.timestamp - a.timestamp
840
+ );
841
+ if (includeTestnet) {
842
+ return sortedAutopoolsByTimestamp;
843
+ } else {
844
+ return sortedAutopoolsByTimestamp.filter((pool) => {
845
+ return AUTOPOOLS_WHITELIST_PROD.includes(getAddress(pool.poolAddress));
846
+ });
847
+ }
848
+ } catch (e) {
849
+ console.error(e);
850
+ }
851
+ };
852
+ var getChainUserAutopools = async ({
853
+ address,
854
+ chainId = 1
855
+ }) => {
856
+ const { GetUserVaultInfo } = getSdkByChainId(chainId);
857
+ try {
858
+ if (address) {
859
+ const { userInfo } = await GetUserVaultInfo({
860
+ address
861
+ });
862
+ return userInfo?.vaults || [];
863
+ }
864
+ return [];
865
+ } catch (e) {
866
+ console.error(e);
867
+ return [];
868
+ }
869
+ };
870
+ var BASE_ASSETS = [
871
+ { ...ETH_TOKEN, symbol: "ETH", coinGeckoId: "ethereum" },
872
+ { ...PXETH_TOKEN, symbol: "PXETH", coinGeckoId: "dinero-staked-eth" },
873
+ { ...USDC_TOKEN, symbol: "USDC", coinGeckoId: "usd-coin" },
874
+ { ...DOLA_TOKEN, symbol: "DOLA", coinGeckoId: "dola-usd" },
875
+ { ...S_TOKEN, symbol: "S" },
876
+ { ...EURC_TOKEN, symbol: "EURC", coinGeckoId: "euro-coin" },
877
+ { ...USDT_TOKEN, symbol: "USDT", coinGeckoId: "tether" },
878
+ { ...USDT0_TOKEN, symbol: "USDT0", coinGeckoId: "tether" }
879
+ ];
880
+ var PRICED_TOKENS = [
881
+ ...BASE_ASSETS,
882
+ { ...TOKE_TOKEN, symbol: "TOKE" },
883
+ { ...SILO_TOKEN, symbol: "SILO" },
884
+ { ...XPL_TOKEN, address: WXPL_TOKEN.address, symbol: "XPL" },
885
+ { ...USDT0_TOKEN, symbol: "USDT0" }
886
+ ];
887
+ [
888
+ { ...WETH_TOKEN, symbol: "WETH" },
889
+ { ...WS_TOKEN, symbol: "WS" },
890
+ { ...WXPL_TOKEN, symbol: "WXPL" }
891
+ ];
892
+ var getUserAutopool = async (wagmiConfig, {
893
+ address,
894
+ autopool
895
+ }) => {
896
+ try {
897
+ if (autopool && address) {
898
+ const autopoolContract = {
899
+ address: autopool?.poolAddress,
900
+ abi: autopoolEthAbi,
901
+ chainId: autopool?.chain?.chainId
902
+ };
903
+ const [
904
+ { result: autopoolRewarderContract },
905
+ { result: pastRewarders },
906
+ { result: unstakedPoolShares, error: unstakedPoolSharesError }
907
+ ] = await readContracts(wagmiConfig, {
908
+ contracts: [
909
+ {
910
+ ...autopoolContract,
911
+ functionName: "rewarder",
912
+ args: []
913
+ },
914
+ {
915
+ ...autopoolContract,
916
+ functionName: "getPastRewarders",
917
+ args: []
918
+ },
919
+ {
920
+ ...autopoolContract,
921
+ functionName: "balanceOf",
922
+ args: [address]
923
+ }
924
+ ]
925
+ });
926
+ if (!autopoolRewarderContract) {
927
+ throw new Error("No rewarder contract found");
928
+ }
929
+ if (unstakedPoolSharesError) {
930
+ throw new Error("Error fetching unstaked pool shares");
931
+ }
932
+ const stakedPoolShares = await readContract(wagmiConfig, {
933
+ address: autopoolRewarderContract,
934
+ abi: erc20Abi,
935
+ functionName: "balanceOf",
936
+ args: [address],
937
+ chainId: autopool?.chain?.chainId
938
+ });
939
+ const stakedShares = formatEtherNum(stakedPoolShares);
940
+ const stakedNav = stakedShares * (autopool?.navPerShare.baseAsset || 0);
941
+ const stakedNavUsd = stakedShares * (autopool?.navPerShare.USD || 0);
942
+ const unstakedShares = formatEtherNum(unstakedPoolShares || 0n);
943
+ const unstakedNav = unstakedShares * (autopool?.navPerShare.USD || 0);
944
+ const unstakedNavUsd = unstakedShares * (autopool?.navPerShare.USD || 0);
945
+ const totalShares = unstakedShares + stakedShares;
946
+ const totalNav = totalShares * (autopool?.navPerShare.baseAsset || 0);
947
+ const totalNavUsd = totalShares * (autopool?.navPerShare.USD || 0);
948
+ const totalNavDenominated = totalShares * (autopool?.navPerShare.denom || 0);
949
+ let pastRewarderBalances = [];
950
+ if (pastRewarders && pastRewarders?.length > 0) {
951
+ const pastRewardBalances = pastRewarders.map((rewarder) => ({
952
+ address: rewarder,
953
+ abi: erc20Abi,
954
+ functionName: "balanceOf",
955
+ args: [address],
956
+ chainId: autopool?.chain?.chainId
957
+ }));
958
+ const pastRewards = await readContracts(wagmiConfig, {
959
+ contracts: pastRewardBalances
960
+ });
961
+ pastRewarderBalances = pastRewards.map(({ result }, index) => {
962
+ const balance = result;
963
+ const shares = formatEtherNum(result);
964
+ const nav = shares * (autopool?.navPerShare.baseAsset || 0);
965
+ const navUsd = shares * (autopool?.navPerShare.USD || 0);
966
+ return {
967
+ rewarderContract: pastRewarders[index],
968
+ balance,
969
+ shares,
970
+ nav,
971
+ navUsd
972
+ };
973
+ });
974
+ }
975
+ return {
976
+ staked: {
977
+ balance: stakedPoolShares,
978
+ shares: stakedShares,
979
+ nav: stakedNav,
980
+ navUsd: stakedNavUsd
981
+ },
982
+ unstaked: {
983
+ balance: unstakedPoolShares,
984
+ shares: unstakedShares,
985
+ nav: unstakedNav,
986
+ navUsd: unstakedNavUsd
987
+ },
988
+ pastRewarderBalances,
989
+ totalShares,
990
+ totalNav,
991
+ totalNavUsd,
992
+ totalNavDenominated,
993
+ useDenomination: autopool?.useDenomination,
994
+ denomination: autopool?.denomination
995
+ };
996
+ }
997
+ return {
998
+ staked: {
999
+ balance: 0n,
1000
+ shares: 0,
1001
+ nav: 0,
1002
+ navUsd: 0
1003
+ },
1004
+ unstaked: {
1005
+ balance: 0n,
1006
+ shares: 0,
1007
+ nav: 0,
1008
+ navUsd: 0
1009
+ },
1010
+ totalNavDenominated: 0,
1011
+ totalShares: 0,
1012
+ totalNav: 0,
1013
+ totalNavUsd: 0
1014
+ };
1015
+ } catch (e) {
1016
+ console.error(e);
1017
+ }
1018
+ };
1019
+
1020
+ // functions/getUserAutopools.ts
1021
+ function accumulateMap(dest, delta, multiplier = 1) {
1022
+ for (const [key, val] of Object.entries(delta)) {
1023
+ dest[key] = (dest[key] ?? 0) + val * multiplier;
1024
+ }
1025
+ }
1026
+ var createInitialCurrencyValues = () => {
1027
+ const currencies = Object.fromEntries([
1028
+ ...PRICED_TOKENS.map((token) => [token.symbol, 0]),
1029
+ ["USD", 0]
1030
+ ]);
1031
+ return currencies;
1032
+ };
1033
+ var initialAutopoolsValue = {
1034
+ nav: createInitialCurrencyValues(),
1035
+ supplied: createInitialCurrencyValues(),
1036
+ returns: createInitialCurrencyValues(),
1037
+ idle: { ...createInitialCurrencyValues(), allocation: 0 },
1038
+ avgDailyReturns: createInitialCurrencyValues(),
1039
+ apr: 0,
1040
+ totalDeposits: 0,
1041
+ totalWithdrawals: 0
1042
+ };
1043
+ var getUserAutopools = async ({
1044
+ address,
1045
+ includeTestnet = false,
1046
+ autopools,
1047
+ userActivity,
1048
+ prices,
1049
+ config
1050
+ }) => {
1051
+ try {
1052
+ const chains = getChainsForEnv({ includeTestnet });
1053
+ const userAutopools = (await Promise.all(
1054
+ chains.map(
1055
+ (chain) => getChainUserAutopools({
1056
+ address,
1057
+ chainId: chain.chainId
1058
+ })
1059
+ )
1060
+ )).flat();
1061
+ const userExchanges = {};
1062
+ const userTokens = {};
1063
+ const userPools = {};
1064
+ if (autopools && autopools.length > 0 && userAutopools.length > 0 && prices) {
1065
+ const userAutoDOLA = await getUserAutopool(config, {
1066
+ address,
1067
+ autopool: autopools.find((autopool) => autopool.symbol === "autoDOLA")
1068
+ });
1069
+ let categories = {
1070
+ usd: { ...initialAutopoolsValue },
1071
+ eth: { ...initialAutopoolsValue },
1072
+ crypto: { ...initialAutopoolsValue },
1073
+ stables: { ...initialAutopoolsValue }
1074
+ };
1075
+ const userAutopoolsWithData = userAutopools.map((userAutopool) => {
1076
+ const autopoolData = autopools.find(
1077
+ (autopool) => autopool.poolAddress.toLowerCase() === userAutopool.vaultAddress.toLowerCase()
1078
+ );
1079
+ if (autopoolData) {
1080
+ const isDOLA = autopoolData.symbol === "autoDOLA" && userAutoDOLA;
1081
+ const userShares = isDOLA ? userAutoDOLA?.totalShares : formatEtherNum(userAutopool?.totalShares);
1082
+ const totalVaultShares = formatEtherNum(autopoolData?.totalSupply);
1083
+ const userShareOfVault = userShares / totalVaultShares;
1084
+ const totalDeposits = formatUnitsNum(
1085
+ userActivity?.totals[userAutopool.vaultAddress]?.totalDeposits || 0n,
1086
+ autopoolData?.baseAsset.decimals
1087
+ );
1088
+ const totalWithdrawals = formatUnitsNum(
1089
+ userActivity?.totals[userAutopool.vaultAddress]?.totalWithdrawals || 0n,
1090
+ autopoolData?.baseAsset.decimals
1091
+ );
1092
+ const poolEvents = userActivity?.events.filter(
1093
+ (event) => event.vaultAddress === userAutopool.vaultAddress
1094
+ );
1095
+ let lastDeposit;
1096
+ if (poolEvents && poolEvents?.length > 0) {
1097
+ lastDeposit = convertTimestampToDate(
1098
+ poolEvents[poolEvents.length - 1].timestamp
1099
+ ).toLocaleDateString("en-US", {
1100
+ day: "2-digit",
1101
+ month: "short",
1102
+ year: "numeric"
1103
+ });
1104
+ }
1105
+ autopoolData?.exchanges.forEach((exchange) => {
1106
+ if (userExchanges[exchange.name]) {
1107
+ userExchanges[exchange.name].value += userShareOfVault * exchange.value;
1108
+ userExchanges[exchange.name].valueUsd += userShareOfVault * exchange.valueUsd;
1109
+ } else {
1110
+ userExchanges[exchange.name] = {
1111
+ ...exchange,
1112
+ value: userShareOfVault * exchange.value,
1113
+ valueUsd: userShareOfVault * exchange.valueUsd
1114
+ };
1115
+ }
1116
+ });
1117
+ let categoryKey = autopoolData.category;
1118
+ autopoolData?.tokens.forEach((token) => {
1119
+ if (userTokens[token.address]) {
1120
+ userTokens[token.address].value += userShareOfVault * token.value;
1121
+ userTokens[token.address].valueUsd += userShareOfVault * token.valueUsd;
1122
+ } else {
1123
+ userTokens[token.address] = {
1124
+ ...token,
1125
+ value: userShareOfVault * token.value,
1126
+ valueUsd: userShareOfVault * token.valueUsd
1127
+ };
1128
+ }
1129
+ });
1130
+ autopoolData?.destinations.forEach((pool) => {
1131
+ if (userPools[pool.vaultAddress]) {
1132
+ userPools[pool.vaultAddress].debtValueHeldByVaultEth += userShareOfVault * pool.debtValueHeldByVaultEth;
1133
+ userPools[pool.vaultAddress].debtValueHeldByVaultUsd += userShareOfVault * pool.debtValueHeldByVaultUsd;
1134
+ } else {
1135
+ userPools[pool.vaultAddress] = {
1136
+ ...pool,
1137
+ debtValueHeldByVaultEth: userShareOfVault * pool.debtValueHeldByVaultEth,
1138
+ debtValueHeldByVaultUsd: userShareOfVault * pool.debtValueHeldByVaultUsd
1139
+ };
1140
+ }
1141
+ });
1142
+ const userNav = userShares * autopoolData?.navPerShare.baseAsset;
1143
+ const userReturns = userNav + totalWithdrawals - totalDeposits;
1144
+ const userSupplied = totalDeposits - totalWithdrawals;
1145
+ const userIdle = userShareOfVault * autopoolData?.idle.baseAsset;
1146
+ let nav2 = convertBaseAssetToTokenPricesAndDenom(
1147
+ userNav,
1148
+ autopoolData?.baseAsset.price,
1149
+ autopoolData.denomination.price,
1150
+ prices
1151
+ );
1152
+ const returns2 = convertBaseAssetToTokenPricesAndDenom(
1153
+ userReturns,
1154
+ autopoolData?.baseAsset.price,
1155
+ autopoolData.denomination.price,
1156
+ prices
1157
+ );
1158
+ const supplied2 = convertBaseAssetToTokenPricesAndDenom(
1159
+ userSupplied,
1160
+ autopoolData?.baseAsset.price,
1161
+ autopoolData.denomination.price,
1162
+ prices
1163
+ );
1164
+ const idle2 = convertBaseAssetToTokenPricesAndDenom(
1165
+ userIdle,
1166
+ autopoolData?.baseAsset.price,
1167
+ autopoolData.denomination.price,
1168
+ prices
1169
+ );
1170
+ const prev = categories[categoryKey];
1171
+ const idleDelta = {
1172
+ [autopoolData.UIBaseAsset.symbol]: autopoolData.idle.baseAsset,
1173
+ USD: autopoolData.idle.USD
1174
+ };
1175
+ const navDelta = nav2;
1176
+ const returnsDelta = returns2;
1177
+ const suppliedDelta = supplied2;
1178
+ const updatedIdle = { ...prev.idle };
1179
+ accumulateMap(updatedIdle, idleDelta, userShareOfVault);
1180
+ updatedIdle.allocation = 0;
1181
+ const updatedNav = { ...prev.nav };
1182
+ accumulateMap(updatedNav, navDelta);
1183
+ const updatedReturns = { ...prev.returns };
1184
+ accumulateMap(updatedReturns, returnsDelta);
1185
+ const updatedSupplied = { ...prev.supplied };
1186
+ accumulateMap(updatedSupplied, suppliedDelta);
1187
+ categories[categoryKey] = {
1188
+ ...prev,
1189
+ idle: updatedIdle,
1190
+ nav: updatedNav,
1191
+ returns: updatedReturns,
1192
+ supplied: updatedSupplied,
1193
+ totalDeposits: prev.totalDeposits + totalDeposits,
1194
+ totalWithdrawals: prev.totalWithdrawals + totalWithdrawals
1195
+ };
1196
+ const stakedBalance = isDOLA ? userAutoDOLA?.staked.balance : BigInt(userAutopool?.stakedShares);
1197
+ const stakedShares = isDOLA ? userAutoDOLA?.staked.shares : formatEtherNum(stakedBalance);
1198
+ const stakedNav = stakedShares * (autopoolData?.navPerShare.baseAsset || 0);
1199
+ const staked = convertBaseAssetToTokenPrices(
1200
+ stakedNav,
1201
+ autopoolData?.baseAsset.price,
1202
+ prices
1203
+ );
1204
+ const unstakedBalance = isDOLA ? userAutoDOLA?.unstaked.balance : BigInt(userAutopool?.walletShares);
1205
+ const unstakedShares = isDOLA ? userAutoDOLA?.unstaked.shares : formatEtherNum(unstakedBalance);
1206
+ const unstakedNav = unstakedShares * (autopoolData?.navPerShare.baseAsset || 0);
1207
+ const unstaked = convertBaseAssetToTokenPrices(
1208
+ unstakedNav,
1209
+ autopoolData?.baseAsset.price,
1210
+ prices
1211
+ );
1212
+ const stakedRatio = stakedShares / userShares;
1213
+ const unstakedRatio = 1 - stakedRatio;
1214
+ const isSilo = autopoolData?.symbol === "siloETH" || autopoolData?.symbol === "siloUSD";
1215
+ const baseApr = autopoolData?.apr?.base ?? 0;
1216
+ const stakedAprForBlend = isSilo ? baseApr : autopoolData?.apr?.combined ?? 0;
1217
+ const blendedApr = stakedRatio * stakedAprForBlend + unstakedRatio * baseApr;
1218
+ return {
1219
+ name: autopoolData?.name,
1220
+ symbol: autopoolData?.symbol,
1221
+ poolAddress: autopoolData?.poolAddress,
1222
+ shares: userShares,
1223
+ nav: nav2,
1224
+ returns: returns2,
1225
+ supplied: supplied2,
1226
+ idle: idle2,
1227
+ totalDeposits,
1228
+ totalWithdrawals,
1229
+ staked: {
1230
+ balance: stakedBalance,
1231
+ shares: stakedShares,
1232
+ nav: staked
1233
+ },
1234
+ unstaked: {
1235
+ balance: unstakedBalance,
1236
+ shares: unstakedShares,
1237
+ nav: unstaked
1238
+ },
1239
+ useDenomination: autopoolData?.useDenomination,
1240
+ baseApr,
1241
+ blendedApr,
1242
+ rewardsClaimed: userAutopool?.rewardsClaimed,
1243
+ lastDeposit,
1244
+ baseAsset: autopoolData?.UIBaseAsset,
1245
+ denomination: autopoolData?.denomination,
1246
+ category: categoryKey
1247
+ };
1248
+ }
1249
+ });
1250
+ let denominatedToken = ETH_TOKEN;
1251
+ const useDenomination = userAutopoolsWithData.filter((autopool) => autopool?.useDenomination).length === 1 && userAutopoolsWithData.length === 1;
1252
+ if (useDenomination) {
1253
+ const autopoolWithDenomination = userAutopoolsWithData.find(
1254
+ (autopool) => autopool?.useDenomination
1255
+ );
1256
+ if (autopoolWithDenomination) {
1257
+ denominatedToken = autopoolWithDenomination?.denomination;
1258
+ }
1259
+ }
1260
+ const nav = Object.values(categories).reduce((acc, { nav: nav2 }) => {
1261
+ return Object.keys(nav2).reduce((sumAcc, key) => {
1262
+ const typedKey = key;
1263
+ sumAcc[typedKey] = (sumAcc[typedKey] || 0) + nav2[typedKey];
1264
+ return sumAcc;
1265
+ }, acc);
1266
+ }, {});
1267
+ const idleWithTokenPrices = userAutopoolsWithData.reduce(
1268
+ (acc, userAutopool) => {
1269
+ if (!userAutopool)
1270
+ return acc;
1271
+ const baseAssetSymbol = userAutopool.baseAsset.symbol;
1272
+ const existingEntry = acc.find(
1273
+ (entry) => entry.token.symbol === baseAssetSymbol
1274
+ );
1275
+ if (existingEntry) {
1276
+ Object.entries(userAutopool.idle).forEach(([key, value]) => {
1277
+ if (key in existingEntry) {
1278
+ existingEntry[key] += value || 0;
1279
+ }
1280
+ });
1281
+ } else {
1282
+ acc.push({
1283
+ ...userAutopool.idle,
1284
+ token: userAutopool.baseAsset
1285
+ });
1286
+ }
1287
+ return acc;
1288
+ },
1289
+ []
1290
+ );
1291
+ const idle = idleWithTokenPrices.map((idle2) => ({
1292
+ ...idle2,
1293
+ allocation: idle2.USD / nav.USD
1294
+ }));
1295
+ const returns = Object.values(categories).reduce((acc, { returns: returns2 }) => {
1296
+ return Object.keys(returns2).reduce((sumAcc, key) => {
1297
+ const typedKey = key;
1298
+ sumAcc[typedKey] = (sumAcc[typedKey] || 0) + returns2[typedKey];
1299
+ return sumAcc;
1300
+ }, acc);
1301
+ }, {});
1302
+ const supplied = Object.values(categories).reduce((acc, { supplied: supplied2 }) => {
1303
+ return Object.keys(supplied2).reduce((sumAcc, key) => {
1304
+ const typedKey = key;
1305
+ sumAcc[typedKey] = (sumAcc[typedKey] || 0) + supplied2[typedKey];
1306
+ return sumAcc;
1307
+ }, acc);
1308
+ }, {});
1309
+ const userExchangesWithAllocations = Object.values(userExchanges).map(
1310
+ (userExchange) => {
1311
+ return {
1312
+ ...userExchange,
1313
+ allocation: userExchange.valueUsd / nav.USD
1314
+ };
1315
+ }
1316
+ );
1317
+ const userTokensWithAllocations = Object.values(userTokens).map(
1318
+ (userToken) => {
1319
+ return {
1320
+ ...userToken,
1321
+ allocation: userToken.valueUsd / nav.USD
1322
+ };
1323
+ }
1324
+ );
1325
+ const userPoolsWithAllocations = Object.values(userPools).map(
1326
+ (userPool) => {
1327
+ return {
1328
+ ...userPool,
1329
+ debtValueHeldByVaultAllocation: userPool.debtValueHeldByVaultUsd / nav.USD
1330
+ };
1331
+ }
1332
+ );
1333
+ const userWeightedApr = userAutopoolsWithData.reduce((acc, autopool) => {
1334
+ if (!autopool)
1335
+ return acc;
1336
+ return acc + autopool?.blendedApr * (autopool?.nav.USD / nav.USD);
1337
+ }, 0);
1338
+ let [lowestApr, highestApr] = [0, 0];
1339
+ const aprValues = userAutopoolsWithData.map((autopool) => autopool?.blendedApr).filter((apr) => apr !== void 0 && apr !== null);
1340
+ lowestApr = Math.min(...aprValues);
1341
+ highestApr = Math.max(...aprValues);
1342
+ const avgDailyReturns = userWeightedApr / 365;
1343
+ const avgDailyReturnsUsd = avgDailyReturns * nav.USD;
1344
+ const activeBaseAssets = Object.values(categories).filter(
1345
+ (category) => category.nav.USD > 0
1346
+ );
1347
+ const hasMultipleBaseAssets = activeBaseAssets.length > 1;
1348
+ const categoryAprs = Object.keys(categories).reduce(
1349
+ (acc, categoryKey) => {
1350
+ const apr = userAutopoolsWithData.reduce((aprAcc, autopool) => {
1351
+ if (!autopool)
1352
+ return aprAcc;
1353
+ if (autopool?.category === categoryKey) {
1354
+ return aprAcc + autopool?.blendedApr * (autopool?.nav.USD / categories[categoryKey].nav.USD);
1355
+ }
1356
+ return aprAcc;
1357
+ }, 0);
1358
+ return {
1359
+ ...acc,
1360
+ [categoryKey]: apr
1361
+ };
1362
+ },
1363
+ {}
1364
+ );
1365
+ Object.keys(categories).forEach((categoryKey) => {
1366
+ const categoryData = categories[categoryKey];
1367
+ const apr = categoryAprs[categoryKey] || 0;
1368
+ const dailyRate = apr / 365;
1369
+ categories[categoryKey] = {
1370
+ ...categoryData,
1371
+ apr,
1372
+ avgDailyReturns: Object.keys(categoryData.nav).reduce((acc, key) => {
1373
+ const typedKey = key;
1374
+ acc[typedKey] = dailyRate * categoryData.nav[typedKey];
1375
+ return acc;
1376
+ }, {})
1377
+ };
1378
+ });
1379
+ return {
1380
+ nav,
1381
+ idle,
1382
+ supplied,
1383
+ returns,
1384
+ categories,
1385
+ exchanges: userExchangesWithAllocations,
1386
+ tokens: userTokensWithAllocations,
1387
+ pools: userPoolsWithAllocations,
1388
+ autopools: userAutopoolsWithData,
1389
+ avgDailyReturnsUsd,
1390
+ useDenomination,
1391
+ denominatedToken,
1392
+ weightedApr: userWeightedApr,
1393
+ lowestApr,
1394
+ highestApr,
1395
+ hasMultipleBaseAssets
1396
+ };
1397
+ }
1398
+ } catch (e) {
1399
+ console.log(e);
1400
+ }
1401
+ };
1402
+ var getChainUserAutopoolsHistory = async ({
1403
+ address,
1404
+ chainId = 1
1405
+ }) => {
1406
+ const { GetUserVaultsDayData } = getSdkByChainId(chainId);
1407
+ const oneYearAgoTimestamp = Math.floor(Date.now() / 1e3) - 365 * 24 * 60 * 60;
1408
+ try {
1409
+ if (address) {
1410
+ const { userVaultDayDatas } = await GetUserVaultsDayData({
1411
+ address,
1412
+ timestamp: oneYearAgoTimestamp
1413
+ });
1414
+ return userVaultDayDatas;
1415
+ }
1416
+ return [];
1417
+ } catch (e) {
1418
+ console.error(e);
1419
+ return [];
1420
+ }
1421
+ };
1422
+ var getTokenValueDayDatas = async (tokenAddress, chainId = mainnet.id) => {
1423
+ const { GetTokenValueDayDatas } = getSdkByChainId(chainId);
1424
+ try {
1425
+ const { tokenValueDayDatas } = await GetTokenValueDayDatas({
1426
+ tokenAddress: tokenAddress.toLowerCase()
1427
+ });
1428
+ const historicalPrice = tokenValueDayDatas.map((tokenValueDayData) => {
1429
+ const date = convertTimestampToDate(
1430
+ tokenValueDayData.lastSnapshotTimestamp
1431
+ );
1432
+ const usdPrice = formatUnits(tokenValueDayData.priceInUsd, 8);
1433
+ return {
1434
+ timestamp: Number(tokenValueDayData.lastSnapshotTimestamp),
1435
+ date,
1436
+ price: usdPrice
1437
+ };
1438
+ });
1439
+ return historicalPrice;
1440
+ } catch (e) {
1441
+ console.error(e);
1442
+ }
1443
+ };
1444
+ var hasCoinGeckoId = (asset) => typeof asset?.coinGeckoId === "string" && asset.coinGeckoId.length > 0;
1445
+ var getBlobHistoricalTokenPrices = async (tokenSymbol) => {
1446
+ const blobName = `historical_v2/${tokenSymbol}-latest-success.json`;
1447
+ const res = await getBlobData(blobName);
1448
+ if (!res?.data || !Array.isArray(res.data))
1449
+ return [];
1450
+ return res.data.map(({ timestamp, date, price }) => ({
1451
+ timestamp,
1452
+ date,
1453
+ price
1454
+ }));
1455
+ };
1456
+ var getHistoricalTokenPrices = async () => {
1457
+ const ETH_ADDRESS_IN_SUBGRAPH = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
1458
+ const historicalBaseAssetPrices = await Promise.all(
1459
+ BASE_ASSETS.map(async (baseAsset) => {
1460
+ if (!hasCoinGeckoId(baseAsset)) {
1461
+ const address = baseAsset.address === ETH_TOKEN.address ? ETH_ADDRESS_IN_SUBGRAPH : baseAsset.address;
1462
+ const prices2 = await getTokenValueDayDatas(
1463
+ address,
1464
+ baseAsset.chainId
1465
+ );
1466
+ return { ...baseAsset, prices: prices2 ?? [] };
1467
+ }
1468
+ const prices = await getBlobHistoricalTokenPrices(baseAsset.symbol);
1469
+ return { ...baseAsset, prices };
1470
+ })
1471
+ );
1472
+ const allTimestamps = /* @__PURE__ */ new Set();
1473
+ historicalBaseAssetPrices.forEach((asset) => {
1474
+ asset.prices?.forEach((priceData) => {
1475
+ if (priceData?.timestamp) {
1476
+ allTimestamps.add(priceData.timestamp);
1477
+ }
1478
+ });
1479
+ });
1480
+ const sortedTimestamps = Array.from(allTimestamps).sort((a, b) => a - b);
1481
+ const historicalTokenPrices = sortedTimestamps.map(
1482
+ (timestamp) => {
1483
+ const prices = {};
1484
+ const baseAssetsWithUsd = [
1485
+ ...BASE_ASSETS.map((asset) => asset.symbol),
1486
+ "USD"
1487
+ ];
1488
+ const usdPrices = /* @__PURE__ */ new Map();
1489
+ baseAssetsWithUsd.forEach((asset) => {
1490
+ if (asset !== "USD") {
1491
+ const assetData = historicalBaseAssetPrices.find(
1492
+ (a) => a.symbol === asset
1493
+ );
1494
+ const priceData = findClosestTimestampEntry(
1495
+ assetData?.prices || [],
1496
+ timestamp
1497
+ );
1498
+ usdPrices.set(asset, priceData ? parseFloat(priceData.price) : 0);
1499
+ }
1500
+ });
1501
+ baseAssetsWithUsd.forEach((asset1) => {
1502
+ prices[asset1] = {};
1503
+ baseAssetsWithUsd.forEach((asset2) => {
1504
+ if (asset1 === asset2) {
1505
+ prices[asset1][asset2] = 1;
1506
+ } else if (asset1 === "USD") {
1507
+ prices[asset1][asset2] = usdPrices.get(asset2) || 0;
1508
+ } else if (asset2 === "USD") {
1509
+ prices[asset1][asset2] = usdPrices.get(asset1) || 0;
1510
+ } else {
1511
+ const asset1UsdPrice = usdPrices.get(asset1) || 0;
1512
+ const asset2UsdPrice = usdPrices.get(asset2) || 0;
1513
+ prices[asset1][asset2] = asset2UsdPrice > 0 ? asset1UsdPrice / asset2UsdPrice : 0;
1514
+ }
1515
+ });
1516
+ });
1517
+ return {
1518
+ timestamp,
1519
+ date: new Date(timestamp * 1e3),
1520
+ prices
1521
+ };
1522
+ }
1523
+ );
1524
+ return historicalTokenPrices;
1525
+ };
1526
+
1527
+ // functions/getUserAutopoolsHistory.ts
1528
+ var ONE_DAY_IN_MS = 24 * 60 * 60 * 1e3;
1529
+ function validateEnhancedUserHistoryEntry(entry) {
1530
+ return entry && typeof entry === "object" && entry.date instanceof Date && typeof entry.timestamp === "string" && Array.isArray(entry.autopools) && entry.nav && typeof entry.nav.ETH === "number" && typeof entry.nav.USD === "number" && typeof entry.nav.PXETH === "number" && typeof entry.nav.USDC === "number" && typeof entry.nav.EURC === "number" && typeof entry.nav.USDT === "number" && Array.isArray(entry.events) && typeof entry.differential === "number";
1531
+ }
1532
+ var getUserAutopoolsHistory = async (address, autopoolsHistory, events, includeTestnet = false) => {
1533
+ if (!autopoolsHistory) {
1534
+ throw new Error("No autopools history found");
1535
+ }
1536
+ if (!events) {
1537
+ throw new Error("No events found");
1538
+ }
1539
+ const chains = getChainsForEnv({ includeTestnet });
1540
+ const userHistory = (await Promise.all(
1541
+ chains.map(
1542
+ (chain) => getChainUserAutopoolsHistory({
1543
+ address,
1544
+ chainId: chain.chainId
1545
+ })
1546
+ )
1547
+ )).flat();
1548
+ const groupedByVault = userHistory.reduce((acc, data) => {
1549
+ const vaultId = data.vaultAddress.toLowerCase();
1550
+ if (!acc[vaultId]) {
1551
+ acc[vaultId] = [];
1552
+ }
1553
+ acc[vaultId].push({
1554
+ ...data,
1555
+ date: new Date(data.timestamp * 1e3),
1556
+ timestamp: data.timestamp,
1557
+ vaultAddress: data.vaultAddress
1558
+ });
1559
+ return acc;
1560
+ }, {});
1561
+ const filledDataByVault = Object.entries(groupedByVault).reduce(
1562
+ (acc, [vaultId, vaultData]) => {
1563
+ acc[vaultId] = fillMissingDates(vaultData);
1564
+ return acc;
1565
+ },
1566
+ {}
1567
+ );
1568
+ const historicalBaseAssetPrices = await getHistoricalTokenPrices();
1569
+ const mergedHistory = Object.keys(filledDataByVault).reduce(
1570
+ (acc, vaultId) => {
1571
+ if (autopoolsHistory[vaultId]) {
1572
+ const autopoolHistory = autopoolsHistory[vaultId];
1573
+ const userHistory2 = filledDataByVault[vaultId];
1574
+ userHistory2.sort((a, b) => a.date.getTime() - b.date.getTime());
1575
+ const mergedData = autopoolHistory.reduce((result2, dayData) => {
1576
+ const userHistoryData = findClosestDateEntry(
1577
+ userHistory2,
1578
+ dayData.date,
1579
+ ONE_DAY_IN_MS
1580
+ );
1581
+ if (userHistoryData) {
1582
+ const DECIMALS = BigInt(dayData.baseAsset.decimals);
1583
+ const SCALE = 10n ** DECIMALS;
1584
+ const sharesRatio = userHistoryData.totalShares / dayData.totalSupply;
1585
+ const sharesRatioBigInt = BigInt(
1586
+ Math.floor(sharesRatio * Number(SCALE))
1587
+ );
1588
+ const navBigInt = sharesRatioBigInt * BigInt(dayData.nav) / SCALE;
1589
+ const navNum = formatUnitsNum(
1590
+ navBigInt,
1591
+ dayData.baseAsset.decimals
1592
+ );
1593
+ if (!historicalBaseAssetPrices) {
1594
+ throw new Error("No historical price data found");
1595
+ }
1596
+ const historicalPriceData = findClosestTimestampEntry(
1597
+ historicalBaseAssetPrices,
1598
+ Number(dayData.timestamp)
1599
+ );
1600
+ if (!historicalPriceData) {
1601
+ throw new Error("No historical price data found");
1602
+ }
1603
+ let pricesOfBaseAsset = historicalPriceData.prices[dayData.baseAsset.symbol.toUpperCase()];
1604
+ if (!pricesOfBaseAsset) {
1605
+ pricesOfBaseAsset = historicalPriceData.prices[dayData.baseAsset.extensions?.parentAsset?.toUpperCase()];
1606
+ }
1607
+ let nav = {};
1608
+ nav = Object.keys(pricesOfBaseAsset).reduce((acc2, currency) => {
1609
+ acc2[currency] = navNum * pricesOfBaseAsset[currency];
1610
+ return acc2;
1611
+ }, {});
1612
+ result2.push({
1613
+ id: dayData.id + `-${address}`,
1614
+ vaultAddress: dayData.vault.id,
1615
+ timestamp: dayData.timestamp,
1616
+ baseAsset: dayData.baseAsset,
1617
+ date: dayData.date,
1618
+ nav,
1619
+ shares: userHistoryData.totalShares,
1620
+ rewardsClaimed: userHistoryData.rewardsClaimed
1621
+ });
1622
+ }
1623
+ return result2;
1624
+ }, []);
1625
+ acc[vaultId] = mergedData;
1626
+ }
1627
+ return acc;
1628
+ },
1629
+ {}
1630
+ );
1631
+ const aggregatedHistoryArray = Object.values(mergedHistory).flat().reduce((acc, dayData) => {
1632
+ const timestampKey = dayData.timestamp;
1633
+ if (!acc[timestampKey]) {
1634
+ acc[timestampKey] = {
1635
+ timestamp: dayData.timestamp,
1636
+ nav: {
1637
+ ETH: 0,
1638
+ USD: 0,
1639
+ PXETH: 0,
1640
+ USDC: 0,
1641
+ EURC: 0,
1642
+ USDT: 0
1643
+ },
1644
+ date: dayData.date,
1645
+ autopools: {}
1646
+ };
1647
+ }
1648
+ acc[timestampKey].autopools[dayData.vaultAddress] = dayData;
1649
+ acc[timestampKey].nav.USD += dayData.nav.USD;
1650
+ acc[timestampKey].nav.ETH += dayData.nav.ETH;
1651
+ acc[timestampKey].nav.PXETH += dayData.nav.PXETH;
1652
+ acc[timestampKey].nav.USDC += dayData.nav.USDC;
1653
+ acc[timestampKey].nav.EURC += dayData.nav.EURC;
1654
+ acc[timestampKey].nav.USDT += dayData.nav.USDT;
1655
+ return acc;
1656
+ }, {});
1657
+ let finalAggregatedHistoryArray = Object.keys(aggregatedHistoryArray).map(
1658
+ (dateKey) => ({
1659
+ date: aggregatedHistoryArray[dateKey].date,
1660
+ formattedDate: formatDateToReadable(aggregatedHistoryArray[dateKey].date),
1661
+ nav: {
1662
+ ETH: aggregatedHistoryArray[dateKey].nav.ETH,
1663
+ USD: aggregatedHistoryArray[dateKey].nav.USD,
1664
+ PXETH: aggregatedHistoryArray[dateKey].nav.PXETH,
1665
+ USDC: aggregatedHistoryArray[dateKey].nav.USDC,
1666
+ EURC: aggregatedHistoryArray[dateKey].nav.EURC,
1667
+ USDT: aggregatedHistoryArray[dateKey].nav.USDT
1668
+ },
1669
+ timestamp: aggregatedHistoryArray[dateKey].timestamp,
1670
+ autopools: Object.values(aggregatedHistoryArray[dateKey].autopools)
1671
+ })
1672
+ );
1673
+ let updatedAggregatedHistoryArray = finalAggregatedHistoryArray.map(
1674
+ (dayData) => {
1675
+ return {
1676
+ ...dayData,
1677
+ nav: {
1678
+ ETH: dayData.nav.ETH,
1679
+ USD: dayData.nav.USD,
1680
+ PXETH: dayData.nav.PXETH,
1681
+ USDC: dayData.nav.USDC,
1682
+ EURC: dayData.nav.EURC,
1683
+ USDT: dayData.nav.USDT
1684
+ }
1685
+ };
1686
+ }
1687
+ );
1688
+ updatedAggregatedHistoryArray = updatedAggregatedHistoryArray.map(
1689
+ (dayData) => {
1690
+ const eventsForDay = events?.filter((event) => {
1691
+ const eventDate = new Date(Number(event.timestamp) * 1e3);
1692
+ return eventDate.getTime() >= dayData.date.getTime() && eventDate.getTime() < dayData.date.getTime() + 24 * 60 * 60 * 1e3;
1693
+ });
1694
+ const differential = eventsForDay.reduce(
1695
+ (sum, event) => sum + formatEtherNum(BigInt(event?.assetChange || 0n)),
1696
+ 0
1697
+ );
1698
+ return {
1699
+ ...dayData,
1700
+ events: eventsForDay,
1701
+ differential
1702
+ };
1703
+ }
1704
+ );
1705
+ const result = updatedAggregatedHistoryArray;
1706
+ const allValid = result.every(validateEnhancedUserHistoryEntry);
1707
+ if (!allValid) {
1708
+ throw new Error(
1709
+ "Invalid history entry detected: missing required properties"
1710
+ );
1711
+ }
1712
+ return result;
1713
+ };
1714
+
1715
+ // functions/getRewardsPayloadV1.ts
1716
+ var getRewardsPayloadV1 = async (cycleHash, account, rewardsV1Url) => {
1717
+ const userRewardUrl = `${rewardsV1Url}/${cycleHash}/${account.toLowerCase()}.json`;
1718
+ try {
1719
+ const response = await fetch(userRewardUrl, { method: "GET" });
1720
+ if (!response.ok) {
1721
+ return null;
1722
+ }
1723
+ const data = await response.json();
1724
+ return data;
1725
+ } catch (e) {
1726
+ console.log("Error fetching rewards payload:", e);
1727
+ return null;
1728
+ }
1729
+ };
1730
+
1731
+ // functions/getUserRewardsV1.ts
1732
+ var getUserRewardsV1 = async (wagmiConfig, {
1733
+ address,
1734
+ rewardsCycleIndex,
1735
+ rewardsV1,
1736
+ rewardsV1Url,
1737
+ rewardsV1Hash,
1738
+ chainId
1739
+ }) => {
1740
+ try {
1741
+ const [latestClaimableHash, cycleRewardsHash] = await readContract(
1742
+ wagmiConfig,
1743
+ {
1744
+ address: rewardsV1Hash,
1745
+ abi: rewardsV1HashAbi,
1746
+ functionName: "cycleHashes",
1747
+ args: [rewardsCycleIndex],
1748
+ chainId
1749
+ }
1750
+ );
1751
+ const rewardsPayload = await getRewardsPayloadV1(
1752
+ cycleRewardsHash,
1753
+ address,
1754
+ rewardsV1Url
1755
+ );
1756
+ const latestClaimablePayload = await getRewardsPayloadV1(
1757
+ latestClaimableHash,
1758
+ address,
1759
+ rewardsV1Url
1760
+ );
1761
+ if (latestClaimablePayload) {
1762
+ const {
1763
+ payload: { chainId: payloadChainId, cycle, wallet, amount }
1764
+ } = latestClaimablePayload;
1765
+ const claimable = await readContract(wagmiConfig, {
1766
+ address: rewardsV1,
1767
+ abi: rewardsV1Abi,
1768
+ functionName: "getClaimableAmount",
1769
+ args: [{ chainId: payloadChainId, cycle, wallet, amount }],
1770
+ chainId
1771
+ });
1772
+ return { claimable, rewardsPayload, latestClaimablePayload };
1773
+ }
1774
+ return { rewardsPayload, latestClaimablePayload };
1775
+ } catch (e) {
1776
+ console.log(e);
1777
+ }
1778
+ };
1779
+ var getUserV1 = async (wagmiConfig, {
1780
+ currentCycleIndex,
1781
+ address,
1782
+ chainId
1783
+ }) => {
1784
+ const {
1785
+ stakingV1,
1786
+ rewardsV1Url,
1787
+ accTokeV1RewardsHash,
1788
+ rewardsV1Hash,
1789
+ accTokeV1Rewards,
1790
+ autoEthGuardedRewards,
1791
+ rewardsV1,
1792
+ missedTokeRewards
1793
+ } = getMainnetConfig(mainnet.id);
1794
+ try {
1795
+ const userStakedTokeV1 = await readContract(wagmiConfig, {
1796
+ address: stakingV1,
1797
+ abi: stakingV1Abi,
1798
+ functionName: "availableForWithdrawal",
1799
+ args: [address, 0n],
1800
+ chainId: mainnet.id
1801
+ });
1802
+ const tokeRewards = await getUserRewardsV1(wagmiConfig, {
1803
+ address,
1804
+ rewardsCycleIndex: currentCycleIndex - 1n,
1805
+ rewardsV1,
1806
+ rewardsV1Url,
1807
+ rewardsV1Hash,
1808
+ chainId
1809
+ });
1810
+ const ethRewards = await getUserRewardsV1(wagmiConfig, {
1811
+ address,
1812
+ rewardsCycleIndex: 306n,
1813
+ rewardsV1: accTokeV1Rewards,
1814
+ rewardsV1Url,
1815
+ rewardsV1Hash: accTokeV1RewardsHash,
1816
+ chainId
1817
+ });
1818
+ const autoEthGuardedRewardsPayload = await getRewardsPayloadV1(
1819
+ "QmcJgQ42aGTsqngSBa98qxnbv2CHyk9DmSg1jDD569bnpp",
1820
+ address,
1821
+ rewardsV1Url
1822
+ );
1823
+ const payloadChainId = autoEthGuardedRewardsPayload?.payload?.chainId;
1824
+ const cycle = autoEthGuardedRewardsPayload?.payload?.cycle;
1825
+ const wallet = autoEthGuardedRewardsPayload?.payload?.wallet;
1826
+ const amount = autoEthGuardedRewardsPayload?.payload?.amount;
1827
+ const claimableAutoEth = await readContract(wagmiConfig, {
1828
+ address: autoEthGuardedRewards,
1829
+ abi: rewardsV1Abi,
1830
+ functionName: "getClaimableAmount",
1831
+ args: [
1832
+ {
1833
+ chainId: payloadChainId || 0n,
1834
+ cycle: cycle || 0n,
1835
+ wallet: wallet || "0x0000000000000000000000000000000000000000",
1836
+ amount: amount || 0n
1837
+ }
1838
+ ],
1839
+ chainId
1840
+ });
1841
+ const missedTokeRewardsPayload = await getRewardsPayloadV1(
1842
+ "QmRpVjVhFqbUTtLnQ4addEwBtvF2CC7t6rbQWYiju9nAqd",
1843
+ address,
1844
+ rewardsV1Url
1845
+ );
1846
+ let claimableMissedToke = 0n;
1847
+ if (missedTokeRewardsPayload) {
1848
+ const {
1849
+ payload: { chainId: payloadChainId2, cycle: cycle2, wallet: wallet2, amount: amount2 }
1850
+ } = missedTokeRewardsPayload;
1851
+ claimableMissedToke = await readContract(wagmiConfig, {
1852
+ address: missedTokeRewards,
1853
+ abi: rewardsV1Abi,
1854
+ functionName: "getClaimableAmount",
1855
+ args: [
1856
+ {
1857
+ chainId: payloadChainId2,
1858
+ cycle: cycle2,
1859
+ wallet: wallet2,
1860
+ amount: amount2 || 0n
1861
+ }
1862
+ ],
1863
+ chainId
1864
+ });
1865
+ }
1866
+ return {
1867
+ stakedToke: userStakedTokeV1,
1868
+ claimableToke: tokeRewards?.claimable,
1869
+ claimableEth: ethRewards?.claimable,
1870
+ claimableAutoEth,
1871
+ tokeRewardsPayload: tokeRewards?.rewardsPayload,
1872
+ ethRewardsPayload: ethRewards?.rewardsPayload,
1873
+ missedTokeRewardsPayload,
1874
+ autoEthGuardedRewardsPayload,
1875
+ claimableMissedToke
1876
+ };
1877
+ } catch (e) {
1878
+ }
1879
+ };
1880
+ var networkToAlchemyUrl = (chainId, apiKey) => {
1881
+ switch (chainId) {
1882
+ case mainnet.id:
1883
+ return `https://eth-mainnet.g.alchemy.com/v2/${apiKey}`;
1884
+ case base.id:
1885
+ return `https://base-mainnet.g.alchemy.com/v2/${apiKey}`;
1886
+ case sonic.id:
1887
+ return `https://sonic-mainnet.g.alchemy.com/v2/${apiKey}`;
1888
+ default:
1889
+ throw new Error("Unsupported network");
1890
+ }
1891
+ };
1892
+ var getUserTokenBalances = async ({
1893
+ address,
1894
+ apiKey,
1895
+ chainId
1896
+ }) => {
1897
+ if (!address)
1898
+ throw new Error("Address is not defined");
1899
+ if (!apiKey)
1900
+ throw new Error("Alchemy API key is not defined");
1901
+ const url = networkToAlchemyUrl(chainId, apiKey);
1902
+ const body = {
1903
+ jsonrpc: "2.0",
1904
+ method: "alchemy_getTokenBalances",
1905
+ params: [address],
1906
+ id: 1
1907
+ };
1908
+ const res = await fetch(url, {
1909
+ method: "POST",
1910
+ headers: { "Content-Type": "application/json" },
1911
+ body: JSON.stringify(body)
1912
+ });
1913
+ if (!res.ok)
1914
+ throw new Error(`Alchemy RPC error: ${res.statusText}`);
1915
+ const data = await res.json();
1916
+ if (!data.result || !data.result.tokenBalances) {
1917
+ throw new Error("No token balances found in response");
1918
+ }
1919
+ const tokenBalances = data.result.tokenBalances.reduce(
1920
+ (acc, balance) => {
1921
+ if (balance.tokenBalance && BigInt(balance.tokenBalance) !== BigInt(0)) {
1922
+ acc[balance.contractAddress] = hexToBigInt(
1923
+ balance.tokenBalance
1924
+ );
1925
+ }
1926
+ return acc;
1927
+ },
1928
+ {}
1929
+ );
1930
+ return tokenBalances;
1931
+ };
1932
+ var getChainUserActivity = async (address, chainId = 1) => {
1933
+ const { GetUserBalanceChangeHistory } = getSdkByChainId(chainId);
1934
+ try {
1935
+ const { userAutopoolBalanceChanges } = await GetUserBalanceChangeHistory({
1936
+ userAddress: address
1937
+ });
1938
+ let userActivityTotals = {};
1939
+ let events = [];
1940
+ userAutopoolBalanceChanges.forEach((activity) => {
1941
+ if (!userActivityTotals[activity.vaultAddress]) {
1942
+ userActivityTotals[activity.vaultAddress] = {
1943
+ totalDeposits: 0n,
1944
+ totalWithdrawals: 0n,
1945
+ totalStakes: 0n,
1946
+ totalUnstakes: 0n,
1947
+ chainId
1948
+ };
1949
+ }
1950
+ activity.items.forEach((item) => {
1951
+ if (item.staked && item.assetChange > 0n) {
1952
+ userActivityTotals[activity.vaultAddress].totalStakes += BigInt(
1953
+ item.assetChange
1954
+ );
1955
+ } else if (item.staked && item.assetChange < 0n) {
1956
+ userActivityTotals[activity.vaultAddress].totalUnstakes += BigInt(
1957
+ BigInt(item.assetChange) * -1n
1958
+ );
1959
+ } else if (!item.staked && item.assetChange > 0n) {
1960
+ userActivityTotals[activity.vaultAddress].totalDeposits += BigInt(
1961
+ item.assetChange
1962
+ );
1963
+ } else if (!item.staked && item.assetChange < 0n) {
1964
+ userActivityTotals[activity.vaultAddress].totalWithdrawals += BigInt(
1965
+ BigInt(item.assetChange) * -1n
1966
+ );
1967
+ }
1968
+ if (!item.staked) {
1969
+ events.push({
1970
+ timestamp: activity.timestamp,
1971
+ shareChange: item.shareChange,
1972
+ assetChange: item.assetChange,
1973
+ vaultAddress: activity.vaultAddress
1974
+ // staked: item.staked,
1975
+ });
1976
+ }
1977
+ });
1978
+ });
1979
+ return { events, totals: userActivityTotals };
1980
+ } catch (e) {
1981
+ console.log(e);
1982
+ }
1983
+ };
1984
+
1985
+ // functions/getUserActivity.ts
1986
+ var getUserActivity = async ({
1987
+ address,
1988
+ includeTestnet = false
1989
+ }) => {
1990
+ const chains = getChainsForEnv({ includeTestnet });
1991
+ const userActivities = await Promise.all(
1992
+ chains.map(
1993
+ (chain) => getChainUserActivity(address, chain.chainId)
1994
+ )
1995
+ );
1996
+ const mergedActivity = userActivities.reduce(
1997
+ (acc, chainActivity) => {
1998
+ if (chainActivity?.events) {
1999
+ acc.events.push(...chainActivity.events);
2000
+ }
2001
+ if (chainActivity?.totals) {
2002
+ acc.totals = { ...acc.totals, ...chainActivity.totals };
2003
+ }
2004
+ return acc;
2005
+ },
2006
+ {
2007
+ events: [],
2008
+ totals: {}
2009
+ }
2010
+ );
2011
+ mergedActivity.events.sort((a, b) => a.timestamp - b.timestamp);
2012
+ return mergedActivity;
2013
+ };
2014
+ var systemRegistryFunctionNames = [
2015
+ "asyncSwapperRegistry",
2016
+ "autoPoolRouter",
2017
+ "autoPoolRegistry",
2018
+ "swapRouter",
2019
+ "weth"
2020
+ ];
2021
+ var getSystemConfig = async (wagmiConfig, { systemRegistry }) => {
2022
+ const systemRegistryContract = {
2023
+ address: systemRegistry,
2024
+ abi: systemRegistryAbi
2025
+ };
2026
+ const systemRegistryCalls = systemRegistryFunctionNames.map(
2027
+ (functionName) => ({
2028
+ ...systemRegistryContract,
2029
+ functionName
2030
+ })
2031
+ );
2032
+ try {
2033
+ const [
2034
+ { result: asyncSwapperRegistry },
2035
+ { result: autopoolRouter },
2036
+ { result: autopoolRegistry },
2037
+ { result: swapRouter }
2038
+ ] = await readContracts(wagmiConfig, {
2039
+ contracts: systemRegistryCalls
2040
+ });
2041
+ return {
2042
+ asyncSwapperRegistry,
2043
+ autopoolRouter,
2044
+ autopoolRegistry,
2045
+ swapRouter
2046
+ };
2047
+ } catch (e) {
2048
+ console.log(e);
2049
+ }
2050
+ };
2051
+ var getCurrentCycleId = async (wagmiConfig, {
2052
+ stoke,
2053
+ chainId
2054
+ }) => {
2055
+ return readContract(wagmiConfig, {
2056
+ address: stoke,
2057
+ abi: accTokeV1Abi,
2058
+ chainId,
2059
+ functionName: "getCurrentCycleID"
2060
+ });
2061
+ };
2062
+
2063
+ export { getAutopools, getCurrentCycleId, getEthPrice, getSystemConfig, getTokePrice, getTokenPrice, getUserActivity, getUserAutopools, getUserAutopoolsHistory, getUserTokenBalances, getUserV1, systemRegistryFunctionNames };