@defisaver/positions-sdk 0.0.186 → 0.0.188-dev-markets

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/cjs/compoundV3/index.js +14 -9
  2. package/cjs/config/contracts.d.ts +670 -228
  3. package/cjs/config/contracts.js +41 -5
  4. package/cjs/contracts.d.ts +2 -0
  5. package/cjs/contracts.js +3 -1
  6. package/cjs/helpers/morphoBlueHelpers/index.d.ts +42 -1
  7. package/cjs/helpers/morphoBlueHelpers/index.js +203 -20
  8. package/cjs/markets/compound/index.d.ts +4 -0
  9. package/cjs/markets/compound/index.js +41 -1
  10. package/cjs/markets/compound/marketsAssets.d.ts +14 -0
  11. package/cjs/markets/compound/marketsAssets.js +17 -3
  12. package/cjs/markets/morphoBlue/index.d.ts +8 -0
  13. package/cjs/markets/morphoBlue/index.js +71 -2
  14. package/cjs/morphoBlue/index.js +27 -10
  15. package/cjs/services/priceService.d.ts +3 -0
  16. package/cjs/services/priceService.js +33 -1
  17. package/cjs/types/compound.d.ts +3 -1
  18. package/cjs/types/compound.js +2 -0
  19. package/cjs/types/contracts/generated/CUSDSv3.d.ts +441 -0
  20. package/cjs/types/contracts/generated/CUSDSv3.js +5 -0
  21. package/cjs/types/contracts/generated/CWstETHv3.d.ts +441 -0
  22. package/cjs/types/contracts/generated/CWstETHv3.js +5 -0
  23. package/cjs/types/contracts/generated/DFSFeedRegistry.d.ts +40 -0
  24. package/cjs/types/contracts/generated/DFSFeedRegistry.js +5 -0
  25. package/cjs/types/contracts/generated/MorphoBlueView.d.ts +5 -5
  26. package/cjs/types/contracts/generated/WstETHPriceFeed.d.ts +39 -0
  27. package/cjs/types/contracts/generated/WstETHPriceFeed.js +5 -0
  28. package/cjs/types/contracts/generated/index.d.ts +4 -0
  29. package/cjs/types/morphoBlue.d.ts +37 -1
  30. package/cjs/types/morphoBlue.js +4 -0
  31. package/esm/compoundV3/index.js +15 -10
  32. package/esm/config/contracts.d.ts +670 -228
  33. package/esm/config/contracts.js +41 -5
  34. package/esm/contracts.d.ts +2 -0
  35. package/esm/contracts.js +2 -0
  36. package/esm/helpers/morphoBlueHelpers/index.d.ts +42 -1
  37. package/esm/helpers/morphoBlueHelpers/index.js +200 -20
  38. package/esm/markets/compound/index.d.ts +4 -0
  39. package/esm/markets/compound/index.js +39 -1
  40. package/esm/markets/compound/marketsAssets.d.ts +14 -0
  41. package/esm/markets/compound/marketsAssets.js +16 -2
  42. package/esm/markets/morphoBlue/index.d.ts +8 -0
  43. package/esm/markets/morphoBlue/index.js +65 -1
  44. package/esm/morphoBlue/index.js +30 -13
  45. package/esm/services/priceService.d.ts +3 -0
  46. package/esm/services/priceService.js +31 -1
  47. package/esm/types/compound.d.ts +3 -1
  48. package/esm/types/compound.js +2 -0
  49. package/esm/types/contracts/generated/CUSDSv3.d.ts +441 -0
  50. package/esm/types/contracts/generated/CUSDSv3.js +4 -0
  51. package/esm/types/contracts/generated/CWstETHv3.d.ts +441 -0
  52. package/esm/types/contracts/generated/CWstETHv3.js +4 -0
  53. package/esm/types/contracts/generated/DFSFeedRegistry.d.ts +40 -0
  54. package/esm/types/contracts/generated/DFSFeedRegistry.js +4 -0
  55. package/esm/types/contracts/generated/MorphoBlueView.d.ts +5 -5
  56. package/esm/types/contracts/generated/WstETHPriceFeed.d.ts +39 -0
  57. package/esm/types/contracts/generated/WstETHPriceFeed.js +4 -0
  58. package/esm/types/contracts/generated/index.d.ts +4 -0
  59. package/esm/types/morphoBlue.d.ts +37 -1
  60. package/esm/types/morphoBlue.js +4 -0
  61. package/package.json +2 -2
  62. package/src/compoundV3/index.ts +20 -11
  63. package/src/config/contracts.js +41 -5
  64. package/src/contracts.ts +2 -0
  65. package/src/helpers/morphoBlueHelpers/index.ts +228 -19
  66. package/src/markets/compound/index.ts +42 -2
  67. package/src/markets/compound/marketsAssets.ts +20 -2
  68. package/src/markets/morphoBlue/index.ts +70 -1
  69. package/src/morphoBlue/index.ts +33 -16
  70. package/src/services/priceService.ts +41 -2
  71. package/src/types/compound.ts +2 -0
  72. package/src/types/contracts/generated/CUSDSv3.ts +685 -0
  73. package/src/types/contracts/generated/CWstETHv3.ts +685 -0
  74. package/src/types/contracts/generated/DFSFeedRegistry.ts +77 -0
  75. package/src/types/contracts/generated/MorphoBlueView.ts +9 -5
  76. package/src/types/contracts/generated/WstETHPriceFeed.ts +59 -0
  77. package/src/types/contracts/generated/index.ts +4 -0
  78. package/src/types/morphoBlue.ts +35 -0
@@ -1,15 +1,18 @@
1
1
  import Dec from 'decimal.js';
2
- import { assetAmountInWei } from '@defisaver/tokens';
2
+ import { assetAmountInWei, getAssetInfoByAddress } from '@defisaver/tokens';
3
3
  import Web3 from 'web3';
4
4
  import { calcLeverageLiqPrice, getAssetsTotal, isLeveragedPos } from '../../moneymarket';
5
5
  import { calculateNetApy } from '../../staking';
6
6
  import { MMAssetsData, MMUsedAssets, NetworkNumber } from '../../types/common';
7
7
  import {
8
8
  MorphoBlueAggregatedPositionData, MorphoBlueAssetsData, MorphoBlueMarketData, MorphoBlueMarketInfo,
9
+ MorphoBluePublicAllocatorItem,
10
+ MorphoBlueRealloactionMarketData,
9
11
  } from '../../types';
10
12
  import { borrowOperations, SECONDS_PER_YEAR, WAD } from '../../constants';
11
13
  import { MorphoBlueViewContract } from '../../contracts';
12
14
  import { MarketParamsStruct } from '../../types/contracts/generated/MorphoBlueView';
15
+ import { compareAddresses } from '../../services/utils';
13
16
 
14
17
  export const getMorphoBlueAggregatedPositionData = ({ usedAssets, assetsData, marketInfo }: { usedAssets: MMUsedAssets, assetsData: MorphoBlueAssetsData, marketInfo: MorphoBlueMarketInfo }): MorphoBlueAggregatedPositionData => {
15
18
  const payload = {} as MorphoBlueAggregatedPositionData;
@@ -89,28 +92,234 @@ export const getBorrowRate = (borrowRate: string, totalBorrowShares: string) =>
89
92
  return new Dec(compound(borrowRate)).div(1e18).mul(100).toString();
90
93
  };
91
94
 
92
- export const getApyAfterValuesEstimation = async (selectedMarket: MorphoBlueMarketData, action: string, amount: string, asset: string, web3: Web3, network: NetworkNumber) => {
95
+ export const getApyAfterValuesEstimation = async (selectedMarket: MorphoBlueMarketData, actions: { action: string, amount: string, asset: string }[], web3: Web3, network: NetworkNumber) => {
93
96
  const morphoBlueViewContract = MorphoBlueViewContract(web3, network);
94
97
  const lltvInWei = assetAmountInWei(selectedMarket.lltv, 'ETH');
95
98
  const marketData: MarketParamsStruct = [selectedMarket.loanToken, selectedMarket.collateralToken, selectedMarket.oracle, selectedMarket.irm, lltvInWei];
96
- const isBorrowOperation = borrowOperations.includes(action);
97
- const amountInWei = assetAmountInWei(amount, asset);
98
- let liquidityAdded;
99
- let liquidityRemoved;
100
- if (isBorrowOperation) {
101
- liquidityAdded = action === 'payback' ? amountInWei : '0';
102
- liquidityRemoved = action === 'borrow' ? amountInWei : '0';
103
- } else {
104
- liquidityAdded = action === 'collateral' ? amountInWei : '0';
105
- liquidityRemoved = action === 'withdraw' ? amountInWei : '0';
106
- }
107
- const data = await morphoBlueViewContract.methods.getApyAfterValuesEstimation([
99
+
100
+ const params = actions.map(({ action, asset, amount }) => {
101
+ const isBorrowOperation = borrowOperations.includes(action);
102
+ const amountInWei = assetAmountInWei(amount, asset);
103
+ let liquidityAdded;
104
+ let liquidityRemoved;
105
+ if (isBorrowOperation) {
106
+ liquidityAdded = action === 'payback' ? amountInWei : '0';
107
+ liquidityRemoved = action === 'borrow' ? amountInWei : '0';
108
+ } else {
109
+ liquidityAdded = action === 'collateral' ? amountInWei : '0';
110
+ liquidityRemoved = action === 'withdraw' ? amountInWei : '0';
111
+ }
112
+ return {
113
+ liquidityAdded,
114
+ liquidityRemoved,
115
+ isBorrowOperation,
116
+ };
117
+ });
118
+ const data = await morphoBlueViewContract.methods.getApyAfterValuesEstimation(
108
119
  marketData,
109
- isBorrowOperation,
110
- liquidityAdded,
111
- liquidityRemoved,
112
- ]).call();
120
+ params,
121
+ ).call();
113
122
  const borrowRate = getBorrowRate(data.borrowRate, data.market.totalBorrowShares);
114
123
  const supplyRate = getSupplyRate(data.market.totalSupplyAssets, data.market.totalBorrowAssets, data.borrowRate, data.market.fee);
115
124
  return { borrowRate, supplyRate };
116
- };
125
+ };
126
+
127
+ const API_URL = 'https://blue-api.morpho.org/graphql';
128
+ const MARKET_QUERY = `
129
+ query MarketByUniqueKey($uniqueKey: String!, $chainId: Int!) {
130
+ marketByUniqueKey(uniqueKey: $uniqueKey, chainId: $chainId) {
131
+ reallocatableLiquidityAssets
132
+ targetBorrowUtilization
133
+ loanAsset {
134
+ address
135
+ decimals
136
+ priceUsd
137
+ }
138
+ state {
139
+ liquidityAssets
140
+ borrowAssets
141
+ supplyAssets
142
+ }
143
+ publicAllocatorSharedLiquidity {
144
+ assets
145
+ vault {
146
+ address
147
+ name
148
+ }
149
+ allocationMarket {
150
+ uniqueKey
151
+ loanAsset {
152
+ address
153
+ }
154
+ collateralAsset {
155
+ address
156
+ }
157
+ irmAddress
158
+ oracle {
159
+ address
160
+ }
161
+ lltv
162
+ }
163
+ }
164
+ loanAsset {
165
+ address
166
+ }
167
+ collateralAsset {
168
+ address
169
+ }
170
+ oracle {
171
+ address
172
+ }
173
+ irmAddress
174
+ lltv
175
+ }
176
+ }
177
+ `;
178
+
179
+ /**
180
+ * Get reallocatable liquidity to a given market and target borrow utilization
181
+ * @param marketId - Unique key of the market liquidity is reallocated to
182
+ * @param network - The network number
183
+ * @returns The reallocatable liquidity and target borrow utilization
184
+ */
185
+ export const getReallocatableLiquidity = async (marketId: string, network: NetworkNumber = NetworkNumber.Eth): Promise<{ reallocatableLiquidity: string, targetBorrowUtilization: string }> => {
186
+ const response = await fetch(API_URL, {
187
+ method: 'POST',
188
+ headers: { 'Content-Type': 'application/json' },
189
+ body: JSON.stringify({
190
+ query: MARKET_QUERY,
191
+ variables: { uniqueKey: marketId, chainId: network },
192
+ }),
193
+ });
194
+
195
+ const data: { data: { marketByUniqueKey: MorphoBlueRealloactionMarketData } } = await response.json();
196
+ const marketData: MorphoBlueRealloactionMarketData = data?.data?.marketByUniqueKey;
197
+
198
+ if (!marketData) throw new Error('Market data not found');
199
+
200
+ return { reallocatableLiquidity: marketData.reallocatableLiquidityAssets, targetBorrowUtilization: marketData.targetBorrowUtilization };
201
+ };
202
+
203
+ /**
204
+ * Get liquidity to allocate for a given amount to borrow.
205
+ * First, the function will try to calculate the amount of liquidity to allocate to be able to
206
+ * hit the target utilization. If it is not possible to allocate enough liquidity to hit the
207
+ * target utilization, the function will allocate the amount of liquidity needed to be able to
208
+ * borrow the selected amount.
209
+ * @param amountToBorrow - The amount to borrow
210
+ * @param totalBorrow - The total amount borrowed from market
211
+ * @param totalSupply - The total amount supplied to market
212
+ * @param targetBorrowUtilization - The target borrow utilization of market
213
+ * @param reallocatableLiquidityAssets - The amount of liquidity that can be reallocated from other markets
214
+ * @returns The amount of liquidity to allocate
215
+ */
216
+ export const getLiquidityToAllocate = (amountToBorrow: string, totalBorrow: string, totalSupply: string, targetBorrowUtilization: string, reallocatableLiquidityAssets: string) => {
217
+ const newTotalBorrowAssets = new Dec(totalBorrow).add(amountToBorrow).toString();
218
+ const leftToBorrow = new Dec(totalSupply).sub(totalBorrow).toString();
219
+ let liquidityToAllocate = new Dec(newTotalBorrowAssets).div(targetBorrowUtilization).mul(1e18).sub(totalSupply)
220
+ .toFixed(0)
221
+ .toString();
222
+
223
+ if (new Dec(reallocatableLiquidityAssets).lt(liquidityToAllocate) || new Dec(liquidityToAllocate).lt('0')) {
224
+ liquidityToAllocate = new Dec(amountToBorrow).lt(leftToBorrow) ? '0' : new Dec(amountToBorrow).sub(leftToBorrow).toString();
225
+ if (new Dec(reallocatableLiquidityAssets).lt(liquidityToAllocate)) throw new Error('Not enough liquidity available to allocate');
226
+ }
227
+
228
+ return liquidityToAllocate;
229
+ };
230
+
231
+ /**
232
+ * Get the vaults and withdrawals needed to reallocate liquidity for a given amount to borrow.
233
+ * Amount to be reallocated is calculated in `getLiquidityToAllocate`
234
+ * @param market - The market data
235
+ * @param assetsData - The assets data
236
+ * @param amountToBorrow - Amount being borrowed (not the amount being reallocated)
237
+ * @param network - The network number
238
+ * @returns The vaults and withdrawals needed to reallocate liquidity
239
+ */
240
+ export const getReallocation = async (market: MorphoBlueMarketData, assetsData: MorphoBlueAssetsData, amountToBorrow: string, network: NetworkNumber = NetworkNumber.Eth): Promise<{ vaults: string[], withdrawals: (string | string[])[][][] }> => {
241
+ const { marketId, loanToken } = market;
242
+ const response = await fetch(API_URL, {
243
+ method: 'POST',
244
+ headers: { 'Content-Type': 'application/json' },
245
+ body: JSON.stringify({
246
+ query: MARKET_QUERY,
247
+ variables: { uniqueKey: marketId, chainId: network },
248
+ }),
249
+ });
250
+
251
+ const data: { data: { marketByUniqueKey: MorphoBlueRealloactionMarketData } } = await response.json();
252
+ const marketData: MorphoBlueRealloactionMarketData = data?.data?.marketByUniqueKey;
253
+
254
+ if (!marketData) throw new Error('Market data not found');
255
+
256
+ const loanAssetInfo = getAssetInfoByAddress(loanToken, network);
257
+ const { totalBorrow, totalSupply } = assetsData[loanAssetInfo.symbol] || { totalBorrow: '0', totalSupply: '0' };
258
+ const totalBorrowWei = assetAmountInWei(totalBorrow!, loanAssetInfo.symbol);
259
+ const totalSupplyWei = assetAmountInWei(totalSupply!, loanAssetInfo.symbol);
260
+
261
+ const newTotalBorrowAssets = new Dec(totalBorrowWei).add(amountToBorrow).toString();
262
+
263
+ const newUtil = new Dec(newTotalBorrowAssets).div(totalSupplyWei).toString();
264
+ const newUtilScaled = new Dec(newUtil).mul(1e18).toString();
265
+
266
+ if (new Dec(newUtilScaled).lt(marketData.targetBorrowUtilization)) return { vaults: [], withdrawals: [] };
267
+
268
+ const liquidityToAllocate = getLiquidityToAllocate(amountToBorrow, totalBorrowWei, totalSupplyWei, marketData.targetBorrowUtilization, marketData.reallocatableLiquidityAssets);
269
+
270
+ const vaultTotalAssets = marketData.publicAllocatorSharedLiquidity.reduce(
271
+ (acc: Record<string, string>, item: MorphoBluePublicAllocatorItem) => {
272
+ const vaultAddress = item.vault.address;
273
+ acc[vaultAddress] = new Dec(acc[vaultAddress] || '0').add(item.assets).toString();
274
+ return acc;
275
+ },
276
+ {},
277
+ );
278
+
279
+ const sortedVaults = Object.entries(vaultTotalAssets).sort(
280
+ ([, a]: [string, string], [, b]: [string, string]) => new Dec(b || '0').sub(a || '0').toNumber(),
281
+ );
282
+
283
+ const withdrawalsPerVault: Record<string, [string[], string, string][]> = {};
284
+ let totalReallocated = '0';
285
+ for (const [vaultAddress] of sortedVaults) {
286
+ if (new Dec(totalReallocated).gte(liquidityToAllocate)) break;
287
+
288
+ const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter(
289
+ (item: MorphoBluePublicAllocatorItem) => compareAddresses(item.vault.address, vaultAddress),
290
+ );
291
+ for (const item of vaultAllocations) {
292
+ if (new Dec(totalReallocated).gte(liquidityToAllocate)) break;
293
+ const itemAmount = item.assets;
294
+ const leftToAllocate = new Dec(liquidityToAllocate).sub(totalReallocated).toString();
295
+ const amountToTake = new Dec(itemAmount).lt(leftToAllocate) ? itemAmount : leftToAllocate;
296
+ totalReallocated = new Dec(totalReallocated).add(amountToTake).toString();
297
+ const withdrawal: [string[], string, string] = [
298
+ [
299
+ item.allocationMarket.loanAsset.address,
300
+ item.allocationMarket.collateralAsset?.address,
301
+ item.allocationMarket.oracle?.address,
302
+ item.allocationMarket.irmAddress,
303
+ item.allocationMarket.lltv,
304
+ ],
305
+ amountToTake.toString(),
306
+ item.allocationMarket.uniqueKey,
307
+ ];
308
+ if (!withdrawalsPerVault[vaultAddress]) {
309
+ withdrawalsPerVault[vaultAddress] = [];
310
+ }
311
+ withdrawalsPerVault[vaultAddress].push(withdrawal);
312
+ }
313
+ }
314
+
315
+ const vaults = Object.keys(withdrawalsPerVault);
316
+ const withdrawals = vaults.map(
317
+ (vaultAddress) => withdrawalsPerVault[vaultAddress].sort(
318
+ (a, b) => a[2].localeCompare(b[2]),
319
+ ).map(w => [w[0], w[1]]),
320
+ );
321
+ return {
322
+ vaults,
323
+ withdrawals,
324
+ };
325
+ };
@@ -6,8 +6,8 @@ import {
6
6
  v3ETHCollAssets,
7
7
  v3USDbCCollAssets,
8
8
  v3USDCCollAssets,
9
- v3USDCeCollAssets,
10
- v3USDTCollAssets,
9
+ v3USDCeCollAssets, v3USDSCollAssets,
10
+ v3USDTCollAssets, v3wstETHCollAssets,
11
11
  } from './marketsAssets';
12
12
 
13
13
  export {
@@ -31,6 +31,8 @@ const BULKER_OPTIONS: Record<NetworkNumber, Record<CompoundVersions, CompoundBul
31
31
  [CompoundVersions.CompoundV3USDC]: { supply: 2, withdraw: 5 },
32
32
  [CompoundVersions.CompoundV3ETH]: STANDARD_BULKER_OPTIONS,
33
33
  [CompoundVersions.CompoundV3USDT]: STANDARD_BULKER_OPTIONS,
34
+ [CompoundVersions.CompoundV3wstETH]: STANDARD_BULKER_OPTIONS,
35
+ [CompoundVersions.CompoundV3USDS]: STANDARD_BULKER_OPTIONS,
34
36
 
35
37
  // Non-existing markets, keeping it because of typescript
36
38
  [CompoundVersions.CompoundV2]: EMPTY_BULKER_OPTIONS,
@@ -46,6 +48,8 @@ const BULKER_OPTIONS: Record<NetworkNumber, Record<CompoundVersions, CompoundBul
46
48
  // Non-existing markets, keeping it because of typescript
47
49
  [CompoundVersions.CompoundV2]: EMPTY_BULKER_OPTIONS,
48
50
  [CompoundVersions.CompoundV3USDbC]: EMPTY_BULKER_OPTIONS,
51
+ [CompoundVersions.CompoundV3wstETH]: EMPTY_BULKER_OPTIONS,
52
+ [CompoundVersions.CompoundV3USDS]: EMPTY_BULKER_OPTIONS,
49
53
  },
50
54
  [NetworkNumber.Base]: {
51
55
  [CompoundVersions.CompoundV3ETH]: STANDARD_BULKER_OPTIONS,
@@ -56,6 +60,8 @@ const BULKER_OPTIONS: Record<NetworkNumber, Record<CompoundVersions, CompoundBul
56
60
  [CompoundVersions.CompoundV2]: EMPTY_BULKER_OPTIONS,
57
61
  [CompoundVersions.CompoundV3USDCe]: EMPTY_BULKER_OPTIONS,
58
62
  [CompoundVersions.CompoundV3USDT]: EMPTY_BULKER_OPTIONS,
63
+ [CompoundVersions.CompoundV3wstETH]: EMPTY_BULKER_OPTIONS,
64
+ [CompoundVersions.CompoundV3USDS]: EMPTY_BULKER_OPTIONS,
59
65
  },
60
66
  [NetworkNumber.Opt]: {
61
67
  [CompoundVersions.CompoundV3USDC]: STANDARD_BULKER_OPTIONS,
@@ -65,6 +71,8 @@ const BULKER_OPTIONS: Record<NetworkNumber, Record<CompoundVersions, CompoundBul
65
71
  [CompoundVersions.CompoundV3USDbC]: EMPTY_BULKER_OPTIONS,
66
72
  [CompoundVersions.CompoundV2]: EMPTY_BULKER_OPTIONS,
67
73
  [CompoundVersions.CompoundV3USDCe]: EMPTY_BULKER_OPTIONS,
74
+ [CompoundVersions.CompoundV3wstETH]: EMPTY_BULKER_OPTIONS,
75
+ [CompoundVersions.CompoundV3USDS]: EMPTY_BULKER_OPTIONS,
68
76
  },
69
77
  };
70
78
 
@@ -164,6 +172,36 @@ export const COMPOUND_V3_USDT = (networkId: NetworkNumber): CompoundMarketData =
164
172
  // icon: SvgAdapter(protocolIcons.compoundv3),
165
173
  });
166
174
 
175
+ export const COMPOUND_V3_WSTETH = (networkId: NetworkNumber): CompoundMarketData => ({
176
+ chainIds: [NetworkNumber.Eth],
177
+ label: 'Compound V3 - wstETH',
178
+ shortLabel: 'v3',
179
+ value: CompoundVersions.CompoundV3wstETH,
180
+ baseAsset: 'wstETH',
181
+ collAssets: networkId ? v3wstETHCollAssets[networkId] : [],
182
+ baseMarket: 'cWstETHv3',
183
+ baseMarketAddress: getConfigContractAddress('cWstETHv3', networkId),
184
+ secondLabel: 'Market',
185
+ bulkerName: networkId === NetworkNumber.Eth ? 'CompV3BulkerMainnetETH' : 'CompV3BulkerL2',
186
+ bulkerAddress: getConfigContractAddress(networkId === NetworkNumber.Eth ? 'CompV3BulkerMainnetETH' : 'CompV3BulkerL2', networkId),
187
+ bulkerOptions: BULKER_OPTIONS[networkId][CompoundVersions.CompoundV3wstETH],
188
+ });
189
+
190
+ export const COMPOUND_V3_USDS = (networkId: NetworkNumber): CompoundMarketData => ({
191
+ chainIds: [NetworkNumber.Eth],
192
+ label: 'Compound V3 - USDS',
193
+ shortLabel: 'v3',
194
+ value: CompoundVersions.CompoundV3USDS,
195
+ baseAsset: 'USDS',
196
+ collAssets: networkId ? v3USDSCollAssets[networkId] : [],
197
+ baseMarket: 'cUSDSv3',
198
+ baseMarketAddress: getConfigContractAddress('cUSDSv3', networkId),
199
+ secondLabel: 'Market',
200
+ bulkerName: networkId === NetworkNumber.Eth ? 'CompV3BulkerMainnetETH' : 'CompV3BulkerL2',
201
+ bulkerAddress: getConfigContractAddress(networkId === NetworkNumber.Eth ? 'CompV3BulkerMainnetETH' : 'CompV3BulkerL2', networkId),
202
+ bulkerOptions: BULKER_OPTIONS[networkId][CompoundVersions.CompoundV3USDS],
203
+ });
204
+
167
205
  export const CompoundMarkets = (networkId: NetworkNumber) => ({
168
206
  [CompoundVersions.CompoundV2]: COMPOUND_V2,
169
207
  [CompoundVersions.CompoundV3ETH]: COMPOUND_V3_ETH(networkId),
@@ -171,4 +209,6 @@ export const CompoundMarkets = (networkId: NetworkNumber) => ({
171
209
  [CompoundVersions.CompoundV3USDbC]: COMPOUND_V3_USDBC(networkId),
172
210
  [CompoundVersions.CompoundV3USDCe]: COMPOUND_V3_USDCe(networkId),
173
211
  [CompoundVersions.CompoundV3USDT]: COMPOUND_V3_USDT(networkId),
212
+ [CompoundVersions.CompoundV3wstETH]: COMPOUND_V3_WSTETH(networkId),
213
+ [CompoundVersions.CompoundV3USDS]: COMPOUND_V3_USDS(networkId),
174
214
  }) as const;
@@ -7,7 +7,7 @@ export const compoundV2CollateralAssets = [
7
7
  ].map((symbol) => getAssetInfo(symbol));
8
8
 
9
9
  export const v3USDCCollAssetsEth = ['COMP', 'WBTC', 'ETH', 'UNI', 'LINK', 'wstETH', 'cbBTC', 'tBTC'];
10
- export const v3USDCCollAssetsArb = ['ARB', 'ETH', 'GMX', 'WBTC', 'wstETH', 'ezETH'];
10
+ export const v3USDCCollAssetsArb = ['ARB', 'ETH', 'GMX', 'WBTC', 'wstETH', 'ezETH', 'wUSDM'];
11
11
  export const v3USDCCollAssetsBase = ['ETH', 'cbETH', 'wstETH', 'cbBTC'];
12
12
  export const v3USDCCollAssetsOpt = ['ETH', 'OP', 'WBTC', 'wstETH'];
13
13
 
@@ -52,7 +52,7 @@ export const v3USDbCCollAssets = {
52
52
  [NetworkNumber.Base]: v3USDbCCollAssetsBase,
53
53
  };
54
54
 
55
- export const v3USDTCollAssetsEth = ['COMP', 'ETH', 'WBTC', 'UNI', 'LINK', 'wstETH', 'cbBTC', 'tBTC', 'wUSDM', 'sFRAX'];
55
+ export const v3USDTCollAssetsEth = ['COMP', 'ETH', 'WBTC', 'UNI', 'LINK', 'wstETH', 'cbBTC', 'tBTC', 'wUSDM', 'sFRAX', 'mETH'];
56
56
  export const v3USDTCollAssetsArb = ['ETH', 'WBTC', 'wstETH', 'ARB', 'GMX'];
57
57
  export const v3USDTCollAssetsOpt = ['ETH', 'WBTC', 'OP', 'wstETH'];
58
58
 
@@ -62,4 +62,22 @@ export const v3USDTCollAssets = {
62
62
  [NetworkNumber.Opt]: v3USDTCollAssetsOpt,
63
63
  [NetworkNumber.Arb]: v3USDTCollAssetsArb,
64
64
  [NetworkNumber.Base]: [],
65
+ };
66
+
67
+ export const v3USDSCollAssetsEth = ['wstETH', 'ETH', 'sUSDS', 'cbETH', 'tBTC', 'USDe'];
68
+
69
+ export const v3USDSCollAssets = {
70
+ [NetworkNumber.Eth]: v3USDSCollAssetsEth,
71
+ [NetworkNumber.Opt]: [],
72
+ [NetworkNumber.Arb]: [],
73
+ [NetworkNumber.Base]: [],
74
+ };
75
+
76
+ export const v3wstETHCollAssetsEth = ['rsETH', 'ezETH'];
77
+
78
+ export const v3wstETHCollAssets = {
79
+ [NetworkNumber.Eth]: v3wstETHCollAssetsEth,
80
+ [NetworkNumber.Opt]: [],
81
+ [NetworkNumber.Arb]: [],
82
+ [NetworkNumber.Base]: [],
65
83
  };
@@ -723,7 +723,7 @@ export const MORPHO_BLUE_CBBTC_USDC_860_BASE = (networkId: NetworkNumber = Netwo
723
723
  export const MORPHO_BLUE_WSUPEROETHB_WETH_915_BASE = (networkId: NetworkNumber = NetworkNumber.Eth): MorphoBlueMarketData => ({
724
724
  chainIds: [NetworkNumber.Base],
725
725
  label: 'Morpho',
726
- shortLabel: 'wsuperOETHb/WETH',
726
+ shortLabel: 'wsuperOETHb/ETH',
727
727
  value: MorphoBlueVersions.MorphoBlueWsuperOETHbWETH_915_Base,
728
728
  url: 'wsuperoethbweth-144bf18d',
729
729
  loanToken: '0x4200000000000000000000000000000000000006',
@@ -736,6 +736,70 @@ export const MORPHO_BLUE_WSUPEROETHB_WETH_915_BASE = (networkId: NetworkNumber =
736
736
  protocolName: 'morpho-blue',
737
737
  });
738
738
 
739
+ export const MORPHO_BLUE_LBTC_WBTC_945 = (networkId: NetworkNumber = NetworkNumber.Eth): MorphoBlueMarketData => ({
740
+ chainIds: [NetworkNumber.Eth],
741
+ label: 'Morpho',
742
+ shortLabel: 'LBTC/WBTC',
743
+ value: MorphoBlueVersions.MorphoBlueLBTCWBTC_945,
744
+ url: 'lbtcwbtc-f6a05662',
745
+ loanToken: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
746
+ collateralToken: '0x8236a87084f8B84306f72007F36F2618A5634494',
747
+ oracle: '0xa98105B8227E0f2157816Feb7A331364A9B74F80',
748
+ oracleType: MorphoBlueOracleType.MARKET_RATE,
749
+ irm: '0x870aC11D48B15DB9a138Cf899d20F13F79Ba00BC',
750
+ lltv: 0.945,
751
+ marketId: '0xf6a056627a51e511ec7f48332421432ea6971fc148d8f3c451e14ea108026549',
752
+ protocolName: 'morpho-blue',
753
+ });
754
+
755
+ export const MORPHO_BLUE_LBTC_CBBTC_945_BASE = (networkId: NetworkNumber = NetworkNumber.Eth): MorphoBlueMarketData => ({
756
+ chainIds: [NetworkNumber.Base],
757
+ label: 'Morpho',
758
+ shortLabel: 'LBTC/cbBTC',
759
+ value: MorphoBlueVersions.MorphoBlueLBTCCbBTC_945_Base,
760
+ url: 'lbtccbbtc-30767836',
761
+ loanToken: '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf',
762
+ collateralToken: '0xecAc9C5F704e954931349Da37F60E39f515c11c1',
763
+ oracle: '0x9Ae0E86e88AEE94B700240eBE0BD17D969BAD0EA',
764
+ oracleType: MorphoBlueOracleType.MARKET_RATE,
765
+ irm: '0x46415998764C29aB2a25CbeA6254146D50D22687',
766
+ lltv: 0.945,
767
+ marketId: '0x30767836635facec1282e6ef4a5981406ed4e72727b3a63a3a72c74e8279a8d7',
768
+ protocolName: 'morpho-blue',
769
+ });
770
+
771
+ export const MORPHO_BLUE_WSTETH_EURC_860_BASE = (networkId: NetworkNumber = NetworkNumber.Eth): MorphoBlueMarketData => ({
772
+ chainIds: [NetworkNumber.Base],
773
+ label: 'Morpho',
774
+ shortLabel: 'wstETH/EURC',
775
+ value: MorphoBlueVersions.MorphoBlueWstEthEURC_860_Base,
776
+ url: 'wstetheurc-f7e40290',
777
+ loanToken: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
778
+ collateralToken: '0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452',
779
+ oracle: '0xa54122f0E0766258377Ffe732e454A3248f454F4',
780
+ oracleType: MorphoBlueOracleType.MARKET_RATE,
781
+ irm: '0x46415998764C29aB2a25CbeA6254146D50D22687',
782
+ lltv: 0.86,
783
+ marketId: '0xf7e40290f8ca1d5848b3c129502599aa0f0602eb5f5235218797a34242719561',
784
+ protocolName: 'morpho-blue',
785
+ });
786
+
787
+ export const MORPHO_BLUE_CBBTC_EURC_860_BASE = (networkId: NetworkNumber = NetworkNumber.Eth): MorphoBlueMarketData => ({
788
+ chainIds: [NetworkNumber.Base],
789
+ label: 'Morpho',
790
+ shortLabel: 'cbBTC/EURC',
791
+ value: MorphoBlueVersions.MorphoBlueCbBTCEURC_860_Base,
792
+ url: 'cbbtceurc-67ebd84b',
793
+ loanToken: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
794
+ collateralToken: '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf',
795
+ oracle: '0xA857411CB2231a6A87a3bEF987a4cED8A067d799',
796
+ oracleType: MorphoBlueOracleType.MARKET_RATE,
797
+ irm: '0x46415998764C29aB2a25CbeA6254146D50D22687',
798
+ lltv: 0.86,
799
+ marketId: '0x67ebd84b2fb39e3bc5a13d97e4c07abe1ea617e40654826e9abce252e95f049e',
800
+ protocolName: 'morpho-blue',
801
+ });
802
+
739
803
  export const MorphoBlueMarkets = (networkId: NetworkNumber) => ({
740
804
  [MorphoBlueVersions.MorphoBlueWstEthUSDC]: MORPHO_BLUE_WSTETH_USDC(networkId),
741
805
  [MorphoBlueVersions.MorphoBlueSDAIUSDC]: MORPHO_BLUE_SDAI_USDC(networkId),
@@ -760,6 +824,7 @@ export const MorphoBlueMarkets = (networkId: NetworkNumber) => ({
760
824
  [MorphoBlueVersions.MorphoBlueCbBTCUSDC_860]: MORPHO_BLUE_CBBTC_USDC_860(networkId),
761
825
  [MorphoBlueVersions.MorphoBlueREthEth_945]: MORPHO_BLUE_RETH_ETH_945(networkId),
762
826
  [MorphoBlueVersions.MorphoBlueSUSDeUSDC_915]: MORPHO_BLUE_SUSDE_USDC_915(networkId),
827
+ [MorphoBlueVersions.MorphoBlueLBTCWBTC_945]: MORPHO_BLUE_LBTC_WBTC_945(networkId),
763
828
 
764
829
  // wstETH/WETH
765
830
  [MorphoBlueVersions.MorphoBlueWstEthEth_945]: MORPHO_BLUE_WSTETH_ETH_945(networkId),
@@ -786,6 +851,10 @@ export const MorphoBlueMarkets = (networkId: NetworkNumber) => ({
786
851
  [MorphoBlueVersions.MorphoBlueREthEth_945_Base]: MORPHO_BLUE_RETH_ETH_945_BASE(networkId),
787
852
  [MorphoBlueVersions.MorphoBlueCbBTCEth_915_Base]: MORPHO_BLUE_CBBTC_ETH_915_BASE(networkId),
788
853
  [MorphoBlueVersions.MorphoBlueCbBTCUSDC_860_Base]: MORPHO_BLUE_CBBTC_USDC_860_BASE(networkId),
854
+ [MorphoBlueVersions.MorphoBlueCbBTCEURC_860_Base]: MORPHO_BLUE_CBBTC_EURC_860_BASE(networkId),
855
+ [MorphoBlueVersions.MorphoBlueWstEthEURC_860_Base]: MORPHO_BLUE_WSTETH_EURC_860_BASE(networkId),
856
+ [MorphoBlueVersions.MorphoBlueLBTCCbBTC_945_Base]: MORPHO_BLUE_LBTC_CBBTC_945_BASE(networkId),
857
+
789
858
 
790
859
  // wsuperOETHb/WETH Base
791
860
  [MorphoBlueVersions.MorphoBlueWsuperOETHbWETH_915_Base]: MORPHO_BLUE_WSUPEROETHB_WETH_915_BASE(networkId),
@@ -1,41 +1,58 @@
1
1
  import Web3 from 'web3';
2
2
  import Dec from 'decimal.js';
3
- import { assetAmountInEth, getAssetInfo, getAssetInfoByAddress } from '@defisaver/tokens';
3
+ import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
4
4
  import {
5
5
  Blockish, EthAddress, MMUsedAssets, NetworkNumber, PositionBalances,
6
6
  } from '../types/common';
7
- import {
8
- FeedRegistryContract,
9
- MorphoBlueViewContract,
10
- } from '../contracts';
7
+ import { DFSFeedRegistryContract, FeedRegistryContract, MorphoBlueViewContract } from '../contracts';
11
8
  import {
12
9
  MorphoBlueAssetsData, MorphoBlueMarketData, MorphoBlueMarketInfo, MorphoBluePositionData,
13
10
  } from '../types';
14
- import { WAD, USD_QUOTE } from '../constants';
11
+ import { USD_QUOTE, WAD } from '../constants';
15
12
  import { getStakingApy, STAKING_ASSETS } from '../staking';
16
13
  import { wethToEth } from '../services/utils';
17
14
  import { getBorrowRate, getMorphoBlueAggregatedPositionData, getSupplyRate } from '../helpers/morphoBlueHelpers';
15
+ import { getChainlinkAssetAddress } from '../services/priceService';
16
+
17
+ const isMainnetNetwork = (network: NetworkNumber) => network === NetworkNumber.Eth;
18
18
 
19
19
  export async function getMorphoBlueMarketData(web3: Web3, network: NetworkNumber, selectedMarket: MorphoBlueMarketData, mainnetWeb3: Web3): Promise<MorphoBlueMarketInfo> {
20
20
  const {
21
21
  loanToken, collateralToken, oracle, irm, lltv, oracleType,
22
22
  } = selectedMarket;
23
+
23
24
  const lltvInWei = new Dec(lltv).mul(WAD).toString();
24
25
  const loanTokenInfo = getAssetInfoByAddress(loanToken, network);
25
26
  const collateralTokenInfo = getAssetInfoByAddress(collateralToken, network);
26
- let loanTokenFeedAddress = loanTokenInfo.addresses[NetworkNumber.Eth];
27
- if (loanTokenInfo.symbol === 'WETH') {
28
- const ethAddress = getAssetInfo('ETH').address;
29
- loanTokenFeedAddress = ethAddress;
30
- }
31
27
 
32
- const feedRegistryContract = FeedRegistryContract(mainnetWeb3, NetworkNumber.Eth);
28
+ const loanTokenFeedAddress = getChainlinkAssetAddress(loanTokenInfo.symbol, network);
29
+
33
30
  const morphoBlueViewContract = MorphoBlueViewContract(web3, network);
34
31
 
35
- const [loanTokenPrice, marketInfo] = await Promise.all([
36
- loanTokenInfo.symbol === 'USDA' ? '100000000' : feedRegistryContract.methods.latestAnswer(loanTokenFeedAddress, USD_QUOTE).call(),
37
- morphoBlueViewContract.methods.getMarketInfoNotTuple(loanToken, collateralToken, oracle, irm, lltvInWei).call(),
38
- ]);
32
+ let marketInfo;
33
+ let loanTokenPrice;
34
+ const isTokenUSDA = loanTokenInfo.symbol === 'USDA';
35
+ const isMainnet = isMainnetNetwork(network);
36
+ if (isMainnet) {
37
+ const feedRegistryContract = FeedRegistryContract(mainnetWeb3, NetworkNumber.Eth);
38
+ const [_loanTokenPrice, _marketInfo] = await Promise.all([
39
+ isTokenUSDA ? Promise.resolve('100000000') : feedRegistryContract.methods.latestAnswer(loanTokenFeedAddress, USD_QUOTE).call(),
40
+ morphoBlueViewContract.methods.getMarketInfoNotTuple(loanToken, collateralToken, oracle, irm, lltvInWei).call(),
41
+ ]);
42
+ marketInfo = _marketInfo;
43
+ loanTokenPrice = _loanTokenPrice;
44
+ } else {
45
+ // Currently only base network is supported
46
+ const feedRegistryContract = DFSFeedRegistryContract(web3, network);
47
+
48
+ const [loanTokenPriceRound, _marketInfo] = await Promise.all([
49
+ isTokenUSDA ? Promise.resolve({ answer: '100000000' }) // Normalize to match the expected object structure
50
+ : feedRegistryContract.methods.latestRoundData(loanTokenFeedAddress, USD_QUOTE).call(),
51
+ morphoBlueViewContract.methods.getMarketInfoNotTuple(loanToken, collateralToken, oracle, irm, lltvInWei).call(),
52
+ ]);
53
+ marketInfo = _marketInfo;
54
+ loanTokenPrice = loanTokenPriceRound.answer;
55
+ }
39
56
 
40
57
  const supplyRate = getSupplyRate(marketInfo.totalSupplyAssets, marketInfo.totalBorrowAssets, marketInfo.borrowRate, marketInfo.fee);
41
58
  const compoundedBorrowRate = getBorrowRate(marketInfo.borrowRate, marketInfo.totalBorrowShares);
@@ -1,7 +1,14 @@
1
1
  import Web3 from 'web3';
2
2
  import Dec from 'decimal.js';
3
- import { COMPPriceFeedContract, ETHPriceFeedContract, USDCPriceFeedContract } from '../contracts';
3
+ import { getAssetInfo } from '@defisaver/tokens';
4
+ import {
5
+ COMPPriceFeedContract,
6
+ ETHPriceFeedContract,
7
+ USDCPriceFeedContract,
8
+ WstETHPriceFeedContract,
9
+ } from '../contracts';
4
10
  import { NetworkNumber } from '../types/common';
11
+ import { multicall } from '../multicall';
5
12
 
6
13
  export const getEthPrice = async (web3: Web3) => {
7
14
  const contract = ETHPriceFeedContract(web3, NetworkNumber.Eth);
@@ -19,4 +26,36 @@ export const getCompPrice = async (web3: Web3) => {
19
26
  const contract = COMPPriceFeedContract(web3, NetworkNumber.Eth);
20
27
  const price = await contract.methods.latestAnswer().call();
21
28
  return new Dec(price).div(1e8).toString();
22
- };
29
+ };
30
+
31
+ export const getWstETHPrice = async (web3: Web3) => {
32
+ const wstETHFeedContract = WstETHPriceFeedContract(web3, NetworkNumber.Eth);
33
+ const ethFeedContract = ETHPriceFeedContract(web3, NetworkNumber.Eth);
34
+ const calls = [
35
+ {
36
+ target: ethFeedContract.options.address,
37
+ abiItem: ethFeedContract.options.jsonInterface.find(({ name }) => name === 'latestAnswer'),
38
+ params: [],
39
+ },
40
+ {
41
+ target: wstETHFeedContract.options.address,
42
+ abiItem: wstETHFeedContract.options.jsonInterface.find(({ name }) => name === 'latestRoundData'),
43
+ params: [],
44
+ },
45
+ ];
46
+
47
+ const multicallRes = await multicall(calls, web3);
48
+
49
+ const ethPrice = new Dec(multicallRes[0][0]).div(1e8);
50
+
51
+ const wstETHRate = new Dec(multicallRes[1].answer).div(1e8);
52
+
53
+ return new Dec(ethPrice).mul(wstETHRate).toString();
54
+ };
55
+
56
+ // chainlink price feed available only on mainnet
57
+ export const getChainlinkAssetAddress = (symbol: string, network: NetworkNumber) => {
58
+ if (['WBTC', 'RENBTC'].includes(symbol?.toUpperCase())) return '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB';
59
+ if (symbol?.toUpperCase() === 'WETH') return getAssetInfo('ETH').addresses[network];
60
+ return getAssetInfo(symbol).addresses[network];
61
+ };
@@ -9,6 +9,8 @@ export enum CompoundVersions {
9
9
  'CompoundV3ETH' = 'v3-ETH',
10
10
  'CompoundV3USDbC' = 'v3-USDbC',
11
11
  'CompoundV3USDT' = 'v3-USDT',
12
+ 'CompoundV3USDS' = 'v3-USDS',
13
+ 'CompoundV3wstETH' = 'v3-wstETH',
12
14
  }
13
15
 
14
16
  export interface CompoundBulkerOptions {