@defisaver/positions-sdk 1.0.19 → 1.0.21-dev-1

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 (88) hide show
  1. package/.mocharc.json +4 -4
  2. package/.nvmrc +1 -1
  3. package/README.md +69 -69
  4. package/cjs/fluid/index.d.ts +13 -1
  5. package/cjs/fluid/index.js +23 -5
  6. package/cjs/helpers/morphoBlueHelpers/index.js +66 -66
  7. package/cjs/markets/morphoBlue/index.d.ts +4 -0
  8. package/cjs/markets/morphoBlue/index.js +38 -2
  9. package/cjs/morphoBlue/index.js +9 -7
  10. package/cjs/types/morphoBlue.d.ts +2 -0
  11. package/cjs/types/morphoBlue.js +3 -0
  12. package/esm/fluid/index.d.ts +13 -1
  13. package/esm/fluid/index.js +21 -4
  14. package/esm/helpers/morphoBlueHelpers/index.js +66 -66
  15. package/esm/markets/morphoBlue/index.d.ts +4 -0
  16. package/esm/markets/morphoBlue/index.js +34 -0
  17. package/esm/morphoBlue/index.js +9 -7
  18. package/esm/types/morphoBlue.d.ts +2 -0
  19. package/esm/types/morphoBlue.js +3 -0
  20. package/package.json +54 -54
  21. package/src/aaveV2/index.ts +227 -227
  22. package/src/aaveV3/index.ts +624 -624
  23. package/src/assets/index.ts +60 -60
  24. package/src/chickenBonds/index.ts +123 -123
  25. package/src/compoundV2/index.ts +220 -220
  26. package/src/compoundV3/index.ts +291 -291
  27. package/src/config/contracts.js +1173 -1173
  28. package/src/constants/index.ts +6 -6
  29. package/src/contracts.ts +138 -138
  30. package/src/curveUsd/index.ts +239 -239
  31. package/src/eulerV2/index.ts +303 -303
  32. package/src/exchange/index.ts +17 -17
  33. package/src/fluid/index.ts +1348 -1325
  34. package/src/helpers/aaveHelpers/index.ts +203 -203
  35. package/src/helpers/chickenBondsHelpers/index.ts +23 -23
  36. package/src/helpers/compoundHelpers/index.ts +248 -248
  37. package/src/helpers/curveUsdHelpers/index.ts +40 -40
  38. package/src/helpers/eulerHelpers/index.ts +234 -234
  39. package/src/helpers/fluidHelpers/index.ts +325 -325
  40. package/src/helpers/index.ts +11 -11
  41. package/src/helpers/liquityV2Helpers/index.ts +80 -80
  42. package/src/helpers/llamaLendHelpers/index.ts +53 -53
  43. package/src/helpers/makerHelpers/index.ts +94 -94
  44. package/src/helpers/morphoBlueHelpers/index.ts +367 -367
  45. package/src/helpers/sparkHelpers/index.ts +154 -154
  46. package/src/index.ts +52 -52
  47. package/src/liquity/index.ts +116 -116
  48. package/src/liquityV2/index.ts +317 -317
  49. package/src/llamaLend/index.ts +275 -275
  50. package/src/maker/index.ts +117 -117
  51. package/src/markets/aave/index.ts +152 -152
  52. package/src/markets/aave/marketAssets.ts +47 -47
  53. package/src/markets/compound/index.ts +213 -213
  54. package/src/markets/compound/marketsAssets.ts +82 -82
  55. package/src/markets/curveUsd/index.ts +69 -69
  56. package/src/markets/euler/index.ts +26 -26
  57. package/src/markets/fluid/index.ts +2456 -2456
  58. package/src/markets/index.ts +27 -27
  59. package/src/markets/liquityV2/index.ts +102 -102
  60. package/src/markets/llamaLend/contractAddresses.ts +141 -141
  61. package/src/markets/llamaLend/index.ts +235 -235
  62. package/src/markets/morphoBlue/index.ts +932 -895
  63. package/src/markets/spark/index.ts +29 -29
  64. package/src/markets/spark/marketAssets.ts +10 -10
  65. package/src/moneymarket/moneymarketCommonService.ts +80 -80
  66. package/src/morphoAaveV2/index.ts +256 -256
  67. package/src/morphoAaveV3/index.ts +630 -630
  68. package/src/morphoBlue/index.ts +204 -202
  69. package/src/multicall/index.ts +33 -33
  70. package/src/services/priceService.ts +143 -143
  71. package/src/services/utils.ts +59 -59
  72. package/src/setup.ts +8 -8
  73. package/src/spark/index.ts +460 -460
  74. package/src/staking/staking.ts +221 -221
  75. package/src/types/aave.ts +275 -275
  76. package/src/types/chickenBonds.ts +45 -45
  77. package/src/types/common.ts +84 -84
  78. package/src/types/compound.ts +133 -133
  79. package/src/types/curveUsd.ts +119 -119
  80. package/src/types/euler.ts +173 -173
  81. package/src/types/fluid.ts +330 -330
  82. package/src/types/index.ts +11 -11
  83. package/src/types/liquity.ts +30 -30
  84. package/src/types/liquityV2.ts +126 -126
  85. package/src/types/llamaLend.ts +155 -155
  86. package/src/types/maker.ts +50 -50
  87. package/src/types/morphoBlue.ts +197 -194
  88. package/src/types/spark.ts +135 -135
@@ -1,317 +1,317 @@
1
- import Web3 from 'web3';
2
- import Dec from 'decimal.js';
3
- import { assetAmountInEth, getAssetInfo } from '@defisaver/tokens';
4
- import { createContractWrapper, LiquityV2LegacyViewContract, LiquityV2ViewContract } from '../contracts';
5
- import { EthAddress, NetworkNumber } from '../types/common';
6
- import {
7
- InnerLiquityV2MarketData,
8
- LIQUITY_V2_TROVE_STATUS_ENUM,
9
- LiquityV2AssetsData, LiquityV2MarketData, LiquityV2MarketInfo, LiquityV2TroveData, LiquityV2UsedAssets,
10
- LiquityV2Versions,
11
- } from '../types';
12
- import { getStakingApy, STAKING_ASSETS } from '../staking';
13
- import { getLiquityV2AggregatedPositionData } from '../helpers/liquityV2Helpers';
14
- import { compareAddresses, ethToWeth, MAXUINT } from '../services/utils';
15
- import { LiquityV2View } from '../types/contracts/generated';
16
- import { ZERO_ADDRESS } from '../constants';
17
- import { LiquityV2Markets } from '../markets';
18
- import { BaseContract } from '../types/contracts/generated/types';
19
-
20
- const getLiquityV2ViewContract = (web3: Web3, network: NetworkNumber, isLegacy: boolean): BaseContract => {
21
- if (isLegacy) return LiquityV2LegacyViewContract(web3, network);
22
- return LiquityV2ViewContract(web3, network);
23
- };
24
-
25
-
26
- export const getLiquityV2MarketData = async (web3: Web3, network: NetworkNumber, selectedMarket: LiquityV2MarketInfo, mainnetWeb3: Web3): Promise<LiquityV2MarketData> => {
27
- const {
28
- marketAddress, debtToken, collateralToken, isLegacy,
29
- } = selectedMarket;
30
- const viewContract = getLiquityV2ViewContract(web3, network, isLegacy);
31
- const data = await viewContract.methods.getMarketData(marketAddress).call();
32
- const hintHelperAddress = data.hintHelpers;
33
- const troveNFTAddress = data.troveNFT;
34
- const borrowerOperationsAddress = data.borrowerOperations;
35
- const troveManagerAddress = data.troveManager;
36
- const stabilityPoolAddress = data.stabilityPool;
37
- const collSurplusPoolAddress = data.collSurplusPool;
38
- const activePoolAddress = data.activePool;
39
-
40
- const minCollRatio = new Dec(data.MCR).div(1e16).toString();
41
- const criticalCollRatio = new Dec(data.CCR).div(1e18).toString();
42
- const batchCollRatio = new Dec(data.BCR || '0').div(1e16).toString();
43
-
44
- const totalMarketBorrow = assetAmountInEth(data.entireSystemDebt);
45
- const totalMarketSupply = assetAmountInEth(data.entireSystemColl);
46
- const collPrice = assetAmountInEth(data.collPrice);
47
-
48
- const totalCollRatio = new Dec(totalMarketSupply).mul(collPrice).div(totalMarketBorrow).toString();
49
- const leftToBorrowGlobal = new Dec(totalMarketSupply).mul(collPrice).div(criticalCollRatio).sub(totalMarketBorrow)
50
- .toString();
51
- const minCollAmountForCurrentBorrow = new Dec(totalMarketBorrow).mul(criticalCollRatio).div(collPrice).toString();
52
- const leftToWithdrawGlobal = new Dec(totalMarketSupply).sub(minCollAmountForCurrentBorrow).toString();
53
-
54
- const assetsData: LiquityV2AssetsData = {};
55
- assetsData[debtToken] = {
56
- symbol: debtToken,
57
- address: getAssetInfo(debtToken, network).address,
58
- price: '1',
59
- totalSupply: '0',
60
- totalBorrow: totalMarketBorrow,
61
- canBeSupplied: false,
62
- canBeBorrowed: true,
63
- leftToBorrowGlobal,
64
- leftToWithdrawGlobal: '0',
65
- };
66
- assetsData[collateralToken] = {
67
- symbol: collateralToken,
68
- address: getAssetInfo(ethToWeth(collateralToken), network).address,
69
- price: collPrice,
70
- totalSupply: totalMarketSupply,
71
- totalBorrow: '0',
72
- canBeSupplied: true,
73
- canBeBorrowed: false,
74
- leftToBorrowGlobal: '0',
75
- leftToWithdrawGlobal,
76
- };
77
- if (STAKING_ASSETS.includes(collateralToken)) {
78
- assetsData[collateralToken].incentiveSupplyApy = await getStakingApy(collateralToken, mainnetWeb3);
79
- assetsData[collateralToken].incentiveSupplyToken = collateralToken;
80
- }
81
-
82
- return {
83
- assetsData,
84
- marketData: {
85
- minCollRatio,
86
- totalCollRatio: new Dec(totalCollRatio).mul(100).toString(),
87
- criticalCollRatio: new Dec(criticalCollRatio).mul(100).toString(),
88
- batchCollRatio,
89
- isUnderCollateralized: new Dec(totalCollRatio).lt(criticalCollRatio),
90
- hintHelperAddress,
91
- troveNFTAddress,
92
- borrowerOperationsAddress,
93
- troveManagerAddress,
94
- stabilityPoolAddress,
95
- collSurplusPoolAddress,
96
- activePoolAddress,
97
- },
98
- };
99
- };
100
-
101
- const _getUserTroves = async (viewContract: any, account: EthAddress, marketAddress: EthAddress, startIndex = 0, endIndex = 100) => viewContract.methods.getUserTroves(account, marketAddress, startIndex, endIndex).call();
102
-
103
- const getUserTroves = async (viewContract: any, account: EthAddress, marketAddress: EthAddress, startIndex = 0, endIndex = 100, troves: LiquityV2View.ExistingTroveStructOutput[] = []): Promise<{ troves: LiquityV2View.ExistingTroveStructOutput[], nextFreeTroveIndex: string }> => {
104
- const result = await _getUserTroves(viewContract, account, marketAddress, startIndex, endIndex);
105
- const newStartIndex = endIndex + 1;
106
- const nextFreeTroveIndex = result.nextFreeTroveIndex;
107
- const existingTroves = [...troves, ...result.troves];
108
- if (nextFreeTroveIndex !== '-1') return { troves: existingTroves.filter((trove) => trove.ownedByUser), nextFreeTroveIndex };
109
- return getUserTroves(viewContract, account, marketAddress, newStartIndex, newStartIndex + 100, existingTroves);
110
- };
111
-
112
- const TransferEventSig = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
113
-
114
- const nftContractCreationBlockMapping = {
115
- [LiquityV2Versions.LiquityV2Eth]: 22516079,
116
- [LiquityV2Versions.LiquityV2WstEth]: 22516099,
117
- [LiquityV2Versions.LiquityV2REth]: 22516118,
118
- // legacy
119
- [LiquityV2Versions.LiquityV2EthLegacy]: 21686215,
120
- [LiquityV2Versions.LiquityV2WstEthLegacy]: 21686238,
121
- [LiquityV2Versions.LiquityV2REthLegacy]: 21686257,
122
- };
123
-
124
- const getTransferredTroves = async (web3: Web3, network: NetworkNumber, troveNFTAddress: EthAddress, limitBlocksForEventFetching: boolean, market: LiquityV2Versions, account: EthAddress): Promise<{ troveId: string }[]> => {
125
- const nftContract = createContractWrapper(web3, network, 'LiquityV2TroveNFT', troveNFTAddress);
126
- const nftContractCreationBlock = nftContractCreationBlockMapping[market];
127
- const currentBlock = await web3.eth.getBlockNumber();
128
- const events = await nftContract.getPastEvents(
129
- TransferEventSig,
130
- {
131
- fromBlock: limitBlocksForEventFetching ? (currentBlock - 1000) : nftContractCreationBlock,
132
- },
133
- );
134
- const userTransferredTroves = events.filter((event) => compareAddresses(event.returnValues.to, account));
135
-
136
- // check if the last know transfer address is the user
137
- userTransferredTroves.forEach((event, index) => {
138
- const otherTransfers = events.filter((e) => event.blockNumber < e.blockNumber && e.returnValues.tokenId === event.returnValues.tokenId);
139
- // @ts-ignore
140
- userTransferredTroves[index].invalid = !!otherTransfers.length;
141
- });
142
- // @ts-ignore
143
- return userTransferredTroves.filter((event) => !event.invalid).map((event) => ({ troveId: event.returnValues.tokenId }));
144
- };
145
-
146
- export const getLiquityV2UserTroveIds = async (web3: Web3, network: NetworkNumber, selectedMarket: LiquityV2MarketInfo, troveNFTAddress: EthAddress, limitBlocksForEventFetching: boolean, account: EthAddress): Promise<{ troves: { troveId: string }[], nextFreeTroveIndex: string }> => {
147
- const viewContract = getLiquityV2ViewContract(web3, network, selectedMarket.isLegacy);
148
- const [{ troves: userTroves, nextFreeTroveIndex }, userTransferredTroves] = await Promise.all([
149
- getUserTroves(viewContract, account, selectedMarket.marketAddress),
150
- getTransferredTroves(web3, network, troveNFTAddress, limitBlocksForEventFetching, selectedMarket.value, account),
151
- ]);
152
- const troves = [...userTroves.map(({ troveId }) => ({ troveId })), ...userTransferredTroves];
153
- const filteredTroves = troves.filter((value, index, self) => index === self.findIndex((t) => (
154
- t.troveId === value.troveId
155
- )),
156
- );
157
- const troveIds = filteredTroves.map((trove) => trove.troveId);
158
- const troveIdsSet = new Set(troveIds);
159
- const troveIdsArray = Array.from(troveIdsSet);
160
- const trovesNoDuplicates = troveIdsArray.map((troveId) => troves.find((trove) => trove.troveId === troveId)) as { troveId: string }[];
161
- return { troves: trovesNoDuplicates, nextFreeTroveIndex };
162
- };
163
-
164
- const _getDebtInFrontForSingleMarket = async (viewContract: any, marketAddress: EthAddress, troveId: string, accumulatedSum = '0', iterations = 2000) => viewContract.methods.getDebtInFront(marketAddress, troveId, accumulatedSum, iterations).call();
165
-
166
- export const getDebtInFrontForSingleMarketLiquityV2 = async (viewContract: any, marketAddress: EthAddress, troveId: string, accumulatedSum = '0', iterations = 2000): Promise<string> => {
167
- const { debt, next } = await _getDebtInFrontForSingleMarket(viewContract, marketAddress, troveId, accumulatedSum, iterations);
168
- if (next === '0') return assetAmountInEth(debt);
169
- return getDebtInFrontForSingleMarketLiquityV2(viewContract, marketAddress, next, debt, iterations);
170
- };
171
-
172
- const _getDebtInFrontForInterestRateSingleMarketLiquityV2 = async (viewContract: any, marketAddress: EthAddress, troveId = '0', accumulatedSum = '0', iterations = 2000, interestRate: string) => viewContract.methods.getDebtInFrontByInterestRate(marketAddress, troveId, accumulatedSum, iterations, interestRate).call();
173
-
174
- export const getDebtInFrontForInterestRateSingleMarketLiquityV2 = async (viewContract: any, marketAddress: EthAddress, interestRate: string, troveId = '0', accumulatedSum = '0', iterations = 2000): Promise<string> => {
175
- if (+interestRate === 0 || !interestRate) return '0';
176
- const interestRateWei = new Dec(interestRate).times(1e16).toFixed(0).toString();
177
- const res = await _getDebtInFrontForInterestRateSingleMarketLiquityV2(viewContract, marketAddress, troveId, accumulatedSum, iterations, interestRateWei);
178
- const { debt, next } = res;
179
- if (next === '0') return assetAmountInEth(debt);
180
- return getDebtInFrontForInterestRateSingleMarketLiquityV2(viewContract, marketAddress, interestRate, next, debt, iterations);
181
- };
182
-
183
- const getUnbackedDebtForSingleMarket = async (totalBorrowed: string, web3: Web3, network: NetworkNumber, stabilityPoolAddress: EthAddress) => {
184
- const stabilityPoolContract = createContractWrapper(web3, network, 'LiquityV2StabilityPool', stabilityPoolAddress);
185
- const totalBoldDeposits = await stabilityPoolContract.methods.getTotalBoldDeposits().call();
186
- const totalBoldDepositsInEth = assetAmountInEth(totalBoldDeposits);
187
-
188
- return Dec.max(new Dec(totalBorrowed).sub(totalBoldDepositsInEth), 0).toString();
189
- };
190
-
191
- export const getAllMarketsUnbackedDebts = async (markets: Record<LiquityV2Versions, LiquityV2MarketData>, web3: Web3, network: NetworkNumber): Promise<Record<LiquityV2Versions, string>> => {
192
- const allMarketsUnbackedDebt = await Promise.all(Object.entries(markets).map(async ([version, market]) => {
193
- const { assetsData, marketData } = market;
194
- const { debtToken } = LiquityV2Markets(network)[version as LiquityV2Versions];
195
- const unbackedDebt = await getUnbackedDebtForSingleMarket(assetsData[debtToken].totalBorrow, web3, network, marketData.stabilityPoolAddress);
196
- return [version, unbackedDebt];
197
- }));
198
-
199
- return Object.fromEntries(allMarketsUnbackedDebt) as Record<LiquityV2Versions, string>;
200
- };
201
-
202
- export const calculateDebtInFrontLiquityV2 = (markets: Record<LiquityV2Versions, LiquityV2MarketData>, selectedMarket: LiquityV2Versions, allMarketsUnbackedDebts: Record<LiquityV2Versions, string>, interestRateDebtInFront: string): string => {
203
- const selectedMarketUnbackedDebt = new Dec(allMarketsUnbackedDebts[selectedMarket]);
204
- if (selectedMarketUnbackedDebt.eq(0)) return 'N/A';
205
-
206
- const amountBeingReedemedOnEachMarket = Object.entries(markets).map(([version, market]) => {
207
- if (version === selectedMarket) return new Dec(interestRateDebtInFront);
208
- const { assetsData } = market;
209
- const { debtToken } = LiquityV2Markets(NetworkNumber.Eth)[version as LiquityV2Versions];
210
- const unbackedDebt = new Dec(allMarketsUnbackedDebts[version as LiquityV2Versions]);
211
- const totalBorrow = new Dec(assetsData[debtToken].totalBorrow);
212
- const amountToReedem = new Dec(interestRateDebtInFront).mul(unbackedDebt).div(selectedMarketUnbackedDebt);
213
- return Dec.min(amountToReedem, totalBorrow);
214
- });
215
-
216
- return amountBeingReedemedOnEachMarket.reduce((acc, val) => acc.plus(val), new Dec(0)).toString();
217
- };
218
-
219
- export const getDebtInFrontLiquityV2 = async (markets: Record<LiquityV2Versions, LiquityV2MarketData>, selectedMarket: LiquityV2Versions, web3: Web3, network: NetworkNumber, viewContract: any, troveId: string) => {
220
- const allMarketsUnbackedDebts = await getAllMarketsUnbackedDebts(markets, web3, network);
221
- const interestRateDebtInFront = await getDebtInFrontForSingleMarketLiquityV2(viewContract, LiquityV2Markets(network)[selectedMarket].marketAddress, troveId);
222
-
223
- return calculateDebtInFrontLiquityV2(markets, selectedMarket, allMarketsUnbackedDebts, interestRateDebtInFront.toString());
224
- };
225
-
226
- export const getDebtInFrontForInterestRateLiquityV2 = async (markets: Record<LiquityV2Versions, LiquityV2MarketData>, selectedMarket: LiquityV2Versions, web3: Web3, network: NetworkNumber, viewContract: any, interestRate: string) => {
227
- const allMarketsUnbackedDebts = await getAllMarketsUnbackedDebts(markets, web3, network);
228
- const interestRateDebtInFront = new Dec(await getDebtInFrontForInterestRateSingleMarketLiquityV2(viewContract, LiquityV2Markets(network)[selectedMarket].marketAddress, interestRate));
229
-
230
- return calculateDebtInFrontLiquityV2(markets, selectedMarket, allMarketsUnbackedDebts, interestRateDebtInFront.toString());
231
- };
232
-
233
- export const getLiquityV2TroveData = async (
234
- web3: Web3,
235
- network: NetworkNumber,
236
- {
237
- selectedMarket,
238
- assetsData,
239
- troveId,
240
- allMarketsData,
241
- }:
242
- {
243
- selectedMarket: LiquityV2MarketInfo,
244
- assetsData: LiquityV2AssetsData,
245
- troveId: string,
246
- allMarketsData: Record<LiquityV2Versions, LiquityV2MarketData>,
247
- },
248
- ): Promise<LiquityV2TroveData> => {
249
- const viewContract = getLiquityV2ViewContract(web3, network, selectedMarket.isLegacy);
250
- const { minCollRatio, batchCollRatio } = allMarketsData[selectedMarket.value].marketData;
251
- const { collateralToken, marketAddress, debtToken } = selectedMarket;
252
- const [_data, debtInFront] = await Promise.all([
253
- viewContract.methods.getTroveInfo(marketAddress, troveId).call(),
254
- getDebtInFrontLiquityV2(allMarketsData, selectedMarket.value, web3, network, viewContract, troveId),
255
- ]);
256
- const data = {
257
- ..._data,
258
- TCRatio: _data.TCRatio === MAXUINT ? '0' : _data.TCRatio, // mistake on contract side when debt is 0
259
- };
260
- const usedAssets: LiquityV2UsedAssets = {};
261
-
262
- const debtAssetData = assetsData[debtToken];
263
- const borrowed = assetAmountInEth(data.debtAmount);
264
- usedAssets[debtToken] = {
265
- symbol: debtToken,
266
- supplied: '0',
267
- suppliedUsd: '0',
268
- borrowed,
269
- borrowedUsd: new Dec(borrowed).mul(debtAssetData.price).toString(),
270
- isBorrowed: true,
271
- isSupplied: false,
272
- };
273
-
274
- const collAssetData = assetsData[collateralToken];
275
- const suppliedColl = assetAmountInEth(data.collAmount);
276
- usedAssets[collateralToken] = {
277
- symbol: collateralToken,
278
- supplied: suppliedColl,
279
- suppliedUsd: new Dec(suppliedColl).mul(collAssetData.price).toString(),
280
- borrowed: '0',
281
- borrowedUsd: '0',
282
- isBorrowed: false,
283
- isSupplied: true,
284
- collateral: true,
285
- };
286
-
287
- const collRatio = new Dec(data.TCRatio).div(1e16).toString();
288
- const interestRate = new Dec(data.annualInterestRate).div(1e16).toString();
289
- const interestBatchManager = data.interestBatchManager;
290
- const lastInterestRateAdjTime = data.lastInterestRateAdjTime;
291
-
292
- const hasInterestBatchManager = !compareAddresses(interestBatchManager, ZERO_ADDRESS);
293
- const liqRatio = hasInterestBatchManager ? new Dec(minCollRatio).add(batchCollRatio).toString() : minCollRatio;
294
-
295
- const payload: LiquityV2TroveData = {
296
- usedAssets,
297
- troveId,
298
- interestRate,
299
- interestBatchManager,
300
- debtInFront,
301
- lastInterestRateAdjTime,
302
- liqRatio,
303
- troveStatus: LIQUITY_V2_TROVE_STATUS_ENUM[parseInt(data.status, 10)],
304
- ...getLiquityV2AggregatedPositionData({
305
- usedAssets, assetsData, minCollRatio: liqRatio, interestRate,
306
- }),
307
- collRatio,
308
- };
309
-
310
- return payload;
311
- };
312
-
313
- export const getLiquityV2ClaimableCollateral = async (collSurplusPoolAddress: EthAddress, account: EthAddress, web3: Web3, network: NetworkNumber): Promise<string> => {
314
- const collSurplusPoolContract = createContractWrapper(web3, network, 'LiquityV2CollSurplusPool', collSurplusPoolAddress);
315
- const claimableCollateral = await collSurplusPoolContract.methods.getCollateral(account).call();
316
- return claimableCollateral;
317
- };
1
+ import Web3 from 'web3';
2
+ import Dec from 'decimal.js';
3
+ import { assetAmountInEth, getAssetInfo } from '@defisaver/tokens';
4
+ import { createContractWrapper, LiquityV2LegacyViewContract, LiquityV2ViewContract } from '../contracts';
5
+ import { EthAddress, NetworkNumber } from '../types/common';
6
+ import {
7
+ InnerLiquityV2MarketData,
8
+ LIQUITY_V2_TROVE_STATUS_ENUM,
9
+ LiquityV2AssetsData, LiquityV2MarketData, LiquityV2MarketInfo, LiquityV2TroveData, LiquityV2UsedAssets,
10
+ LiquityV2Versions,
11
+ } from '../types';
12
+ import { getStakingApy, STAKING_ASSETS } from '../staking';
13
+ import { getLiquityV2AggregatedPositionData } from '../helpers/liquityV2Helpers';
14
+ import { compareAddresses, ethToWeth, MAXUINT } from '../services/utils';
15
+ import { LiquityV2View } from '../types/contracts/generated';
16
+ import { ZERO_ADDRESS } from '../constants';
17
+ import { LiquityV2Markets } from '../markets';
18
+ import { BaseContract } from '../types/contracts/generated/types';
19
+
20
+ const getLiquityV2ViewContract = (web3: Web3, network: NetworkNumber, isLegacy: boolean): BaseContract => {
21
+ if (isLegacy) return LiquityV2LegacyViewContract(web3, network);
22
+ return LiquityV2ViewContract(web3, network);
23
+ };
24
+
25
+
26
+ export const getLiquityV2MarketData = async (web3: Web3, network: NetworkNumber, selectedMarket: LiquityV2MarketInfo, mainnetWeb3: Web3): Promise<LiquityV2MarketData> => {
27
+ const {
28
+ marketAddress, debtToken, collateralToken, isLegacy,
29
+ } = selectedMarket;
30
+ const viewContract = getLiquityV2ViewContract(web3, network, isLegacy);
31
+ const data = await viewContract.methods.getMarketData(marketAddress).call();
32
+ const hintHelperAddress = data.hintHelpers;
33
+ const troveNFTAddress = data.troveNFT;
34
+ const borrowerOperationsAddress = data.borrowerOperations;
35
+ const troveManagerAddress = data.troveManager;
36
+ const stabilityPoolAddress = data.stabilityPool;
37
+ const collSurplusPoolAddress = data.collSurplusPool;
38
+ const activePoolAddress = data.activePool;
39
+
40
+ const minCollRatio = new Dec(data.MCR).div(1e16).toString();
41
+ const criticalCollRatio = new Dec(data.CCR).div(1e18).toString();
42
+ const batchCollRatio = new Dec(data.BCR || '0').div(1e16).toString();
43
+
44
+ const totalMarketBorrow = assetAmountInEth(data.entireSystemDebt);
45
+ const totalMarketSupply = assetAmountInEth(data.entireSystemColl);
46
+ const collPrice = assetAmountInEth(data.collPrice);
47
+
48
+ const totalCollRatio = new Dec(totalMarketSupply).mul(collPrice).div(totalMarketBorrow).toString();
49
+ const leftToBorrowGlobal = new Dec(totalMarketSupply).mul(collPrice).div(criticalCollRatio).sub(totalMarketBorrow)
50
+ .toString();
51
+ const minCollAmountForCurrentBorrow = new Dec(totalMarketBorrow).mul(criticalCollRatio).div(collPrice).toString();
52
+ const leftToWithdrawGlobal = new Dec(totalMarketSupply).sub(minCollAmountForCurrentBorrow).toString();
53
+
54
+ const assetsData: LiquityV2AssetsData = {};
55
+ assetsData[debtToken] = {
56
+ symbol: debtToken,
57
+ address: getAssetInfo(debtToken, network).address,
58
+ price: '1',
59
+ totalSupply: '0',
60
+ totalBorrow: totalMarketBorrow,
61
+ canBeSupplied: false,
62
+ canBeBorrowed: true,
63
+ leftToBorrowGlobal,
64
+ leftToWithdrawGlobal: '0',
65
+ };
66
+ assetsData[collateralToken] = {
67
+ symbol: collateralToken,
68
+ address: getAssetInfo(ethToWeth(collateralToken), network).address,
69
+ price: collPrice,
70
+ totalSupply: totalMarketSupply,
71
+ totalBorrow: '0',
72
+ canBeSupplied: true,
73
+ canBeBorrowed: false,
74
+ leftToBorrowGlobal: '0',
75
+ leftToWithdrawGlobal,
76
+ };
77
+ if (STAKING_ASSETS.includes(collateralToken)) {
78
+ assetsData[collateralToken].incentiveSupplyApy = await getStakingApy(collateralToken, mainnetWeb3);
79
+ assetsData[collateralToken].incentiveSupplyToken = collateralToken;
80
+ }
81
+
82
+ return {
83
+ assetsData,
84
+ marketData: {
85
+ minCollRatio,
86
+ totalCollRatio: new Dec(totalCollRatio).mul(100).toString(),
87
+ criticalCollRatio: new Dec(criticalCollRatio).mul(100).toString(),
88
+ batchCollRatio,
89
+ isUnderCollateralized: new Dec(totalCollRatio).lt(criticalCollRatio),
90
+ hintHelperAddress,
91
+ troveNFTAddress,
92
+ borrowerOperationsAddress,
93
+ troveManagerAddress,
94
+ stabilityPoolAddress,
95
+ collSurplusPoolAddress,
96
+ activePoolAddress,
97
+ },
98
+ };
99
+ };
100
+
101
+ const _getUserTroves = async (viewContract: any, account: EthAddress, marketAddress: EthAddress, startIndex = 0, endIndex = 100) => viewContract.methods.getUserTroves(account, marketAddress, startIndex, endIndex).call();
102
+
103
+ const getUserTroves = async (viewContract: any, account: EthAddress, marketAddress: EthAddress, startIndex = 0, endIndex = 100, troves: LiquityV2View.ExistingTroveStructOutput[] = []): Promise<{ troves: LiquityV2View.ExistingTroveStructOutput[], nextFreeTroveIndex: string }> => {
104
+ const result = await _getUserTroves(viewContract, account, marketAddress, startIndex, endIndex);
105
+ const newStartIndex = endIndex + 1;
106
+ const nextFreeTroveIndex = result.nextFreeTroveIndex;
107
+ const existingTroves = [...troves, ...result.troves];
108
+ if (nextFreeTroveIndex !== '-1') return { troves: existingTroves.filter((trove) => trove.ownedByUser), nextFreeTroveIndex };
109
+ return getUserTroves(viewContract, account, marketAddress, newStartIndex, newStartIndex + 100, existingTroves);
110
+ };
111
+
112
+ const TransferEventSig = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
113
+
114
+ const nftContractCreationBlockMapping = {
115
+ [LiquityV2Versions.LiquityV2Eth]: 22516079,
116
+ [LiquityV2Versions.LiquityV2WstEth]: 22516099,
117
+ [LiquityV2Versions.LiquityV2REth]: 22516118,
118
+ // legacy
119
+ [LiquityV2Versions.LiquityV2EthLegacy]: 21686215,
120
+ [LiquityV2Versions.LiquityV2WstEthLegacy]: 21686238,
121
+ [LiquityV2Versions.LiquityV2REthLegacy]: 21686257,
122
+ };
123
+
124
+ const getTransferredTroves = async (web3: Web3, network: NetworkNumber, troveNFTAddress: EthAddress, limitBlocksForEventFetching: boolean, market: LiquityV2Versions, account: EthAddress): Promise<{ troveId: string }[]> => {
125
+ const nftContract = createContractWrapper(web3, network, 'LiquityV2TroveNFT', troveNFTAddress);
126
+ const nftContractCreationBlock = nftContractCreationBlockMapping[market];
127
+ const currentBlock = await web3.eth.getBlockNumber();
128
+ const events = await nftContract.getPastEvents(
129
+ TransferEventSig,
130
+ {
131
+ fromBlock: limitBlocksForEventFetching ? (currentBlock - 1000) : nftContractCreationBlock,
132
+ },
133
+ );
134
+ const userTransferredTroves = events.filter((event) => compareAddresses(event.returnValues.to, account));
135
+
136
+ // check if the last know transfer address is the user
137
+ userTransferredTroves.forEach((event, index) => {
138
+ const otherTransfers = events.filter((e) => event.blockNumber < e.blockNumber && e.returnValues.tokenId === event.returnValues.tokenId);
139
+ // @ts-ignore
140
+ userTransferredTroves[index].invalid = !!otherTransfers.length;
141
+ });
142
+ // @ts-ignore
143
+ return userTransferredTroves.filter((event) => !event.invalid).map((event) => ({ troveId: event.returnValues.tokenId }));
144
+ };
145
+
146
+ export const getLiquityV2UserTroveIds = async (web3: Web3, network: NetworkNumber, selectedMarket: LiquityV2MarketInfo, troveNFTAddress: EthAddress, limitBlocksForEventFetching: boolean, account: EthAddress): Promise<{ troves: { troveId: string }[], nextFreeTroveIndex: string }> => {
147
+ const viewContract = getLiquityV2ViewContract(web3, network, selectedMarket.isLegacy);
148
+ const [{ troves: userTroves, nextFreeTroveIndex }, userTransferredTroves] = await Promise.all([
149
+ getUserTroves(viewContract, account, selectedMarket.marketAddress),
150
+ getTransferredTroves(web3, network, troveNFTAddress, limitBlocksForEventFetching, selectedMarket.value, account),
151
+ ]);
152
+ const troves = [...userTroves.map(({ troveId }) => ({ troveId })), ...userTransferredTroves];
153
+ const filteredTroves = troves.filter((value, index, self) => index === self.findIndex((t) => (
154
+ t.troveId === value.troveId
155
+ )),
156
+ );
157
+ const troveIds = filteredTroves.map((trove) => trove.troveId);
158
+ const troveIdsSet = new Set(troveIds);
159
+ const troveIdsArray = Array.from(troveIdsSet);
160
+ const trovesNoDuplicates = troveIdsArray.map((troveId) => troves.find((trove) => trove.troveId === troveId)) as { troveId: string }[];
161
+ return { troves: trovesNoDuplicates, nextFreeTroveIndex };
162
+ };
163
+
164
+ const _getDebtInFrontForSingleMarket = async (viewContract: any, marketAddress: EthAddress, troveId: string, accumulatedSum = '0', iterations = 2000) => viewContract.methods.getDebtInFront(marketAddress, troveId, accumulatedSum, iterations).call();
165
+
166
+ export const getDebtInFrontForSingleMarketLiquityV2 = async (viewContract: any, marketAddress: EthAddress, troveId: string, accumulatedSum = '0', iterations = 2000): Promise<string> => {
167
+ const { debt, next } = await _getDebtInFrontForSingleMarket(viewContract, marketAddress, troveId, accumulatedSum, iterations);
168
+ if (next === '0') return assetAmountInEth(debt);
169
+ return getDebtInFrontForSingleMarketLiquityV2(viewContract, marketAddress, next, debt, iterations);
170
+ };
171
+
172
+ const _getDebtInFrontForInterestRateSingleMarketLiquityV2 = async (viewContract: any, marketAddress: EthAddress, troveId = '0', accumulatedSum = '0', iterations = 2000, interestRate: string) => viewContract.methods.getDebtInFrontByInterestRate(marketAddress, troveId, accumulatedSum, iterations, interestRate).call();
173
+
174
+ export const getDebtInFrontForInterestRateSingleMarketLiquityV2 = async (viewContract: any, marketAddress: EthAddress, interestRate: string, troveId = '0', accumulatedSum = '0', iterations = 2000): Promise<string> => {
175
+ if (+interestRate === 0 || !interestRate) return '0';
176
+ const interestRateWei = new Dec(interestRate).times(1e16).toFixed(0).toString();
177
+ const res = await _getDebtInFrontForInterestRateSingleMarketLiquityV2(viewContract, marketAddress, troveId, accumulatedSum, iterations, interestRateWei);
178
+ const { debt, next } = res;
179
+ if (next === '0') return assetAmountInEth(debt);
180
+ return getDebtInFrontForInterestRateSingleMarketLiquityV2(viewContract, marketAddress, interestRate, next, debt, iterations);
181
+ };
182
+
183
+ const getUnbackedDebtForSingleMarket = async (totalBorrowed: string, web3: Web3, network: NetworkNumber, stabilityPoolAddress: EthAddress) => {
184
+ const stabilityPoolContract = createContractWrapper(web3, network, 'LiquityV2StabilityPool', stabilityPoolAddress);
185
+ const totalBoldDeposits = await stabilityPoolContract.methods.getTotalBoldDeposits().call();
186
+ const totalBoldDepositsInEth = assetAmountInEth(totalBoldDeposits);
187
+
188
+ return Dec.max(new Dec(totalBorrowed).sub(totalBoldDepositsInEth), 0).toString();
189
+ };
190
+
191
+ export const getAllMarketsUnbackedDebts = async (markets: Record<LiquityV2Versions, LiquityV2MarketData>, web3: Web3, network: NetworkNumber): Promise<Record<LiquityV2Versions, string>> => {
192
+ const allMarketsUnbackedDebt = await Promise.all(Object.entries(markets).map(async ([version, market]) => {
193
+ const { assetsData, marketData } = market;
194
+ const { debtToken } = LiquityV2Markets(network)[version as LiquityV2Versions];
195
+ const unbackedDebt = await getUnbackedDebtForSingleMarket(assetsData[debtToken].totalBorrow, web3, network, marketData.stabilityPoolAddress);
196
+ return [version, unbackedDebt];
197
+ }));
198
+
199
+ return Object.fromEntries(allMarketsUnbackedDebt) as Record<LiquityV2Versions, string>;
200
+ };
201
+
202
+ export const calculateDebtInFrontLiquityV2 = (markets: Record<LiquityV2Versions, LiquityV2MarketData>, selectedMarket: LiquityV2Versions, allMarketsUnbackedDebts: Record<LiquityV2Versions, string>, interestRateDebtInFront: string): string => {
203
+ const selectedMarketUnbackedDebt = new Dec(allMarketsUnbackedDebts[selectedMarket]);
204
+ if (selectedMarketUnbackedDebt.eq(0)) return 'N/A';
205
+
206
+ const amountBeingReedemedOnEachMarket = Object.entries(markets).map(([version, market]) => {
207
+ if (version === selectedMarket) return new Dec(interestRateDebtInFront);
208
+ const { assetsData } = market;
209
+ const { debtToken } = LiquityV2Markets(NetworkNumber.Eth)[version as LiquityV2Versions];
210
+ const unbackedDebt = new Dec(allMarketsUnbackedDebts[version as LiquityV2Versions]);
211
+ const totalBorrow = new Dec(assetsData[debtToken].totalBorrow);
212
+ const amountToReedem = new Dec(interestRateDebtInFront).mul(unbackedDebt).div(selectedMarketUnbackedDebt);
213
+ return Dec.min(amountToReedem, totalBorrow);
214
+ });
215
+
216
+ return amountBeingReedemedOnEachMarket.reduce((acc, val) => acc.plus(val), new Dec(0)).toString();
217
+ };
218
+
219
+ export const getDebtInFrontLiquityV2 = async (markets: Record<LiquityV2Versions, LiquityV2MarketData>, selectedMarket: LiquityV2Versions, web3: Web3, network: NetworkNumber, viewContract: any, troveId: string) => {
220
+ const allMarketsUnbackedDebts = await getAllMarketsUnbackedDebts(markets, web3, network);
221
+ const interestRateDebtInFront = await getDebtInFrontForSingleMarketLiquityV2(viewContract, LiquityV2Markets(network)[selectedMarket].marketAddress, troveId);
222
+
223
+ return calculateDebtInFrontLiquityV2(markets, selectedMarket, allMarketsUnbackedDebts, interestRateDebtInFront.toString());
224
+ };
225
+
226
+ export const getDebtInFrontForInterestRateLiquityV2 = async (markets: Record<LiquityV2Versions, LiquityV2MarketData>, selectedMarket: LiquityV2Versions, web3: Web3, network: NetworkNumber, viewContract: any, interestRate: string) => {
227
+ const allMarketsUnbackedDebts = await getAllMarketsUnbackedDebts(markets, web3, network);
228
+ const interestRateDebtInFront = new Dec(await getDebtInFrontForInterestRateSingleMarketLiquityV2(viewContract, LiquityV2Markets(network)[selectedMarket].marketAddress, interestRate));
229
+
230
+ return calculateDebtInFrontLiquityV2(markets, selectedMarket, allMarketsUnbackedDebts, interestRateDebtInFront.toString());
231
+ };
232
+
233
+ export const getLiquityV2TroveData = async (
234
+ web3: Web3,
235
+ network: NetworkNumber,
236
+ {
237
+ selectedMarket,
238
+ assetsData,
239
+ troveId,
240
+ allMarketsData,
241
+ }:
242
+ {
243
+ selectedMarket: LiquityV2MarketInfo,
244
+ assetsData: LiquityV2AssetsData,
245
+ troveId: string,
246
+ allMarketsData: Record<LiquityV2Versions, LiquityV2MarketData>,
247
+ },
248
+ ): Promise<LiquityV2TroveData> => {
249
+ const viewContract = getLiquityV2ViewContract(web3, network, selectedMarket.isLegacy);
250
+ const { minCollRatio, batchCollRatio } = allMarketsData[selectedMarket.value].marketData;
251
+ const { collateralToken, marketAddress, debtToken } = selectedMarket;
252
+ const [_data, debtInFront] = await Promise.all([
253
+ viewContract.methods.getTroveInfo(marketAddress, troveId).call(),
254
+ getDebtInFrontLiquityV2(allMarketsData, selectedMarket.value, web3, network, viewContract, troveId),
255
+ ]);
256
+ const data = {
257
+ ..._data,
258
+ TCRatio: _data.TCRatio === MAXUINT ? '0' : _data.TCRatio, // mistake on contract side when debt is 0
259
+ };
260
+ const usedAssets: LiquityV2UsedAssets = {};
261
+
262
+ const debtAssetData = assetsData[debtToken];
263
+ const borrowed = assetAmountInEth(data.debtAmount);
264
+ usedAssets[debtToken] = {
265
+ symbol: debtToken,
266
+ supplied: '0',
267
+ suppliedUsd: '0',
268
+ borrowed,
269
+ borrowedUsd: new Dec(borrowed).mul(debtAssetData.price).toString(),
270
+ isBorrowed: true,
271
+ isSupplied: false,
272
+ };
273
+
274
+ const collAssetData = assetsData[collateralToken];
275
+ const suppliedColl = assetAmountInEth(data.collAmount);
276
+ usedAssets[collateralToken] = {
277
+ symbol: collateralToken,
278
+ supplied: suppliedColl,
279
+ suppliedUsd: new Dec(suppliedColl).mul(collAssetData.price).toString(),
280
+ borrowed: '0',
281
+ borrowedUsd: '0',
282
+ isBorrowed: false,
283
+ isSupplied: true,
284
+ collateral: true,
285
+ };
286
+
287
+ const collRatio = new Dec(data.TCRatio).div(1e16).toString();
288
+ const interestRate = new Dec(data.annualInterestRate).div(1e16).toString();
289
+ const interestBatchManager = data.interestBatchManager;
290
+ const lastInterestRateAdjTime = data.lastInterestRateAdjTime;
291
+
292
+ const hasInterestBatchManager = !compareAddresses(interestBatchManager, ZERO_ADDRESS);
293
+ const liqRatio = hasInterestBatchManager ? new Dec(minCollRatio).add(batchCollRatio).toString() : minCollRatio;
294
+
295
+ const payload: LiquityV2TroveData = {
296
+ usedAssets,
297
+ troveId,
298
+ interestRate,
299
+ interestBatchManager,
300
+ debtInFront,
301
+ lastInterestRateAdjTime,
302
+ liqRatio,
303
+ troveStatus: LIQUITY_V2_TROVE_STATUS_ENUM[parseInt(data.status, 10)],
304
+ ...getLiquityV2AggregatedPositionData({
305
+ usedAssets, assetsData, minCollRatio: liqRatio, interestRate,
306
+ }),
307
+ collRatio,
308
+ };
309
+
310
+ return payload;
311
+ };
312
+
313
+ export const getLiquityV2ClaimableCollateral = async (collSurplusPoolAddress: EthAddress, account: EthAddress, web3: Web3, network: NetworkNumber): Promise<string> => {
314
+ const collSurplusPoolContract = createContractWrapper(web3, network, 'LiquityV2CollSurplusPool', collSurplusPoolAddress);
315
+ const claimableCollateral = await collSurplusPoolContract.methods.getCollateral(account).call();
316
+ return claimableCollateral;
317
+ };