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