@1delta/margin-fetcher 0.0.319 → 0.0.320

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @1delta/margin-fetcher
2
2
 
3
- Multi-protocol lending data fetcher supporting Morpho Blue, Aave V2/V3, Compound V2/V3, Euler, Init, and Lista DAO. Provides public market data (rates, TVL, configs) and per-user position data (balances, shares, collateral) in a unified format.
3
+ Multi-protocol lending data fetcher supporting Morpho Blue, Aave V2/V3, Compound V2/V3, Euler, Init, Lista DAO, and Morpho Midnight. Provides public market data (rates, TVL, configs) and per-user position data (balances, shares, collateral) in a unified format.
4
4
 
5
5
  ## How Morpho Blue Works
6
6
 
@@ -299,3 +299,36 @@ Brokered markets emit **no variable debt** — the position's `debt` is `0`, the
299
299
 
300
300
  > **Note:** `terms[]` currently itemizes only fixed loans. The dynamic position (post-refinance) is
301
301
  > included in the `debtStable` total but not yet surfaced as its own repayable term.
302
+
303
+ ## Morpho Midnight extension
304
+
305
+ Morpho Midnight is a Base-only, **fixed-rate / fixed-maturity** lending protocol built on an
306
+ **order book of off-chain signed offers** (zero-coupon credit/debt units) — it is **NOT a Morpho
307
+ Blue fork**, so it is modeled as its own `'midnight'` provider (`Lender.MORPHO_MIDNIGHT`,
308
+ `isMidnight`), not `isMorphoType`. There is no pool and no utilization curve: rates are derived
309
+ from each offer's `tick` → price and its time-to-maturity, knowable only via the API. Each
310
+ maturity is its own isolated market, keyed `MORPHO_MIDNIGHT_<marketId>`.
311
+
312
+ ### API-first fetching
313
+
314
+ Unlike Morpho Blue's GraphQL-then-on-chain hybrid, Midnight public data is **API-only** — there
315
+ is no on-chain public fallback in `fetchLenderAll`. The fetcher lives in
316
+ [`lending/public-data/midnight/`](src/lending/public-data/midnight/):
317
+
318
+ - **`apiClient` / `fetchPublic`** — an `ApiBookSource` reads the order book per market
319
+ (`GET /books/{id}`) from the Midnight API. The base URL resolves override → config → hosted
320
+ default (`https://api.morpho.org/v0/midnight`); the worker can point it at a dev API via
321
+ `MIDNIGHT_API_BASE` (see `data-sdk` `setMidnightApiBase`).
322
+ - **`math.ts`** — ported `TickLib` (`tickToApr` etc.), bit-verified against `@morpho-org/midnight-sdk`.
323
+ - **`convertPublic`** — normalizes the book into a `MorphoGeneralPublicResponse` keyed by
324
+ `MORPHO_MIDNIGHT_<id>`, emitting the fixed borrow/supply APR and a single-term
325
+ `params.market.terms[]` (`{termId, durationSecs, durationDays, apr}`) carrying the maturity — so
326
+ the maturity flows through the same generic `terms[]` path as Lista's fixed loans. Units are in
327
+ the **loan token's decimals** (not 18): `assets = units × price / WAD`, so `units ≈ assets`.
328
+
329
+ User positions (`{credit, debt, collateral[128], collateralBitmap}`) are read via multicall in
330
+ [`lending/user-data/midnight/`](src/lending/user-data/midnight/), one `UserData` per market.
331
+
332
+ > **Scope:** the fetcher and direct spot actions (supply collateral / borrow-via-`take` / repay /
333
+ > withdraw) are wired. Composer-routed leverage and native ETH are deferred — see
334
+ > [`calldata-sdk/src/evm/generic/midnight/COMPOSER_PLAN.md`](../calldata-sdk/src/evm/generic/midnight/COMPOSER_PLAN.md).
package/dist/index.js CHANGED
@@ -20885,9 +20885,63 @@ function tickToApr(tick, timeToMaturity) {
20885
20885
  function tickToAprNumber(tick, timeToMaturity) {
20886
20886
  return Number(tickToApr(tick, timeToMaturity)) / 1e18;
20887
20887
  }
20888
+ function midnightMaxLif(lltv, liquidationCursor) {
20889
+ const denom = WAD2 - liquidationCursor * (WAD2 - lltv) / WAD2;
20890
+ if (denom <= 0n) return 0n;
20891
+ return WAD2 * WAD2 / denom;
20892
+ }
20893
+ function midnightLiquidationPenaltyNumber(lltv, liquidationCursor) {
20894
+ if (lltv <= 0n || liquidationCursor <= 0n) return 0;
20895
+ const lif = midnightMaxLif(lltv, liquidationCursor);
20896
+ if (lif <= WAD2) return 0;
20897
+ return Number(lif - WAD2) / 1e18;
20898
+ }
20899
+ var SETTLEMENT_FEE_BREAKPOINTS_SECS = [
20900
+ 0,
20901
+ 86400,
20902
+ 604800,
20903
+ 2592e3,
20904
+ 7776e3,
20905
+ 15552e3,
20906
+ 31104e3
20907
+ ];
20908
+ var CBP_TO_FRACTION = 1e-6;
20909
+ function midnightSettlementFeeForTtm(settlementFeeCbp, ttmSecs) {
20910
+ const cbp = settlementFeeCbp;
20911
+ if (!cbp || cbp.length < 7) return 0;
20912
+ const bps = SETTLEMENT_FEE_BREAKPOINTS_SECS;
20913
+ if (ttmSecs <= 0) return cbp[0] * CBP_TO_FRACTION;
20914
+ if (ttmSecs >= bps[6]) return cbp[6] * CBP_TO_FRACTION;
20915
+ let i = 0;
20916
+ while (i < 6 && ttmSecs > bps[i + 1]) i++;
20917
+ const spanLo = bps[i];
20918
+ const spanHi = bps[i + 1];
20919
+ const t = (ttmSecs - spanLo) / (spanHi - spanLo);
20920
+ const feeCbp = cbp[i] + (cbp[i + 1] - cbp[i]) * t;
20921
+ return feeCbp * CBP_TO_FRACTION;
20922
+ }
20923
+ function midnightContinuousFeeAprPercent(continuousFeeRaw) {
20924
+ if (continuousFeeRaw == null) return 0;
20925
+ let raw;
20926
+ try {
20927
+ raw = typeof continuousFeeRaw === "bigint" ? continuousFeeRaw : BigInt(continuousFeeRaw);
20928
+ } catch {
20929
+ return 0;
20930
+ }
20931
+ if (raw <= 0n) return 0;
20932
+ return Number(raw * SECONDS_PER_YEAR7) / 1e18 * 100;
20933
+ }
20888
20934
 
20889
20935
  // src/lending/public-data/midnight/convertPublic.ts
20890
20936
  var nowSec = () => Math.floor(Date.now() / 1e3);
20937
+ function toBigIntOr0(v) {
20938
+ if (v === void 0 || v === null || v === "") return 0n;
20939
+ try {
20940
+ return typeof v === "bigint" ? v : BigInt(v);
20941
+ } catch {
20942
+ return 0n;
20943
+ }
20944
+ }
20891
20945
  function midnightLenderKey(marketId) {
20892
20946
  return "MORPHO_MIDNIGHT_" + marketId.slice(2).toUpperCase();
20893
20947
  }
@@ -20916,6 +20970,13 @@ function convertMidnightMarketsToResponse(raw, chainId, prices = {}, _additional
20916
20970
  const ttmBig = BigInt(Math.max(0, ttm));
20917
20971
  const supplyAprPct = top?.bestSupplyTick != null && !matured ? tickToAprNumber(top.bestSupplyTick, ttmBig) * 100 : 0;
20918
20972
  const borrowAprPct = top?.bestBorrowTick != null && !matured ? tickToAprNumber(top.bestBorrowTick, ttmBig) * 100 : 0;
20973
+ const settlementFee = midnightSettlementFeeForTtm(
20974
+ config.settlementFeeCbp,
20975
+ ttm
20976
+ );
20977
+ const continuousFeeAprPct = midnightContinuousFeeAprPercent(
20978
+ config.continuousFee
20979
+ );
20919
20980
  const terms = borrowAprPct > 0 && !matured ? [
20920
20981
  {
20921
20982
  termId: 0,
@@ -20926,10 +20987,16 @@ function convertMidnightMarketsToResponse(raw, chainId, prices = {}, _additional
20926
20987
  ] : void 0;
20927
20988
  const loanAddr = config.loanToken.toLowerCase();
20928
20989
  const loanAsset = tokens[loanAddr];
20929
- const loanKey = toOracleKey(loanAsset?.assetGroup) ?? toGenericPriceKey(loanAsset?.address ?? config.loanToken, chainId);
20990
+ const loanKey = toOracleKey(loanAsset?.assetGroup) || toGenericPriceKey(loanAddr, chainId);
20930
20991
  const loanPrice = prices[loanKey] ?? 0;
20931
- const supplyLiquidity = formatNr(top?.supplyDepthAssets ?? 0n, config.loanDecimals);
20932
- const borrowLiquidity = formatNr(top?.borrowDepthAssets ?? 0n, config.loanDecimals);
20992
+ const supplyLiquidity = formatNr(
20993
+ top?.supplyDepthAssets ?? 0n,
20994
+ config.loanDecimals
20995
+ );
20996
+ const borrowLiquidity = formatNr(
20997
+ top?.borrowDepthAssets ?? 0n,
20998
+ config.loanDecimals
20999
+ );
20933
21000
  const entry = { data: {} };
20934
21001
  const loanUid = createMarketUid(chainId, m, loanAddr);
20935
21002
  entry.data[loanUid] = {
@@ -20954,6 +21021,11 @@ function convertMidnightMarketsToResponse(raw, chainId, prices = {}, _additional
20954
21021
  // Midnight is fixed-rate: expose the fixed borrow APR on stableBorrowRate
20955
21022
  // too, so fixed-rate consumers pick it up like a term product.
20956
21023
  stableBorrowRate: borrowAprPct,
21024
+ // Mutable per-market fees (display convenience; authoritative raw values
21025
+ // live on params.market). continuousFeeApr is a %/yr lender-side haircut;
21026
+ // settlementFee is the effective fraction at the current TTM.
21027
+ continuousFeeApr: continuousFeeAprPct,
21028
+ settlementFee,
20957
21029
  intrinsicYield: 0,
20958
21030
  rewards: void 0,
20959
21031
  decimals: config.loanDecimals,
@@ -20984,7 +21056,8 @@ function convertMidnightMarketsToResponse(raw, chainId, prices = {}, _additional
20984
21056
  config.collateralParams.forEach((c) => {
20985
21057
  const collAddr = c.token.toLowerCase();
20986
21058
  const ltv = parseLtv(c.lltv);
20987
- const liquidationPenalty = liquidationPenaltyFromLltv(ltv);
21059
+ const cursor = toBigIntOr0(c.liquidationCursor);
21060
+ const liquidationPenalty = cursor > 0n ? midnightLiquidationPenaltyNumber(toBigIntOr0(c.lltv), cursor) : liquidationPenaltyFromLltv(ltv);
20988
21061
  const collUid = createMarketUid(chainId, m, collAddr);
20989
21062
  entry.data[collUid] = {
20990
21063
  marketUid: collUid,
@@ -21048,6 +21121,13 @@ function convertMidnightMarketsToResponse(raw, chainId, prices = {}, _additional
21048
21121
  // Midnight-specific extras (consumed by calldata / worker-api resolvers)
21049
21122
  maturity: config.maturity,
21050
21123
  rcfThreshold: config.rcfThreshold,
21124
+ // Mutable, on-chain-snapshotted fees. Raw values are authoritative for
21125
+ // downstream ingest (yield-tracer); the derived `continuousFeeApr` (%)
21126
+ // and `settlementFee` (fraction at current TTM) are convenience fields.
21127
+ settlementFeeCbp: config.settlementFeeCbp,
21128
+ continuousFee: config.continuousFee,
21129
+ continuousFeeApr: continuousFeeAprPct,
21130
+ settlementFee,
21051
21131
  enterGate: config.enterGate,
21052
21132
  liquidatorGate: config.liquidatorGate,
21053
21133
  collateralParams: config.collateralParams