@defisaver/positions-sdk 2.0.1 → 2.0.5

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.
Files changed (78) hide show
  1. package/.mocharc.json +4 -4
  2. package/.nvmrc +1 -1
  3. package/README.md +64 -64
  4. package/cjs/config/contracts.d.ts +1018 -16
  5. package/cjs/config/contracts.js +12 -4
  6. package/cjs/helpers/morphoBlueHelpers/index.js +66 -66
  7. package/cjs/markets/aave/marketAssets.js +1 -1
  8. package/cjs/staking/staking.js +1 -1
  9. package/esm/config/contracts.d.ts +1018 -16
  10. package/esm/config/contracts.js +12 -4
  11. package/esm/helpers/morphoBlueHelpers/index.js +66 -66
  12. package/esm/markets/aave/marketAssets.js +1 -1
  13. package/esm/staking/staking.js +1 -1
  14. package/package.json +47 -47
  15. package/src/aaveV2/index.ts +236 -236
  16. package/src/aaveV3/index.ts +489 -489
  17. package/src/compoundV2/index.ts +240 -240
  18. package/src/compoundV3/index.ts +270 -270
  19. package/src/config/contracts.ts +1090 -1082
  20. package/src/constants/index.ts +6 -6
  21. package/src/contracts.ts +107 -107
  22. package/src/curveUsd/index.ts +250 -250
  23. package/src/eulerV2/index.ts +314 -314
  24. package/src/exchange/index.ts +25 -25
  25. package/src/fluid/index.ts +1568 -1568
  26. package/src/helpers/aaveHelpers/index.ts +170 -170
  27. package/src/helpers/compoundHelpers/index.ts +261 -261
  28. package/src/helpers/curveUsdHelpers/index.ts +40 -40
  29. package/src/helpers/eulerHelpers/index.ts +259 -259
  30. package/src/helpers/fluidHelpers/index.ts +324 -324
  31. package/src/helpers/index.ts +10 -10
  32. package/src/helpers/liquityV2Helpers/index.ts +80 -80
  33. package/src/helpers/llamaLendHelpers/index.ts +53 -53
  34. package/src/helpers/makerHelpers/index.ts +52 -52
  35. package/src/helpers/morphoBlueHelpers/index.ts +390 -390
  36. package/src/helpers/sparkHelpers/index.ts +155 -155
  37. package/src/index.ts +45 -45
  38. package/src/liquity/index.ts +104 -104
  39. package/src/liquityV2/index.ts +408 -408
  40. package/src/llamaLend/index.ts +296 -296
  41. package/src/maker/index.ts +223 -223
  42. package/src/markets/aave/index.ts +116 -116
  43. package/src/markets/aave/marketAssets.ts +44 -44
  44. package/src/markets/compound/index.ts +216 -216
  45. package/src/markets/compound/marketsAssets.ts +83 -83
  46. package/src/markets/curveUsd/index.ts +69 -69
  47. package/src/markets/euler/index.ts +26 -26
  48. package/src/markets/fluid/index.ts +2456 -2456
  49. package/src/markets/index.ts +25 -25
  50. package/src/markets/liquityV2/index.ts +102 -102
  51. package/src/markets/llamaLend/contractAddresses.ts +141 -141
  52. package/src/markets/llamaLend/index.ts +235 -235
  53. package/src/markets/morphoBlue/index.ts +895 -895
  54. package/src/markets/spark/index.ts +29 -29
  55. package/src/markets/spark/marketAssets.ts +10 -10
  56. package/src/moneymarket/moneymarketCommonService.ts +80 -80
  57. package/src/morphoBlue/index.ts +222 -222
  58. package/src/portfolio/index.ts +285 -285
  59. package/src/services/priceService.ts +159 -159
  60. package/src/services/utils.ts +63 -63
  61. package/src/services/viem.ts +30 -30
  62. package/src/setup.ts +8 -8
  63. package/src/spark/index.ts +456 -456
  64. package/src/staking/staking.ts +192 -192
  65. package/src/types/aave.ts +194 -194
  66. package/src/types/common.ts +87 -87
  67. package/src/types/compound.ts +136 -136
  68. package/src/types/curveUsd.ts +121 -121
  69. package/src/types/euler.ts +174 -174
  70. package/src/types/fluid.ts +450 -450
  71. package/src/types/index.ts +11 -11
  72. package/src/types/liquity.ts +30 -30
  73. package/src/types/liquityV2.ts +126 -126
  74. package/src/types/llamaLend.ts +157 -157
  75. package/src/types/maker.ts +63 -63
  76. package/src/types/morphoBlue.ts +194 -194
  77. package/src/types/portfolio.ts +60 -60
  78. package/src/types/spark.ts +137 -137
@@ -1,193 +1,193 @@
1
- import Dec from 'decimal.js';
2
- import memoize from 'memoizee';
3
- import { MMAssetsData, MMUsedAssets } from '../types/common';
4
- import { BLOCKS_IN_A_YEAR } from '../constants';
5
- import { DEFAULT_TIMEOUT } from '../services/utils';
6
-
7
- const getSsrApy = async () => {
8
- try {
9
- const res = await fetch('https://fe.defisaver.com/api/sky/data',
10
- { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
11
- const data = await res.json();
12
- return new Dec(data.data.skyData[0].sky_savings_rate_apy).mul(100).toString();
13
- } catch (e) {
14
- console.error('External API Failure: Failed to fetch SSR APY from external API', e);
15
- return '0';
16
- }
17
- };
18
-
19
- const getSuperOETHApy = async () => {
20
- try {
21
- const res = await fetch('https://origin.squids.live/origin-squid/graphql', {
22
- method: 'POST',
23
- headers: {
24
- 'Content-Type': 'application/json',
25
- },
26
- body: JSON.stringify({
27
- query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
28
- variables: {
29
- token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
30
- chainId: 8453,
31
- },
32
- }),
33
- signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
34
- });
35
-
36
- const data = await res.json();
37
- return new Dec(data.data.oTokenApies[0].apy).mul(100).toString();
38
- } catch (e) {
39
- console.error('External API Failure: Failed to fetch Super OETH APY from external API', e);
40
- return '0';
41
- }
42
- };
43
-
44
- const getApyFromDfsApi = async (asset: string) => {
45
- try {
46
- const res = await fetch(`https://fe.defisaver.com/api/staking/apy?asset=${asset}`,
47
- { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
48
- if (!res.ok) throw new Error(`Failed to fetch APY for ${asset}`);
49
- const data = await res.json();
50
- return String(data.apy);
51
- } catch (e) {
52
- console.error(`External API Failure: Failed to fetch APY for ${asset} from DFS API`, e);
53
- return '0';
54
- }
55
- };
56
-
57
- export const STAKING_ASSETS = ['cbETH', 'wstETH', 'cbETH', 'rETH', 'sDAI', 'weETH', 'sUSDe', 'osETH', 'ezETH', 'ETHx', 'rsETH', 'pufETH', 'wrsETH', 'wsuperOETHb', 'sUSDS', 'PT eUSDe May', 'PT sUSDe July', 'PT USDe July', 'PT eUSDe Aug', 'tETH', 'PT sUSDe Sep', 'PT USDe Sep'];
58
-
59
- export const getStakingApy = memoize(async (asset: string) => {
60
- try {
61
- if (asset === 'stETH' || asset === 'wstETH') return await getApyFromDfsApi('wstETH');
62
- if (asset === 'cbETH') return await getApyFromDfsApi('cbETH');
63
- if (asset === 'rETH') return await getApyFromDfsApi('rETH');
64
- if (asset === 'sDAI') return await getApyFromDfsApi('sDAI');
65
- if (asset === 'sUSDe') return await getApyFromDfsApi('sUSDe');
66
- if (asset === 'weETH') return await getApyFromDfsApi('weETH');
67
- if (asset === 'ezETH') return await getApyFromDfsApi('ezETH');
68
- if (asset === 'osETH') return await getApyFromDfsApi('osETH');
69
- if (asset === 'ETHx') return await getApyFromDfsApi('ETHx');
70
- if (asset === 'rsETH' || asset === 'wrsETH') return await getApyFromDfsApi('rsETH');
71
- if (asset === 'pufETH') return await getApyFromDfsApi('pufETH');
72
- if (asset === 'wsuperOETHb') return await getSuperOETHApy();
73
- if (asset === 'sUSDS') return await getSsrApy();
74
- if (asset === 'PT eUSDe May') return await getApyFromDfsApi('PT eUSDe May');
75
- if (asset === 'PT sUSDe July') return await getApyFromDfsApi('PT sUSDe July');
76
- if (asset === 'PT USDe July') return await getApyFromDfsApi('PT USDe July');
77
- if (asset === 'PT eUSDe Aug') return await getApyFromDfsApi('PT eUSDe Aug');
78
- if (asset === 'PT sUSDe Sep') return await getApyFromDfsApi('PT sUSDe Sep');
79
- if (asset === 'PT USDe Sep') return await getApyFromDfsApi('PT USDe Sep');
80
- if (asset === 'tETH') return await getApyFromDfsApi('tETH');
81
- if (asset === 'USDe') return await getApyFromDfsApi('USDe');
82
- } catch (e) {
83
- console.error(`Failed to fetch APY for ${asset}`);
84
- }
85
- return '0';
86
- }, { promise: true, maxAge: 2 * 60 * 1000 });
87
-
88
- export const calculateInterestEarned = (principal: string, interest: string, type: string, apy = false) => {
89
- let interval = 1;
90
-
91
- if (+interest === 0) return 0;
92
-
93
- if (type === 'month') interval = 1 / 12;
94
- if (type === 'week') interval = 1 / 52.1429;
95
-
96
- if (apy) {
97
- // interest rate already compounded
98
- return (+principal * (1 + (+interest / 100 * interval))) - +principal;
99
- }
100
-
101
- return (+principal * (((1 + (+interest / 100) / BLOCKS_IN_A_YEAR)) ** (BLOCKS_IN_A_YEAR * interval))) - +principal; // eslint-disable-line
102
- };
103
-
104
- export const isEligibleForEthenaUSDeRewards = (usedAssets: MMUsedAssets) => {
105
- const USDeUSDAmountSupplied = usedAssets.USDe?.suppliedUsd || '0';
106
- const sUSDeUSDAmountSupplied = usedAssets.sUSDe?.suppliedUsd || '0';
107
- const anythingElseSupplied = Object.values(usedAssets).some((asset) => asset.symbol !== 'USDe' && asset.symbol !== 'sUSDe' && asset.isSupplied);
108
- if (anythingElseSupplied) return { isEligible: false, eligibleUSDAmount: '0' };
109
- const totalAmountSupplied = new Dec(USDeUSDAmountSupplied).add(sUSDeUSDAmountSupplied).toString();
110
- const percentageInUSDe = new Dec(USDeUSDAmountSupplied).div(totalAmountSupplied).toNumber();
111
- if (percentageInUSDe < 0.45 || percentageInUSDe > 0.55) return { isEligible: false, eligibleUSDAmount: '0' }; // 45% - 55% of total amount supplied must be in USDe
112
- const percentageInSUSDe = new Dec(sUSDeUSDAmountSupplied).div(totalAmountSupplied).toNumber();
113
- if (percentageInSUSDe < 0.45 || percentageInSUSDe > 0.55) return { isEligible: false, eligibleUSDAmount: '0' }; // 45% - 55% of total amount supplied must be in sUSDe
114
-
115
- const allowedBorrowAssets = ['USDC', 'USDT', 'USDS'];
116
- const anythingBorrowedNotAllowed = Object.values(usedAssets).some((asset) => asset.isBorrowed && !allowedBorrowAssets.includes(asset.symbol));
117
- if (anythingBorrowedNotAllowed) return { isEligible: false, eligibleUSDAmount: '0' };
118
-
119
- const totalAmountBorrowed = Object.values(usedAssets).reduce((acc, asset) => {
120
- if (asset.isBorrowed) {
121
- return acc.add(asset.borrowedUsd);
122
- }
123
- return acc;
124
- }, new Dec(0)).toString();
125
-
126
- const borrowPercentage = new Dec(totalAmountBorrowed).div(totalAmountSupplied).toNumber();
127
- if (borrowPercentage < 0.5) return { isEligible: false, eligibleUSDAmount: '0' }; // must be looped at least once
128
-
129
- const halfAmountSupplied = new Dec(totalAmountSupplied).div(2).toString();
130
- const USDeAmountEligibleForRewards = Dec.min(USDeUSDAmountSupplied, halfAmountSupplied).toString(); // rewards are given to amount of USDe supplied up to half of total amount supplied
131
-
132
- return { isEligible: true, eligibleUSDAmount: USDeAmountEligibleForRewards };
133
- };
134
-
135
- export const calculateNetApy = ({
136
- usedAssets, assetsData, isMorpho = false, isAave = false,
137
- }: { usedAssets: MMUsedAssets, assetsData: MMAssetsData, isMorpho?: boolean, isAave?: boolean }) => {
138
- const { isEligible, eligibleUSDAmount } = isAave ? isEligibleForEthenaUSDeRewards(usedAssets) : { isEligible: true, eligibleUSDAmount: '0' };
139
- const sumValues = Object.values(usedAssets).reduce((_acc, usedAsset) => {
140
- const acc = { ..._acc };
141
- const assetData = assetsData[usedAsset.symbol];
142
-
143
- if (usedAsset.isSupplied) {
144
- const amount = usedAsset.suppliedUsd;
145
- acc.suppliedUsd = new Dec(acc.suppliedUsd).add(amount).toString();
146
- const rate = isMorpho
147
- ? usedAsset.supplyRate === '0' ? assetData.supplyRateP2P : usedAsset.supplyRate
148
- : assetData.supplyRate;
149
- const supplyInterest = calculateInterestEarned(amount, rate as string, 'year', true);
150
- acc.supplyInterest = new Dec(acc.supplyInterest).add(supplyInterest.toString()).toString();
151
- if (assetData.incentiveSupplyApy) {
152
- // take COMP/AAVE yield into account
153
- const incentiveInterest = calculateInterestEarned(amount, assetData.incentiveSupplyApy, 'year', true);
154
- acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
155
- }
156
-
157
- if (usedAsset.symbol === 'USDe' && isEligible) {
158
- // @ts-ignore
159
- const incentiveInterest = calculateInterestEarned(eligibleUSDAmount, assetData.supplyIncentives?.[0]?.apy || '0', 'year', true);
160
- acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
161
- }
162
- }
163
-
164
- if (usedAsset.isBorrowed) {
165
- const amount = usedAsset.borrowedUsd;
166
- acc.borrowedUsd = new Dec(acc.borrowedUsd).add(amount).toString();
167
- const rate = isMorpho
168
- ? usedAsset.borrowRate === '0' ? assetData.borrowRateP2P : usedAsset.borrowRate
169
- : (usedAsset?.interestMode === '1' ? usedAsset.stableBorrowRate : assetData.borrowRate);
170
- const borrowInterest = calculateInterestEarned(amount, rate as string, 'year', true);
171
- acc.borrowInterest = new Dec(acc.borrowInterest).sub(borrowInterest.toString()).toString();
172
- if (assetData.incentiveBorrowApy) {
173
- // take COMP/AAVE yield into account
174
- const incentiveInterest = calculateInterestEarned(amount, assetData.incentiveBorrowApy, 'year', true);
175
- acc.incentiveUsd = new Dec(acc.incentiveUsd).sub(incentiveInterest).toString();
176
- }
177
- }
178
-
179
- return acc;
180
- }, {
181
- borrowInterest: '0', supplyInterest: '0', incentiveUsd: '0', borrowedUsd: '0', suppliedUsd: '0',
182
- });
183
-
184
- const {
185
- borrowedUsd, suppliedUsd, borrowInterest, supplyInterest, incentiveUsd,
186
- } = sumValues;
187
-
188
- const totalInterestUsd = new Dec(borrowInterest).add(supplyInterest).add(incentiveUsd).toString();
189
- const balance = new Dec(suppliedUsd).sub(borrowedUsd);
190
- const netApy = new Dec(totalInterestUsd).div(balance).times(100).toString();
191
-
192
- return { netApy, totalInterestUsd, incentiveUsd };
1
+ import Dec from 'decimal.js';
2
+ import memoize from 'memoizee';
3
+ import { MMAssetsData, MMUsedAssets } from '../types/common';
4
+ import { BLOCKS_IN_A_YEAR } from '../constants';
5
+ import { DEFAULT_TIMEOUT } from '../services/utils';
6
+
7
+ const getSsrApy = async () => {
8
+ try {
9
+ const res = await fetch('https://fe.defisaver.com/api/sky/data',
10
+ { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
11
+ const data = await res.json();
12
+ return new Dec(data.data.skyData[0].sky_savings_rate_apy).mul(100).toString();
13
+ } catch (e) {
14
+ console.error('External API Failure: Failed to fetch SSR APY from external API', e);
15
+ return '0';
16
+ }
17
+ };
18
+
19
+ const getSuperOETHApy = async () => {
20
+ try {
21
+ const res = await fetch('https://origin.squids.live/origin-squid/graphql', {
22
+ method: 'POST',
23
+ headers: {
24
+ 'Content-Type': 'application/json',
25
+ },
26
+ body: JSON.stringify({
27
+ query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
28
+ variables: {
29
+ token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
30
+ chainId: 8453,
31
+ },
32
+ }),
33
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
34
+ });
35
+
36
+ const data = await res.json();
37
+ return new Dec(data.data.oTokenApies[0].apy).mul(100).toString();
38
+ } catch (e) {
39
+ console.error('External API Failure: Failed to fetch Super OETH APY from external API', e);
40
+ return '0';
41
+ }
42
+ };
43
+
44
+ const getApyFromDfsApi = async (asset: string) => {
45
+ try {
46
+ const res = await fetch(`https://fe.defisaver.com/api/staking/apy?asset=${asset}`,
47
+ { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
48
+ if (!res.ok) throw new Error(`Failed to fetch APY for ${asset}`);
49
+ const data = await res.json();
50
+ return String(data.apy);
51
+ } catch (e) {
52
+ console.error(`External API Failure: Failed to fetch APY for ${asset} from DFS API`, e);
53
+ return '0';
54
+ }
55
+ };
56
+
57
+ export const STAKING_ASSETS = ['cbETH', 'wstETH', 'cbETH', 'rETH', 'sDAI', 'weETH', 'sUSDe', 'osETH', 'ezETH', 'ETHx', 'rsETH', 'pufETH', 'wrsETH', 'wsuperOETHb', 'sUSDS', 'tETH', 'PT sUSDe Sep', 'PT USDe Sep'];
58
+
59
+ export const getStakingApy = memoize(async (asset: string) => {
60
+ try {
61
+ if (asset === 'stETH' || asset === 'wstETH') return await getApyFromDfsApi('wstETH');
62
+ if (asset === 'cbETH') return await getApyFromDfsApi('cbETH');
63
+ if (asset === 'rETH') return await getApyFromDfsApi('rETH');
64
+ if (asset === 'sDAI') return await getApyFromDfsApi('sDAI');
65
+ if (asset === 'sUSDe') return await getApyFromDfsApi('sUSDe');
66
+ if (asset === 'weETH') return await getApyFromDfsApi('weETH');
67
+ if (asset === 'ezETH') return await getApyFromDfsApi('ezETH');
68
+ if (asset === 'osETH') return await getApyFromDfsApi('osETH');
69
+ if (asset === 'ETHx') return await getApyFromDfsApi('ETHx');
70
+ if (asset === 'rsETH' || asset === 'wrsETH') return await getApyFromDfsApi('rsETH');
71
+ if (asset === 'pufETH') return await getApyFromDfsApi('pufETH');
72
+ if (asset === 'wsuperOETHb') return await getSuperOETHApy();
73
+ if (asset === 'sUSDS') return await getSsrApy();
74
+ if (asset === 'PT eUSDe May') return await getApyFromDfsApi('PT eUSDe May');
75
+ if (asset === 'PT sUSDe July') return await getApyFromDfsApi('PT sUSDe July');
76
+ if (asset === 'PT USDe July') return await getApyFromDfsApi('PT USDe July');
77
+ if (asset === 'PT eUSDe Aug') return await getApyFromDfsApi('PT eUSDe Aug');
78
+ if (asset === 'PT sUSDe Sep') return await getApyFromDfsApi('PT sUSDe Sep');
79
+ if (asset === 'PT USDe Sep') return await getApyFromDfsApi('PT USDe Sep');
80
+ if (asset === 'tETH') return await getApyFromDfsApi('tETH');
81
+ if (asset === 'USDe') return await getApyFromDfsApi('USDe');
82
+ } catch (e) {
83
+ console.error(`Failed to fetch APY for ${asset}`);
84
+ }
85
+ return '0';
86
+ }, { promise: true, maxAge: 2 * 60 * 1000 });
87
+
88
+ export const calculateInterestEarned = (principal: string, interest: string, type: string, apy = false) => {
89
+ let interval = 1;
90
+
91
+ if (+interest === 0) return 0;
92
+
93
+ if (type === 'month') interval = 1 / 12;
94
+ if (type === 'week') interval = 1 / 52.1429;
95
+
96
+ if (apy) {
97
+ // interest rate already compounded
98
+ return (+principal * (1 + (+interest / 100 * interval))) - +principal;
99
+ }
100
+
101
+ return (+principal * (((1 + (+interest / 100) / BLOCKS_IN_A_YEAR)) ** (BLOCKS_IN_A_YEAR * interval))) - +principal; // eslint-disable-line
102
+ };
103
+
104
+ export const isEligibleForEthenaUSDeRewards = (usedAssets: MMUsedAssets) => {
105
+ const USDeUSDAmountSupplied = usedAssets.USDe?.suppliedUsd || '0';
106
+ const sUSDeUSDAmountSupplied = usedAssets.sUSDe?.suppliedUsd || '0';
107
+ const anythingElseSupplied = Object.values(usedAssets).some((asset) => asset.symbol !== 'USDe' && asset.symbol !== 'sUSDe' && asset.isSupplied);
108
+ if (anythingElseSupplied) return { isEligible: false, eligibleUSDAmount: '0' };
109
+ const totalAmountSupplied = new Dec(USDeUSDAmountSupplied).add(sUSDeUSDAmountSupplied).toString();
110
+ const percentageInUSDe = new Dec(USDeUSDAmountSupplied).div(totalAmountSupplied).toNumber();
111
+ if (percentageInUSDe < 0.45 || percentageInUSDe > 0.55) return { isEligible: false, eligibleUSDAmount: '0' }; // 45% - 55% of total amount supplied must be in USDe
112
+ const percentageInSUSDe = new Dec(sUSDeUSDAmountSupplied).div(totalAmountSupplied).toNumber();
113
+ if (percentageInSUSDe < 0.45 || percentageInSUSDe > 0.55) return { isEligible: false, eligibleUSDAmount: '0' }; // 45% - 55% of total amount supplied must be in sUSDe
114
+
115
+ const allowedBorrowAssets = ['USDC', 'USDT', 'USDS'];
116
+ const anythingBorrowedNotAllowed = Object.values(usedAssets).some((asset) => asset.isBorrowed && !allowedBorrowAssets.includes(asset.symbol));
117
+ if (anythingBorrowedNotAllowed) return { isEligible: false, eligibleUSDAmount: '0' };
118
+
119
+ const totalAmountBorrowed = Object.values(usedAssets).reduce((acc, asset) => {
120
+ if (asset.isBorrowed) {
121
+ return acc.add(asset.borrowedUsd);
122
+ }
123
+ return acc;
124
+ }, new Dec(0)).toString();
125
+
126
+ const borrowPercentage = new Dec(totalAmountBorrowed).div(totalAmountSupplied).toNumber();
127
+ if (borrowPercentage < 0.5) return { isEligible: false, eligibleUSDAmount: '0' }; // must be looped at least once
128
+
129
+ const halfAmountSupplied = new Dec(totalAmountSupplied).div(2).toString();
130
+ const USDeAmountEligibleForRewards = Dec.min(USDeUSDAmountSupplied, halfAmountSupplied).toString(); // rewards are given to amount of USDe supplied up to half of total amount supplied
131
+
132
+ return { isEligible: true, eligibleUSDAmount: USDeAmountEligibleForRewards };
133
+ };
134
+
135
+ export const calculateNetApy = ({
136
+ usedAssets, assetsData, isMorpho = false, isAave = false,
137
+ }: { usedAssets: MMUsedAssets, assetsData: MMAssetsData, isMorpho?: boolean, isAave?: boolean }) => {
138
+ const { isEligible, eligibleUSDAmount } = isAave ? isEligibleForEthenaUSDeRewards(usedAssets) : { isEligible: true, eligibleUSDAmount: '0' };
139
+ const sumValues = Object.values(usedAssets).reduce((_acc, usedAsset) => {
140
+ const acc = { ..._acc };
141
+ const assetData = assetsData[usedAsset.symbol];
142
+
143
+ if (usedAsset.isSupplied) {
144
+ const amount = usedAsset.suppliedUsd;
145
+ acc.suppliedUsd = new Dec(acc.suppliedUsd).add(amount).toString();
146
+ const rate = isMorpho
147
+ ? usedAsset.supplyRate === '0' ? assetData.supplyRateP2P : usedAsset.supplyRate
148
+ : assetData.supplyRate;
149
+ const supplyInterest = calculateInterestEarned(amount, rate as string, 'year', true);
150
+ acc.supplyInterest = new Dec(acc.supplyInterest).add(supplyInterest.toString()).toString();
151
+ if (assetData.incentiveSupplyApy) {
152
+ // take COMP/AAVE yield into account
153
+ const incentiveInterest = calculateInterestEarned(amount, assetData.incentiveSupplyApy, 'year', true);
154
+ acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
155
+ }
156
+
157
+ if (usedAsset.symbol === 'USDe' && isEligible) {
158
+ // @ts-ignore
159
+ const incentiveInterest = calculateInterestEarned(eligibleUSDAmount, assetData.supplyIncentives?.[0]?.apy || '0', 'year', true);
160
+ acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
161
+ }
162
+ }
163
+
164
+ if (usedAsset.isBorrowed) {
165
+ const amount = usedAsset.borrowedUsd;
166
+ acc.borrowedUsd = new Dec(acc.borrowedUsd).add(amount).toString();
167
+ const rate = isMorpho
168
+ ? usedAsset.borrowRate === '0' ? assetData.borrowRateP2P : usedAsset.borrowRate
169
+ : (usedAsset?.interestMode === '1' ? usedAsset.stableBorrowRate : assetData.borrowRate);
170
+ const borrowInterest = calculateInterestEarned(amount, rate as string, 'year', true);
171
+ acc.borrowInterest = new Dec(acc.borrowInterest).sub(borrowInterest.toString()).toString();
172
+ if (assetData.incentiveBorrowApy) {
173
+ // take COMP/AAVE yield into account
174
+ const incentiveInterest = calculateInterestEarned(amount, assetData.incentiveBorrowApy, 'year', true);
175
+ acc.incentiveUsd = new Dec(acc.incentiveUsd).sub(incentiveInterest).toString();
176
+ }
177
+ }
178
+
179
+ return acc;
180
+ }, {
181
+ borrowInterest: '0', supplyInterest: '0', incentiveUsd: '0', borrowedUsd: '0', suppliedUsd: '0',
182
+ });
183
+
184
+ const {
185
+ borrowedUsd, suppliedUsd, borrowInterest, supplyInterest, incentiveUsd,
186
+ } = sumValues;
187
+
188
+ const totalInterestUsd = new Dec(borrowInterest).add(supplyInterest).add(incentiveUsd).toString();
189
+ const balance = new Dec(suppliedUsd).sub(borrowedUsd);
190
+ const netApy = new Dec(totalInterestUsd).div(balance).times(100).toString();
191
+
192
+ return { netApy, totalInterestUsd, incentiveUsd };
193
193
  };