@defisaver/positions-sdk 2.0.15-dev → 2.0.15-dev-2

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