@defisaver/positions-sdk 2.1.127-midnight-12-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.
@@ -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
@@ -91,7 +92,7 @@ export declare const getMorphoMidnightUserBorrowInfo: (account: string, marketId
91
92
  * it was opened at. Mirroring a borrow instead — principal growing by the assets received, interest by the
92
93
  * rest — hands back a negative interest as soon as the book sells units back cheaper than they were bought.
93
94
  */
94
- export declare const scaleMorphoMidnightDebtSplit: <T extends Pick<MorphoMidnightBorrowInfo, "debtBase" | "debtInterest">>({ debtBase, debtInterest }: T, borrowedBefore: Dec.Value, borrowedAfter: Dec.Value) => Pick<MorphoMidnightBorrowInfo, "debtBase" | "debtInterest">;
95
+ export declare const scaleMorphoMidnightDebtSplit: ({ debtBase, debtInterest }: Pick<MorphoMidnightBorrowInfo, "debtBase" | "debtInterest">, borrowedBefore: Dec.Value, borrowedAfter: Dec.Value) => Pick<MorphoMidnightBorrowInfo, "debtBase" | "debtInterest">;
95
96
  /**
96
97
  * One side of a market's resting order book, as rates rather than the API's WAD-scaled loan-per-unit
97
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.scaleMorphoMidnightDebtSplit = 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");
@@ -123,6 +123,8 @@ const MIDNIGHT_SLIPPAGE_MAX = 100;
123
123
  */
124
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();
125
125
  exports.midnightSlippageParam = midnightSlippageParam;
126
+ const morphoMidnightMarketReportsBorrowInfo = (market) => (!(0, morphoMidnight_1.isTenorMidnightMarket)(market));
127
+ exports.morphoMidnightMarketReportsBorrowInfo = morphoMidnightMarketReportsBorrowInfo;
126
128
  /**
127
129
  * Current borrower rate + debt breakdown from the Midnight positions API. Reconstructing this from the raw
128
130
  * `/transactions` fill history only sums `borrow` fills, so it overstates debt for any position with an
@@ -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
- try {
171
- const borrowInfo = yield (0, morphoMidnightHelpers_1.getMorphoMidnightUserBorrowInfo)(account, marketId, marketInfo.loanToken);
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
- catch (err) {
182
- // Orderbook API unavailable — keep the on-chain-only fallback above.
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, maturity: marketInfo.maturity, isMatured: marketInfo.isMatured }, (0, morphoMidnightHelpers_1.getMorphoMidnightAggregatedPositionData)({ usedAssets, assetsData: assetsDataForApy, marketInfo }));
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
@@ -91,7 +92,7 @@ export declare const getMorphoMidnightUserBorrowInfo: (account: string, marketId
91
92
  * it was opened at. Mirroring a borrow instead — principal growing by the assets received, interest by the
92
93
  * rest — hands back a negative interest as soon as the book sells units back cheaper than they were bought.
93
94
  */
94
- export declare const scaleMorphoMidnightDebtSplit: <T extends Pick<MorphoMidnightBorrowInfo, "debtBase" | "debtInterest">>({ debtBase, debtInterest }: T, borrowedBefore: Dec.Value, borrowedAfter: Dec.Value) => Pick<MorphoMidnightBorrowInfo, "debtBase" | "debtInterest">;
95
+ export declare const scaleMorphoMidnightDebtSplit: ({ debtBase, debtInterest }: Pick<MorphoMidnightBorrowInfo, "debtBase" | "debtInterest">, borrowedBefore: Dec.Value, borrowedAfter: Dec.Value) => Pick<MorphoMidnightBorrowInfo, "debtBase" | "debtInterest">;
95
96
  /**
96
97
  * One side of a market's resting order book, as rates rather than the API's WAD-scaled loan-per-unit
97
98
  * prices. Annualizing each price against time-to-maturity gives the rate a taker filling that offer gets
@@ -104,6 +104,7 @@ const MIDNIGHT_SLIPPAGE_MAX = 100;
104
104
  * Rounded **down**, since a wider slippage is a looser cap than the caller asked for.
105
105
  */
106
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));
107
108
  /**
108
109
  * Current borrower rate + debt breakdown from the Midnight positions API. Reconstructing this from the raw
109
110
  * `/transactions` fill history only sums `borrow` fills, so it overstates debt for any position with an
@@ -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
- try {
160
- const borrowInfo = yield getMorphoMidnightUserBorrowInfo(account, marketId, marketInfo.loanToken);
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
- catch (err) {
171
- // Orderbook API unavailable — keep the on-chain-only fallback above.
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, maturity: marketInfo.maturity, isMatured: marketInfo.isMatured }, getMorphoMidnightAggregatedPositionData({ usedAssets, assetsData: assetsDataForApy, marketInfo }));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defisaver/positions-sdk",
3
- "version": "2.1.127-midnight-12-dev",
3
+ "version": "2.1.127-midnight-13-dev",
4
4
  "description": "",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./esm/index.js",
@@ -227,6 +227,10 @@ export const midnightSlippageParam = (slippagePercent: Dec.Value): string => Dec
227
227
  MIDNIGHT_SLIPPAGE_MAX,
228
228
  ).toDP(1, Dec.ROUND_DOWN).toString();
229
229
 
230
+ export const morphoMidnightMarketReportsBorrowInfo = (market: Pick<MorphoMidnightMarketData, 'curator'> | string): boolean => (
231
+ !isTenorMidnightMarket(market)
232
+ );
233
+
230
234
  /**
231
235
  * Current borrower rate + debt breakdown from the Midnight positions API. Reconstructing this from the raw
232
236
  * `/transactions` fill history only sums `borrow` fills, so it overstates debt for any position with an
@@ -273,8 +277,8 @@ export const getMorphoMidnightUserBorrowInfo = async (
273
277
  * it was opened at. Mirroring a borrow instead — principal growing by the assets received, interest by the
274
278
  * rest — hands back a negative interest as soon as the book sells units back cheaper than they were bought.
275
279
  */
276
- export const scaleMorphoMidnightDebtSplit = <T extends Pick<MorphoMidnightBorrowInfo, 'debtBase' | 'debtInterest'>>(
277
- { debtBase, debtInterest }: T,
280
+ export const scaleMorphoMidnightDebtSplit = (
281
+ { debtBase, debtInterest }: Pick<MorphoMidnightBorrowInfo, 'debtBase' | 'debtInterest'>,
278
282
  borrowedBefore: Dec.Value,
279
283
  borrowedAfter: Dec.Value,
280
284
  ): Pick<MorphoMidnightBorrowInfo, 'debtBase' | 'debtInterest'> => {
@@ -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
- try {
169
- const borrowInfo = await getMorphoMidnightUserBorrowInfo(account, marketId, marketInfo.loanToken);
170
- if (new Dec(borrowInfo.debtTotal).gt(0)) {
171
- borrowRate = borrowInfo.borrowRate;
172
- debtBase = borrowInfo.debtBase;
173
- debtInterest = borrowInfo.debtInterest;
174
- usedAssets[marketInfo.loanToken].borrowRate = borrowRate;
175
- // Reflect the real borrow cost in netApy without mutating the shared marketInfo.assetsData.
176
- assetsDataForApy = {
177
- ...marketInfo.assetsData,
178
- [marketInfo.loanToken]: { ...loanTokenData, borrowRate },
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
  }