@defisaver/positions-sdk 2.1.127-midnight-4-dev → 2.1.127-midnight-6-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.
Files changed (36) hide show
  1. package/cjs/constants/index.d.ts +1 -0
  2. package/cjs/constants/index.js +2 -1
  3. package/cjs/helpers/morphoMidnightHelpers/index.d.ts +34 -13
  4. package/cjs/helpers/morphoMidnightHelpers/index.js +85 -61
  5. package/cjs/helpers/morphoMidnightHelpers/rate.d.ts +11 -0
  6. package/cjs/helpers/morphoMidnightHelpers/rate.js +49 -0
  7. package/cjs/helpers/morphoMidnightHelpers/tenor.d.ts +125 -0
  8. package/cjs/helpers/morphoMidnightHelpers/tenor.js +256 -0
  9. package/cjs/markets/index.d.ts +1 -1
  10. package/cjs/markets/index.js +2 -1
  11. package/cjs/markets/morphoMidnight/index.d.ts +37 -0
  12. package/cjs/markets/morphoMidnight/index.js +434 -42
  13. package/cjs/types/morphoMidnight.d.ts +18 -1
  14. package/cjs/types/morphoMidnight.js +16 -0
  15. package/esm/constants/index.d.ts +1 -0
  16. package/esm/constants/index.js +1 -0
  17. package/esm/helpers/morphoMidnightHelpers/index.d.ts +34 -13
  18. package/esm/helpers/morphoMidnightHelpers/index.js +65 -48
  19. package/esm/helpers/morphoMidnightHelpers/rate.d.ts +11 -0
  20. package/esm/helpers/morphoMidnightHelpers/rate.js +38 -0
  21. package/esm/helpers/morphoMidnightHelpers/tenor.d.ts +125 -0
  22. package/esm/helpers/morphoMidnightHelpers/tenor.js +239 -0
  23. package/esm/markets/index.d.ts +1 -1
  24. package/esm/markets/index.js +1 -1
  25. package/esm/markets/morphoMidnight/index.d.ts +37 -0
  26. package/esm/markets/morphoMidnight/index.js +399 -23
  27. package/esm/types/morphoMidnight.d.ts +18 -1
  28. package/esm/types/morphoMidnight.js +16 -0
  29. package/package.json +1 -1
  30. package/src/constants/index.ts +1 -0
  31. package/src/helpers/morphoMidnightHelpers/index.ts +120 -48
  32. package/src/helpers/morphoMidnightHelpers/rate.ts +46 -0
  33. package/src/helpers/morphoMidnightHelpers/tenor.ts +411 -0
  34. package/src/markets/index.ts +1 -1
  35. package/src/markets/morphoMidnight/index.ts +424 -23
  36. package/src/types/morphoMidnight.ts +19 -0
@@ -6,6 +6,7 @@ export const SECONDS_PER_WEEK = 7 * SECONDS_PER_DAY;
6
6
  export const AVG_BLOCK_TIME = 12;
7
7
  export const BLOCKS_IN_A_YEAR = SECONDS_PER_YEAR / AVG_BLOCK_TIME;
8
8
  export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
9
+ export const ZERO_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000';
9
10
  export const WAD = 1e18;
10
11
  export const USD_QUOTE = '0x0000000000000000000000000000000000000348';
11
12
  export const borrowOperations = ['borrow', 'payback'];
@@ -1,6 +1,8 @@
1
1
  import Dec from 'decimal.js';
2
2
  import { MMUsedAssets, NetworkNumber } from '../../types/common';
3
3
  import { MorphoMidnightAggregatedPositionData, MorphoMidnightAssetsData, MorphoMidnightBookSide, MorphoMidnightMarketData, MorphoMidnightMarketInfo, MorphoMidnightParsedBook } from '../../types';
4
+ export { buildMidnightParsedBook, midnightApyFromPrice, midnightBookBestFirst, midnightPriceFromApy, midnightTimeToMaturityDays, } from './rate';
5
+ export { tenorBookKeyFor, tenorBookRateToApyPercent, tenorOfferFillToApiFill, tenorOfferToApiOffer, } from './tenor';
4
6
  /**
5
7
  * Aggregate a Morpho Midnight position. Midnight markets are multi-collateral, so the borrow limit is
6
8
  * the sum of each collateral's USD value times its own lltv (Aave-v4 style), rather than a single pair.
@@ -43,17 +45,17 @@ export interface MorphoMidnightPaybackQuote {
43
45
  availableUnits: string;
44
46
  takeableOffers: any[];
45
47
  }
46
- export declare const midnightTimeToMaturityDays: (maturity: number, atSeconds?: number) => number;
47
- export declare const midnightApyFromPrice: (price: Dec.Value, ttmDays: Dec.Value) => string;
48
- /**
49
- * Inverse of `midnightApyFromPrice`: the loan-per-unit price a borrow APY implies,
50
- * price = (1 + rate)^(−ttmDays / 365).
51
- *
52
- * This is what turns an absolute rate ceiling into an on-chain `maxUnits` cap (units = assets / price),
53
- * and equally the principal a unit of borrow power is worth — Midnight debt is recorded at its maturity
54
- * face value, so borrowing the full limit as principal would overshoot it by the interest.
55
- */
56
- export declare const midnightPriceFromApy: (ratePercent: Dec.Value, ttmDays: Dec.Value) => string;
48
+ export interface MorphoMidnightPaybackUnitsQuote {
49
+ bestPrice: string;
50
+ worstPrice: string;
51
+ estPaybackRate: string;
52
+ minRate: string;
53
+ newAssets: string;
54
+ maxAssets: string;
55
+ availableAssets: string;
56
+ availableUnits: string;
57
+ takeableOffers: any[];
58
+ }
57
59
  /**
58
60
  * Coerce a slippage into what the quote endpoint accepts: 0.1–100 with at most one decimal place. The
59
61
  * validation is lexical, so a computed value (`4.15066671050631467`) is rejected outright — without this
@@ -102,8 +104,11 @@ export declare const getMorphoMidnightMarketBook: (market: MorphoMidnightMarketD
102
104
  *
103
105
  * A `maxBorrowRate` below `estBorrowRate` yields `maxUnits < newUnits` — the borrow would revert on-chain.
104
106
  * Compare the two before submitting and tell the user their ceiling is under the market rate.
107
+ *
108
+ * Tenor-hosted markets (see `isTenorMidnightMarket`) are quoted against Tenor's router instead of Morpho's
109
+ * book API. Slippage is applied locally to `maxUnits`; `taker` is the position owner (the smart wallet).
105
110
  */
106
- export declare const getMorphoMidnightBorrowQuote: (marketId: string, assetsRaw: string, slippagePercent: Dec.Value, maturity: number, maxBorrowRate?: Dec.Value) => Promise<MorphoMidnightBorrowQuote>;
111
+ export declare const getMorphoMidnightBorrowQuote: (marketId: string, assetsRaw: string, slippagePercent: Dec.Value, maturity: number, maxBorrowRate?: Dec.Value, taker?: string) => Promise<MorphoMidnightBorrowQuote>;
107
112
  /**
108
113
  * Quote a prospective payback against the ask side of the Midnight order book: the rate the repayment
109
114
  * retires debt at, the debt units it buys, and the `minUnits` floor sent on-chain to protect the user if
@@ -122,4 +127,20 @@ export declare const getMorphoMidnightBorrowQuote: (marketId: string, assetsRaw:
122
127
  * A `minPaybackRate` above `estPaybackRate` yields `minUnits > newUnits` — the payback would revert
123
128
  * on-chain. Compare the two before submitting and tell the user their floor is over the market rate.
124
129
  */
125
- export declare const getMorphoMidnightPaybackQuote: (marketId: string, assetsRaw: string, slippagePercent: Dec.Value, maturity: number, minPaybackRate?: Dec.Value) => Promise<MorphoMidnightPaybackQuote>;
130
+ export declare const getMorphoMidnightPaybackQuote: (marketId: string, assetsRaw: string, slippagePercent: Dec.Value, maturity: number, minPaybackRate?: Dec.Value, taker?: string) => Promise<MorphoMidnightPaybackQuote>;
131
+ /**
132
+ * Payback quoted against a **units** target instead of a spend: "retire exactly these debt units, and tell
133
+ * me what that costs". The sibling of `getMorphoMidnightPaybackQuote` in every other respect — same ask
134
+ * side, same rate floor, same offer list — but with the two amounts swapped, so the on-chain guard becomes
135
+ * a ceiling on assets spent rather than a floor on units bought.
136
+ *
137
+ * This is what a **full close** must be sized with. Retiring N units costs less than N loan tokens before
138
+ * maturity, so quoting the close as a spend of the debt's face value asks the book for more depth than the
139
+ * close needs (and can be refused for liquidity that is in fact there), and caps the taker's spend at more
140
+ * than the position is worth. Callers that cannot refund an overspend — a taker calling the bundler
141
+ * directly, rather than through an action contract that sweeps the remainder back — need this quote.
142
+ *
143
+ * A `minPaybackRate` above `estPaybackRate` yields `maxAssets < newAssets`: the ceiling is under what the
144
+ * buy costs and it would revert on-chain, the mirror of the assets-target quote's `minUnits > newUnits`.
145
+ */
146
+ export declare const getMorphoMidnightPaybackUnitsQuote: (marketId: string, unitsRaw: string, slippagePercent: Dec.Value, maturity: number, minPaybackRate?: Dec.Value, taker?: string) => Promise<MorphoMidnightPaybackUnitsQuote>;
@@ -12,8 +12,13 @@ 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';
15
- import { SECONDS_PER_DAY, WAD } from '../../constants';
15
+ import { WAD } from '../../constants';
16
16
  import { LONGER_TIMEOUT } from '../../services/utils';
17
+ import { isTenorMidnightMarket } from '../../markets/morphoMidnight';
18
+ import { buildMidnightParsedBook, midnightApyFromPrice, midnightPriceFromApy, midnightTimeToMaturityDays, } from './rate';
19
+ import { getTenorBorrowQuote, getTenorMarketBook, getTenorPaybackQuote, getTenorPaybackUnitsQuote, } from './tenor';
20
+ export { buildMidnightParsedBook, midnightApyFromPrice, midnightBookBestFirst, midnightPriceFromApy, midnightTimeToMaturityDays, } from './rate';
21
+ export { tenorBookKeyFor, tenorBookRateToApyPercent, tenorOfferFillToApiFill, tenorOfferToApiOffer, } from './tenor';
17
22
  /**
18
23
  * Aggregate a Morpho Midnight position. Midnight markets are multi-collateral, so the borrow limit is
19
24
  * the sum of each collateral's USD value times its own lltv (Aave-v4 style), rather than a single pair.
@@ -78,7 +83,6 @@ export const getMorphoMidnightAggregatedPositionData = ({ usedAssets, assetsData
78
83
  // Quote prices are WAD-scaled
79
84
  // loan-per-unit ratios (< 1 for a discounted fixed-term borrow); annualizing them yields the borrow APY.
80
85
  const MIDNIGHT_API_BASE = 'https://api.morpho.org/v0/midnight';
81
- const nowInSeconds = () => Math.floor(Date.now() / 1000);
82
86
  // The book endpoint is markedly slower than the rest of the API — `LONGER_TIMEOUT` (5s) aborts it often
83
87
  // enough that markets drop out of the list for no reason.
84
88
  const MIDNIGHT_BOOK_TIMEOUT = 30000;
@@ -86,34 +90,6 @@ const MIDNIGHT_BOOK_TIMEOUT = 30000;
86
90
  // place (`0.50` is rejected even though `0.5` passes). See `midnightSlippageParam`.
87
91
  const MIDNIGHT_SLIPPAGE_MIN = 0.1;
88
92
  const MIDNIGHT_SLIPPAGE_MAX = 100;
89
- // Days remaining until maturity, optionally measured at a past timestamp (for historical fills).
90
- export const midnightTimeToMaturityDays = (maturity, atSeconds = nowInSeconds()) => new Dec(maturity).sub(atSeconds).div(SECONDS_PER_DAY).toNumber();
91
- // Annualize a fixed-term discount price into an APY percent: (1 / price)^(365 / ttmDays) − 1.
92
- // `price` is loan-per-unit (assets received / units owed), so 1/price ≥ 1.
93
- export const midnightApyFromPrice = (price, ttmDays) => {
94
- const p = new Dec(price);
95
- const ttm = new Dec(ttmDays);
96
- if (p.lte(0) || ttm.lte(0))
97
- return '0';
98
- return new Dec(1).div(p).pow(new Dec(365).div(ttm)).sub(1)
99
- .mul(100)
100
- .toString();
101
- };
102
- /**
103
- * Inverse of `midnightApyFromPrice`: the loan-per-unit price a borrow APY implies,
104
- * price = (1 + rate)^(−ttmDays / 365).
105
- *
106
- * This is what turns an absolute rate ceiling into an on-chain `maxUnits` cap (units = assets / price),
107
- * and equally the principal a unit of borrow power is worth — Midnight debt is recorded at its maturity
108
- * face value, so borrowing the full limit as principal would overshoot it by the interest.
109
- */
110
- export const midnightPriceFromApy = (ratePercent, ttmDays) => {
111
- const rate = new Dec(ratePercent);
112
- const ttm = new Dec(ttmDays);
113
- if (rate.lte(0) || ttm.lte(0))
114
- return '1';
115
- return new Dec(1).div(new Dec(1).add(rate.div(100)).pow(ttm.div(365))).toString();
116
- };
117
93
  /**
118
94
  * Coerce a slippage into what the quote endpoint accepts: 0.1–100 with at most one decimal place. The
119
95
  * validation is lexical, so a computed value (`4.15066671050631467`) is rejected outright — without this
@@ -159,26 +135,20 @@ export const getMorphoMidnightUserBorrowInfo = (account, marketId, loanTokenSymb
159
135
  */
160
136
  export const getMorphoMidnightMarketBook = (market_1, network_1, ...args_1) => __awaiter(void 0, [market_1, network_1, ...args_1], void 0, function* (market, network, side = 'bids') {
161
137
  var _a;
138
+ if (isTenorMidnightMarket(market)) {
139
+ return getTenorMarketBook(market, network, side);
140
+ }
162
141
  const loanSymbol = getAssetInfoByAddress(market.loanToken, network).symbol;
163
142
  const res = yield fetch(`${MIDNIGHT_API_BASE}/books/${market.marketId}`, { signal: AbortSignal.timeout(MIDNIGHT_BOOK_TIMEOUT) });
164
143
  if (!res.ok)
165
144
  throw new Error(`Midnight book request failed for ${market.value} (${res.status})`);
166
145
  const json = yield res.json();
167
146
  const ttmDays = midnightTimeToMaturityDays(market.maturity);
168
- const bestFirst = side === 'asks' ? -1 : 1;
169
- const offers = (((_a = json === null || json === void 0 ? void 0 : json.data) === null || _a === void 0 ? void 0 : _a[side]) || [])
170
- .map((offer) => ({
147
+ const offers = (((_a = json === null || json === void 0 ? void 0 : json.data) === null || _a === void 0 ? void 0 : _a[side]) || []).map((offer) => ({
171
148
  rate: midnightApyFromPrice(new Dec(offer.price).div(WAD), ttmDays),
172
149
  liquidity: assetAmountInEth(offer.assets, loanSymbol),
173
- }))
174
- .sort((a, b) => new Dec(a.rate).minus(b.rate).mul(bestFirst).toNumber());
175
- if (offers.length === 0)
176
- return null;
177
- return {
178
- bestRate: offers[0].rate,
179
- totalLiquidity: offers.reduce((sum, offer) => sum.add(offer.liquidity), new Dec(0)).toString(),
180
- offers,
181
- };
150
+ }));
151
+ return buildMidnightParsedBook(offers, side);
182
152
  });
183
153
  // The API says why a quote failed — NOT_FOUND (market matured or not open yet), INSUFFICIENT_LIQUIDITY
184
154
  // (book can't fill the size), VALIDATION_ERROR (bad param, with the offending field in `details`).
@@ -189,8 +159,9 @@ const midnightQuoteError = (error) => {
189
159
  return reason ? `Morpho Midnight quote unavailable: ${reason}` : 'Morpho Midnight quote unavailable';
190
160
  };
191
161
  // The raw quote both sides share: prices descaled from WAD, everything else forwarded verbatim.
192
- const fetchMorphoMidnightQuote = (marketId, side, assetsRaw, slippagePercent) => __awaiter(void 0, void 0, void 0, function* () {
193
- const url = `${MIDNIGHT_API_BASE}/books/${marketId}/${side}/quote?assets=${assetsRaw}&slippage=${midnightSlippageParam(slippagePercent)}`;
162
+ const fetchMorphoMidnightQuote = (marketId, side, size, slippagePercent) => __awaiter(void 0, void 0, void 0, function* () {
163
+ const [sizeParam, sizeValue] = 'assets' in size ? ['assets', size.assets] : ['units', size.units];
164
+ const url = `${MIDNIGHT_API_BASE}/books/${marketId}/${side}/quote?${sizeParam}=${sizeValue}&slippage=${midnightSlippageParam(slippagePercent)}`;
194
165
  const res = yield fetch(url, { signal: AbortSignal.timeout(LONGER_TIMEOUT) });
195
166
  const json = yield res.json();
196
167
  const d = json === null || json === void 0 ? void 0 : json.data;
@@ -220,9 +191,15 @@ const fetchMorphoMidnightQuote = (marketId, side, assetsRaw, slippagePercent) =>
220
191
  *
221
192
  * A `maxBorrowRate` below `estBorrowRate` yields `maxUnits < newUnits` — the borrow would revert on-chain.
222
193
  * Compare the two before submitting and tell the user their ceiling is under the market rate.
194
+ *
195
+ * Tenor-hosted markets (see `isTenorMidnightMarket`) are quoted against Tenor's router instead of Morpho's
196
+ * book API. Slippage is applied locally to `maxUnits`; `taker` is the position owner (the smart wallet).
223
197
  */
224
- export const getMorphoMidnightBorrowQuote = (marketId, assetsRaw, slippagePercent, maturity, maxBorrowRate) => __awaiter(void 0, void 0, void 0, function* () {
225
- const quote = yield fetchMorphoMidnightQuote(marketId, 'bids', assetsRaw, slippagePercent);
198
+ export const getMorphoMidnightBorrowQuote = (marketId, assetsRaw, slippagePercent, maturity, maxBorrowRate, taker) => __awaiter(void 0, void 0, void 0, function* () {
199
+ if (isTenorMidnightMarket(marketId)) {
200
+ return getTenorBorrowQuote(marketId, assetsRaw, slippagePercent, maturity, maxBorrowRate, taker);
201
+ }
202
+ const quote = yield fetchMorphoMidnightQuote(marketId, 'bids', { assets: assetsRaw }, slippagePercent);
226
203
  const { bestPrice, worstPrice } = quote;
227
204
  const ttmDays = midnightTimeToMaturityDays(maturity);
228
205
  const estBorrowRate = midnightApyFromPrice(bestPrice, ttmDays);
@@ -257,8 +234,11 @@ export const getMorphoMidnightBorrowQuote = (marketId, assetsRaw, slippagePercen
257
234
  * A `minPaybackRate` above `estPaybackRate` yields `minUnits > newUnits` — the payback would revert
258
235
  * on-chain. Compare the two before submitting and tell the user their floor is over the market rate.
259
236
  */
260
- export const getMorphoMidnightPaybackQuote = (marketId, assetsRaw, slippagePercent, maturity, minPaybackRate) => __awaiter(void 0, void 0, void 0, function* () {
261
- const quote = yield fetchMorphoMidnightQuote(marketId, 'asks', assetsRaw, slippagePercent);
237
+ export const getMorphoMidnightPaybackQuote = (marketId, assetsRaw, slippagePercent, maturity, minPaybackRate, taker) => __awaiter(void 0, void 0, void 0, function* () {
238
+ if (isTenorMidnightMarket(marketId)) {
239
+ return getTenorPaybackQuote(marketId, assetsRaw, slippagePercent, maturity, minPaybackRate, taker);
240
+ }
241
+ const quote = yield fetchMorphoMidnightQuote(marketId, 'asks', { assets: assetsRaw }, slippagePercent);
262
242
  const { bestPrice, worstPrice } = quote;
263
243
  const ttmDays = midnightTimeToMaturityDays(maturity);
264
244
  const estPaybackRate = midnightApyFromPrice(bestPrice, ttmDays);
@@ -275,3 +255,40 @@ export const getMorphoMidnightPaybackQuote = (marketId, assetsRaw, slippagePerce
275
255
  newUnits,
276
256
  minUnits });
277
257
  });
258
+ /**
259
+ * Payback quoted against a **units** target instead of a spend: "retire exactly these debt units, and tell
260
+ * me what that costs". The sibling of `getMorphoMidnightPaybackQuote` in every other respect — same ask
261
+ * side, same rate floor, same offer list — but with the two amounts swapped, so the on-chain guard becomes
262
+ * a ceiling on assets spent rather than a floor on units bought.
263
+ *
264
+ * This is what a **full close** must be sized with. Retiring N units costs less than N loan tokens before
265
+ * maturity, so quoting the close as a spend of the debt's face value asks the book for more depth than the
266
+ * close needs (and can be refused for liquidity that is in fact there), and caps the taker's spend at more
267
+ * than the position is worth. Callers that cannot refund an overspend — a taker calling the bundler
268
+ * directly, rather than through an action contract that sweeps the remainder back — need this quote.
269
+ *
270
+ * A `minPaybackRate` above `estPaybackRate` yields `maxAssets < newAssets`: the ceiling is under what the
271
+ * buy costs and it would revert on-chain, the mirror of the assets-target quote's `minUnits > newUnits`.
272
+ */
273
+ export const getMorphoMidnightPaybackUnitsQuote = (marketId, unitsRaw, slippagePercent, maturity, minPaybackRate, taker) => __awaiter(void 0, void 0, void 0, function* () {
274
+ if (isTenorMidnightMarket(marketId)) {
275
+ return getTenorPaybackUnitsQuote(marketId, unitsRaw, slippagePercent, maturity, minPaybackRate, taker);
276
+ }
277
+ const quote = yield fetchMorphoMidnightQuote(marketId, 'asks', { units: unitsRaw }, slippagePercent);
278
+ const { bestPrice, worstPrice } = quote;
279
+ const ttmDays = midnightTimeToMaturityDays(maturity);
280
+ const estPaybackRate = midnightApyFromPrice(bestPrice, ttmDays);
281
+ const capPrice = minPaybackRate !== undefined && new Dec(minPaybackRate).gt(0)
282
+ ? midnightPriceFromApy(minPaybackRate, ttmDays)
283
+ : worstPrice;
284
+ const minRate = midnightApyFromPrice(capPrice, ttmDays);
285
+ // Rounded UP on both counts — the mirror of the assets-target quote's ROUND_DOWN. Here the figures are
286
+ // what the taker SPENDS, so a value rounded down understates the cost by a base unit and would leave the
287
+ // buy short of the units it was asked for.
288
+ const newAssets = new Dec(unitsRaw).mul(bestPrice).toFixed(0, Dec.ROUND_UP);
289
+ const maxAssets = new Dec(unitsRaw).mul(capPrice).toFixed(0, Dec.ROUND_UP);
290
+ return Object.assign(Object.assign({}, quote), { estPaybackRate,
291
+ minRate,
292
+ newAssets,
293
+ maxAssets });
294
+ });
@@ -0,0 +1,11 @@
1
+ import Dec from 'decimal.js';
2
+ import { MorphoMidnightBookOffer, MorphoMidnightBookSide, MorphoMidnightParsedBook } from '../../types';
3
+ export declare const midnightTimeToMaturityDays: (maturity: number, atSeconds?: number) => number;
4
+ export declare const midnightApyFromPrice: (price: Dec.Value, ttmDays: Dec.Value) => string;
5
+ /**
6
+ * Inverse of `midnightApyFromPrice`: the loan-per-unit price a borrow APY implies,
7
+ * price = (1 + rate)^(−ttmDays / 365).
8
+ */
9
+ export declare const midnightPriceFromApy: (ratePercent: Dec.Value, ttmDays: Dec.Value) => string;
10
+ export declare const midnightBookBestFirst: (side: MorphoMidnightBookSide) => 1 | -1;
11
+ export declare const buildMidnightParsedBook: (offers: MorphoMidnightBookOffer[], side: MorphoMidnightBookSide) => MorphoMidnightParsedBook | null;
@@ -0,0 +1,38 @@
1
+ import Dec from 'decimal.js';
2
+ import { SECONDS_PER_DAY } from '../../constants';
3
+ const nowInSeconds = () => Math.floor(Date.now() / 1000);
4
+ // Days remaining until maturity, optionally measured at a past timestamp (for historical fills).
5
+ export const midnightTimeToMaturityDays = (maturity, atSeconds = nowInSeconds()) => new Dec(maturity).sub(atSeconds).div(SECONDS_PER_DAY).toNumber();
6
+ // Annualize a fixed-term discount price into an APY percent: (1 / price)^(365 / ttmDays) − 1.
7
+ // `price` is loan-per-unit (assets received / units owed), so 1/price ≥ 1.
8
+ export const midnightApyFromPrice = (price, ttmDays) => {
9
+ const p = new Dec(price);
10
+ const ttm = new Dec(ttmDays);
11
+ if (p.lte(0) || ttm.lte(0))
12
+ return '0';
13
+ return new Dec(1).div(p).pow(new Dec(365).div(ttm)).sub(1)
14
+ .mul(100)
15
+ .toString();
16
+ };
17
+ /**
18
+ * Inverse of `midnightApyFromPrice`: the loan-per-unit price a borrow APY implies,
19
+ * price = (1 + rate)^(−ttmDays / 365).
20
+ */
21
+ export const midnightPriceFromApy = (ratePercent, ttmDays) => {
22
+ const rate = new Dec(ratePercent);
23
+ const ttm = new Dec(ttmDays);
24
+ if (rate.lte(0) || ttm.lte(0))
25
+ return '1';
26
+ return new Dec(1).div(new Dec(1).add(rate.div(100)).pow(ttm.div(365))).toString();
27
+ };
28
+ export const midnightBookBestFirst = (side) => (side === 'asks' ? -1 : 1);
29
+ export const buildMidnightParsedBook = (offers, side) => {
30
+ const bestFirst = [...offers].sort((a, b) => new Dec(a.rate).minus(b.rate).mul(midnightBookBestFirst(side)).toNumber());
31
+ if (bestFirst.length === 0)
32
+ return null;
33
+ return {
34
+ bestRate: bestFirst[0].rate,
35
+ totalLiquidity: bestFirst.reduce((sum, offer) => sum.add(offer.liquidity), new Dec(0)).toString(),
36
+ offers: bestFirst,
37
+ };
38
+ };
@@ -0,0 +1,125 @@
1
+ import Dec from 'decimal.js';
2
+ import { MorphoMidnightBookSide, MorphoMidnightMarketData, MorphoMidnightParsedBook, NetworkNumber } from '../../types';
3
+ import type { MorphoMidnightBorrowQuote, MorphoMidnightPaybackQuote, MorphoMidnightPaybackUnitsQuote } from './index';
4
+ type TenorBookKey = 'asks' | 'bids';
5
+ export declare const tenorBookKeyFor: (side: MorphoMidnightBookSide) => TenorBookKey;
6
+ export declare const tenorBookRateToApyPercent: (rate: Dec.Value) => string;
7
+ export declare const tenorCapPrice: (bestPrice: string, slippagePercent: Dec.Value, direction: 1 | -1, ttmDays: Dec.Value, boundRate?: Dec.Value) => string;
8
+ interface TenorOfferCollateral {
9
+ token: string;
10
+ lltv: string | number;
11
+ liquidation_cursor: string | number;
12
+ oracle: string;
13
+ }
14
+ interface TenorOffer {
15
+ start: string | number;
16
+ group?: string;
17
+ callback?: string;
18
+ tick: string | number;
19
+ chain_id: string | number;
20
+ maturity: string | number;
21
+ buy: boolean;
22
+ maker: string;
23
+ loan_token_address: string;
24
+ callback_data?: string;
25
+ expiry: string | number;
26
+ ratifier?: string;
27
+ collaterals: TenorOfferCollateral[];
28
+ continuous_fee_cap: string | number;
29
+ enter_gate: string;
30
+ liquidator_gate: string;
31
+ max_assets: string | number;
32
+ max_units: string | number;
33
+ ratifier_data?: string;
34
+ rcf_threshold: string | number;
35
+ receiver_if_maker_is_seller?: string;
36
+ reduce_only: boolean;
37
+ }
38
+ interface TenorOfferFill {
39
+ units: string | number;
40
+ offer: TenorOffer;
41
+ }
42
+ /**
43
+ * Tenor's offer JSON is flat (market fields live on the offer). Morpho's is nested, and that nested
44
+ * shape is what the app encodes for `Midnight.take`. Map Tenor into that shape so recipes stay on one encoder.
45
+ */
46
+ export declare const tenorOfferToApiOffer: (offer: TenorOffer) => {
47
+ market: {
48
+ chain_id: string | number;
49
+ midnight: "0xAdedD8ab6dE832766Fedf0FaC4992E5C4D3EA18A";
50
+ loan_token: string;
51
+ collaterals: TenorOfferCollateral[];
52
+ maturity: string | number;
53
+ rcf_threshold: string | number;
54
+ enter_gate: string;
55
+ liquidator_gate: string;
56
+ };
57
+ buy: boolean;
58
+ maker: string;
59
+ start: string | number;
60
+ expiry: string | number;
61
+ tick: string | number;
62
+ group: string;
63
+ callback: string;
64
+ callback_data: string;
65
+ receiver_if_maker_is_seller: string;
66
+ ratifier: string;
67
+ reduce_only: boolean;
68
+ max_units: string | number;
69
+ max_assets: string | number;
70
+ continuous_fee_cap: string | number;
71
+ };
72
+ export declare const tenorOfferFillToApiFill: (fill: TenorOfferFill) => {
73
+ units: string | number;
74
+ offer: {
75
+ market: {
76
+ chain_id: string | number;
77
+ midnight: "0xAdedD8ab6dE832766Fedf0FaC4992E5C4D3EA18A";
78
+ loan_token: string;
79
+ collaterals: TenorOfferCollateral[];
80
+ maturity: string | number;
81
+ rcf_threshold: string | number;
82
+ enter_gate: string;
83
+ liquidator_gate: string;
84
+ };
85
+ buy: boolean;
86
+ maker: string;
87
+ start: string | number;
88
+ expiry: string | number;
89
+ tick: string | number;
90
+ group: string;
91
+ callback: string;
92
+ callback_data: string;
93
+ receiver_if_maker_is_seller: string;
94
+ ratifier: string;
95
+ reduce_only: boolean;
96
+ max_units: string | number;
97
+ max_assets: string | number;
98
+ continuous_fee_cap: string | number;
99
+ };
100
+ ratifier_data: string;
101
+ market_id: string;
102
+ };
103
+ interface TenorBookBucket {
104
+ rate: number | string;
105
+ liquidity: number | string;
106
+ cumulative_liquidity?: number | string;
107
+ }
108
+ interface TenorBookSidePayload {
109
+ buckets?: TenorBookBucket[];
110
+ }
111
+ interface TenorBookResponse {
112
+ asks?: TenorBookSidePayload | TenorBookBucket[];
113
+ bids?: TenorBookSidePayload | TenorBookBucket[];
114
+ }
115
+ /** The levels resting on the Tenor side a taker of `side` fills, in raw loan-token base units. */
116
+ export declare const parseTenorBookSide: (json: TenorBookResponse, side: MorphoMidnightBookSide) => {
117
+ rate: string;
118
+ liquidityRaw: string;
119
+ }[];
120
+ export declare const parseTenorOrderBook: (json: TenorBookResponse, side: MorphoMidnightBookSide, loanSymbol: string) => MorphoMidnightParsedBook | null;
121
+ export declare const getTenorMarketBook: (market: MorphoMidnightMarketData, network: NetworkNumber, side?: MorphoMidnightBookSide) => Promise<MorphoMidnightParsedBook | null>;
122
+ export declare const getTenorBorrowQuote: (marketId: string, assetsRaw: string, slippagePercent: Dec.Value, maturity: number, maxBorrowRate?: Dec.Value, taker?: string, network?: NetworkNumber) => Promise<MorphoMidnightBorrowQuote>;
123
+ export declare const getTenorPaybackQuote: (marketId: string, assetsRaw: string, slippagePercent: Dec.Value, maturity: number, minPaybackRate?: Dec.Value, taker?: string, network?: NetworkNumber) => Promise<MorphoMidnightPaybackQuote>;
124
+ export declare const getTenorPaybackUnitsQuote: (marketId: string, unitsRaw: string, slippagePercent: Dec.Value, maturity: number, minPaybackRate?: Dec.Value, taker?: string, network?: NetworkNumber) => Promise<MorphoMidnightPaybackUnitsQuote>;
125
+ export {};