@defisaver/positions-sdk 2.1.127-midnight-2-dev → 2.1.127-midnight-3-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.
@@ -1,6 +1,6 @@
1
1
  import Dec from 'decimal.js';
2
- import { MMUsedAssets } from '../../types/common';
3
- import { MorphoMidnightAggregatedPositionData, MorphoMidnightAssetsData, MorphoMidnightMarketInfo } from '../../types';
2
+ import { MMUsedAssets, NetworkNumber } from '../../types/common';
3
+ import { MorphoMidnightAggregatedPositionData, MorphoMidnightAssetsData, MorphoMidnightMarketData, MorphoMidnightMarketInfo, MorphoMidnightParsedBook } from '../../types';
4
4
  /**
5
5
  * Aggregate a Morpho Midnight position. Midnight markets are multi-collateral, so the borrow limit is
6
6
  * the sum of each collateral's USD value times its own lltv (Aave-v4 style), rather than a single pair.
@@ -58,6 +58,16 @@ export declare const midnightSlippageParam: (slippagePercent: Dec.Value) => stri
58
58
  * The caller swallows errors — a missing rate must never block position rendering.
59
59
  */
60
60
  export declare const getMorphoMidnightUserBorrowInfo: (account: string, marketId: string, maturity: number, loanTokenSymbol: string) => Promise<MorphoMidnightBorrowInfo>;
61
+ /**
62
+ * A market's resting bids, as rates rather than the API's WAD-scaled loan-per-unit prices. Annualizing
63
+ * each price against time-to-maturity gives the rate a borrower filling that offer pays — verified
64
+ * against Morpho's fixed-market UI, where per-offer rates match to the cent.
65
+ *
66
+ * Returns `null` for an empty book: there is nothing to borrow against, so a market listing should skip
67
+ * the market rather than advertise it at a 0% rate. Throws when the request fails — an error response is
68
+ * rarely JSON, so without the `res.ok` check it parses as an empty book and the market silently vanishes.
69
+ */
70
+ export declare const getMorphoMidnightMarketBook: (market: MorphoMidnightMarketData, network: NetworkNumber) => Promise<MorphoMidnightParsedBook | null>;
61
71
  /**
62
72
  * Quote a prospective borrow against the Midnight order book: the estimated rate, the debt units it adds,
63
73
  * and the `maxUnits` cap sent on-chain to protect the user if better offers get filled first. `assetsRaw`
@@ -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.getMorphoMidnightBorrowQuote = exports.getMorphoMidnightUserBorrowInfo = exports.midnightSlippageParam = exports.midnightPriceFromApy = exports.midnightApyFromPrice = exports.midnightTimeToMaturityDays = exports.getMorphoMidnightAggregatedPositionData = void 0;
15
+ exports.getMorphoMidnightBorrowQuote = exports.getMorphoMidnightMarketBook = exports.getMorphoMidnightUserBorrowInfo = exports.midnightSlippageParam = exports.midnightPriceFromApy = exports.midnightApyFromPrice = exports.midnightTimeToMaturityDays = exports.getMorphoMidnightAggregatedPositionData = 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");
@@ -86,6 +86,9 @@ exports.getMorphoMidnightAggregatedPositionData = getMorphoMidnightAggregatedPos
86
86
  // loan-per-unit ratios (< 1 for a discounted fixed-term borrow); annualizing them yields the borrow APY.
87
87
  const MIDNIGHT_API_BASE = 'https://api.morpho.org/v0/midnight';
88
88
  const nowInSeconds = () => Math.floor(Date.now() / 1000);
89
+ // The book endpoint is markedly slower than the rest of the API — `LONGER_TIMEOUT` (5s) aborts it often
90
+ // enough that markets drop out of the list for no reason.
91
+ const MIDNIGHT_BOOK_TIMEOUT = 30000;
89
92
  // The quote endpoint's `slippage` query param is validated as a string: 0.1–100, at most one decimal
90
93
  // place (`0.50` is rejected even though `0.5` passes). See `midnightSlippageParam`.
91
94
  const MIDNIGHT_SLIPPAGE_MIN = 0.1;
@@ -164,6 +167,38 @@ const getMorphoMidnightUserBorrowInfo = (account, marketId, maturity, loanTokenS
164
167
  };
165
168
  });
166
169
  exports.getMorphoMidnightUserBorrowInfo = getMorphoMidnightUserBorrowInfo;
170
+ /**
171
+ * A market's resting bids, as rates rather than the API's WAD-scaled loan-per-unit prices. Annualizing
172
+ * each price against time-to-maturity gives the rate a borrower filling that offer pays — verified
173
+ * against Morpho's fixed-market UI, where per-offer rates match to the cent.
174
+ *
175
+ * Returns `null` for an empty book: there is nothing to borrow against, so a market listing should skip
176
+ * the market rather than advertise it at a 0% rate. Throws when the request fails — an error response is
177
+ * rarely JSON, so without the `res.ok` check it parses as an empty book and the market silently vanishes.
178
+ */
179
+ const getMorphoMidnightMarketBook = (market, network) => __awaiter(void 0, void 0, void 0, function* () {
180
+ var _a;
181
+ const loanSymbol = (0, tokens_1.getAssetInfoByAddress)(market.loanToken, network).symbol;
182
+ const res = yield fetch(`${MIDNIGHT_API_BASE}/books/${market.marketId}`, { signal: AbortSignal.timeout(MIDNIGHT_BOOK_TIMEOUT) });
183
+ if (!res.ok)
184
+ throw new Error(`Midnight book request failed for ${market.value} (${res.status})`);
185
+ const json = yield res.json();
186
+ const ttmDays = (0, exports.midnightTimeToMaturityDays)(market.maturity);
187
+ const offers = (((_a = json === null || json === void 0 ? void 0 : json.data) === null || _a === void 0 ? void 0 : _a.bids) || [])
188
+ .map((bid) => ({
189
+ rate: (0, exports.midnightApyFromPrice)(new decimal_js_1.default(bid.price).div(constants_1.WAD), ttmDays),
190
+ liquidity: (0, tokens_1.assetAmountInEth)(bid.assets, loanSymbol),
191
+ }))
192
+ .sort((a, b) => new decimal_js_1.default(a.rate).minus(b.rate).toNumber());
193
+ if (offers.length === 0)
194
+ return null;
195
+ return {
196
+ bestRate: offers[0].rate,
197
+ totalLiquidity: offers.reduce((sum, offer) => sum.add(offer.liquidity), new decimal_js_1.default(0)).toString(),
198
+ offers,
199
+ };
200
+ });
201
+ exports.getMorphoMidnightMarketBook = getMorphoMidnightMarketBook;
167
202
  // The API says why a quote failed — NOT_FOUND (market matured or not open yet), INSUFFICIENT_LIQUIDITY
168
203
  // (book can't fill the size), VALIDATION_ERROR (bad param, with the offending field in `details`).
169
204
  // Callers surface this to the user, so keep the reason rather than collapsing everything into one string.
@@ -60,6 +60,15 @@ export interface MorphoMidnightMarketInfo {
60
60
  utillization: string;
61
61
  assetsData: MorphoMidnightAssetsData;
62
62
  }
63
+ export interface MorphoMidnightBookOffer {
64
+ rate: string;
65
+ liquidity: string;
66
+ }
67
+ export interface MorphoMidnightParsedBook {
68
+ bestRate: string;
69
+ totalLiquidity: string;
70
+ offers: MorphoMidnightBookOffer[];
71
+ }
63
72
  export interface MorphoMidnightAggregatedPositionData {
64
73
  suppliedUsd: string;
65
74
  suppliedCollateralUsd: string;
@@ -1,6 +1,6 @@
1
1
  import Dec from 'decimal.js';
2
- import { MMUsedAssets } from '../../types/common';
3
- import { MorphoMidnightAggregatedPositionData, MorphoMidnightAssetsData, MorphoMidnightMarketInfo } from '../../types';
2
+ import { MMUsedAssets, NetworkNumber } from '../../types/common';
3
+ import { MorphoMidnightAggregatedPositionData, MorphoMidnightAssetsData, MorphoMidnightMarketData, MorphoMidnightMarketInfo, MorphoMidnightParsedBook } from '../../types';
4
4
  /**
5
5
  * Aggregate a Morpho Midnight position. Midnight markets are multi-collateral, so the borrow limit is
6
6
  * the sum of each collateral's USD value times its own lltv (Aave-v4 style), rather than a single pair.
@@ -58,6 +58,16 @@ export declare const midnightSlippageParam: (slippagePercent: Dec.Value) => stri
58
58
  * The caller swallows errors — a missing rate must never block position rendering.
59
59
  */
60
60
  export declare const getMorphoMidnightUserBorrowInfo: (account: string, marketId: string, maturity: number, loanTokenSymbol: string) => Promise<MorphoMidnightBorrowInfo>;
61
+ /**
62
+ * A market's resting bids, as rates rather than the API's WAD-scaled loan-per-unit prices. Annualizing
63
+ * each price against time-to-maturity gives the rate a borrower filling that offer pays — verified
64
+ * against Morpho's fixed-market UI, where per-offer rates match to the cent.
65
+ *
66
+ * Returns `null` for an empty book: there is nothing to borrow against, so a market listing should skip
67
+ * the market rather than advertise it at a 0% rate. Throws when the request fails — an error response is
68
+ * rarely JSON, so without the `res.ok` check it parses as an empty book and the market silently vanishes.
69
+ */
70
+ export declare const getMorphoMidnightMarketBook: (market: MorphoMidnightMarketData, network: NetworkNumber) => Promise<MorphoMidnightParsedBook | null>;
61
71
  /**
62
72
  * Quote a prospective borrow against the Midnight order book: the estimated rate, the debt units it adds,
63
73
  * and the `maxUnits` cap sent on-chain to protect the user if better offers get filled first. `assetsRaw`
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import Dec from 'decimal.js';
11
- import { assetAmountInEth } from '@defisaver/tokens';
11
+ import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
12
12
  import { calcLeverageLiqPrice, getAssetsTotal, getExposure, isLeveragedPos, } from '../../moneymarket';
13
13
  import { calculateNetApy } from '../../staking';
14
14
  import { LeverageType, } from '../../types/common';
@@ -79,6 +79,9 @@ export const getMorphoMidnightAggregatedPositionData = ({ usedAssets, assetsData
79
79
  // loan-per-unit ratios (< 1 for a discounted fixed-term borrow); annualizing them yields the borrow APY.
80
80
  const MIDNIGHT_API_BASE = 'https://api.morpho.org/v0/midnight';
81
81
  const nowInSeconds = () => Math.floor(Date.now() / 1000);
82
+ // The book endpoint is markedly slower than the rest of the API — `LONGER_TIMEOUT` (5s) aborts it often
83
+ // enough that markets drop out of the list for no reason.
84
+ const MIDNIGHT_BOOK_TIMEOUT = 30000;
82
85
  // The quote endpoint's `slippage` query param is validated as a string: 0.1–100, at most one decimal
83
86
  // place (`0.50` is rejected even though `0.5` passes). See `midnightSlippageParam`.
84
87
  const MIDNIGHT_SLIPPAGE_MIN = 0.1;
@@ -152,6 +155,37 @@ export const getMorphoMidnightUserBorrowInfo = (account, marketId, maturity, loa
152
155
  borrowRate, debtBase, debtInterest, debtTotal,
153
156
  };
154
157
  });
158
+ /**
159
+ * A market's resting bids, as rates rather than the API's WAD-scaled loan-per-unit prices. Annualizing
160
+ * each price against time-to-maturity gives the rate a borrower filling that offer pays — verified
161
+ * against Morpho's fixed-market UI, where per-offer rates match to the cent.
162
+ *
163
+ * Returns `null` for an empty book: there is nothing to borrow against, so a market listing should skip
164
+ * the market rather than advertise it at a 0% rate. Throws when the request fails — an error response is
165
+ * rarely JSON, so without the `res.ok` check it parses as an empty book and the market silently vanishes.
166
+ */
167
+ export const getMorphoMidnightMarketBook = (market, network) => __awaiter(void 0, void 0, void 0, function* () {
168
+ var _a;
169
+ const loanSymbol = getAssetInfoByAddress(market.loanToken, network).symbol;
170
+ const res = yield fetch(`${MIDNIGHT_API_BASE}/books/${market.marketId}`, { signal: AbortSignal.timeout(MIDNIGHT_BOOK_TIMEOUT) });
171
+ if (!res.ok)
172
+ throw new Error(`Midnight book request failed for ${market.value} (${res.status})`);
173
+ const json = yield res.json();
174
+ const ttmDays = midnightTimeToMaturityDays(market.maturity);
175
+ const offers = (((_a = json === null || json === void 0 ? void 0 : json.data) === null || _a === void 0 ? void 0 : _a.bids) || [])
176
+ .map((bid) => ({
177
+ rate: midnightApyFromPrice(new Dec(bid.price).div(WAD), ttmDays),
178
+ liquidity: assetAmountInEth(bid.assets, loanSymbol),
179
+ }))
180
+ .sort((a, b) => new Dec(a.rate).minus(b.rate).toNumber());
181
+ if (offers.length === 0)
182
+ return null;
183
+ return {
184
+ bestRate: offers[0].rate,
185
+ totalLiquidity: offers.reduce((sum, offer) => sum.add(offer.liquidity), new Dec(0)).toString(),
186
+ offers,
187
+ };
188
+ });
155
189
  // The API says why a quote failed — NOT_FOUND (market matured or not open yet), INSUFFICIENT_LIQUIDITY
156
190
  // (book can't fill the size), VALIDATION_ERROR (bad param, with the offending field in `details`).
157
191
  // Callers surface this to the user, so keep the reason rather than collapsing everything into one string.
@@ -60,6 +60,15 @@ export interface MorphoMidnightMarketInfo {
60
60
  utillization: string;
61
61
  assetsData: MorphoMidnightAssetsData;
62
62
  }
63
+ export interface MorphoMidnightBookOffer {
64
+ rate: string;
65
+ liquidity: string;
66
+ }
67
+ export interface MorphoMidnightParsedBook {
68
+ bestRate: string;
69
+ totalLiquidity: string;
70
+ offers: MorphoMidnightBookOffer[];
71
+ }
63
72
  export interface MorphoMidnightAggregatedPositionData {
64
73
  suppliedUsd: string;
65
74
  suppliedCollateralUsd: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defisaver/positions-sdk",
3
- "version": "2.1.127-midnight-2-dev",
3
+ "version": "2.1.127-midnight-3-dev",
4
4
  "description": "",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./esm/index.js",
@@ -1,13 +1,20 @@
1
1
  import Dec from 'decimal.js';
2
- import { assetAmountInEth } from '@defisaver/tokens';
2
+ import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
3
3
  import {
4
4
  calcLeverageLiqPrice, getAssetsTotal, getExposure, isLeveragedPos,
5
5
  } from '../../moneymarket';
6
6
  import { calculateNetApy } from '../../staking';
7
7
  import {
8
- LeverageType, MMAssetsData, MMUsedAsset, MMUsedAssets,
8
+ LeverageType, MMAssetsData, MMUsedAsset, MMUsedAssets, NetworkNumber,
9
9
  } from '../../types/common';
10
- import { MorphoMidnightAggregatedPositionData, MorphoMidnightAssetsData, MorphoMidnightMarketInfo } from '../../types';
10
+ import {
11
+ MorphoMidnightAggregatedPositionData,
12
+ MorphoMidnightAssetsData,
13
+ MorphoMidnightBookOffer,
14
+ MorphoMidnightMarketData,
15
+ MorphoMidnightMarketInfo,
16
+ MorphoMidnightParsedBook,
17
+ } from '../../types';
11
18
  import { SECONDS_PER_DAY, WAD } from '../../constants';
12
19
  import { LONGER_TIMEOUT } from '../../services/utils';
13
20
 
@@ -98,6 +105,10 @@ export const getMorphoMidnightAggregatedPositionData = ({
98
105
  const MIDNIGHT_API_BASE = 'https://api.morpho.org/v0/midnight';
99
106
  const nowInSeconds = () => Math.floor(Date.now() / 1000);
100
107
 
108
+ // The book endpoint is markedly slower than the rest of the API — `LONGER_TIMEOUT` (5s) aborts it often
109
+ // enough that markets drop out of the list for no reason.
110
+ const MIDNIGHT_BOOK_TIMEOUT = 30000;
111
+
101
112
  // The quote endpoint's `slippage` query param is validated as a string: 0.1–100, at most one decimal
102
113
  // place (`0.50` is rejected even though `0.5` passes). See `midnightSlippageParam`.
103
114
  const MIDNIGHT_SLIPPAGE_MIN = 0.1;
@@ -116,6 +127,11 @@ interface MidnightApiError {
116
127
  details?: ({ field?: string, issue?: string })[] | null,
117
128
  }
118
129
 
130
+ interface MidnightRawBid {
131
+ price: string, // WAD-scaled loan-per-unit
132
+ assets: string, // loan-token base units available at this offer
133
+ }
134
+
119
135
  interface MidnightQuoteResponse {
120
136
  average_best_price?: string,
121
137
  average_worst_price?: string,
@@ -225,6 +241,42 @@ export const getMorphoMidnightUserBorrowInfo = async (
225
241
  };
226
242
  };
227
243
 
244
+ /**
245
+ * A market's resting bids, as rates rather than the API's WAD-scaled loan-per-unit prices. Annualizing
246
+ * each price against time-to-maturity gives the rate a borrower filling that offer pays — verified
247
+ * against Morpho's fixed-market UI, where per-offer rates match to the cent.
248
+ *
249
+ * Returns `null` for an empty book: there is nothing to borrow against, so a market listing should skip
250
+ * the market rather than advertise it at a 0% rate. Throws when the request fails — an error response is
251
+ * rarely JSON, so without the `res.ok` check it parses as an empty book and the market silently vanishes.
252
+ */
253
+ export const getMorphoMidnightMarketBook = async (
254
+ market: MorphoMidnightMarketData,
255
+ network: NetworkNumber,
256
+ ): Promise<MorphoMidnightParsedBook | null> => {
257
+ const loanSymbol = getAssetInfoByAddress(market.loanToken, network).symbol;
258
+ const res = await fetch(`${MIDNIGHT_API_BASE}/books/${market.marketId}`, { signal: AbortSignal.timeout(MIDNIGHT_BOOK_TIMEOUT) });
259
+ if (!res.ok) throw new Error(`Midnight book request failed for ${market.value} (${res.status})`);
260
+
261
+ const json: { data?: { bids?: MidnightRawBid[] } } = await res.json();
262
+ const ttmDays = midnightTimeToMaturityDays(market.maturity);
263
+
264
+ const offers: MorphoMidnightBookOffer[] = (json?.data?.bids || [])
265
+ .map((bid) => ({
266
+ rate: midnightApyFromPrice(new Dec(bid.price).div(WAD), ttmDays),
267
+ liquidity: assetAmountInEth(bid.assets, loanSymbol),
268
+ }))
269
+ .sort((a, b) => new Dec(a.rate).minus(b.rate).toNumber());
270
+
271
+ if (offers.length === 0) return null;
272
+
273
+ return {
274
+ bestRate: offers[0].rate,
275
+ totalLiquidity: offers.reduce((sum, offer) => sum.add(offer.liquidity), new Dec(0)).toString(),
276
+ offers,
277
+ };
278
+ };
279
+
228
280
  // The API says why a quote failed — NOT_FOUND (market matured or not open yet), INSUFFICIENT_LIQUIDITY
229
281
  // (book can't fill the size), VALIDATION_ERROR (bad param, with the offending field in `details`).
230
282
  // Callers surface this to the user, so keep the reason rather than collapsing everything into one string.
@@ -70,6 +70,18 @@ export interface MorphoMidnightMarketInfo {
70
70
  assetsData: MorphoMidnightAssetsData,
71
71
  }
72
72
 
73
+ // One resting bid on a market's order book, as an annualized rate rather than the API's raw WAD price.
74
+ export interface MorphoMidnightBookOffer {
75
+ rate: string, // fixed borrow APY, percent
76
+ liquidity: string, // loan-token amount available at this rate
77
+ }
78
+
79
+ export interface MorphoMidnightParsedBook {
80
+ bestRate: string, // cheapest rate on the book (= offers[0].rate)
81
+ totalLiquidity: string, // Σ offers[].liquidity, loan-token units
82
+ offers: MorphoMidnightBookOffer[], // ascending by rate
83
+ }
84
+
73
85
  export interface MorphoMidnightAggregatedPositionData {
74
86
  suppliedUsd: string,
75
87
  suppliedCollateralUsd: string,