@defisaver/positions-sdk 2.1.55 → 2.1.56-dev-exposure

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 (184) hide show
  1. package/.mocharc.json +4 -4
  2. package/.nvmrc +1 -1
  3. package/README.md +64 -64
  4. package/cjs/aaveV3/index.d.ts +1 -0
  5. package/cjs/aaveV3/index.js +1 -0
  6. package/cjs/compoundV2/index.d.ts +1 -0
  7. package/cjs/compoundV2/index.js +1 -0
  8. package/cjs/compoundV3/index.d.ts +1 -0
  9. package/cjs/compoundV3/index.js +1 -0
  10. package/cjs/eulerV2/index.d.ts +1 -0
  11. package/cjs/eulerV2/index.js +1 -0
  12. package/cjs/fluid/index.d.ts +3 -0
  13. package/cjs/helpers/aaveHelpers/index.js +1 -0
  14. package/cjs/helpers/compoundHelpers/index.js +2 -0
  15. package/cjs/helpers/curveUsdHelpers/index.js +1 -0
  16. package/cjs/helpers/eulerHelpers/index.js +1 -0
  17. package/cjs/helpers/fluidHelpers/index.js +1 -0
  18. package/cjs/helpers/liquityV2Helpers/index.js +1 -0
  19. package/cjs/helpers/llamaLendHelpers/index.js +1 -0
  20. package/cjs/helpers/morphoBlueHelpers/index.js +67 -66
  21. package/cjs/helpers/sparkHelpers/index.js +1 -0
  22. package/cjs/liquity/index.js +2 -0
  23. package/cjs/maker/index.js +2 -0
  24. package/cjs/markets/morphoBlue/index.d.ts +4 -0
  25. package/cjs/markets/morphoBlue/index.js +36 -2
  26. package/cjs/moneymarket/moneymarketCommonService.d.ts +1 -0
  27. package/cjs/moneymarket/moneymarketCommonService.js +8 -1
  28. package/cjs/savings/morphoVaults/index.js +17 -17
  29. package/cjs/spark/index.d.ts +1 -0
  30. package/cjs/spark/index.js +1 -0
  31. package/cjs/types/aave.d.ts +1 -0
  32. package/cjs/types/common.d.ts +1 -0
  33. package/cjs/types/compound.d.ts +1 -0
  34. package/cjs/types/curveUsd.d.ts +2 -0
  35. package/cjs/types/euler.d.ts +1 -0
  36. package/cjs/types/fluid.d.ts +1 -0
  37. package/cjs/types/liquity.d.ts +1 -0
  38. package/cjs/types/liquityV2.d.ts +2 -0
  39. package/cjs/types/llamaLend.d.ts +2 -0
  40. package/cjs/types/maker.d.ts +1 -0
  41. package/cjs/types/morphoBlue.d.ts +4 -0
  42. package/cjs/types/morphoBlue.js +2 -0
  43. package/cjs/types/spark.d.ts +1 -0
  44. package/esm/aaveV3/index.d.ts +1 -0
  45. package/esm/aaveV3/index.js +1 -0
  46. package/esm/compoundV2/index.d.ts +1 -0
  47. package/esm/compoundV2/index.js +1 -0
  48. package/esm/compoundV3/index.d.ts +1 -0
  49. package/esm/compoundV3/index.js +1 -0
  50. package/esm/eulerV2/index.d.ts +1 -0
  51. package/esm/eulerV2/index.js +1 -0
  52. package/esm/fluid/index.d.ts +3 -0
  53. package/esm/helpers/aaveHelpers/index.js +2 -1
  54. package/esm/helpers/compoundHelpers/index.js +3 -1
  55. package/esm/helpers/curveUsdHelpers/index.js +2 -1
  56. package/esm/helpers/eulerHelpers/index.js +2 -1
  57. package/esm/helpers/fluidHelpers/index.js +2 -1
  58. package/esm/helpers/liquityV2Helpers/index.js +2 -1
  59. package/esm/helpers/llamaLendHelpers/index.js +2 -1
  60. package/esm/helpers/morphoBlueHelpers/index.js +68 -67
  61. package/esm/helpers/sparkHelpers/index.js +2 -1
  62. package/esm/liquity/index.js +2 -0
  63. package/esm/maker/index.js +2 -0
  64. package/esm/markets/morphoBlue/index.d.ts +4 -0
  65. package/esm/markets/morphoBlue/index.js +32 -0
  66. package/esm/moneymarket/moneymarketCommonService.d.ts +1 -0
  67. package/esm/moneymarket/moneymarketCommonService.js +6 -0
  68. package/esm/savings/morphoVaults/index.js +17 -17
  69. package/esm/spark/index.d.ts +1 -0
  70. package/esm/spark/index.js +1 -0
  71. package/esm/types/aave.d.ts +1 -0
  72. package/esm/types/common.d.ts +1 -0
  73. package/esm/types/compound.d.ts +1 -0
  74. package/esm/types/curveUsd.d.ts +2 -0
  75. package/esm/types/euler.d.ts +1 -0
  76. package/esm/types/fluid.d.ts +1 -0
  77. package/esm/types/liquity.d.ts +1 -0
  78. package/esm/types/liquityV2.d.ts +2 -0
  79. package/esm/types/llamaLend.d.ts +2 -0
  80. package/esm/types/maker.d.ts +1 -0
  81. package/esm/types/morphoBlue.d.ts +4 -0
  82. package/esm/types/morphoBlue.js +2 -0
  83. package/esm/types/spark.d.ts +1 -0
  84. package/package.json +48 -48
  85. package/src/aaveV2/index.ts +240 -240
  86. package/src/aaveV3/index.ts +638 -637
  87. package/src/aaveV3/merit.ts +97 -97
  88. package/src/aaveV3/merkl.ts +74 -74
  89. package/src/claiming/aaveV3.ts +154 -154
  90. package/src/claiming/compV3.ts +22 -22
  91. package/src/claiming/ethena.ts +61 -61
  92. package/src/claiming/index.ts +12 -12
  93. package/src/claiming/king.ts +66 -66
  94. package/src/claiming/morphoBlue.ts +118 -118
  95. package/src/claiming/spark.ts +225 -225
  96. package/src/compoundV2/index.ts +245 -244
  97. package/src/compoundV3/index.ts +275 -274
  98. package/src/config/contracts.ts +1320 -1320
  99. package/src/constants/index.ts +10 -10
  100. package/src/contracts.ts +171 -171
  101. package/src/curveUsd/index.ts +254 -254
  102. package/src/eulerV2/index.ts +325 -324
  103. package/src/exchange/index.ts +25 -25
  104. package/src/fluid/index.ts +1800 -1800
  105. package/src/helpers/aaveHelpers/index.ts +203 -202
  106. package/src/helpers/compoundHelpers/index.ts +278 -276
  107. package/src/helpers/curveUsdHelpers/index.ts +44 -40
  108. package/src/helpers/eulerHelpers/index.ts +230 -229
  109. package/src/helpers/fluidHelpers/index.ts +338 -335
  110. package/src/helpers/index.ts +10 -10
  111. package/src/helpers/liquityV2Helpers/index.ts +85 -82
  112. package/src/helpers/llamaLendHelpers/index.ts +56 -53
  113. package/src/helpers/makerHelpers/index.ts +52 -52
  114. package/src/helpers/morphoBlueHelpers/index.ts +406 -405
  115. package/src/helpers/sparkHelpers/index.ts +170 -169
  116. package/src/index.ts +49 -49
  117. package/src/liquity/index.ts +161 -159
  118. package/src/liquityV2/index.ts +703 -703
  119. package/src/llamaLend/index.ts +305 -305
  120. package/src/maker/index.ts +225 -223
  121. package/src/markets/aave/index.ts +118 -118
  122. package/src/markets/aave/marketAssets.ts +54 -54
  123. package/src/markets/compound/index.ts +243 -243
  124. package/src/markets/compound/marketsAssets.ts +97 -97
  125. package/src/markets/curveUsd/index.ts +69 -69
  126. package/src/markets/euler/index.ts +26 -26
  127. package/src/markets/fluid/index.ts +2900 -2900
  128. package/src/markets/index.ts +25 -25
  129. package/src/markets/liquityV2/index.ts +102 -102
  130. package/src/markets/llamaLend/contractAddresses.ts +141 -141
  131. package/src/markets/llamaLend/index.ts +235 -235
  132. package/src/markets/morphoBlue/index.ts +1020 -988
  133. package/src/markets/spark/index.ts +29 -29
  134. package/src/markets/spark/marketAssets.ts +12 -12
  135. package/src/moneymarket/moneymarketCommonService.ts +90 -84
  136. package/src/morphoBlue/index.ts +274 -274
  137. package/src/portfolio/index.ts +586 -586
  138. package/src/savings/index.ts +95 -95
  139. package/src/savings/makerDsr/index.ts +53 -53
  140. package/src/savings/makerDsr/options.ts +9 -9
  141. package/src/savings/morphoVaults/index.ts +80 -80
  142. package/src/savings/morphoVaults/options.ts +193 -193
  143. package/src/savings/skyOptions/index.ts +95 -95
  144. package/src/savings/skyOptions/options.ts +10 -10
  145. package/src/savings/sparkSavingsVaults/index.ts +60 -60
  146. package/src/savings/sparkSavingsVaults/options.ts +35 -35
  147. package/src/savings/yearnV3Vaults/index.ts +61 -61
  148. package/src/savings/yearnV3Vaults/options.ts +55 -55
  149. package/src/savings/yearnVaults/index.ts +73 -73
  150. package/src/savings/yearnVaults/options.ts +32 -32
  151. package/src/services/priceService.ts +278 -278
  152. package/src/services/utils.ts +115 -115
  153. package/src/services/viem.ts +57 -57
  154. package/src/setup.ts +8 -8
  155. package/src/spark/index.ts +460 -459
  156. package/src/staking/eligibility.ts +53 -53
  157. package/src/staking/index.ts +1 -1
  158. package/src/staking/staking.ts +192 -192
  159. package/src/types/aave.ts +200 -199
  160. package/src/types/claiming.ts +114 -114
  161. package/src/types/common.ts +116 -115
  162. package/src/types/compound.ts +146 -145
  163. package/src/types/curveUsd.ts +125 -123
  164. package/src/types/euler.ts +177 -176
  165. package/src/types/fluid.ts +486 -485
  166. package/src/types/index.ts +15 -15
  167. package/src/types/liquity.ts +31 -30
  168. package/src/types/liquityV2.ts +130 -128
  169. package/src/types/llamaLend.ts +163 -161
  170. package/src/types/maker.ts +64 -63
  171. package/src/types/merit.ts +1 -1
  172. package/src/types/merkl.ts +70 -70
  173. package/src/types/morphoBlue.ts +206 -202
  174. package/src/types/portfolio.ts +60 -60
  175. package/src/types/savings/index.ts +23 -23
  176. package/src/types/savings/makerDsr.ts +13 -13
  177. package/src/types/savings/morphoVaults.ts +32 -32
  178. package/src/types/savings/sky.ts +14 -14
  179. package/src/types/savings/sparkSavingsVaults.ts +15 -15
  180. package/src/types/savings/yearnV3Vaults.ts +17 -17
  181. package/src/types/savings/yearnVaults.ts +14 -14
  182. package/src/types/spark.ts +136 -135
  183. package/src/umbrella/index.ts +69 -69
  184. package/src/umbrella/umbrellaUtils.ts +29 -29
@@ -1,405 +1,406 @@
1
- import Dec from 'decimal.js';
2
- import { assetAmountInWei, getAssetInfo, getAssetInfoByAddress } from '@defisaver/tokens';
3
- import {
4
- aprToApy, calcLeverageLiqPrice, getAssetsTotal, isLeveragedPos,
5
- } from '../../moneymarket';
6
- import { calculateNetApy } from '../../staking';
7
- import {
8
- EthereumProvider, LeverageType, MMAssetsData, MMUsedAsset, MMUsedAssets, NetworkNumber,
9
- } from '../../types/common';
10
- import {
11
- MorphoBlueAggregatedPositionData, MorphoBlueAssetsData, MorphoBlueMarketData, MorphoBlueMarketInfo,
12
- MorphoBluePublicAllocatorItem,
13
- MorphoBlueRealloactionMarketData,
14
- } from '../../types';
15
- import { borrowOperations, SECONDS_PER_YEAR, WAD } from '../../constants';
16
- import { MorphoBlueViewContractViem } from '../../contracts';
17
- import { compareAddresses, DEFAULT_TIMEOUT, wethToEth } from '../../services/utils';
18
- import { getViemProvider } from '../../services/viem';
19
-
20
- export const getMorphoBlueAggregatedPositionData = ({ usedAssets, assetsData, marketInfo }: { usedAssets: MMUsedAssets, assetsData: MorphoBlueAssetsData, marketInfo: MorphoBlueMarketInfo }): MorphoBlueAggregatedPositionData => {
21
- const payload = {} as MorphoBlueAggregatedPositionData;
22
- payload.suppliedUsd = getAssetsTotal(usedAssets, ({ isSupplied }: { isSupplied: boolean }) => isSupplied, ({ suppliedUsd }: { suppliedUsd: string }) => suppliedUsd);
23
- payload.suppliedCollateralUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral, ({ suppliedUsd }: { suppliedUsd: string }) => suppliedUsd);
24
- payload.borrowedUsd = getAssetsTotal(usedAssets, ({ isBorrowed }: { isBorrowed: boolean }) => isBorrowed, ({ borrowedUsd }: { borrowedUsd: string }) => borrowedUsd);
25
-
26
- const {
27
- lltv, oracle, collateralToken, loanToken,
28
- } = marketInfo;
29
-
30
- payload.borrowLimitUsd = getAssetsTotal(
31
- usedAssets,
32
- ({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral,
33
- ({ symbol, suppliedUsd }: { symbol: string, suppliedUsd: string }) => {
34
- const suppliedUsdAmount = suppliedUsd;
35
-
36
- return new Dec(suppliedUsdAmount).mul(lltv);
37
- },
38
- );
39
- payload.liquidationLimitUsd = payload.borrowLimitUsd;
40
- const leftToBorrowUsd = new Dec(payload.borrowLimitUsd).sub(payload.borrowedUsd);
41
- payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
42
-
43
- payload.leftToBorrow = new Dec(usedAssets[collateralToken]?.supplied || 0).mul(oracle).mul(lltv).sub(usedAssets[loanToken]?.borrowed || 0)
44
- .toString();
45
-
46
- const { netApy, incentiveUsd, totalInterestUsd } = calculateNetApy({ usedAssets, assetsData: assetsData as unknown as MMAssetsData });
47
- payload.netApy = netApy;
48
- payload.incentiveUsd = incentiveUsd;
49
- payload.totalInterestUsd = totalInterestUsd;
50
-
51
- payload.ltv = new Dec(payload.borrowedUsd).div(payload.suppliedCollateralUsd).toString();
52
- payload.ltv = new Dec(usedAssets[loanToken]?.borrowed || 0).div(oracle).div(usedAssets[collateralToken]?.supplied || 1).toString(); // default to 1 because can't div 0
53
- payload.ratio = new Dec(usedAssets[collateralToken]?.supplied || 0).mul(oracle).div(usedAssets[loanToken]?.borrowed || 1).mul(100)
54
- .toString();
55
-
56
- const { leveragedType, leveragedAsset } = isLeveragedPos(usedAssets);
57
- payload.leveragedType = leveragedType;
58
- if (leveragedType !== '') {
59
- payload.leveragedAsset = leveragedAsset;
60
- let assetPrice = assetsData[leveragedAsset].price;
61
- if (leveragedType === LeverageType.VolatilePair) {
62
- const borrowedAsset = (Object.values(usedAssets) as MMUsedAsset[]).find(({ borrowedUsd }: { borrowedUsd: string }) => +borrowedUsd > 0);
63
- const borrowedAssetPrice = assetsData[borrowedAsset!.symbol].price;
64
- const leveragedAssetPrice = assetsData[leveragedAsset].price;
65
- const isReverse = new Dec(leveragedAssetPrice).lt(borrowedAssetPrice);
66
- if (isReverse) {
67
- payload.leveragedType = LeverageType.VolatilePairReverse;
68
- payload.currentVolatilePairRatio = new Dec(borrowedAssetPrice).div(leveragedAssetPrice).toDP(18).toString();
69
- assetPrice = new Dec(borrowedAssetPrice).div(assetPrice).toString();
70
- } else {
71
- assetPrice = new Dec(assetPrice).div(borrowedAssetPrice).toString();
72
- payload.currentVolatilePairRatio = new Dec(leveragedAssetPrice).div(borrowedAssetPrice).toDP(18).toString();
73
- }
74
- }
75
- payload.liquidationPrice = calcLeverageLiqPrice(payload.leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
76
- }
77
- payload.minCollRatio = new Dec(payload.suppliedCollateralUsd).div(payload.borrowLimitUsd).mul(100).toString();
78
- payload.collLiquidationRatio = new Dec(payload.suppliedCollateralUsd).div(payload.liquidationLimitUsd).mul(100).toString();
79
-
80
- return payload;
81
- };
82
-
83
- const compound = (ratePerSeconds: string) => {
84
- const compounding = new Dec(ratePerSeconds).mul(SECONDS_PER_YEAR).toString();
85
- const apyNumber = Math.expm1(new Dec(compounding).div(WAD).toNumber());
86
- return new Dec(apyNumber).mul(WAD).floor().toString();
87
- };
88
-
89
- export const getSupplyRate = (totalSupplyAssets: string, totalBorrowAssets: string, borrowRate: string, fee: string) => {
90
- if (totalBorrowAssets === '0' || totalSupplyAssets === '0') {
91
- return '0';
92
- }
93
- const utillization = new Dec(totalBorrowAssets).mul(WAD).div(totalSupplyAssets).ceil()
94
- .toString();
95
- const supplyRate = new Dec(utillization).mul(borrowRate).div(WAD).ceil()
96
- .toString();
97
- const ratePerSecond = new Dec(supplyRate).mul(new Dec(WAD).minus(fee)).div(WAD).ceil()
98
- .toString();
99
- return new Dec(compound(ratePerSecond)).div(1e18).mul(100).toString();
100
- };
101
-
102
- export const getBorrowRate = (borrowRate: string, totalBorrowShares: string) => {
103
- if (totalBorrowShares === '0') {
104
- return '0';
105
- }
106
- return new Dec(compound(borrowRate)).div(1e18).mul(100).toString();
107
- };
108
-
109
- export const getApyAfterValuesEstimation = async (selectedMarket: MorphoBlueMarketData, actions: { action: string, amount: string, asset: string }[], provider: EthereumProvider, network: NetworkNumber) => {
110
- const client = getViemProvider(provider, network);
111
- const morphoBlueViewContract = MorphoBlueViewContractViem(client, network);
112
- const lltvInWei = assetAmountInWei(selectedMarket.lltv, 'ETH');
113
- const marketData = {
114
- loanToken: selectedMarket.loanToken,
115
- collateralToken: selectedMarket.collateralToken,
116
- oracle: selectedMarket.oracle,
117
- irm: selectedMarket.irm,
118
- lltv: BigInt(lltvInWei),
119
- };
120
-
121
- const params = actions.map(({ action, asset, amount }) => {
122
- const isBorrowOperation = borrowOperations.includes(action);
123
- const amountInWei = assetAmountInWei(amount, asset);
124
- let liquidityAdded;
125
- let liquidityRemoved;
126
- if (isBorrowOperation) {
127
- liquidityAdded = action === 'payback' ? amountInWei : '0';
128
- liquidityRemoved = action === 'borrow' ? amountInWei : '0';
129
- } else {
130
- liquidityAdded = action === 'collateral' ? amountInWei : '0';
131
- liquidityRemoved = action === 'withdraw' ? amountInWei : '0';
132
- }
133
- return {
134
- liquidityAdded: BigInt(liquidityAdded),
135
- liquidityRemoved: BigInt(liquidityRemoved),
136
- isBorrowOperation,
137
- };
138
- });
139
- const data = await morphoBlueViewContract.read.getApyAfterValuesEstimation([
140
- marketData,
141
- params,
142
- ]);
143
- const borrowRate = getBorrowRate(data[0].toString(), data[1].totalBorrowShares.toString());
144
- const supplyRate = getSupplyRate(data[1].totalSupplyAssets.toString(), data[1].totalBorrowAssets.toString(), data[0].toString(), data[1].fee.toString());
145
- return { borrowRate, supplyRate };
146
- };
147
-
148
- const API_URL = 'https://blue-api.morpho.org/graphql';
149
- const MARKET_QUERY = `
150
- query MarketByUniqueKey($uniqueKey: String!, $chainId: Int!) {
151
- marketByUniqueKey(uniqueKey: $uniqueKey, chainId: $chainId) {
152
- reallocatableLiquidityAssets
153
- targetBorrowUtilization
154
- loanAsset {
155
- address
156
- decimals
157
- priceUsd
158
- }
159
- state {
160
- liquidityAssets
161
- borrowAssets
162
- supplyAssets
163
- }
164
- publicAllocatorSharedLiquidity {
165
- assets
166
- vault {
167
- address
168
- name
169
- }
170
- allocationMarket {
171
- uniqueKey
172
- loanAsset {
173
- address
174
- }
175
- collateralAsset {
176
- address
177
- }
178
- irmAddress
179
- oracle {
180
- address
181
- }
182
- lltv
183
- }
184
- }
185
- loanAsset {
186
- address
187
- }
188
- collateralAsset {
189
- address
190
- }
191
- oracle {
192
- address
193
- }
194
- irmAddress
195
- lltv
196
- }
197
- }
198
- `;
199
-
200
- const REWARDS_QUERY = `
201
- query MarketByUniqueKey($uniqueKey: String!, $chainId: Int!) {
202
- marketByUniqueKey(uniqueKey: $uniqueKey, chainId: $chainId) {
203
- uniqueKey
204
- state {
205
- rewards {
206
- amountPerSuppliedToken
207
- supplyApr
208
- amountPerBorrowedToken
209
- borrowApr
210
- asset {
211
- address
212
- }
213
- }
214
- }
215
- }
216
- }
217
- `;
218
-
219
- /**
220
- * Get reallocatable liquidity to a given market and target borrow utilization
221
- * @param marketId - Unique key of the market liquidity is reallocated to
222
- * @param network - The network number
223
- * @returns The reallocatable liquidity and target borrow utilization
224
- */
225
- export const getReallocatableLiquidity = async (marketId: string, network: NetworkNumber = NetworkNumber.Eth): Promise<{ reallocatableLiquidity: string, targetBorrowUtilization: string }> => {
226
- try {
227
- const response = await fetch(API_URL, {
228
- method: 'POST',
229
- headers: { 'Content-Type': 'application/json' },
230
- body: JSON.stringify({
231
- query: MARKET_QUERY,
232
- variables: { uniqueKey: marketId, chainId: network },
233
- }),
234
- signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
235
- });
236
-
237
- const data: { data: { marketByUniqueKey: MorphoBlueRealloactionMarketData } } = await response.json();
238
- const marketData: MorphoBlueRealloactionMarketData = data?.data?.marketByUniqueKey;
239
-
240
- if (!marketData) throw new Error('Market data not found');
241
-
242
- return {
243
- reallocatableLiquidity: marketData.reallocatableLiquidityAssets,
244
- targetBorrowUtilization: marketData.targetBorrowUtilization,
245
- };
246
- } catch (error) {
247
- console.error('External API Failure: Morpho blue reallocatable liquidity', error);
248
- throw new Error('Failed to fetch reallocatable liquidity');
249
- }
250
- };
251
-
252
- /**
253
- * Get liquidity to allocate for a given amount to borrow.
254
- * First, the function will try to calculate the amount of liquidity to allocate to be able to
255
- * hit the target utilization. If it is not possible to allocate enough liquidity to hit the
256
- * target utilization, the function will allocate the amount of liquidity needed to be able to
257
- * borrow the selected amount.
258
- * @param amountToBorrow - The amount to borrow
259
- * @param totalBorrow - The total amount borrowed from market
260
- * @param totalSupply - The total amount supplied to market
261
- * @param targetBorrowUtilization - The target borrow utilization of market
262
- * @param reallocatableLiquidityAssets - The amount of liquidity that can be reallocated from other markets
263
- * @returns The amount of liquidity to allocate
264
- */
265
- export const getLiquidityToAllocate = (amountToBorrow: string, totalBorrow: string, totalSupply: string, targetBorrowUtilization: string, reallocatableLiquidityAssets: string) => {
266
- const newTotalBorrowAssets = new Dec(totalBorrow).add(amountToBorrow).toString();
267
- const leftToBorrow = new Dec(totalSupply).sub(totalBorrow).toString();
268
- let liquidityToAllocate = new Dec(newTotalBorrowAssets).div(targetBorrowUtilization).mul(1e18).sub(totalSupply)
269
- .toFixed(0)
270
- .toString();
271
-
272
- if (new Dec(reallocatableLiquidityAssets).lt(liquidityToAllocate) || new Dec(liquidityToAllocate).lt('0')) {
273
- liquidityToAllocate = new Dec(amountToBorrow).lt(leftToBorrow) ? '0' : new Dec(amountToBorrow).sub(leftToBorrow).toString();
274
- if (new Dec(reallocatableLiquidityAssets).lt(liquidityToAllocate)) throw new Error('Not enough liquidity available to allocate');
275
- }
276
-
277
- return liquidityToAllocate;
278
- };
279
-
280
- /**
281
- * Get the vaults and withdrawals needed to reallocate liquidity for a given amount to borrow.
282
- * Amount to be reallocated is calculated in `getLiquidityToAllocate`
283
- * @param market - The market data
284
- * @param assetsData - The assets data
285
- * @param amountToBorrow - Amount being borrowed (not the amount being reallocated)
286
- * @param network - The network number
287
- * @returns The vaults and withdrawals needed to reallocate liquidity
288
- */
289
- export const getReallocation = async (market: MorphoBlueMarketData, assetsData: MorphoBlueAssetsData, amountToBorrow: string, network: NetworkNumber = NetworkNumber.Eth): Promise<{ vaults: string[], withdrawals: (string | string[])[][][] }> => {
290
- try {
291
- const { marketId, loanToken } = market;
292
- const response = await fetch(API_URL, {
293
- method: 'POST',
294
- headers: { 'Content-Type': 'application/json' },
295
- body: JSON.stringify({
296
- query: MARKET_QUERY,
297
- variables: { uniqueKey: marketId, chainId: network },
298
- }),
299
- signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
300
- });
301
-
302
- const data: { data: { marketByUniqueKey: MorphoBlueRealloactionMarketData } } = await response.json();
303
- const marketData: MorphoBlueRealloactionMarketData = data?.data?.marketByUniqueKey;
304
-
305
- if (!marketData) throw new Error('Market data not found');
306
-
307
- const loanAssetInfo = getAssetInfoByAddress(loanToken, network);
308
- const { totalBorrow, totalSupply } = assetsData[loanAssetInfo.symbol] || { totalBorrow: '0', totalSupply: '0' };
309
- const totalBorrowWei = assetAmountInWei(totalBorrow!, loanAssetInfo.symbol);
310
- const totalSupplyWei = assetAmountInWei(totalSupply!, loanAssetInfo.symbol);
311
-
312
- const newTotalBorrowAssets = new Dec(totalBorrowWei).add(amountToBorrow).toString();
313
-
314
- const newUtil = new Dec(newTotalBorrowAssets).div(totalSupplyWei).toString();
315
- const newUtilScaled = new Dec(newUtil).mul(1e18).toString();
316
-
317
- if (new Dec(newUtilScaled).lt(marketData.targetBorrowUtilization)) return { vaults: [], withdrawals: [] };
318
-
319
- const liquidityToAllocate = getLiquidityToAllocate(amountToBorrow, totalBorrowWei, totalSupplyWei, marketData.targetBorrowUtilization, marketData.reallocatableLiquidityAssets);
320
-
321
- const vaultTotalAssets = marketData.publicAllocatorSharedLiquidity.reduce(
322
- (acc: Record<string, string>, item: MorphoBluePublicAllocatorItem) => {
323
- const vaultAddress = item.vault.address;
324
- acc[vaultAddress] = new Dec(acc[vaultAddress] || '0').add(item.assets).toString();
325
- return acc;
326
- },
327
- {},
328
- );
329
-
330
- const sortedVaults = Object.entries(vaultTotalAssets).sort(
331
- ([, a]: [string, string], [, b]: [string, string]) => new Dec(b || '0').sub(a || '0').toNumber(),
332
- );
333
-
334
- const withdrawalsPerVault: Record<string, [string[], string, string][]> = {};
335
- let totalReallocated = '0';
336
- for (const [vaultAddress] of sortedVaults) {
337
- if (new Dec(totalReallocated).gte(liquidityToAllocate)) break;
338
-
339
- const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter(
340
- (item: MorphoBluePublicAllocatorItem) => compareAddresses(item.vault.address, vaultAddress),
341
- );
342
- for (const item of vaultAllocations) {
343
- if (new Dec(totalReallocated).gte(liquidityToAllocate)) break;
344
- const itemAmount = item.assets;
345
- const leftToAllocate = new Dec(liquidityToAllocate).sub(totalReallocated).toString();
346
- const amountToTake = new Dec(itemAmount).lt(leftToAllocate) ? itemAmount : leftToAllocate;
347
- totalReallocated = new Dec(totalReallocated).add(amountToTake).toString();
348
- const withdrawal: [string[], string, string] = [
349
- [
350
- item.allocationMarket.loanAsset.address,
351
- item.allocationMarket.collateralAsset?.address,
352
- item.allocationMarket.oracle?.address,
353
- item.allocationMarket.irmAddress,
354
- item.allocationMarket.lltv,
355
- ],
356
- amountToTake.toString(),
357
- item.allocationMarket.uniqueKey,
358
- ];
359
- if (!withdrawalsPerVault[vaultAddress]) {
360
- withdrawalsPerVault[vaultAddress] = [];
361
- }
362
- withdrawalsPerVault[vaultAddress].push(withdrawal);
363
- }
364
- }
365
-
366
- const vaults = Object.keys(withdrawalsPerVault);
367
- const withdrawals = vaults.map(
368
- (vaultAddress) => withdrawalsPerVault[vaultAddress].sort(
369
- (a, b) => a[2].localeCompare(b[2]),
370
- ).map(w => [w[0], w[1]]),
371
- );
372
- return {
373
- vaults,
374
- withdrawals,
375
- };
376
- } catch (error) {
377
- console.error('External API Failure: Morpho blue reallocation', error);
378
- throw new Error('Failed to fetch reallocation data');
379
- }
380
- };
381
-
382
- export const getRewardsForMarket = async (marketId: string, network: NetworkNumber = NetworkNumber.Eth) => {
383
- const response = await fetch(API_URL, {
384
- method: 'POST',
385
- headers: { 'Content-Type': 'application/json' },
386
- body: JSON.stringify({
387
- query: REWARDS_QUERY,
388
- variables: { uniqueKey: marketId, chainId: network },
389
- }),
390
- });
391
-
392
- const data = await response.json();
393
- const marketData = data?.data?.marketByUniqueKey;
394
- if (!marketData) throw new Error('Market data not found');
395
- const morphoAssetInfo = getAssetInfo('MORPHO');
396
- const { supplyApr, borrowApr } = marketData.state.rewards.find((reward: any) => compareAddresses(reward.asset.address, morphoAssetInfo.addresses[network])) || { supplyApr: '0', borrowApr: '0' };
397
- const supplyAprPercent = new Dec(supplyApr).mul(100).toString();
398
- const borrowAprPercent = new Dec(borrowApr).mul(100).toString();
399
- return { supplyApy: aprToApy(supplyAprPercent), borrowApy: aprToApy(borrowAprPercent) };
400
- };
401
-
402
- export const getMorphoUnderlyingSymbol = (_symbol: string) => {
403
- if (_symbol === 'MORPHO Legacy') return 'MORPHO';
404
- return wethToEth(_symbol);
405
- };
1
+ import Dec from 'decimal.js';
2
+ import { assetAmountInWei, getAssetInfo, getAssetInfoByAddress } from '@defisaver/tokens';
3
+ import {
4
+ aprToApy, calcLeverageLiqPrice, getAssetsTotal, getExposure, isLeveragedPos,
5
+ } from '../../moneymarket';
6
+ import { calculateNetApy } from '../../staking';
7
+ import {
8
+ EthereumProvider, LeverageType, MMAssetsData, MMUsedAsset, MMUsedAssets, NetworkNumber,
9
+ } from '../../types/common';
10
+ import {
11
+ MorphoBlueAggregatedPositionData, MorphoBlueAssetsData, MorphoBlueMarketData, MorphoBlueMarketInfo,
12
+ MorphoBluePublicAllocatorItem,
13
+ MorphoBlueRealloactionMarketData,
14
+ } from '../../types';
15
+ import { borrowOperations, SECONDS_PER_YEAR, WAD } from '../../constants';
16
+ import { MorphoBlueViewContractViem } from '../../contracts';
17
+ import { compareAddresses, DEFAULT_TIMEOUT, wethToEth } from '../../services/utils';
18
+ import { getViemProvider } from '../../services/viem';
19
+
20
+ export const getMorphoBlueAggregatedPositionData = ({ usedAssets, assetsData, marketInfo }: { usedAssets: MMUsedAssets, assetsData: MorphoBlueAssetsData, marketInfo: MorphoBlueMarketInfo }): MorphoBlueAggregatedPositionData => {
21
+ const payload = {} as MorphoBlueAggregatedPositionData;
22
+ payload.suppliedUsd = getAssetsTotal(usedAssets, ({ isSupplied }: { isSupplied: boolean }) => isSupplied, ({ suppliedUsd }: { suppliedUsd: string }) => suppliedUsd);
23
+ payload.suppliedCollateralUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral, ({ suppliedUsd }: { suppliedUsd: string }) => suppliedUsd);
24
+ payload.borrowedUsd = getAssetsTotal(usedAssets, ({ isBorrowed }: { isBorrowed: boolean }) => isBorrowed, ({ borrowedUsd }: { borrowedUsd: string }) => borrowedUsd);
25
+
26
+ const {
27
+ lltv, oracle, collateralToken, loanToken,
28
+ } = marketInfo;
29
+
30
+ payload.borrowLimitUsd = getAssetsTotal(
31
+ usedAssets,
32
+ ({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral,
33
+ ({ symbol, suppliedUsd }: { symbol: string, suppliedUsd: string }) => {
34
+ const suppliedUsdAmount = suppliedUsd;
35
+
36
+ return new Dec(suppliedUsdAmount).mul(lltv);
37
+ },
38
+ );
39
+ payload.liquidationLimitUsd = payload.borrowLimitUsd;
40
+ const leftToBorrowUsd = new Dec(payload.borrowLimitUsd).sub(payload.borrowedUsd);
41
+ payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
42
+
43
+ payload.leftToBorrow = new Dec(usedAssets[collateralToken]?.supplied || 0).mul(oracle).mul(lltv).sub(usedAssets[loanToken]?.borrowed || 0)
44
+ .toString();
45
+
46
+ const { netApy, incentiveUsd, totalInterestUsd } = calculateNetApy({ usedAssets, assetsData: assetsData as unknown as MMAssetsData });
47
+ payload.netApy = netApy;
48
+ payload.incentiveUsd = incentiveUsd;
49
+ payload.totalInterestUsd = totalInterestUsd;
50
+
51
+ payload.ltv = new Dec(payload.borrowedUsd).div(payload.suppliedCollateralUsd).toString();
52
+ payload.ltv = new Dec(usedAssets[loanToken]?.borrowed || 0).div(oracle).div(usedAssets[collateralToken]?.supplied || 1).toString(); // default to 1 because can't div 0
53
+ payload.ratio = new Dec(usedAssets[collateralToken]?.supplied || 0).mul(oracle).div(usedAssets[loanToken]?.borrowed || 1).mul(100)
54
+ .toString();
55
+
56
+ const { leveragedType, leveragedAsset } = isLeveragedPos(usedAssets);
57
+ payload.leveragedType = leveragedType;
58
+ if (leveragedType !== '') {
59
+ payload.leveragedAsset = leveragedAsset;
60
+ let assetPrice = assetsData[leveragedAsset].price;
61
+ if (leveragedType === LeverageType.VolatilePair) {
62
+ const borrowedAsset = (Object.values(usedAssets) as MMUsedAsset[]).find(({ borrowedUsd }: { borrowedUsd: string }) => +borrowedUsd > 0);
63
+ const borrowedAssetPrice = assetsData[borrowedAsset!.symbol].price;
64
+ const leveragedAssetPrice = assetsData[leveragedAsset].price;
65
+ const isReverse = new Dec(leveragedAssetPrice).lt(borrowedAssetPrice);
66
+ if (isReverse) {
67
+ payload.leveragedType = LeverageType.VolatilePairReverse;
68
+ payload.currentVolatilePairRatio = new Dec(borrowedAssetPrice).div(leveragedAssetPrice).toDP(18).toString();
69
+ assetPrice = new Dec(borrowedAssetPrice).div(assetPrice).toString();
70
+ } else {
71
+ assetPrice = new Dec(assetPrice).div(borrowedAssetPrice).toString();
72
+ payload.currentVolatilePairRatio = new Dec(leveragedAssetPrice).div(borrowedAssetPrice).toDP(18).toString();
73
+ }
74
+ }
75
+ payload.liquidationPrice = calcLeverageLiqPrice(payload.leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
76
+ }
77
+ payload.minCollRatio = new Dec(payload.suppliedCollateralUsd).div(payload.borrowLimitUsd).mul(100).toString();
78
+ payload.collLiquidationRatio = new Dec(payload.suppliedCollateralUsd).div(payload.liquidationLimitUsd).mul(100).toString();
79
+ payload.exposure = getExposure(payload.borrowedUsd, payload.suppliedUsd);
80
+
81
+ return payload;
82
+ };
83
+
84
+ const compound = (ratePerSeconds: string) => {
85
+ const compounding = new Dec(ratePerSeconds).mul(SECONDS_PER_YEAR).toString();
86
+ const apyNumber = Math.expm1(new Dec(compounding).div(WAD).toNumber());
87
+ return new Dec(apyNumber).mul(WAD).floor().toString();
88
+ };
89
+
90
+ export const getSupplyRate = (totalSupplyAssets: string, totalBorrowAssets: string, borrowRate: string, fee: string) => {
91
+ if (totalBorrowAssets === '0' || totalSupplyAssets === '0') {
92
+ return '0';
93
+ }
94
+ const utillization = new Dec(totalBorrowAssets).mul(WAD).div(totalSupplyAssets).ceil()
95
+ .toString();
96
+ const supplyRate = new Dec(utillization).mul(borrowRate).div(WAD).ceil()
97
+ .toString();
98
+ const ratePerSecond = new Dec(supplyRate).mul(new Dec(WAD).minus(fee)).div(WAD).ceil()
99
+ .toString();
100
+ return new Dec(compound(ratePerSecond)).div(1e18).mul(100).toString();
101
+ };
102
+
103
+ export const getBorrowRate = (borrowRate: string, totalBorrowShares: string) => {
104
+ if (totalBorrowShares === '0') {
105
+ return '0';
106
+ }
107
+ return new Dec(compound(borrowRate)).div(1e18).mul(100).toString();
108
+ };
109
+
110
+ export const getApyAfterValuesEstimation = async (selectedMarket: MorphoBlueMarketData, actions: { action: string, amount: string, asset: string }[], provider: EthereumProvider, network: NetworkNumber) => {
111
+ const client = getViemProvider(provider, network);
112
+ const morphoBlueViewContract = MorphoBlueViewContractViem(client, network);
113
+ const lltvInWei = assetAmountInWei(selectedMarket.lltv, 'ETH');
114
+ const marketData = {
115
+ loanToken: selectedMarket.loanToken,
116
+ collateralToken: selectedMarket.collateralToken,
117
+ oracle: selectedMarket.oracle,
118
+ irm: selectedMarket.irm,
119
+ lltv: BigInt(lltvInWei),
120
+ };
121
+
122
+ const params = actions.map(({ action, asset, amount }) => {
123
+ const isBorrowOperation = borrowOperations.includes(action);
124
+ const amountInWei = assetAmountInWei(amount, asset);
125
+ let liquidityAdded;
126
+ let liquidityRemoved;
127
+ if (isBorrowOperation) {
128
+ liquidityAdded = action === 'payback' ? amountInWei : '0';
129
+ liquidityRemoved = action === 'borrow' ? amountInWei : '0';
130
+ } else {
131
+ liquidityAdded = action === 'collateral' ? amountInWei : '0';
132
+ liquidityRemoved = action === 'withdraw' ? amountInWei : '0';
133
+ }
134
+ return {
135
+ liquidityAdded: BigInt(liquidityAdded),
136
+ liquidityRemoved: BigInt(liquidityRemoved),
137
+ isBorrowOperation,
138
+ };
139
+ });
140
+ const data = await morphoBlueViewContract.read.getApyAfterValuesEstimation([
141
+ marketData,
142
+ params,
143
+ ]);
144
+ const borrowRate = getBorrowRate(data[0].toString(), data[1].totalBorrowShares.toString());
145
+ const supplyRate = getSupplyRate(data[1].totalSupplyAssets.toString(), data[1].totalBorrowAssets.toString(), data[0].toString(), data[1].fee.toString());
146
+ return { borrowRate, supplyRate };
147
+ };
148
+
149
+ const API_URL = 'https://blue-api.morpho.org/graphql';
150
+ const MARKET_QUERY = `
151
+ query MarketByUniqueKey($uniqueKey: String!, $chainId: Int!) {
152
+ marketByUniqueKey(uniqueKey: $uniqueKey, chainId: $chainId) {
153
+ reallocatableLiquidityAssets
154
+ targetBorrowUtilization
155
+ loanAsset {
156
+ address
157
+ decimals
158
+ priceUsd
159
+ }
160
+ state {
161
+ liquidityAssets
162
+ borrowAssets
163
+ supplyAssets
164
+ }
165
+ publicAllocatorSharedLiquidity {
166
+ assets
167
+ vault {
168
+ address
169
+ name
170
+ }
171
+ allocationMarket {
172
+ uniqueKey
173
+ loanAsset {
174
+ address
175
+ }
176
+ collateralAsset {
177
+ address
178
+ }
179
+ irmAddress
180
+ oracle {
181
+ address
182
+ }
183
+ lltv
184
+ }
185
+ }
186
+ loanAsset {
187
+ address
188
+ }
189
+ collateralAsset {
190
+ address
191
+ }
192
+ oracle {
193
+ address
194
+ }
195
+ irmAddress
196
+ lltv
197
+ }
198
+ }
199
+ `;
200
+
201
+ const REWARDS_QUERY = `
202
+ query MarketByUniqueKey($uniqueKey: String!, $chainId: Int!) {
203
+ marketByUniqueKey(uniqueKey: $uniqueKey, chainId: $chainId) {
204
+ uniqueKey
205
+ state {
206
+ rewards {
207
+ amountPerSuppliedToken
208
+ supplyApr
209
+ amountPerBorrowedToken
210
+ borrowApr
211
+ asset {
212
+ address
213
+ }
214
+ }
215
+ }
216
+ }
217
+ }
218
+ `;
219
+
220
+ /**
221
+ * Get reallocatable liquidity to a given market and target borrow utilization
222
+ * @param marketId - Unique key of the market liquidity is reallocated to
223
+ * @param network - The network number
224
+ * @returns The reallocatable liquidity and target borrow utilization
225
+ */
226
+ export const getReallocatableLiquidity = async (marketId: string, network: NetworkNumber = NetworkNumber.Eth): Promise<{ reallocatableLiquidity: string, targetBorrowUtilization: string }> => {
227
+ try {
228
+ const response = await fetch(API_URL, {
229
+ method: 'POST',
230
+ headers: { 'Content-Type': 'application/json' },
231
+ body: JSON.stringify({
232
+ query: MARKET_QUERY,
233
+ variables: { uniqueKey: marketId, chainId: network },
234
+ }),
235
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
236
+ });
237
+
238
+ const data: { data: { marketByUniqueKey: MorphoBlueRealloactionMarketData } } = await response.json();
239
+ const marketData: MorphoBlueRealloactionMarketData = data?.data?.marketByUniqueKey;
240
+
241
+ if (!marketData) throw new Error('Market data not found');
242
+
243
+ return {
244
+ reallocatableLiquidity: marketData.reallocatableLiquidityAssets,
245
+ targetBorrowUtilization: marketData.targetBorrowUtilization,
246
+ };
247
+ } catch (error) {
248
+ console.error('External API Failure: Morpho blue reallocatable liquidity', error);
249
+ throw new Error('Failed to fetch reallocatable liquidity');
250
+ }
251
+ };
252
+
253
+ /**
254
+ * Get liquidity to allocate for a given amount to borrow.
255
+ * First, the function will try to calculate the amount of liquidity to allocate to be able to
256
+ * hit the target utilization. If it is not possible to allocate enough liquidity to hit the
257
+ * target utilization, the function will allocate the amount of liquidity needed to be able to
258
+ * borrow the selected amount.
259
+ * @param amountToBorrow - The amount to borrow
260
+ * @param totalBorrow - The total amount borrowed from market
261
+ * @param totalSupply - The total amount supplied to market
262
+ * @param targetBorrowUtilization - The target borrow utilization of market
263
+ * @param reallocatableLiquidityAssets - The amount of liquidity that can be reallocated from other markets
264
+ * @returns The amount of liquidity to allocate
265
+ */
266
+ export const getLiquidityToAllocate = (amountToBorrow: string, totalBorrow: string, totalSupply: string, targetBorrowUtilization: string, reallocatableLiquidityAssets: string) => {
267
+ const newTotalBorrowAssets = new Dec(totalBorrow).add(amountToBorrow).toString();
268
+ const leftToBorrow = new Dec(totalSupply).sub(totalBorrow).toString();
269
+ let liquidityToAllocate = new Dec(newTotalBorrowAssets).div(targetBorrowUtilization).mul(1e18).sub(totalSupply)
270
+ .toFixed(0)
271
+ .toString();
272
+
273
+ if (new Dec(reallocatableLiquidityAssets).lt(liquidityToAllocate) || new Dec(liquidityToAllocate).lt('0')) {
274
+ liquidityToAllocate = new Dec(amountToBorrow).lt(leftToBorrow) ? '0' : new Dec(amountToBorrow).sub(leftToBorrow).toString();
275
+ if (new Dec(reallocatableLiquidityAssets).lt(liquidityToAllocate)) throw new Error('Not enough liquidity available to allocate');
276
+ }
277
+
278
+ return liquidityToAllocate;
279
+ };
280
+
281
+ /**
282
+ * Get the vaults and withdrawals needed to reallocate liquidity for a given amount to borrow.
283
+ * Amount to be reallocated is calculated in `getLiquidityToAllocate`
284
+ * @param market - The market data
285
+ * @param assetsData - The assets data
286
+ * @param amountToBorrow - Amount being borrowed (not the amount being reallocated)
287
+ * @param network - The network number
288
+ * @returns The vaults and withdrawals needed to reallocate liquidity
289
+ */
290
+ export const getReallocation = async (market: MorphoBlueMarketData, assetsData: MorphoBlueAssetsData, amountToBorrow: string, network: NetworkNumber = NetworkNumber.Eth): Promise<{ vaults: string[], withdrawals: (string | string[])[][][] }> => {
291
+ try {
292
+ const { marketId, loanToken } = market;
293
+ const response = await fetch(API_URL, {
294
+ method: 'POST',
295
+ headers: { 'Content-Type': 'application/json' },
296
+ body: JSON.stringify({
297
+ query: MARKET_QUERY,
298
+ variables: { uniqueKey: marketId, chainId: network },
299
+ }),
300
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
301
+ });
302
+
303
+ const data: { data: { marketByUniqueKey: MorphoBlueRealloactionMarketData } } = await response.json();
304
+ const marketData: MorphoBlueRealloactionMarketData = data?.data?.marketByUniqueKey;
305
+
306
+ if (!marketData) throw new Error('Market data not found');
307
+
308
+ const loanAssetInfo = getAssetInfoByAddress(loanToken, network);
309
+ const { totalBorrow, totalSupply } = assetsData[loanAssetInfo.symbol] || { totalBorrow: '0', totalSupply: '0' };
310
+ const totalBorrowWei = assetAmountInWei(totalBorrow!, loanAssetInfo.symbol);
311
+ const totalSupplyWei = assetAmountInWei(totalSupply!, loanAssetInfo.symbol);
312
+
313
+ const newTotalBorrowAssets = new Dec(totalBorrowWei).add(amountToBorrow).toString();
314
+
315
+ const newUtil = new Dec(newTotalBorrowAssets).div(totalSupplyWei).toString();
316
+ const newUtilScaled = new Dec(newUtil).mul(1e18).toString();
317
+
318
+ if (new Dec(newUtilScaled).lt(marketData.targetBorrowUtilization)) return { vaults: [], withdrawals: [] };
319
+
320
+ const liquidityToAllocate = getLiquidityToAllocate(amountToBorrow, totalBorrowWei, totalSupplyWei, marketData.targetBorrowUtilization, marketData.reallocatableLiquidityAssets);
321
+
322
+ const vaultTotalAssets = marketData.publicAllocatorSharedLiquidity.reduce(
323
+ (acc: Record<string, string>, item: MorphoBluePublicAllocatorItem) => {
324
+ const vaultAddress = item.vault.address;
325
+ acc[vaultAddress] = new Dec(acc[vaultAddress] || '0').add(item.assets).toString();
326
+ return acc;
327
+ },
328
+ {},
329
+ );
330
+
331
+ const sortedVaults = Object.entries(vaultTotalAssets).sort(
332
+ ([, a]: [string, string], [, b]: [string, string]) => new Dec(b || '0').sub(a || '0').toNumber(),
333
+ );
334
+
335
+ const withdrawalsPerVault: Record<string, [string[], string, string][]> = {};
336
+ let totalReallocated = '0';
337
+ for (const [vaultAddress] of sortedVaults) {
338
+ if (new Dec(totalReallocated).gte(liquidityToAllocate)) break;
339
+
340
+ const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter(
341
+ (item: MorphoBluePublicAllocatorItem) => compareAddresses(item.vault.address, vaultAddress),
342
+ );
343
+ for (const item of vaultAllocations) {
344
+ if (new Dec(totalReallocated).gte(liquidityToAllocate)) break;
345
+ const itemAmount = item.assets;
346
+ const leftToAllocate = new Dec(liquidityToAllocate).sub(totalReallocated).toString();
347
+ const amountToTake = new Dec(itemAmount).lt(leftToAllocate) ? itemAmount : leftToAllocate;
348
+ totalReallocated = new Dec(totalReallocated).add(amountToTake).toString();
349
+ const withdrawal: [string[], string, string] = [
350
+ [
351
+ item.allocationMarket.loanAsset.address,
352
+ item.allocationMarket.collateralAsset?.address,
353
+ item.allocationMarket.oracle?.address,
354
+ item.allocationMarket.irmAddress,
355
+ item.allocationMarket.lltv,
356
+ ],
357
+ amountToTake.toString(),
358
+ item.allocationMarket.uniqueKey,
359
+ ];
360
+ if (!withdrawalsPerVault[vaultAddress]) {
361
+ withdrawalsPerVault[vaultAddress] = [];
362
+ }
363
+ withdrawalsPerVault[vaultAddress].push(withdrawal);
364
+ }
365
+ }
366
+
367
+ const vaults = Object.keys(withdrawalsPerVault);
368
+ const withdrawals = vaults.map(
369
+ (vaultAddress) => withdrawalsPerVault[vaultAddress].sort(
370
+ (a, b) => a[2].localeCompare(b[2]),
371
+ ).map(w => [w[0], w[1]]),
372
+ );
373
+ return {
374
+ vaults,
375
+ withdrawals,
376
+ };
377
+ } catch (error) {
378
+ console.error('External API Failure: Morpho blue reallocation', error);
379
+ throw new Error('Failed to fetch reallocation data');
380
+ }
381
+ };
382
+
383
+ export const getRewardsForMarket = async (marketId: string, network: NetworkNumber = NetworkNumber.Eth) => {
384
+ const response = await fetch(API_URL, {
385
+ method: 'POST',
386
+ headers: { 'Content-Type': 'application/json' },
387
+ body: JSON.stringify({
388
+ query: REWARDS_QUERY,
389
+ variables: { uniqueKey: marketId, chainId: network },
390
+ }),
391
+ });
392
+
393
+ const data = await response.json();
394
+ const marketData = data?.data?.marketByUniqueKey;
395
+ if (!marketData) throw new Error('Market data not found');
396
+ const morphoAssetInfo = getAssetInfo('MORPHO');
397
+ const { supplyApr, borrowApr } = marketData.state.rewards.find((reward: any) => compareAddresses(reward.asset.address, morphoAssetInfo.addresses[network])) || { supplyApr: '0', borrowApr: '0' };
398
+ const supplyAprPercent = new Dec(supplyApr).mul(100).toString();
399
+ const borrowAprPercent = new Dec(borrowApr).mul(100).toString();
400
+ return { supplyApy: aprToApy(supplyAprPercent), borrowApy: aprToApy(borrowAprPercent) };
401
+ };
402
+
403
+ export const getMorphoUnderlyingSymbol = (_symbol: string) => {
404
+ if (_symbol === 'MORPHO Legacy') return 'MORPHO';
405
+ return wethToEth(_symbol);
406
+ };