@defisaver/positions-sdk 2.1.144-dev → 2.1.145
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.
- package/cjs/config/contracts.d.ts +249 -0
- package/cjs/config/contracts.js +11 -1
- package/cjs/constants/index.d.ts +1 -0
- package/cjs/constants/index.js +2 -1
- package/cjs/contracts.d.ts +1510 -0
- package/cjs/contracts.js +3 -2
- package/cjs/fluid/index.js +6 -2
- package/cjs/helpers/fluidHelpers/index.js +5 -4
- package/cjs/helpers/index.d.ts +1 -0
- package/cjs/helpers/index.js +2 -1
- package/cjs/helpers/morphoMidnightHelpers/headroom.d.ts +48 -0
- package/cjs/helpers/morphoMidnightHelpers/headroom.js +52 -0
- package/cjs/helpers/morphoMidnightHelpers/index.d.ts +113 -0
- package/cjs/helpers/morphoMidnightHelpers/index.js +298 -0
- package/cjs/helpers/morphoMidnightHelpers/rate.d.ts +15 -0
- package/cjs/helpers/morphoMidnightHelpers/rate.js +62 -0
- package/cjs/helpers/morphoMidnightHelpers/tenor.d.ts +129 -0
- package/cjs/helpers/morphoMidnightHelpers/tenor.js +254 -0
- package/cjs/index.d.ts +2 -1
- package/cjs/index.js +3 -1
- package/cjs/markets/index.d.ts +1 -0
- package/cjs/markets/index.js +6 -1
- package/cjs/markets/morphoMidnight/index.d.ts +60 -0
- package/cjs/markets/morphoMidnight/index.js +659 -0
- package/cjs/morphoMidnight/index.d.ts +14 -0
- package/cjs/morphoMidnight/index.js +261 -0
- package/cjs/portfolio/discovery.js +4 -0
- package/cjs/portfolio/index.js +44 -0
- package/cjs/services/viem.d.ts +11 -11
- package/cjs/staking/eligibility.d.ts +15 -1
- package/cjs/staking/eligibility.js +12 -1
- package/cjs/staking/staking.js +18 -22
- package/cjs/types/index.d.ts +1 -0
- package/cjs/types/index.js +1 -0
- package/cjs/types/morphoMidnight.d.ts +141 -0
- package/cjs/types/morphoMidnight.js +47 -0
- package/cjs/types/portfolio.d.ts +4 -0
- package/esm/config/contracts.d.ts +249 -0
- package/esm/config/contracts.js +9 -0
- package/esm/constants/index.d.ts +1 -0
- package/esm/constants/index.js +1 -0
- package/esm/contracts.d.ts +1510 -0
- package/esm/contracts.js +1 -0
- package/esm/fluid/index.js +6 -2
- package/esm/helpers/fluidHelpers/index.js +6 -5
- package/esm/helpers/index.d.ts +1 -0
- package/esm/helpers/index.js +1 -0
- package/esm/helpers/morphoMidnightHelpers/headroom.d.ts +48 -0
- package/esm/helpers/morphoMidnightHelpers/headroom.js +45 -0
- package/esm/helpers/morphoMidnightHelpers/index.d.ts +113 -0
- package/esm/helpers/morphoMidnightHelpers/index.js +271 -0
- package/esm/helpers/morphoMidnightHelpers/rate.d.ts +15 -0
- package/esm/helpers/morphoMidnightHelpers/rate.js +50 -0
- package/esm/helpers/morphoMidnightHelpers/tenor.d.ts +129 -0
- package/esm/helpers/morphoMidnightHelpers/tenor.js +238 -0
- package/esm/index.d.ts +2 -1
- package/esm/index.js +2 -1
- package/esm/markets/index.d.ts +1 -0
- package/esm/markets/index.js +1 -0
- package/esm/markets/morphoMidnight/index.d.ts +60 -0
- package/esm/markets/morphoMidnight/index.js +631 -0
- package/esm/morphoMidnight/index.d.ts +14 -0
- package/esm/morphoMidnight/index.js +248 -0
- package/esm/portfolio/discovery.js +5 -1
- package/esm/portfolio/index.js +45 -1
- package/esm/services/viem.d.ts +11 -11
- package/esm/staking/eligibility.d.ts +15 -1
- package/esm/staking/eligibility.js +10 -0
- package/esm/staking/staking.js +19 -23
- package/esm/types/index.d.ts +1 -0
- package/esm/types/index.js +1 -0
- package/esm/types/morphoMidnight.d.ts +141 -0
- package/esm/types/morphoMidnight.js +44 -0
- package/esm/types/portfolio.d.ts +4 -0
- package/package.json +1 -1
- package/src/config/contracts.ts +9 -0
- package/src/constants/index.ts +1 -0
- package/src/contracts.ts +1 -0
- package/src/fluid/index.ts +6 -2
- package/src/helpers/fluidHelpers/index.ts +6 -5
- package/src/helpers/index.ts +1 -0
- package/src/helpers/morphoMidnightHelpers/headroom.ts +82 -0
- package/src/helpers/morphoMidnightHelpers/index.ts +485 -0
- package/src/helpers/morphoMidnightHelpers/rate.ts +71 -0
- package/src/helpers/morphoMidnightHelpers/tenor.ts +407 -0
- package/src/index.ts +2 -0
- package/src/markets/index.ts +3 -0
- package/src/markets/morphoMidnight/index.ts +675 -0
- package/src/morphoMidnight/index.ts +276 -0
- package/src/portfolio/discovery.ts +6 -0
- package/src/portfolio/index.ts +44 -0
- package/src/staking/eligibility.ts +29 -2
- package/src/staking/staking.ts +20 -22
- package/src/types/index.ts +1 -0
- package/src/types/morphoMidnight.ts +163 -0
- package/src/types/portfolio.ts +4 -0
package/esm/staking/staking.js
CHANGED
|
@@ -12,7 +12,7 @@ import memoize from 'memoizee';
|
|
|
12
12
|
import { NetworkNumber, } from '../types/common';
|
|
13
13
|
import { BLOCKS_IN_A_YEAR } from '../constants';
|
|
14
14
|
import { DEFAULT_TIMEOUT } from '../services/utils';
|
|
15
|
-
import {
|
|
15
|
+
import { getIncentiveEligibility } from './eligibility';
|
|
16
16
|
const getSsrApy = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
17
|
try {
|
|
18
18
|
const res = yield fetch('https://fe.defisaver.com/api/sky/data', { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
|
|
@@ -194,17 +194,15 @@ export const calculateNetApy = ({ usedAssets, assetsData, optionalData, }) => {
|
|
|
194
194
|
acc.supplyInterest = new Dec(acc.supplyInterest).add(supplyInterest.toString()).toString();
|
|
195
195
|
if (assetData.supplyIncentives) {
|
|
196
196
|
for (const supplyIncentive of assetData.supplyIncentives) {
|
|
197
|
-
const { apy
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
|
|
207
|
-
}
|
|
197
|
+
const { apy } = supplyIncentive;
|
|
198
|
+
const { isEligible, eligibleUSDAmount } = getIncentiveEligibility({
|
|
199
|
+
incentive: supplyIncentive,
|
|
200
|
+
usedAssets,
|
|
201
|
+
defaultEligibleUSDAmount: amount,
|
|
202
|
+
optionalData,
|
|
203
|
+
});
|
|
204
|
+
const incentiveInterest = isEligible ? calculateInterestEarned(eligibleUSDAmount, apy, 'year', true) : '0';
|
|
205
|
+
acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
|
|
208
206
|
}
|
|
209
207
|
}
|
|
210
208
|
}
|
|
@@ -216,17 +214,15 @@ export const calculateNetApy = ({ usedAssets, assetsData, optionalData, }) => {
|
|
|
216
214
|
acc.borrowInterest = new Dec(acc.borrowInterest).sub(borrowInterest.toString()).toString();
|
|
217
215
|
if (assetData.borrowIncentives) {
|
|
218
216
|
for (const borrowIncentive of assetData.borrowIncentives) {
|
|
219
|
-
const { apy
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
|
|
229
|
-
}
|
|
217
|
+
const { apy } = borrowIncentive;
|
|
218
|
+
const { isEligible, eligibleUSDAmount } = getIncentiveEligibility({
|
|
219
|
+
incentive: borrowIncentive,
|
|
220
|
+
usedAssets,
|
|
221
|
+
defaultEligibleUSDAmount: amount,
|
|
222
|
+
optionalData,
|
|
223
|
+
});
|
|
224
|
+
const incentiveInterest = isEligible ? calculateInterestEarned(eligibleUSDAmount, apy, 'year', true) : '0';
|
|
225
|
+
acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
|
|
230
226
|
}
|
|
231
227
|
}
|
|
232
228
|
}
|
package/esm/types/index.d.ts
CHANGED
package/esm/types/index.js
CHANGED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { EthAddress, IncentiveData, LeverageType, MMUsedAssets, NetworkNumber } from './common';
|
|
2
|
+
export declare enum MorphoMidnightVersions {
|
|
3
|
+
MorphoMidnightCbBTCUSDC_860_20260731_Base = "morphomidnightcbbtcusdc_860_20260731_base",
|
|
4
|
+
MorphoMidnightCbBTCUSDC_860_20260828_Base = "morphomidnightcbbtcusdc_860_20260828_base",
|
|
5
|
+
MorphoMidnightCbBTCUSDC_860_20260925_Base = "morphomidnightcbbtcusdc_860_20260925_base",
|
|
6
|
+
MorphoMidnightCbBTCUSDC_860_20261030_Base = "morphomidnightcbbtcusdc_860_20261030_base",
|
|
7
|
+
MorphoMidnightCbBTCUSDC_860_20261127_Base = "morphomidnightcbbtcusdc_860_20261127_base",
|
|
8
|
+
MorphoMidnightCbBTCUSDC_860_20261225_Base = "morphomidnightcbbtcusdc_860_20261225_base",
|
|
9
|
+
MorphoMidnightTenorCbBTCUSDC_20260827_Base = "morphomidnighttenorcbbtcusdc_20260827_base",
|
|
10
|
+
MorphoMidnightTenorCbBTCUSDC_20260924_Base = "morphomidnighttenorcbbtcusdc_20260924_base",
|
|
11
|
+
MorphoMidnightTenorCbBTCUSDC_20261022_Base = "morphomidnighttenorcbbtcusdc_20261022_base",
|
|
12
|
+
MorphoMidnightTenorCbBTCUSDC_20261119_Base = "morphomidnighttenorcbbtcusdc_20261119_base",
|
|
13
|
+
MorphoMidnightTenorCbBTCUSDC_20261217_Base = "morphomidnighttenorcbbtcusdc_20261217_base",
|
|
14
|
+
MorphoMidnightTenorWETHUSDC_20260827_Base = "morphomidnighttenorwethusdc_20260827_base",
|
|
15
|
+
MorphoMidnightTenorWETHUSDC_20260924_Base = "morphomidnighttenorwethusdc_20260924_base",
|
|
16
|
+
MorphoMidnightTenorWETHUSDC_20261022_Base = "morphomidnighttenorwethusdc_20261022_base",
|
|
17
|
+
MorphoMidnightTenorWETHUSDC_20261119_Base = "morphomidnighttenorwethusdc_20261119_base",
|
|
18
|
+
MorphoMidnightTenorWETHUSDC_20261217_Base = "morphomidnighttenorwethusdc_20261217_base",
|
|
19
|
+
MorphoMidnightTenorCbETHWETH_20260827_Base = "morphomidnighttenorcbethweth_20260827_base",
|
|
20
|
+
MorphoMidnightTenorCbETHWETH_20260924_Base = "morphomidnighttenorcbethweth_20260924_base",
|
|
21
|
+
MorphoMidnightTenorCbETHWETH_20261022_Base = "morphomidnighttenorcbethweth_20261022_base",
|
|
22
|
+
MorphoMidnightTenorCbETHWETH_20261119_Base = "morphomidnighttenorcbethweth_20261119_base",
|
|
23
|
+
MorphoMidnightTenorCbETHWETH_20261217_Base = "morphomidnighttenorcbethweth_20261217_base"
|
|
24
|
+
}
|
|
25
|
+
export type MorphoMidnightCurator = 'Morpho' | 'Tenor';
|
|
26
|
+
export interface MorphoMidnightCollateralParams {
|
|
27
|
+
token: EthAddress;
|
|
28
|
+
lltv: number | string;
|
|
29
|
+
liquidationCursor: number | string;
|
|
30
|
+
oracle: EthAddress;
|
|
31
|
+
}
|
|
32
|
+
export interface MorphoMidnightMarketData {
|
|
33
|
+
chainIds: NetworkNumber[];
|
|
34
|
+
label: string;
|
|
35
|
+
shortLabel: string;
|
|
36
|
+
url: string;
|
|
37
|
+
value: MorphoMidnightVersions;
|
|
38
|
+
midnight: EthAddress;
|
|
39
|
+
loanToken: EthAddress;
|
|
40
|
+
collaterals: MorphoMidnightCollateralParams[];
|
|
41
|
+
/**
|
|
42
|
+
* Tenor's curated markets list the curator's own vault share token next to the real collateral.
|
|
43
|
+
*/
|
|
44
|
+
hiddenCollaterals?: MorphoMidnightCollateralParams[];
|
|
45
|
+
maturity: number;
|
|
46
|
+
rcfThreshold: number | string;
|
|
47
|
+
enterGate: EthAddress;
|
|
48
|
+
liquidatorGate: EthAddress;
|
|
49
|
+
marketId: string;
|
|
50
|
+
protocolName: string;
|
|
51
|
+
curator: MorphoMidnightCurator;
|
|
52
|
+
}
|
|
53
|
+
export interface MorphoMidnightAssetData {
|
|
54
|
+
symbol: string;
|
|
55
|
+
address: string;
|
|
56
|
+
price: string;
|
|
57
|
+
supplyRate: string;
|
|
58
|
+
borrowRate: string;
|
|
59
|
+
supplyIncentives: IncentiveData[];
|
|
60
|
+
borrowIncentives: IncentiveData[];
|
|
61
|
+
totalSupply?: string;
|
|
62
|
+
totalBorrow?: string;
|
|
63
|
+
canBeSupplied?: boolean;
|
|
64
|
+
canBeBorrowed?: boolean;
|
|
65
|
+
lltv?: string;
|
|
66
|
+
}
|
|
67
|
+
export type MorphoMidnightAssetsData = {
|
|
68
|
+
[key: string]: MorphoMidnightAssetData;
|
|
69
|
+
};
|
|
70
|
+
export interface MorphoMidnightMarketInfo {
|
|
71
|
+
id: string;
|
|
72
|
+
loanToken: string;
|
|
73
|
+
collaterals: string[];
|
|
74
|
+
maturity: number;
|
|
75
|
+
isMatured: boolean;
|
|
76
|
+
totalUnits: string;
|
|
77
|
+
withdrawable: string;
|
|
78
|
+
totalDebt: string;
|
|
79
|
+
lossFactor: string;
|
|
80
|
+
tickSpacing: number;
|
|
81
|
+
utillization: string;
|
|
82
|
+
assetsData: MorphoMidnightAssetsData;
|
|
83
|
+
}
|
|
84
|
+
export type MorphoMidnightBookSide = 'bids' | 'asks';
|
|
85
|
+
export interface MorphoMidnightBookOffer {
|
|
86
|
+
rate: string;
|
|
87
|
+
liquidity: string;
|
|
88
|
+
}
|
|
89
|
+
export interface MorphoMidnightParsedBook {
|
|
90
|
+
bestRate: string;
|
|
91
|
+
totalLiquidity: string;
|
|
92
|
+
offers: MorphoMidnightBookOffer[];
|
|
93
|
+
}
|
|
94
|
+
export interface MorphoMidnightAggregatedPositionData {
|
|
95
|
+
suppliedUsd: string;
|
|
96
|
+
suppliedCollateralUsd: string;
|
|
97
|
+
borrowedUsd: string;
|
|
98
|
+
borrowLimitUsd: string;
|
|
99
|
+
liquidationLimitUsd: string;
|
|
100
|
+
leftToBorrowUsd: string;
|
|
101
|
+
leftToBorrow: string;
|
|
102
|
+
netApy: string;
|
|
103
|
+
incentiveUsd: string;
|
|
104
|
+
totalInterestUsd: string;
|
|
105
|
+
ltv: string;
|
|
106
|
+
ratio: string;
|
|
107
|
+
healthRatio: string;
|
|
108
|
+
leveragedType: LeverageType;
|
|
109
|
+
leveragedAsset?: string;
|
|
110
|
+
currentVolatilePairRatio?: string;
|
|
111
|
+
liquidationPrice?: string;
|
|
112
|
+
minCollRatio?: string;
|
|
113
|
+
collLiquidationRatio?: string;
|
|
114
|
+
exposure: string;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* How much weight `borrowRate` / `debtBase` / `debtInterest` carry on a given position. They fall back to
|
|
118
|
+
* `'0'` / `debt` / `'0'`, which is indistinguishable from a real 0%-interest position, so anything
|
|
119
|
+
* displaying them has to read this to know whether it is looking at a number or at a placeholder.
|
|
120
|
+
*
|
|
121
|
+
* - `Available` — reported and reconciled against the on-chain debt (also lenders and debt-free positions,
|
|
122
|
+
* which have nothing to report).
|
|
123
|
+
* - `Pending` — the indexer has not caught up with the chain yet: it does not know the position, or its
|
|
124
|
+
* split describes a different debt. Refetching resolves it; `getMorphoMidnightUserBorrowInfo` failing
|
|
125
|
+
* outright lands here too, since the next call may well succeed.
|
|
126
|
+
*/
|
|
127
|
+
export declare enum MorphoMidnightBorrowInfoStatus {
|
|
128
|
+
Available = "available",
|
|
129
|
+
Pending = "pending"
|
|
130
|
+
}
|
|
131
|
+
export interface MorphoMidnightPositionData extends MorphoMidnightAggregatedPositionData {
|
|
132
|
+
usedAssets: MMUsedAssets;
|
|
133
|
+
credit: string;
|
|
134
|
+
debt: string;
|
|
135
|
+
borrowRate: string;
|
|
136
|
+
debtBase: string;
|
|
137
|
+
debtInterest: string;
|
|
138
|
+
borrowInfoStatus: MorphoMidnightBorrowInfoStatus;
|
|
139
|
+
maturity: number;
|
|
140
|
+
isMatured: boolean;
|
|
141
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export var MorphoMidnightVersions;
|
|
2
|
+
(function (MorphoMidnightVersions) {
|
|
3
|
+
// BASE
|
|
4
|
+
// Fixed-term markets are disambiguated by maturity (YYYYMMDD), so the same pair recurs across dates.
|
|
5
|
+
// Sourced from the official listing at https://markets.morpho.org/fixed/base (see sitemap.xml).
|
|
6
|
+
MorphoMidnightVersions["MorphoMidnightCbBTCUSDC_860_20260731_Base"] = "morphomidnightcbbtcusdc_860_20260731_base";
|
|
7
|
+
MorphoMidnightVersions["MorphoMidnightCbBTCUSDC_860_20260828_Base"] = "morphomidnightcbbtcusdc_860_20260828_base";
|
|
8
|
+
MorphoMidnightVersions["MorphoMidnightCbBTCUSDC_860_20260925_Base"] = "morphomidnightcbbtcusdc_860_20260925_base";
|
|
9
|
+
MorphoMidnightVersions["MorphoMidnightCbBTCUSDC_860_20261030_Base"] = "morphomidnightcbbtcusdc_860_20261030_base";
|
|
10
|
+
MorphoMidnightVersions["MorphoMidnightCbBTCUSDC_860_20261127_Base"] = "morphomidnightcbbtcusdc_860_20261127_base";
|
|
11
|
+
MorphoMidnightVersions["MorphoMidnightCbBTCUSDC_860_20261225_Base"] = "morphomidnightcbbtcusdc_860_20261225_base";
|
|
12
|
+
// Tenor-hosted Midnight markets (same core, different order book)
|
|
13
|
+
MorphoMidnightVersions["MorphoMidnightTenorCbBTCUSDC_20260827_Base"] = "morphomidnighttenorcbbtcusdc_20260827_base";
|
|
14
|
+
MorphoMidnightVersions["MorphoMidnightTenorCbBTCUSDC_20260924_Base"] = "morphomidnighttenorcbbtcusdc_20260924_base";
|
|
15
|
+
MorphoMidnightVersions["MorphoMidnightTenorCbBTCUSDC_20261022_Base"] = "morphomidnighttenorcbbtcusdc_20261022_base";
|
|
16
|
+
MorphoMidnightVersions["MorphoMidnightTenorCbBTCUSDC_20261119_Base"] = "morphomidnighttenorcbbtcusdc_20261119_base";
|
|
17
|
+
MorphoMidnightVersions["MorphoMidnightTenorCbBTCUSDC_20261217_Base"] = "morphomidnighttenorcbbtcusdc_20261217_base";
|
|
18
|
+
MorphoMidnightVersions["MorphoMidnightTenorWETHUSDC_20260827_Base"] = "morphomidnighttenorwethusdc_20260827_base";
|
|
19
|
+
MorphoMidnightVersions["MorphoMidnightTenorWETHUSDC_20260924_Base"] = "morphomidnighttenorwethusdc_20260924_base";
|
|
20
|
+
MorphoMidnightVersions["MorphoMidnightTenorWETHUSDC_20261022_Base"] = "morphomidnighttenorwethusdc_20261022_base";
|
|
21
|
+
MorphoMidnightVersions["MorphoMidnightTenorWETHUSDC_20261119_Base"] = "morphomidnighttenorwethusdc_20261119_base";
|
|
22
|
+
MorphoMidnightVersions["MorphoMidnightTenorWETHUSDC_20261217_Base"] = "morphomidnighttenorwethusdc_20261217_base";
|
|
23
|
+
MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20260827_Base"] = "morphomidnighttenorcbethweth_20260827_base";
|
|
24
|
+
MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20260924_Base"] = "morphomidnighttenorcbethweth_20260924_base";
|
|
25
|
+
MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20261022_Base"] = "morphomidnighttenorcbethweth_20261022_base";
|
|
26
|
+
MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20261119_Base"] = "morphomidnighttenorcbethweth_20261119_base";
|
|
27
|
+
MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20261217_Base"] = "morphomidnighttenorcbethweth_20261217_base";
|
|
28
|
+
})(MorphoMidnightVersions || (MorphoMidnightVersions = {}));
|
|
29
|
+
/**
|
|
30
|
+
* How much weight `borrowRate` / `debtBase` / `debtInterest` carry on a given position. They fall back to
|
|
31
|
+
* `'0'` / `debt` / `'0'`, which is indistinguishable from a real 0%-interest position, so anything
|
|
32
|
+
* displaying them has to read this to know whether it is looking at a number or at a placeholder.
|
|
33
|
+
*
|
|
34
|
+
* - `Available` — reported and reconciled against the on-chain debt (also lenders and debt-free positions,
|
|
35
|
+
* which have nothing to report).
|
|
36
|
+
* - `Pending` — the indexer has not caught up with the chain yet: it does not know the position, or its
|
|
37
|
+
* split describes a different debt. Refetching resolves it; `getMorphoMidnightUserBorrowInfo` failing
|
|
38
|
+
* outright lands here too, since the next call may well succeed.
|
|
39
|
+
*/
|
|
40
|
+
export var MorphoMidnightBorrowInfoStatus;
|
|
41
|
+
(function (MorphoMidnightBorrowInfoStatus) {
|
|
42
|
+
MorphoMidnightBorrowInfoStatus["Available"] = "available";
|
|
43
|
+
MorphoMidnightBorrowInfoStatus["Pending"] = "pending";
|
|
44
|
+
})(MorphoMidnightBorrowInfoStatus || (MorphoMidnightBorrowInfoStatus = {}));
|
package/esm/types/portfolio.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { LiquityTroveInfo } from './liquity';
|
|
|
8
8
|
import { LlamaLendUserData, LlamaLendVersionsType } from './llamaLend';
|
|
9
9
|
import { CdpData } from './maker';
|
|
10
10
|
import { MorphoBluePositionData, MorphoBlueVersions } from './morphoBlue';
|
|
11
|
+
import { MorphoMidnightPositionData, MorphoMidnightVersions } from './morphoMidnight';
|
|
11
12
|
import { SparkPositionData, SparkVersions } from './spark';
|
|
12
13
|
export interface PortfolioProtocolData<T> {
|
|
13
14
|
error: string;
|
|
@@ -20,6 +21,9 @@ export interface PortfolioPositionsDataForAddress {
|
|
|
20
21
|
morphoBlue: {
|
|
21
22
|
[key in MorphoBlueVersions]?: PortfolioProtocolData<MorphoBluePositionData>;
|
|
22
23
|
};
|
|
24
|
+
morphoMidnight: {
|
|
25
|
+
[key in MorphoMidnightVersions]?: PortfolioProtocolData<MorphoMidnightPositionData>;
|
|
26
|
+
};
|
|
23
27
|
compoundV3: {
|
|
24
28
|
[key in CompoundVersions]?: PortfolioProtocolData<CompoundV3PositionData>;
|
|
25
29
|
};
|
package/package.json
CHANGED
package/src/config/contracts.ts
CHANGED
|
@@ -1113,6 +1113,15 @@ export const MorphoBlueView = {
|
|
|
1113
1113
|
}
|
|
1114
1114
|
},
|
|
1115
1115
|
} as const;
|
|
1116
|
+
export const MidnightView = {
|
|
1117
|
+
"abi": [{"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"}],"name":"getMarketInfo","outputs":[{"components":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"uint128","name":"totalUnits","type":"uint128"},{"internalType":"uint128","name":"lossFactor","type":"uint128"},{"internalType":"uint128","name":"withdrawable","type":"uint128"},{"internalType":"uint128","name":"continuousFeeCredit","type":"uint128"},{"internalType":"uint16[7]","name":"settlementFees","type":"uint16[7]"},{"internalType":"uint32","name":"continuousFee","type":"uint32"},{"internalType":"uint8","name":"tickSpacing","type":"uint8"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"internalType":"struct MidnightView.MarketInfo","name":"info","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"},{"internalType":"address","name":"_user","type":"address"}],"name":"getPositionInfo","outputs":[{"components":[{"internalType":"uint128","name":"credit","type":"uint128"},{"internalType":"uint128","name":"pendingFee","type":"uint128"},{"internalType":"uint128","name":"debt","type":"uint128"},{"internalType":"uint128","name":"collateralBitmap","type":"uint128"},{"internalType":"uint128[]","name":"collateral","type":"uint128[]"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct MidnightView.PositionInfo","name":"pos","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"},{"internalType":"address","name":"_user","type":"address"}],"name":"getRatio","outputs":[{"internalType":"uint256","name":"ratio","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"midnight","type":"address"},{"internalType":"address","name":"loanToken","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"},{"internalType":"uint256","name":"liquidationCursor","type":"uint256"},{"internalType":"address","name":"oracle","type":"address"}],"internalType":"struct CollateralParams[]","name":"collateralParams","type":"tuple[]"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint256","name":"rcfThreshold","type":"uint256"},{"internalType":"address","name":"enterGate","type":"address"},{"internalType":"address","name":"liquidatorGate","type":"address"}],"internalType":"struct Market","name":"_market","type":"tuple"}],"name":"toId","outputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"}],"name":"toMarket","outputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"midnight","type":"address"},{"internalType":"address","name":"loanToken","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"},{"internalType":"uint256","name":"liquidationCursor","type":"uint256"},{"internalType":"address","name":"oracle","type":"address"}],"internalType":"struct CollateralParams[]","name":"collateralParams","type":"tuple[]"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint256","name":"rcfThreshold","type":"uint256"},{"internalType":"address","name":"enterGate","type":"address"},{"internalType":"address","name":"liquidatorGate","type":"address"}],"internalType":"struct Market","name":"market","type":"tuple"}],"stateMutability":"view","type":"function"}],
|
|
1118
|
+
"networks": {
|
|
1119
|
+
"8453": {
|
|
1120
|
+
"address": "0x3aa272f329E8B562A3bA56Bb6979a44D23A28839",
|
|
1121
|
+
"createdBlock": 48932293,
|
|
1122
|
+
}
|
|
1123
|
+
},
|
|
1124
|
+
} as const;
|
|
1116
1125
|
export const FeedRegistry = {
|
|
1117
1126
|
"abi": [{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"address","name":"aggregator","type":"address"}],"name":"confirmFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"description","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAccessController","outputs":[{"internalType":"contract AccessControllerInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"getAnswer","outputs":[{"internalType":"int256","name":"answer","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"getCurrentPhaseId","outputs":[{"internalType":"uint16","name":"currentPhaseId","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"getFeed","outputs":[{"internalType":"contract AggregatorV2V3Interface","name":"aggregator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"getNextRoundId","outputs":[{"internalType":"uint80","name":"nextRoundId","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint16","name":"phaseId","type":"uint16"}],"name":"getPhase","outputs":[{"components":[{"internalType":"uint16","name":"phaseId","type":"uint16"},{"internalType":"uint80","name":"startingAggregatorRoundId","type":"uint80"},{"internalType":"uint80","name":"endingAggregatorRoundId","type":"uint80"}],"internalType":"struct FeedRegistryInterface.Phase","name":"phase","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint16","name":"phaseId","type":"uint16"}],"name":"getPhaseFeed","outputs":[{"internalType":"contract AggregatorV2V3Interface","name":"aggregator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint16","name":"phaseId","type":"uint16"}],"name":"getPhaseRange","outputs":[{"internalType":"uint80","name":"startingRoundId","type":"uint80"},{"internalType":"uint80","name":"endingRoundId","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"getPreviousRoundId","outputs":[{"internalType":"uint80","name":"previousRoundId","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"getProposedFeed","outputs":[{"internalType":"contract AggregatorV2V3Interface","name":"proposedAggregator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint80","name":"_roundId","type":"uint80"}],"name":"getRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"getRoundFeed","outputs":[{"internalType":"contract AggregatorV2V3Interface","name":"aggregator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"getTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"aggregator","type":"address"}],"name":"isFeedEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"latestAnswer","outputs":[{"internalType":"int256","name":"answer","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"latestRound","outputs":[{"internalType":"uint256","name":"roundId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"latestRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"latestTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"address","name":"aggregator","type":"address"}],"name":"proposeFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"proposedGetRoundData","outputs":[{"internalType":"uint80","name":"id","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"proposedLatestRoundData","outputs":[{"internalType":"uint80","name":"id","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract AccessControllerInterface","name":"_accessController","type":"address"}],"name":"setAccessController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}],
|
|
1118
1127
|
"networks": {
|
package/src/constants/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export const SECONDS_PER_WEEK = 7 * SECONDS_PER_DAY;
|
|
|
6
6
|
export const AVG_BLOCK_TIME = 12;
|
|
7
7
|
export const BLOCKS_IN_A_YEAR = SECONDS_PER_YEAR / AVG_BLOCK_TIME;
|
|
8
8
|
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
9
|
+
export const ZERO_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
|
9
10
|
export const WAD = 1e18;
|
|
10
11
|
export const USD_QUOTE = '0x0000000000000000000000000000000000000348';
|
|
11
12
|
export const borrowOperations = ['borrow', 'payback'];
|
package/src/contracts.ts
CHANGED
|
@@ -122,6 +122,7 @@ export const getYearnV3VaultContractViem = (client: Client, address: HexString)
|
|
|
122
122
|
};
|
|
123
123
|
|
|
124
124
|
export const MorphoBlueViewContractViem = createViemContractFromConfigFunc('MorphoBlueView');
|
|
125
|
+
export const MorphoMidnightViewContractViem = createViemContractFromConfigFunc('MidnightView');
|
|
125
126
|
export const AaveLoanInfoV2ContractViem = createViemContractFromConfigFunc('AaveLoanInfoV2');
|
|
126
127
|
export const AaveV3ViewContractViem = createViemContractFromConfigFunc('AaveV3View');
|
|
127
128
|
export const AaveIncentiveDataProviderV3ContractViem = createViemContractFromConfigFunc('AaveUiIncentiveDataProviderV3');
|
package/src/fluid/index.ts
CHANGED
|
@@ -1689,9 +1689,13 @@ export const getAllUserEarnPositionsWithFTokens = async (
|
|
|
1689
1689
|
export const _getUserPositions = async (provider: PublicClient, network: NetworkNumber, user: EthAddress) => {
|
|
1690
1690
|
const view = FluidViewContractViem(provider, network);
|
|
1691
1691
|
|
|
1692
|
-
const data = await
|
|
1692
|
+
const [data, merklCampaigns] = await Promise.all([
|
|
1693
|
+
view.read.getUserPositions([user]),
|
|
1694
|
+
getFluidMerklCampaigns(network),
|
|
1695
|
+
]);
|
|
1693
1696
|
|
|
1694
|
-
const parsedMarketData = (await Promise.all(data[1].map(async (vaultData) => parseMarketData(provider, vaultData, network))))
|
|
1697
|
+
const parsedMarketData = (await Promise.all(data[1].map(async (vaultData) => parseMarketData(provider, vaultData, network))))
|
|
1698
|
+
.map(marketData => (marketData ? attachFluidMerklIncentives(marketData, merklCampaigns) : marketData));
|
|
1695
1699
|
|
|
1696
1700
|
const userData = data[0].map((position, i) => (parsedMarketData[i] && { ...parseUserData(position, parsedMarketData[i]) }));
|
|
1697
1701
|
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
calcLeverageLiqPrice, getAssetsTotal, getExposure, isLeveragedPos,
|
|
14
14
|
} from '../../moneymarket';
|
|
15
|
-
import { calculateInterestEarned, calculateNetApy,
|
|
15
|
+
import { calculateInterestEarned, calculateNetApy, getIncentiveEligibility } from '../../staking';
|
|
16
16
|
import { IncentiveSide, LeverageType, MMAssetsData } from '../../types/common';
|
|
17
17
|
import { getEthAmountForDecimals } from '../../services/utils';
|
|
18
18
|
|
|
@@ -33,10 +33,11 @@ const getMerklIncentiveInterest = (
|
|
|
33
33
|
principal: string,
|
|
34
34
|
usedAssets: FluidUsedAssets,
|
|
35
35
|
) => incentives.reduce((total, { apy, eligibilityId }) => {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
:
|
|
36
|
+
const { isEligible, eligibleUSDAmount } = getIncentiveEligibility({
|
|
37
|
+
incentive: { eligibilityId },
|
|
38
|
+
usedAssets,
|
|
39
|
+
defaultEligibleUSDAmount: principal,
|
|
40
|
+
});
|
|
40
41
|
const incentiveInterest = isEligible ? calculateInterestEarned(eligibleUSDAmount, apy, 'year', true) : 0;
|
|
41
42
|
|
|
42
43
|
return new Dec(total).add(incentiveInterest).toString();
|
package/src/helpers/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * as sparkHelpers from './sparkHelpers';
|
|
|
4
4
|
export * as curveUsdHelpers from './curveUsdHelpers';
|
|
5
5
|
export * as makerHelpers from './makerHelpers';
|
|
6
6
|
export * as morphoBlueHelpers from './morphoBlueHelpers';
|
|
7
|
+
export * as morphoMidnightHelpers from './morphoMidnightHelpers';
|
|
7
8
|
export * as llamaLendHelpers from './llamaLendHelpers';
|
|
8
9
|
export * as liquityV2Helpers from './liquityV2Helpers';
|
|
9
10
|
export * as fluidHelpers from './fluidHelpers';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import Dec from 'decimal.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Safety margin every Midnight position is held at. On Midnight the lltv is *both* the borrow cap and the
|
|
5
|
+
* liquidation threshold (`liquidationLimitUsd === borrowLimitUsd` in the aggregate), so a position borrowed
|
|
6
|
+
* to its full limit opens at a health ratio of exactly 1 — immediately liquidatable. This is what holds it
|
|
7
|
+
* off that line, and what absorbs swap price impact on a boost.
|
|
8
|
+
*/
|
|
9
|
+
export const MIDNIGHT_MIN_HEALTH_RATIO = 1.025;
|
|
10
|
+
|
|
11
|
+
export interface MidnightBorrowHeadroomParams {
|
|
12
|
+
borrowLimitUsd: Dec.Value;
|
|
13
|
+
borrowedUsd: Dec.Value;
|
|
14
|
+
/** Principal a unit of borrow power is worth (loan-per-unit price, ≤ 1), off a book rate. */
|
|
15
|
+
principalPerUnit?: Dec.Value;
|
|
16
|
+
/** Depth the book can fill, in loan tokens, from the same quote. Omitted leaves the answer uncapped. */
|
|
17
|
+
availableAssets?: Dec.Value;
|
|
18
|
+
loanTokenPrice?: Dec.Value;
|
|
19
|
+
/** Loan token decimals. Omitted skips the quantisation, which only a caller ignoring `leftToBorrow` wants. */
|
|
20
|
+
loanTokenDecimals?: number;
|
|
21
|
+
isMatured?: boolean;
|
|
22
|
+
minHealthRatio?: Dec.Value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface MidnightBorrowHeadroom {
|
|
26
|
+
/** Face-value headroom, before the order-book discount. What a *boost* solve scales itself. */
|
|
27
|
+
headroomUsd: string;
|
|
28
|
+
/** The borrowable amount in loan tokens — the Borrow action's Max, quantised exactly as it is typed in. */
|
|
29
|
+
leftToBorrow: string;
|
|
30
|
+
/** `leftToBorrow · price`. Derived FROM the token amount, never computed alongside it — see below. */
|
|
31
|
+
leftToBorrowUsd: string;
|
|
32
|
+
borrowPowerUsed: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The one answer to "how much more can this position borrow". Every surface that sizes a Midnight borrow
|
|
37
|
+
* derives from here — the max-borrow getter, the position overview's headroom rows, and the max-boost
|
|
38
|
+
* solve, which scales `headroomUsd` by its own leverage factors — so a max and the figure shown next to it
|
|
39
|
+
* cannot drift into two different formulas. They did: the overview used to price
|
|
40
|
+
* the whole headroom off the top-of-book offer (often dust) with no rate pad and no depth cap, while the
|
|
41
|
+
* max walked the book for its real size, which is what put the two 1–2% apart.
|
|
42
|
+
*
|
|
43
|
+
* Three corrections separate this from a plain `limit − debt`, and dropping any one overstates the answer:
|
|
44
|
+
* `minHealthRatio` reserves the margin, `principalPerUnit` turns face-value headroom into the principal a
|
|
45
|
+
* borrow actually receives (a correction that grows with the term, past 7% on a one-year market), and
|
|
46
|
+
* `availableAssets` caps it at what the book can currently fill.
|
|
47
|
+
*
|
|
48
|
+
* `leftToBorrowUsd` is deliberately `leftToBorrow · price` rather than the USD figure the token amount was
|
|
49
|
+
* derived from: the two differ by the round-down to token decimals, and a UI showing one next to an input
|
|
50
|
+
* holding the other would be reporting a difference the user cannot spend. Everything quantises once, here.
|
|
51
|
+
*/
|
|
52
|
+
export const getMorphoMidnightBorrowHeadroom = ({
|
|
53
|
+
borrowLimitUsd,
|
|
54
|
+
borrowedUsd,
|
|
55
|
+
principalPerUnit = 1,
|
|
56
|
+
availableAssets,
|
|
57
|
+
loanTokenPrice = 0,
|
|
58
|
+
loanTokenDecimals,
|
|
59
|
+
isMatured = false,
|
|
60
|
+
minHealthRatio = MIDNIGHT_MIN_HEALTH_RATIO,
|
|
61
|
+
}: MidnightBorrowHeadroomParams): MidnightBorrowHeadroom => {
|
|
62
|
+
const usableLimitUsd = new Dec(borrowLimitUsd).div(minHealthRatio);
|
|
63
|
+
const headroomUsd = isMatured ? new Dec(0) : Dec.max(0, usableLimitUsd.sub(borrowedUsd));
|
|
64
|
+
|
|
65
|
+
const price = new Dec(loanTokenPrice);
|
|
66
|
+
// Capped in loan tokens rather than in USD: `availableAssets` is quoted in tokens, and converting it to
|
|
67
|
+
// USD only to divide the result back out again would re-round the one number that has to land exactly.
|
|
68
|
+
const payout = price.lte(0) ? new Dec(0) : headroomUsd.mul(principalPerUnit).div(price);
|
|
69
|
+
const capped = availableAssets === undefined ? payout : Dec.min(payout, availableAssets);
|
|
70
|
+
// Round down: rounding the last decimal up would eat into the margin we just reserved.
|
|
71
|
+
const leftToBorrow = loanTokenDecimals === undefined ? capped : capped.toDP(loanTokenDecimals, Dec.ROUND_DOWN);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
headroomUsd: headroomUsd.toString(),
|
|
75
|
+
leftToBorrow: leftToBorrow.toString(),
|
|
76
|
+
leftToBorrowUsd: leftToBorrow.mul(price).toString(),
|
|
77
|
+
// Measured against the margined limit rather than the raw one, so it reads 100% at max borrow instead
|
|
78
|
+
// of 97.56%, and can exceed 100% inside the reserved band. Deliberately left uncapped by book depth:
|
|
79
|
+
// how much a position has borrowed is a property of the position, not of what the book can fill today.
|
|
80
|
+
borrowPowerUsed: usableLimitUsd.lte(0) ? '0' : new Dec(borrowedUsd).div(usableLimitUsd).mul(100).toString(),
|
|
81
|
+
};
|
|
82
|
+
};
|