@defisaver/positions-sdk 2.1.127-midnight-11-dev → 2.1.127-midnight-13-dev
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/helpers/morphoMidnightHelpers/index.d.ts +19 -0
- package/cjs/helpers/morphoMidnightHelpers/index.js +36 -1
- package/cjs/morphoMidnight/index.js +31 -13
- package/cjs/types/morphoMidnight.d.ts +19 -0
- package/cjs/types/morphoMidnight.js +20 -1
- package/esm/helpers/morphoMidnightHelpers/index.d.ts +19 -0
- package/esm/helpers/morphoMidnightHelpers/index.js +33 -0
- package/esm/morphoMidnight/index.js +32 -14
- package/esm/types/morphoMidnight.d.ts +19 -0
- package/esm/types/morphoMidnight.js +19 -0
- package/package.json +1 -1
- package/src/helpers/morphoMidnightHelpers/index.ts +42 -0
- package/src/morphoMidnight/index.ts +31 -16
- package/src/types/morphoMidnight.ts +20 -3
|
@@ -64,6 +64,7 @@ export interface MorphoMidnightPaybackUnitsQuote {
|
|
|
64
64
|
* Rounded **down**, since a wider slippage is a looser cap than the caller asked for.
|
|
65
65
|
*/
|
|
66
66
|
export declare const midnightSlippageParam: (slippagePercent: Dec.Value) => string;
|
|
67
|
+
export declare const morphoMidnightMarketReportsBorrowInfo: (market: Pick<MorphoMidnightMarketData, "curator"> | string) => boolean;
|
|
67
68
|
/**
|
|
68
69
|
* Current borrower rate + debt breakdown from the Midnight positions API. Reconstructing this from the raw
|
|
69
70
|
* `/transactions` fill history only sums `borrow` fills, so it overstates debt for any position with an
|
|
@@ -74,6 +75,24 @@ export declare const midnightSlippageParam: (slippagePercent: Dec.Value) => stri
|
|
|
74
75
|
* The caller swallows errors — a missing rate must never block position rendering.
|
|
75
76
|
*/
|
|
76
77
|
export declare const getMorphoMidnightUserBorrowInfo: (account: string, marketId: string, loanTokenSymbol: string) => Promise<MorphoMidnightBorrowInfo>;
|
|
78
|
+
/**
|
|
79
|
+
* The inverse of the split above: where `getMorphoMidnightUserBorrowInfo` reads a position's principal and
|
|
80
|
+
* interest off the indexer, this carries that same split forward through a payback, from a debt of
|
|
81
|
+
* `borrowedBefore` down to one of `borrowedAfter`. Retiring units retires principal and interest pro rata,
|
|
82
|
+
* so both scale by the same fraction.
|
|
83
|
+
*
|
|
84
|
+
* Deriving the interest as `borrowedAfter − debtBase` instead reads it off two different sources —
|
|
85
|
+
* `borrowed` is the chain's debt, `debtBase` the indexer's principal — so the entire gap between them lands
|
|
86
|
+
* in the interest whenever they disagree, which they do until the indexer catches up with a fresh borrow or
|
|
87
|
+
* payback: behind a borrow it inflates the interest by the debt the indexer has not seen yet, behind a
|
|
88
|
+
* payback it goes negative on a position that still owes. Callers detect that window as
|
|
89
|
+
* `debtBase + debtInterest !== debt`.
|
|
90
|
+
*
|
|
91
|
+
* Pro rata is also what keeps the answer sane when a payback retires debt at a different rate than the one
|
|
92
|
+
* it was opened at. Mirroring a borrow instead — principal growing by the assets received, interest by the
|
|
93
|
+
* rest — hands back a negative interest as soon as the book sells units back cheaper than they were bought.
|
|
94
|
+
*/
|
|
95
|
+
export declare const scaleMorphoMidnightDebtSplit: ({ debtBase, debtInterest }: Pick<MorphoMidnightBorrowInfo, "debtBase" | "debtInterest">, borrowedBefore: Dec.Value, borrowedAfter: Dec.Value) => Pick<MorphoMidnightBorrowInfo, "debtBase" | "debtInterest">;
|
|
77
96
|
/**
|
|
78
97
|
* One side of a market's resting order book, as rates rather than the API's WAD-scaled loan-per-unit
|
|
79
98
|
* prices. Annualizing each price against time-to-maturity gives the rate a taker filling that offer gets
|
|
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.getMorphoMidnightPaybackUnitsQuote = exports.getMorphoMidnightPaybackQuote = exports.getMorphoMidnightBorrowQuote = exports.getMorphoMidnightMarketBook = exports.getMorphoMidnightUserBorrowInfo = exports.midnightSlippageParam = exports.getMorphoMidnightAggregatedPositionData = exports.tenorOfferToApiOffer = exports.tenorOfferFillToApiFill = exports.tenorBookRateToApyPercent = exports.tenorBookKeyFor = exports.MIDNIGHT_DEFAULT_RATE_SLIPPAGE = exports.midnightTimeToMaturityDays = exports.midnightPriceFromApy = exports.midnightBoundPrice = exports.midnightBookBestFirst = exports.midnightApyFromPrice = exports.buildMidnightParsedBook = void 0;
|
|
15
|
+
exports.getMorphoMidnightPaybackUnitsQuote = exports.getMorphoMidnightPaybackQuote = exports.getMorphoMidnightBorrowQuote = exports.getMorphoMidnightMarketBook = exports.scaleMorphoMidnightDebtSplit = exports.getMorphoMidnightUserBorrowInfo = exports.morphoMidnightMarketReportsBorrowInfo = exports.midnightSlippageParam = exports.getMorphoMidnightAggregatedPositionData = exports.tenorOfferToApiOffer = exports.tenorOfferFillToApiFill = exports.tenorBookRateToApyPercent = exports.tenorBookKeyFor = exports.MIDNIGHT_DEFAULT_RATE_SLIPPAGE = exports.midnightTimeToMaturityDays = exports.midnightPriceFromApy = exports.midnightBoundPrice = exports.midnightBookBestFirst = exports.midnightApyFromPrice = exports.buildMidnightParsedBook = void 0;
|
|
16
16
|
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
17
17
|
const tokens_1 = require("@defisaver/tokens");
|
|
18
18
|
const moneymarket_1 = require("../../moneymarket");
|
|
@@ -54,6 +54,12 @@ const getMorphoMidnightAggregatedPositionData = ({ usedAssets, assetsData, marke
|
|
|
54
54
|
// borrowLimit = Σ collateralUsd_i * lltv_i (per-collateral lltv carried on assetsData)
|
|
55
55
|
payload.borrowLimitUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral, ({ symbol, suppliedUsd }) => { var _a; return new decimal_js_1.default(suppliedUsd).mul(((_a = assetsData[symbol]) === null || _a === void 0 ? void 0 : _a.lltv) || 0); });
|
|
56
56
|
payload.liquidationLimitUsd = payload.borrowLimitUsd;
|
|
57
|
+
// Same subtraction every other money market uses, but it does NOT mean the same thing here. Elsewhere
|
|
58
|
+
// `borrowedUsd` is debt at present value, so the remainder is what a borrow would pay out. Midnight
|
|
59
|
+
// records debt at its face value at maturity, so this is face-value headroom: borrowing it would add
|
|
60
|
+
// more debt than the number says, by the market's discount. Anything surfacing this as "what you can
|
|
61
|
+
// borrow" has to scale it by the loan-per-unit price first (`midnightPriceFromApy` off a book rate) —
|
|
62
|
+
// a correction that grows with the term, past 7% on a one-year market.
|
|
57
63
|
const leftToBorrowUsd = new decimal_js_1.default(payload.borrowLimitUsd).sub(payload.borrowedUsd);
|
|
58
64
|
payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
|
|
59
65
|
const loanTokenPrice = ((_a = assetsData[marketInfo.loanToken]) === null || _a === void 0 ? void 0 : _a.price) || '0';
|
|
@@ -117,6 +123,8 @@ const MIDNIGHT_SLIPPAGE_MAX = 100;
|
|
|
117
123
|
*/
|
|
118
124
|
const midnightSlippageParam = (slippagePercent) => decimal_js_1.default.min(decimal_js_1.default.max(new decimal_js_1.default(slippagePercent), MIDNIGHT_SLIPPAGE_MIN), MIDNIGHT_SLIPPAGE_MAX).toDP(1, decimal_js_1.default.ROUND_DOWN).toString();
|
|
119
125
|
exports.midnightSlippageParam = midnightSlippageParam;
|
|
126
|
+
const morphoMidnightMarketReportsBorrowInfo = (market) => (!(0, morphoMidnight_1.isTenorMidnightMarket)(market));
|
|
127
|
+
exports.morphoMidnightMarketReportsBorrowInfo = morphoMidnightMarketReportsBorrowInfo;
|
|
120
128
|
/**
|
|
121
129
|
* Current borrower rate + debt breakdown from the Midnight positions API. Reconstructing this from the raw
|
|
122
130
|
* `/transactions` fill history only sums `borrow` fills, so it overstates debt for any position with an
|
|
@@ -140,6 +148,33 @@ const getMorphoMidnightUserBorrowInfo = (account, marketId, loanTokenSymbol) =>
|
|
|
140
148
|
};
|
|
141
149
|
});
|
|
142
150
|
exports.getMorphoMidnightUserBorrowInfo = getMorphoMidnightUserBorrowInfo;
|
|
151
|
+
/**
|
|
152
|
+
* The inverse of the split above: where `getMorphoMidnightUserBorrowInfo` reads a position's principal and
|
|
153
|
+
* interest off the indexer, this carries that same split forward through a payback, from a debt of
|
|
154
|
+
* `borrowedBefore` down to one of `borrowedAfter`. Retiring units retires principal and interest pro rata,
|
|
155
|
+
* so both scale by the same fraction.
|
|
156
|
+
*
|
|
157
|
+
* Deriving the interest as `borrowedAfter − debtBase` instead reads it off two different sources —
|
|
158
|
+
* `borrowed` is the chain's debt, `debtBase` the indexer's principal — so the entire gap between them lands
|
|
159
|
+
* in the interest whenever they disagree, which they do until the indexer catches up with a fresh borrow or
|
|
160
|
+
* payback: behind a borrow it inflates the interest by the debt the indexer has not seen yet, behind a
|
|
161
|
+
* payback it goes negative on a position that still owes. Callers detect that window as
|
|
162
|
+
* `debtBase + debtInterest !== debt`.
|
|
163
|
+
*
|
|
164
|
+
* Pro rata is also what keeps the answer sane when a payback retires debt at a different rate than the one
|
|
165
|
+
* it was opened at. Mirroring a borrow instead — principal growing by the assets received, interest by the
|
|
166
|
+
* rest — hands back a negative interest as soon as the book sells units back cheaper than they were bought.
|
|
167
|
+
*/
|
|
168
|
+
const scaleMorphoMidnightDebtSplit = ({ debtBase, debtInterest }, borrowedBefore, borrowedAfter) => {
|
|
169
|
+
const fractionRemaining = new decimal_js_1.default(borrowedBefore).lte(0)
|
|
170
|
+
? new decimal_js_1.default(0)
|
|
171
|
+
: decimal_js_1.default.max(0, new decimal_js_1.default(borrowedAfter)).div(borrowedBefore);
|
|
172
|
+
return {
|
|
173
|
+
debtBase: new decimal_js_1.default(debtBase).mul(fractionRemaining).toString(),
|
|
174
|
+
debtInterest: new decimal_js_1.default(debtInterest).mul(fractionRemaining).toString(),
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
exports.scaleMorphoMidnightDebtSplit = scaleMorphoMidnightDebtSplit;
|
|
143
178
|
/**
|
|
144
179
|
* One side of a market's resting order book, as rates rather than the API's WAD-scaled loan-per-unit
|
|
145
180
|
* prices. Annualizing each price against time-to-maturity gives the rate a taker filling that offer gets
|
|
@@ -22,6 +22,7 @@ const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
|
22
22
|
const tokens_1 = require("@defisaver/tokens");
|
|
23
23
|
const common_1 = require("../types/common");
|
|
24
24
|
const contracts_1 = require("../contracts");
|
|
25
|
+
const types_1 = require("../types");
|
|
25
26
|
const constants_1 = require("../constants");
|
|
26
27
|
const staking_1 = require("../staking");
|
|
27
28
|
const utils_1 = require("../services/utils");
|
|
@@ -165,21 +166,37 @@ function _getMorphoMidnightAccountData(provider, network, account, selectedMarke
|
|
|
165
166
|
let borrowRate = '0';
|
|
166
167
|
let debtBase = debt; // fallback: treat the full on-chain debt as principal until fill history is known
|
|
167
168
|
let debtInterest = '0';
|
|
169
|
+
let borrowInfoStatus = types_1.MorphoMidnightBorrowInfoStatus.Available;
|
|
168
170
|
let assetsDataForApy = marketInfo.assetsData;
|
|
169
171
|
if (new decimal_js_1.default(positionInfo.debt.toString()).gt(0)) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (new decimal_js_1.default(borrowInfo.debtTotal).gt(0)) {
|
|
173
|
-
borrowRate = borrowInfo.borrowRate;
|
|
174
|
-
debtBase = borrowInfo.debtBase;
|
|
175
|
-
debtInterest = borrowInfo.debtInterest;
|
|
176
|
-
usedAssets[marketInfo.loanToken].borrowRate = borrowRate;
|
|
177
|
-
// Reflect the real borrow cost in netApy without mutating the shared marketInfo.assetsData.
|
|
178
|
-
assetsDataForApy = Object.assign(Object.assign({}, marketInfo.assetsData), { [marketInfo.loanToken]: Object.assign(Object.assign({}, loanTokenData), { borrowRate }) });
|
|
179
|
-
}
|
|
172
|
+
if (!(0, morphoMidnightHelpers_1.morphoMidnightMarketReportsBorrowInfo)(selectedMarket)) {
|
|
173
|
+
borrowInfoStatus = types_1.MorphoMidnightBorrowInfoStatus.Unavailable;
|
|
180
174
|
}
|
|
181
|
-
|
|
182
|
-
|
|
175
|
+
else {
|
|
176
|
+
try {
|
|
177
|
+
const borrowInfo = yield (0, morphoMidnightHelpers_1.getMorphoMidnightUserBorrowInfo)(account, marketId, marketInfo.loanToken);
|
|
178
|
+
if (new decimal_js_1.default(borrowInfo.debtTotal).gt(0)) {
|
|
179
|
+
borrowRate = borrowInfo.borrowRate;
|
|
180
|
+
debtBase = borrowInfo.debtBase;
|
|
181
|
+
debtInterest = borrowInfo.debtInterest;
|
|
182
|
+
usedAssets[marketInfo.loanToken].borrowRate = borrowRate;
|
|
183
|
+
// Reflect the real borrow cost in netApy without mutating the shared marketInfo.assetsData.
|
|
184
|
+
assetsDataForApy = Object.assign(Object.assign({}, marketInfo.assetsData), { [marketInfo.loanToken]: Object.assign(Object.assign({}, loanTokenData), { borrowRate }) });
|
|
185
|
+
const describesChainDebt = new decimal_js_1.default(borrowInfo.debtTotal).eq(debt);
|
|
186
|
+
const ratePriced = new decimal_js_1.default(borrowRate).gt(0) || new decimal_js_1.default(debtInterest).lte(0);
|
|
187
|
+
borrowInfoStatus = describesChainDebt && ratePriced
|
|
188
|
+
? types_1.MorphoMidnightBorrowInfoStatus.Available
|
|
189
|
+
: types_1.MorphoMidnightBorrowInfoStatus.Pending;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
// No entry for the position: the API does not know it yet, rather than it having no principal.
|
|
193
|
+
borrowInfoStatus = types_1.MorphoMidnightBorrowInfoStatus.Pending;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
// Positions API unreachable — the fallback above still renders, and the next call may succeed.
|
|
198
|
+
borrowInfoStatus = types_1.MorphoMidnightBorrowInfoStatus.Pending;
|
|
199
|
+
}
|
|
183
200
|
}
|
|
184
201
|
}
|
|
185
202
|
return Object.assign({ usedAssets,
|
|
@@ -187,7 +204,8 @@ function _getMorphoMidnightAccountData(provider, network, account, selectedMarke
|
|
|
187
204
|
debt,
|
|
188
205
|
borrowRate,
|
|
189
206
|
debtBase,
|
|
190
|
-
debtInterest,
|
|
207
|
+
debtInterest,
|
|
208
|
+
borrowInfoStatus, maturity: marketInfo.maturity, isMatured: marketInfo.isMatured }, (0, morphoMidnightHelpers_1.getMorphoMidnightAggregatedPositionData)({ usedAssets, assetsData: assetsDataForApy, marketInfo }));
|
|
191
209
|
});
|
|
192
210
|
}
|
|
193
211
|
function getMorphoMidnightAccountData(provider, network, account, selectedMarket, marketInfo) {
|
|
@@ -113,6 +113,24 @@ export interface MorphoMidnightAggregatedPositionData {
|
|
|
113
113
|
collLiquidationRatio?: string;
|
|
114
114
|
exposure: string;
|
|
115
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
|
+
* - `Unavailable` — no source covers this market, so refetching changes nothing. See
|
|
127
|
+
* `morphoMidnightMarketReportsBorrowInfo`.
|
|
128
|
+
*/
|
|
129
|
+
export declare enum MorphoMidnightBorrowInfoStatus {
|
|
130
|
+
Available = "available",
|
|
131
|
+
Pending = "pending",
|
|
132
|
+
Unavailable = "unavailable"
|
|
133
|
+
}
|
|
116
134
|
export interface MorphoMidnightPositionData extends MorphoMidnightAggregatedPositionData {
|
|
117
135
|
usedAssets: MMUsedAssets;
|
|
118
136
|
credit: string;
|
|
@@ -120,6 +138,7 @@ export interface MorphoMidnightPositionData extends MorphoMidnightAggregatedPosi
|
|
|
120
138
|
borrowRate: string;
|
|
121
139
|
debtBase: string;
|
|
122
140
|
debtInterest: string;
|
|
141
|
+
borrowInfoStatus: MorphoMidnightBorrowInfoStatus;
|
|
123
142
|
maturity: number;
|
|
124
143
|
isMatured: boolean;
|
|
125
144
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MorphoMidnightVersions = void 0;
|
|
3
|
+
exports.MorphoMidnightBorrowInfoStatus = exports.MorphoMidnightVersions = void 0;
|
|
4
4
|
var MorphoMidnightVersions;
|
|
5
5
|
(function (MorphoMidnightVersions) {
|
|
6
6
|
// BASE
|
|
@@ -29,3 +29,22 @@ var MorphoMidnightVersions;
|
|
|
29
29
|
MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20261119_Base"] = "morphomidnighttenorcbethweth_20261119_base";
|
|
30
30
|
MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20261217_Base"] = "morphomidnighttenorcbethweth_20261217_base";
|
|
31
31
|
})(MorphoMidnightVersions || (exports.MorphoMidnightVersions = MorphoMidnightVersions = {}));
|
|
32
|
+
/**
|
|
33
|
+
* How much weight `borrowRate` / `debtBase` / `debtInterest` carry on a given position. They fall back to
|
|
34
|
+
* `'0'` / `debt` / `'0'`, which is indistinguishable from a real 0%-interest position, so anything
|
|
35
|
+
* displaying them has to read this to know whether it is looking at a number or at a placeholder.
|
|
36
|
+
*
|
|
37
|
+
* - `Available` — reported and reconciled against the on-chain debt (also lenders and debt-free positions,
|
|
38
|
+
* which have nothing to report).
|
|
39
|
+
* - `Pending` — the indexer has not caught up with the chain yet: it does not know the position, or its
|
|
40
|
+
* split describes a different debt. Refetching resolves it; `getMorphoMidnightUserBorrowInfo` failing
|
|
41
|
+
* outright lands here too, since the next call may well succeed.
|
|
42
|
+
* - `Unavailable` — no source covers this market, so refetching changes nothing. See
|
|
43
|
+
* `morphoMidnightMarketReportsBorrowInfo`.
|
|
44
|
+
*/
|
|
45
|
+
var MorphoMidnightBorrowInfoStatus;
|
|
46
|
+
(function (MorphoMidnightBorrowInfoStatus) {
|
|
47
|
+
MorphoMidnightBorrowInfoStatus["Available"] = "available";
|
|
48
|
+
MorphoMidnightBorrowInfoStatus["Pending"] = "pending";
|
|
49
|
+
MorphoMidnightBorrowInfoStatus["Unavailable"] = "unavailable";
|
|
50
|
+
})(MorphoMidnightBorrowInfoStatus || (exports.MorphoMidnightBorrowInfoStatus = MorphoMidnightBorrowInfoStatus = {}));
|
|
@@ -64,6 +64,7 @@ export interface MorphoMidnightPaybackUnitsQuote {
|
|
|
64
64
|
* Rounded **down**, since a wider slippage is a looser cap than the caller asked for.
|
|
65
65
|
*/
|
|
66
66
|
export declare const midnightSlippageParam: (slippagePercent: Dec.Value) => string;
|
|
67
|
+
export declare const morphoMidnightMarketReportsBorrowInfo: (market: Pick<MorphoMidnightMarketData, "curator"> | string) => boolean;
|
|
67
68
|
/**
|
|
68
69
|
* Current borrower rate + debt breakdown from the Midnight positions API. Reconstructing this from the raw
|
|
69
70
|
* `/transactions` fill history only sums `borrow` fills, so it overstates debt for any position with an
|
|
@@ -74,6 +75,24 @@ export declare const midnightSlippageParam: (slippagePercent: Dec.Value) => stri
|
|
|
74
75
|
* The caller swallows errors — a missing rate must never block position rendering.
|
|
75
76
|
*/
|
|
76
77
|
export declare const getMorphoMidnightUserBorrowInfo: (account: string, marketId: string, loanTokenSymbol: string) => Promise<MorphoMidnightBorrowInfo>;
|
|
78
|
+
/**
|
|
79
|
+
* The inverse of the split above: where `getMorphoMidnightUserBorrowInfo` reads a position's principal and
|
|
80
|
+
* interest off the indexer, this carries that same split forward through a payback, from a debt of
|
|
81
|
+
* `borrowedBefore` down to one of `borrowedAfter`. Retiring units retires principal and interest pro rata,
|
|
82
|
+
* so both scale by the same fraction.
|
|
83
|
+
*
|
|
84
|
+
* Deriving the interest as `borrowedAfter − debtBase` instead reads it off two different sources —
|
|
85
|
+
* `borrowed` is the chain's debt, `debtBase` the indexer's principal — so the entire gap between them lands
|
|
86
|
+
* in the interest whenever they disagree, which they do until the indexer catches up with a fresh borrow or
|
|
87
|
+
* payback: behind a borrow it inflates the interest by the debt the indexer has not seen yet, behind a
|
|
88
|
+
* payback it goes negative on a position that still owes. Callers detect that window as
|
|
89
|
+
* `debtBase + debtInterest !== debt`.
|
|
90
|
+
*
|
|
91
|
+
* Pro rata is also what keeps the answer sane when a payback retires debt at a different rate than the one
|
|
92
|
+
* it was opened at. Mirroring a borrow instead — principal growing by the assets received, interest by the
|
|
93
|
+
* rest — hands back a negative interest as soon as the book sells units back cheaper than they were bought.
|
|
94
|
+
*/
|
|
95
|
+
export declare const scaleMorphoMidnightDebtSplit: ({ debtBase, debtInterest }: Pick<MorphoMidnightBorrowInfo, "debtBase" | "debtInterest">, borrowedBefore: Dec.Value, borrowedAfter: Dec.Value) => Pick<MorphoMidnightBorrowInfo, "debtBase" | "debtInterest">;
|
|
77
96
|
/**
|
|
78
97
|
* One side of a market's resting order book, as rates rather than the API's WAD-scaled loan-per-unit
|
|
79
98
|
* prices. Annualizing each price against time-to-maturity gives the rate a taker filling that offer gets
|
|
@@ -37,6 +37,12 @@ export const getMorphoMidnightAggregatedPositionData = ({ usedAssets, assetsData
|
|
|
37
37
|
// borrowLimit = Σ collateralUsd_i * lltv_i (per-collateral lltv carried on assetsData)
|
|
38
38
|
payload.borrowLimitUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral, ({ symbol, suppliedUsd }) => { var _a; return new Dec(suppliedUsd).mul(((_a = assetsData[symbol]) === null || _a === void 0 ? void 0 : _a.lltv) || 0); });
|
|
39
39
|
payload.liquidationLimitUsd = payload.borrowLimitUsd;
|
|
40
|
+
// Same subtraction every other money market uses, but it does NOT mean the same thing here. Elsewhere
|
|
41
|
+
// `borrowedUsd` is debt at present value, so the remainder is what a borrow would pay out. Midnight
|
|
42
|
+
// records debt at its face value at maturity, so this is face-value headroom: borrowing it would add
|
|
43
|
+
// more debt than the number says, by the market's discount. Anything surfacing this as "what you can
|
|
44
|
+
// borrow" has to scale it by the loan-per-unit price first (`midnightPriceFromApy` off a book rate) —
|
|
45
|
+
// a correction that grows with the term, past 7% on a one-year market.
|
|
40
46
|
const leftToBorrowUsd = new Dec(payload.borrowLimitUsd).sub(payload.borrowedUsd);
|
|
41
47
|
payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
|
|
42
48
|
const loanTokenPrice = ((_a = assetsData[marketInfo.loanToken]) === null || _a === void 0 ? void 0 : _a.price) || '0';
|
|
@@ -98,6 +104,7 @@ const MIDNIGHT_SLIPPAGE_MAX = 100;
|
|
|
98
104
|
* Rounded **down**, since a wider slippage is a looser cap than the caller asked for.
|
|
99
105
|
*/
|
|
100
106
|
export const midnightSlippageParam = (slippagePercent) => Dec.min(Dec.max(new Dec(slippagePercent), MIDNIGHT_SLIPPAGE_MIN), MIDNIGHT_SLIPPAGE_MAX).toDP(1, Dec.ROUND_DOWN).toString();
|
|
107
|
+
export const morphoMidnightMarketReportsBorrowInfo = (market) => (!isTenorMidnightMarket(market));
|
|
101
108
|
/**
|
|
102
109
|
* Current borrower rate + debt breakdown from the Midnight positions API. Reconstructing this from the raw
|
|
103
110
|
* `/transactions` fill history only sums `borrow` fills, so it overstates debt for any position with an
|
|
@@ -120,6 +127,32 @@ export const getMorphoMidnightUserBorrowInfo = (account, marketId, loanTokenSymb
|
|
|
120
127
|
borrowRate, debtBase, debtInterest, debtTotal,
|
|
121
128
|
};
|
|
122
129
|
});
|
|
130
|
+
/**
|
|
131
|
+
* The inverse of the split above: where `getMorphoMidnightUserBorrowInfo` reads a position's principal and
|
|
132
|
+
* interest off the indexer, this carries that same split forward through a payback, from a debt of
|
|
133
|
+
* `borrowedBefore` down to one of `borrowedAfter`. Retiring units retires principal and interest pro rata,
|
|
134
|
+
* so both scale by the same fraction.
|
|
135
|
+
*
|
|
136
|
+
* Deriving the interest as `borrowedAfter − debtBase` instead reads it off two different sources —
|
|
137
|
+
* `borrowed` is the chain's debt, `debtBase` the indexer's principal — so the entire gap between them lands
|
|
138
|
+
* in the interest whenever they disagree, which they do until the indexer catches up with a fresh borrow or
|
|
139
|
+
* payback: behind a borrow it inflates the interest by the debt the indexer has not seen yet, behind a
|
|
140
|
+
* payback it goes negative on a position that still owes. Callers detect that window as
|
|
141
|
+
* `debtBase + debtInterest !== debt`.
|
|
142
|
+
*
|
|
143
|
+
* Pro rata is also what keeps the answer sane when a payback retires debt at a different rate than the one
|
|
144
|
+
* it was opened at. Mirroring a borrow instead — principal growing by the assets received, interest by the
|
|
145
|
+
* rest — hands back a negative interest as soon as the book sells units back cheaper than they were bought.
|
|
146
|
+
*/
|
|
147
|
+
export const scaleMorphoMidnightDebtSplit = ({ debtBase, debtInterest }, borrowedBefore, borrowedAfter) => {
|
|
148
|
+
const fractionRemaining = new Dec(borrowedBefore).lte(0)
|
|
149
|
+
? new Dec(0)
|
|
150
|
+
: Dec.max(0, new Dec(borrowedAfter)).div(borrowedBefore);
|
|
151
|
+
return {
|
|
152
|
+
debtBase: new Dec(debtBase).mul(fractionRemaining).toString(),
|
|
153
|
+
debtInterest: new Dec(debtInterest).mul(fractionRemaining).toString(),
|
|
154
|
+
};
|
|
155
|
+
};
|
|
123
156
|
/**
|
|
124
157
|
* One side of a market's resting order book, as rates rather than the API's WAD-scaled loan-per-unit
|
|
125
158
|
* prices. Annualizing each price against time-to-maturity gives the rate a taker filling that offer gets
|
|
@@ -11,10 +11,11 @@ import Dec from 'decimal.js';
|
|
|
11
11
|
import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
|
|
12
12
|
import { NetworkNumber, } from '../types/common';
|
|
13
13
|
import { DFSFeedRegistryContractViem, FeedRegistryContractViem, MorphoMidnightViewContractViem, } from '../contracts';
|
|
14
|
+
import { MorphoMidnightBorrowInfoStatus, } from '../types';
|
|
14
15
|
import { USD_QUOTE } from '../constants';
|
|
15
16
|
import { calculateNetApy } from '../staking';
|
|
16
17
|
import { isMainnetNetwork, wethToEth } from '../services/utils';
|
|
17
|
-
import { getMorphoMidnightAggregatedPositionData, getMorphoMidnightUserBorrowInfo } from '../helpers/morphoMidnightHelpers';
|
|
18
|
+
import { getMorphoMidnightAggregatedPositionData, getMorphoMidnightUserBorrowInfo, morphoMidnightMarketReportsBorrowInfo } from '../helpers/morphoMidnightHelpers';
|
|
18
19
|
import { getChainlinkAssetAddress } from '../services/priceService';
|
|
19
20
|
import { getViemProvider, setViemBlockNumber } from '../services/viem';
|
|
20
21
|
const HARDCODED_USD_STABLE_PRICE = '100000000'; // $1 with 8 decimals
|
|
@@ -154,21 +155,37 @@ export function _getMorphoMidnightAccountData(provider, network, account, select
|
|
|
154
155
|
let borrowRate = '0';
|
|
155
156
|
let debtBase = debt; // fallback: treat the full on-chain debt as principal until fill history is known
|
|
156
157
|
let debtInterest = '0';
|
|
158
|
+
let borrowInfoStatus = MorphoMidnightBorrowInfoStatus.Available;
|
|
157
159
|
let assetsDataForApy = marketInfo.assetsData;
|
|
158
160
|
if (new Dec(positionInfo.debt.toString()).gt(0)) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (new Dec(borrowInfo.debtTotal).gt(0)) {
|
|
162
|
-
borrowRate = borrowInfo.borrowRate;
|
|
163
|
-
debtBase = borrowInfo.debtBase;
|
|
164
|
-
debtInterest = borrowInfo.debtInterest;
|
|
165
|
-
usedAssets[marketInfo.loanToken].borrowRate = borrowRate;
|
|
166
|
-
// Reflect the real borrow cost in netApy without mutating the shared marketInfo.assetsData.
|
|
167
|
-
assetsDataForApy = Object.assign(Object.assign({}, marketInfo.assetsData), { [marketInfo.loanToken]: Object.assign(Object.assign({}, loanTokenData), { borrowRate }) });
|
|
168
|
-
}
|
|
161
|
+
if (!morphoMidnightMarketReportsBorrowInfo(selectedMarket)) {
|
|
162
|
+
borrowInfoStatus = MorphoMidnightBorrowInfoStatus.Unavailable;
|
|
169
163
|
}
|
|
170
|
-
|
|
171
|
-
|
|
164
|
+
else {
|
|
165
|
+
try {
|
|
166
|
+
const borrowInfo = yield getMorphoMidnightUserBorrowInfo(account, marketId, marketInfo.loanToken);
|
|
167
|
+
if (new Dec(borrowInfo.debtTotal).gt(0)) {
|
|
168
|
+
borrowRate = borrowInfo.borrowRate;
|
|
169
|
+
debtBase = borrowInfo.debtBase;
|
|
170
|
+
debtInterest = borrowInfo.debtInterest;
|
|
171
|
+
usedAssets[marketInfo.loanToken].borrowRate = borrowRate;
|
|
172
|
+
// Reflect the real borrow cost in netApy without mutating the shared marketInfo.assetsData.
|
|
173
|
+
assetsDataForApy = Object.assign(Object.assign({}, marketInfo.assetsData), { [marketInfo.loanToken]: Object.assign(Object.assign({}, loanTokenData), { borrowRate }) });
|
|
174
|
+
const describesChainDebt = new Dec(borrowInfo.debtTotal).eq(debt);
|
|
175
|
+
const ratePriced = new Dec(borrowRate).gt(0) || new Dec(debtInterest).lte(0);
|
|
176
|
+
borrowInfoStatus = describesChainDebt && ratePriced
|
|
177
|
+
? MorphoMidnightBorrowInfoStatus.Available
|
|
178
|
+
: MorphoMidnightBorrowInfoStatus.Pending;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// No entry for the position: the API does not know it yet, rather than it having no principal.
|
|
182
|
+
borrowInfoStatus = MorphoMidnightBorrowInfoStatus.Pending;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
// Positions API unreachable — the fallback above still renders, and the next call may succeed.
|
|
187
|
+
borrowInfoStatus = MorphoMidnightBorrowInfoStatus.Pending;
|
|
188
|
+
}
|
|
172
189
|
}
|
|
173
190
|
}
|
|
174
191
|
return Object.assign({ usedAssets,
|
|
@@ -176,7 +193,8 @@ export function _getMorphoMidnightAccountData(provider, network, account, select
|
|
|
176
193
|
debt,
|
|
177
194
|
borrowRate,
|
|
178
195
|
debtBase,
|
|
179
|
-
debtInterest,
|
|
196
|
+
debtInterest,
|
|
197
|
+
borrowInfoStatus, maturity: marketInfo.maturity, isMatured: marketInfo.isMatured }, getMorphoMidnightAggregatedPositionData({ usedAssets, assetsData: assetsDataForApy, marketInfo }));
|
|
180
198
|
});
|
|
181
199
|
}
|
|
182
200
|
export function getMorphoMidnightAccountData(provider, network, account, selectedMarket, marketInfo) {
|
|
@@ -113,6 +113,24 @@ export interface MorphoMidnightAggregatedPositionData {
|
|
|
113
113
|
collLiquidationRatio?: string;
|
|
114
114
|
exposure: string;
|
|
115
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
|
+
* - `Unavailable` — no source covers this market, so refetching changes nothing. See
|
|
127
|
+
* `morphoMidnightMarketReportsBorrowInfo`.
|
|
128
|
+
*/
|
|
129
|
+
export declare enum MorphoMidnightBorrowInfoStatus {
|
|
130
|
+
Available = "available",
|
|
131
|
+
Pending = "pending",
|
|
132
|
+
Unavailable = "unavailable"
|
|
133
|
+
}
|
|
116
134
|
export interface MorphoMidnightPositionData extends MorphoMidnightAggregatedPositionData {
|
|
117
135
|
usedAssets: MMUsedAssets;
|
|
118
136
|
credit: string;
|
|
@@ -120,6 +138,7 @@ export interface MorphoMidnightPositionData extends MorphoMidnightAggregatedPosi
|
|
|
120
138
|
borrowRate: string;
|
|
121
139
|
debtBase: string;
|
|
122
140
|
debtInterest: string;
|
|
141
|
+
borrowInfoStatus: MorphoMidnightBorrowInfoStatus;
|
|
123
142
|
maturity: number;
|
|
124
143
|
isMatured: boolean;
|
|
125
144
|
}
|
|
@@ -26,3 +26,22 @@ export var MorphoMidnightVersions;
|
|
|
26
26
|
MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20261119_Base"] = "morphomidnighttenorcbethweth_20261119_base";
|
|
27
27
|
MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20261217_Base"] = "morphomidnighttenorcbethweth_20261217_base";
|
|
28
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
|
+
* - `Unavailable` — no source covers this market, so refetching changes nothing. See
|
|
40
|
+
* `morphoMidnightMarketReportsBorrowInfo`.
|
|
41
|
+
*/
|
|
42
|
+
export var MorphoMidnightBorrowInfoStatus;
|
|
43
|
+
(function (MorphoMidnightBorrowInfoStatus) {
|
|
44
|
+
MorphoMidnightBorrowInfoStatus["Available"] = "available";
|
|
45
|
+
MorphoMidnightBorrowInfoStatus["Pending"] = "pending";
|
|
46
|
+
MorphoMidnightBorrowInfoStatus["Unavailable"] = "unavailable";
|
|
47
|
+
})(MorphoMidnightBorrowInfoStatus || (MorphoMidnightBorrowInfoStatus = {}));
|
package/package.json
CHANGED
|
@@ -74,6 +74,12 @@ export const getMorphoMidnightAggregatedPositionData = ({
|
|
|
74
74
|
);
|
|
75
75
|
payload.liquidationLimitUsd = payload.borrowLimitUsd;
|
|
76
76
|
|
|
77
|
+
// Same subtraction every other money market uses, but it does NOT mean the same thing here. Elsewhere
|
|
78
|
+
// `borrowedUsd` is debt at present value, so the remainder is what a borrow would pay out. Midnight
|
|
79
|
+
// records debt at its face value at maturity, so this is face-value headroom: borrowing it would add
|
|
80
|
+
// more debt than the number says, by the market's discount. Anything surfacing this as "what you can
|
|
81
|
+
// borrow" has to scale it by the loan-per-unit price first (`midnightPriceFromApy` off a book rate) —
|
|
82
|
+
// a correction that grows with the term, past 7% on a one-year market.
|
|
77
83
|
const leftToBorrowUsd = new Dec(payload.borrowLimitUsd).sub(payload.borrowedUsd);
|
|
78
84
|
payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
|
|
79
85
|
|
|
@@ -221,6 +227,10 @@ export const midnightSlippageParam = (slippagePercent: Dec.Value): string => Dec
|
|
|
221
227
|
MIDNIGHT_SLIPPAGE_MAX,
|
|
222
228
|
).toDP(1, Dec.ROUND_DOWN).toString();
|
|
223
229
|
|
|
230
|
+
export const morphoMidnightMarketReportsBorrowInfo = (market: Pick<MorphoMidnightMarketData, 'curator'> | string): boolean => (
|
|
231
|
+
!isTenorMidnightMarket(market)
|
|
232
|
+
);
|
|
233
|
+
|
|
224
234
|
/**
|
|
225
235
|
* Current borrower rate + debt breakdown from the Midnight positions API. Reconstructing this from the raw
|
|
226
236
|
* `/transactions` fill history only sums `borrow` fills, so it overstates debt for any position with an
|
|
@@ -250,6 +260,38 @@ export const getMorphoMidnightUserBorrowInfo = async (
|
|
|
250
260
|
};
|
|
251
261
|
};
|
|
252
262
|
|
|
263
|
+
/**
|
|
264
|
+
* The inverse of the split above: where `getMorphoMidnightUserBorrowInfo` reads a position's principal and
|
|
265
|
+
* interest off the indexer, this carries that same split forward through a payback, from a debt of
|
|
266
|
+
* `borrowedBefore` down to one of `borrowedAfter`. Retiring units retires principal and interest pro rata,
|
|
267
|
+
* so both scale by the same fraction.
|
|
268
|
+
*
|
|
269
|
+
* Deriving the interest as `borrowedAfter − debtBase` instead reads it off two different sources —
|
|
270
|
+
* `borrowed` is the chain's debt, `debtBase` the indexer's principal — so the entire gap between them lands
|
|
271
|
+
* in the interest whenever they disagree, which they do until the indexer catches up with a fresh borrow or
|
|
272
|
+
* payback: behind a borrow it inflates the interest by the debt the indexer has not seen yet, behind a
|
|
273
|
+
* payback it goes negative on a position that still owes. Callers detect that window as
|
|
274
|
+
* `debtBase + debtInterest !== debt`.
|
|
275
|
+
*
|
|
276
|
+
* Pro rata is also what keeps the answer sane when a payback retires debt at a different rate than the one
|
|
277
|
+
* it was opened at. Mirroring a borrow instead — principal growing by the assets received, interest by the
|
|
278
|
+
* rest — hands back a negative interest as soon as the book sells units back cheaper than they were bought.
|
|
279
|
+
*/
|
|
280
|
+
export const scaleMorphoMidnightDebtSplit = (
|
|
281
|
+
{ debtBase, debtInterest }: Pick<MorphoMidnightBorrowInfo, 'debtBase' | 'debtInterest'>,
|
|
282
|
+
borrowedBefore: Dec.Value,
|
|
283
|
+
borrowedAfter: Dec.Value,
|
|
284
|
+
): Pick<MorphoMidnightBorrowInfo, 'debtBase' | 'debtInterest'> => {
|
|
285
|
+
const fractionRemaining = new Dec(borrowedBefore).lte(0)
|
|
286
|
+
? new Dec(0)
|
|
287
|
+
: Dec.max(0, new Dec(borrowedAfter)).div(borrowedBefore);
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
debtBase: new Dec(debtBase).mul(fractionRemaining).toString(),
|
|
291
|
+
debtInterest: new Dec(debtInterest).mul(fractionRemaining).toString(),
|
|
292
|
+
};
|
|
293
|
+
};
|
|
294
|
+
|
|
253
295
|
/**
|
|
254
296
|
* One side of a market's resting order book, as rates rather than the API's WAD-scaled loan-per-unit
|
|
255
297
|
* prices. Annualizing each price against time-to-maturity gives the rate a taker filling that offer gets
|
|
@@ -8,12 +8,12 @@ import {
|
|
|
8
8
|
DFSFeedRegistryContractViem, FeedRegistryContractViem, MorphoMidnightViewContractViem,
|
|
9
9
|
} from '../contracts';
|
|
10
10
|
import {
|
|
11
|
-
MorphoMidnightAssetsData, MorphoMidnightMarketData, MorphoMidnightMarketInfo, MorphoMidnightPositionData,
|
|
11
|
+
MorphoMidnightAssetsData, MorphoMidnightBorrowInfoStatus, MorphoMidnightMarketData, MorphoMidnightMarketInfo, MorphoMidnightPositionData,
|
|
12
12
|
} from '../types';
|
|
13
13
|
import { USD_QUOTE } from '../constants';
|
|
14
14
|
import { calculateNetApy } from '../staking';
|
|
15
15
|
import { isMainnetNetwork, wethToEth } from '../services/utils';
|
|
16
|
-
import { getMorphoMidnightAggregatedPositionData, getMorphoMidnightUserBorrowInfo } from '../helpers/morphoMidnightHelpers';
|
|
16
|
+
import { getMorphoMidnightAggregatedPositionData, getMorphoMidnightUserBorrowInfo, morphoMidnightMarketReportsBorrowInfo } from '../helpers/morphoMidnightHelpers';
|
|
17
17
|
import { getChainlinkAssetAddress } from '../services/priceService';
|
|
18
18
|
import { getViemProvider, setViemBlockNumber } from '../services/viem';
|
|
19
19
|
|
|
@@ -163,23 +163,37 @@ export async function _getMorphoMidnightAccountData(provider: Client, network: N
|
|
|
163
163
|
let borrowRate = '0';
|
|
164
164
|
let debtBase = debt; // fallback: treat the full on-chain debt as principal until fill history is known
|
|
165
165
|
let debtInterest = '0';
|
|
166
|
+
let borrowInfoStatus = MorphoMidnightBorrowInfoStatus.Available;
|
|
166
167
|
let assetsDataForApy = marketInfo.assetsData;
|
|
167
168
|
if (new Dec(positionInfo.debt.toString()).gt(0)) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
169
|
+
if (!morphoMidnightMarketReportsBorrowInfo(selectedMarket)) {
|
|
170
|
+
borrowInfoStatus = MorphoMidnightBorrowInfoStatus.Unavailable;
|
|
171
|
+
} else {
|
|
172
|
+
try {
|
|
173
|
+
const borrowInfo = await getMorphoMidnightUserBorrowInfo(account, marketId, marketInfo.loanToken);
|
|
174
|
+
if (new Dec(borrowInfo.debtTotal).gt(0)) {
|
|
175
|
+
borrowRate = borrowInfo.borrowRate;
|
|
176
|
+
debtBase = borrowInfo.debtBase;
|
|
177
|
+
debtInterest = borrowInfo.debtInterest;
|
|
178
|
+
usedAssets[marketInfo.loanToken].borrowRate = borrowRate;
|
|
179
|
+
// Reflect the real borrow cost in netApy without mutating the shared marketInfo.assetsData.
|
|
180
|
+
assetsDataForApy = {
|
|
181
|
+
...marketInfo.assetsData,
|
|
182
|
+
[marketInfo.loanToken]: { ...loanTokenData, borrowRate },
|
|
183
|
+
};
|
|
184
|
+
const describesChainDebt = new Dec(borrowInfo.debtTotal).eq(debt);
|
|
185
|
+
const ratePriced = new Dec(borrowRate).gt(0) || new Dec(debtInterest).lte(0);
|
|
186
|
+
borrowInfoStatus = describesChainDebt && ratePriced
|
|
187
|
+
? MorphoMidnightBorrowInfoStatus.Available
|
|
188
|
+
: MorphoMidnightBorrowInfoStatus.Pending;
|
|
189
|
+
} else {
|
|
190
|
+
// No entry for the position: the API does not know it yet, rather than it having no principal.
|
|
191
|
+
borrowInfoStatus = MorphoMidnightBorrowInfoStatus.Pending;
|
|
192
|
+
}
|
|
193
|
+
} catch (err) {
|
|
194
|
+
// Positions API unreachable — the fallback above still renders, and the next call may succeed.
|
|
195
|
+
borrowInfoStatus = MorphoMidnightBorrowInfoStatus.Pending;
|
|
180
196
|
}
|
|
181
|
-
} catch (err) {
|
|
182
|
-
// Orderbook API unavailable — keep the on-chain-only fallback above.
|
|
183
197
|
}
|
|
184
198
|
}
|
|
185
199
|
|
|
@@ -190,6 +204,7 @@ export async function _getMorphoMidnightAccountData(provider: Client, network: N
|
|
|
190
204
|
borrowRate,
|
|
191
205
|
debtBase,
|
|
192
206
|
debtInterest,
|
|
207
|
+
borrowInfoStatus,
|
|
193
208
|
maturity: marketInfo.maturity,
|
|
194
209
|
isMatured: marketInfo.isMatured,
|
|
195
210
|
...getMorphoMidnightAggregatedPositionData({ usedAssets, assetsData: assetsDataForApy, marketInfo }),
|
|
@@ -132,18 +132,35 @@ export interface MorphoMidnightAggregatedPositionData {
|
|
|
132
132
|
exposure: string,
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/**
|
|
136
|
+
* How much weight `borrowRate` / `debtBase` / `debtInterest` carry on a given position. They fall back to
|
|
137
|
+
* `'0'` / `debt` / `'0'`, which is indistinguishable from a real 0%-interest position, so anything
|
|
138
|
+
* displaying them has to read this to know whether it is looking at a number or at a placeholder.
|
|
139
|
+
*
|
|
140
|
+
* - `Available` — reported and reconciled against the on-chain debt (also lenders and debt-free positions,
|
|
141
|
+
* which have nothing to report).
|
|
142
|
+
* - `Pending` — the indexer has not caught up with the chain yet: it does not know the position, or its
|
|
143
|
+
* split describes a different debt. Refetching resolves it; `getMorphoMidnightUserBorrowInfo` failing
|
|
144
|
+
* outright lands here too, since the next call may well succeed.
|
|
145
|
+
* - `Unavailable` — no source covers this market, so refetching changes nothing. See
|
|
146
|
+
* `morphoMidnightMarketReportsBorrowInfo`.
|
|
147
|
+
*/
|
|
148
|
+
export enum MorphoMidnightBorrowInfoStatus {
|
|
149
|
+
Available = 'available',
|
|
150
|
+
Pending = 'pending',
|
|
151
|
+
Unavailable = 'unavailable',
|
|
152
|
+
}
|
|
153
|
+
|
|
135
154
|
// Fixed-rate/YTM (derived from entry price + orderbook) is intentionally absent in MVP:
|
|
136
155
|
// MidnightView exposes no per-position rate, so a variable-MM-style APY would be misleading.
|
|
137
156
|
export interface MorphoMidnightPositionData extends MorphoMidnightAggregatedPositionData {
|
|
138
157
|
usedAssets: MMUsedAssets,
|
|
139
158
|
credit: string, // lender credit units, face value at maturity (with interest); 0 for borrowers
|
|
140
159
|
debt: string, // borrower debt, face value at maturity (with interest); 0 for lenders
|
|
141
|
-
// Borrow rate + base/interest split are orderbook-derived off-chain (from the Midnight transactions API):
|
|
142
|
-
// MidnightView only stores `debt` (= face value at maturity), so principal-vs-interest and the effective
|
|
143
|
-
// rate are computed from the fill history. Default to '0'/`debt`/'0' for lenders or when the API is unavailable.
|
|
144
160
|
borrowRate: string, // weighted-average borrow APY as a percent
|
|
145
161
|
debtBase: string, // base borrowed (principal), loan-token units
|
|
146
162
|
debtInterest: string, // debt − debtBase (fixed interest owed at maturity), loan-token units
|
|
163
|
+
borrowInfoStatus: MorphoMidnightBorrowInfoStatus,
|
|
147
164
|
maturity: number,
|
|
148
165
|
isMatured: boolean,
|
|
149
166
|
}
|