@1delta/margin-fetcher 5.0.6 → 5.0.8

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/dist/index.d.ts CHANGED
@@ -8416,7 +8416,16 @@ interface TermAssetRef {
8416
8416
  * `tags.ts` — never hand-written per lender, so they cannot drift from the
8417
8417
  * numbers they summarize.
8418
8418
  */
8419
- type TermTag = Open<'fixed-rate' | 'variable-rate' | 'user-set-rate' | 'zero-interest' | 'prepaid-interest' | 'nav-accrual' | 'has-maturity' | 'perpetual' | 'rolling-duration' | 'static-debt' | 'accruing-debt' | 'time-liquidation' | 'price-liquidation' | 'redeemable' | 'no-liquidation' | 'full-collateral-seizure' | 'early-exit-free' | 'early-exit-penalty' | 'early-exit-discount' | 'exit-instant' | 'exit-capped' | 'exit-cooldown' | 'exit-queued' | 'exit-market-sale' | 'exit-may-be-impossible' | 'permissioned' | 'capped' | 'cap-full' | 'first-loss' | 'socialized-loss' | 'physical-delivery' | 'undercollateralized' | 'nav-attested' | 'immutable' | 'no-timelock' | 'eoa-controlled' | 'points-rewards'
8419
+ type TermTag = Open<'fixed-rate' | 'variable-rate' | 'user-set-rate' | 'zero-interest' | 'prepaid-interest' | 'nav-accrual' | 'has-maturity' | 'perpetual' | 'rolling-duration' | 'static-debt' | 'accruing-debt' | 'time-liquidation' | 'price-liquidation' | 'redeemable' | 'no-liquidation' | 'full-collateral-seizure' | 'early-exit-free' | 'early-exit-penalty' | 'early-exit-discount' | 'exit-instant' | 'exit-capped' | 'exit-cooldown'
8420
+ /**
8421
+ * The DEBT cannot be repaid for a period after opening or topping up.
8422
+ *
8423
+ * Deliberately distinct from `exit-cooldown` (a supply-side withdrawal
8424
+ * delay): this one means the position cannot be closed, deleveraged or
8425
+ * migrated out — and, on the lender that has it, that an incoming
8426
+ * liquidation cannot be averted by repaying.
8427
+ */
8428
+ | 'repay-locked' | 'exit-queued' | 'exit-market-sale' | 'exit-may-be-impossible' | 'permissioned' | 'capped' | 'cap-full' | 'first-loss' | 'socialized-loss' | 'physical-delivery' | 'undercollateralized' | 'nav-attested' | 'immutable' | 'no-timelock' | 'eoa-controlled' | 'points-rewards'
8420
8429
  /**
8421
8430
  * The headline yield is MOSTLY the asset's own (staking / RWA / savings)
8422
8431
  * yield, not interest this market pays. Set at >= 50 % of `aprTotal`.
@@ -8799,8 +8808,17 @@ interface LiquidationTerms {
8799
8808
  ltv?: number;
8800
8809
  /** Threshold at which liquidation becomes possible. */
8801
8810
  liquidationLtv?: number;
8802
- /** Fraction of repaid debt paid to the liquidator on top of par. */
8803
- penalty: number;
8811
+ /**
8812
+ * Fraction of repaid debt paid to the liquidator on top of par.
8813
+ *
8814
+ * OPTIONAL, and the distinction is load-bearing: `0` means "the liquidator
8815
+ * gets no bonus", `undefined` means "we do not know it". The first cut
8816
+ * defaulted the unknown case to `0` and every market on `/lending/latest`
8817
+ * rendered a confident "Liquidator takes debt repaid + 0%" — the origin's
8818
+ * `market_config` had no penalty column at all, so NOTHING was known. A row
8819
+ * we cannot fill must be absent, not zero.
8820
+ */
8821
+ penalty?: number;
8804
8822
  closeFactor: number;
8805
8823
  targetHealthFactor?: number;
8806
8824
  /**
package/dist/index.js CHANGED
@@ -62293,9 +62293,11 @@ function deriveBorrowTags(borrow, market = {}) {
62293
62293
  if (borrow.liquidation.redeemable) push(out, "redeemable");
62294
62294
  if (borrow.liquidation.seizure === "full-collateral")
62295
62295
  push(out, "full-collateral-seizure");
62296
+ const repayLocked = (borrow.exit.cooldownSecs ?? 0) > 0;
62297
+ if (repayLocked) push(out, "repay-locked");
62296
62298
  switch (borrow.exit.earlyRepay) {
62297
62299
  case "free":
62298
- push(out, "early-exit-free");
62300
+ if (!repayLocked) push(out, "early-exit-free");
62299
62301
  break;
62300
62302
  case "penalty":
62301
62303
  push(out, "early-exit-penalty");
@@ -62451,6 +62453,9 @@ function borrowHeadline(b) {
62451
62453
  ) : `${rateLabel(b)} ${pct(b.rate.apr)}`;
62452
62454
  const mat = b.maturity.kind === "perpetual" ? "" : ` ${maturityPhrase(b.maturity)}`;
62453
62455
  const exit = earlyRepayPhrase[String(b.exit.earlyRepay)] ?? "see repay terms";
62456
+ if (b.exit.cooldownSecs) {
62457
+ return `${rate}${mat} \xB7 no repayment for ${duration(b.exit.cooldownSecs)}, then ${exit}`;
62458
+ }
62454
62459
  return `${rate}${mat} \xB7 ${exit}`;
62455
62460
  }
62456
62461
  function supplyDescription(s, sheet = {}) {
@@ -62535,8 +62540,9 @@ function borrowDescription(b) {
62535
62540
  "Liquidation here is triggered by TIME, not price \u2014 being late is the trigger, and being over-collateralised does not protect you."
62536
62541
  );
62537
62542
  } else if (b.liquidation.liquidationLtv != null) {
62543
+ const pen = b.liquidation.penalty;
62538
62544
  parts.push(
62539
- `Liquidation starts at ${pct(b.liquidation.liquidationLtv * 100)} LTV, with a ${pct(b.liquidation.penalty * 100)} penalty.`
62545
+ `Liquidation starts at ${pct(b.liquidation.liquidationLtv * 100)} LTV` + (pen != null ? `, with a ${pct(pen * 100)} penalty` : "") + "."
62540
62546
  );
62541
62547
  }
62542
62548
  return parts.join(" ");
@@ -63162,7 +63168,20 @@ function buildConstraints(input, siblings, acceptedCollateral) {
63162
63168
  }
63163
63169
  };
63164
63170
  }
63165
- function liquidationFrom(cfg, input) {
63171
+ function marketLltv(input) {
63172
+ const raw = input.market?.lltv;
63173
+ if (raw == null) return void 0;
63174
+ const n = Number(raw);
63175
+ if (!Number.isFinite(n) || n <= 0) return void 0;
63176
+ return n / 1e18;
63177
+ }
63178
+ function liquidationFrom(cfg, input, acceptedCollateral) {
63179
+ const lltv = marketLltv(input);
63180
+ const soleCollateral = acceptedCollateral?.count === 1 ? acceptedCollateral.items[0] : void 0;
63181
+ const isolated = lltv != null || !hasCrossMarginRisk(input.lender) && soleCollateral != null;
63182
+ const ownLtv = cfg?.borrowCollateralFactor;
63183
+ const ownLiqLtv = cfg?.collateralFactor;
63184
+ const ownPenalty = cfg?.liquidationPenalty;
63166
63185
  return {
63167
63186
  // The ordinary model: a liquidator repays part of the debt and takes
63168
63187
  // collateral plus a bonus. Adapters override for the four lenders where
@@ -63170,9 +63189,11 @@ function liquidationFrom(cfg, input) {
63170
63189
  model: "repay-seize",
63171
63190
  absorber: "liquidator",
63172
63191
  trigger: "price",
63173
- ltv: cfg?.borrowCollateralFactor,
63174
- liquidationLtv: cfg?.collateralFactor,
63175
- penalty: cfg?.liquidationPenalty ?? 0,
63192
+ ltv: ownLtv || (isolated ? soleCollateral?.ltv ?? lltv : void 0) || ownLtv,
63193
+ liquidationLtv: ownLiqLtv || (isolated ? soleCollateral?.liquidationLtv ?? lltv : void 0) || ownLiqLtv,
63194
+ // No `?? 0` — see `LiquidationTerms.penalty`. An unknown penalty is
63195
+ // undefined so the row is omitted rather than rendered as "+0%".
63196
+ penalty: ownPenalty || (isolated ? soleCollateral?.liquidationPenalty : void 0) || ownPenalty,
63176
63197
  closeFactor: cfg?.closeFactor ?? input.closeFactor ?? 1,
63177
63198
  targetHealthFactor: cfg?.targetHealthFactor ?? input.targetHealthFactor,
63178
63199
  seizure: "proportional",
@@ -63199,7 +63220,8 @@ function buildModes(input) {
63199
63220
  liquidation: {
63200
63221
  ltv: c.borrowCollateralFactor,
63201
63222
  liquidationLtv: c.collateralFactor,
63202
- penalty: c.liquidationPenalty ?? 0,
63223
+ // Same rule as the top-level penalty: unknown stays unknown.
63224
+ penalty: c.liquidationPenalty,
63203
63225
  closeFactor: c.closeFactor ?? input.closeFactor ?? 1,
63204
63226
  targetHealthFactor: c.targetHealthFactor ?? input.targetHealthFactor
63205
63227
  },
@@ -63294,6 +63316,7 @@ function buildBorrow(input, now, siblings) {
63294
63316
  const rate = buildRate(input, "borrow");
63295
63317
  const maturity = buildMaturity(input, now);
63296
63318
  const fees = buildFees(input, "borrow");
63319
+ const acceptedCollateral = buildExposures2(input, siblings, "accepted");
63297
63320
  const borrow = {
63298
63321
  rate,
63299
63322
  maturity,
@@ -63308,8 +63331,8 @@ function buildBorrow(input, now, siblings) {
63308
63331
  minDebt: resolveMinDebt(input),
63309
63332
  fees: fees.filter((f) => f.when === "exit" || f.when === "late")
63310
63333
  },
63311
- liquidation: liquidationFrom(cfg, input),
63312
- acceptedCollateral: buildExposures2(input, siblings, "accepted"),
63334
+ liquidation: liquidationFrom(cfg, input, acceptedCollateral),
63335
+ acceptedCollateral,
63313
63336
  modes: buildModes(input),
63314
63337
  fees,
63315
63338
  counterparty: { kind: "pool", solvency: "overcollateralized" },
@@ -63633,6 +63656,16 @@ function supplyFindings(supply) {
63633
63656
  function borrowFindings(borrow) {
63634
63657
  const out = [];
63635
63658
  const liq = borrow.liquidation;
63659
+ if (borrow.exit.cooldownSecs) {
63660
+ out.push({
63661
+ severity: "critical",
63662
+ id: "repay-locked",
63663
+ side: "borrow",
63664
+ message: `The debt CANNOT be repaid for ${fmtSecs(
63665
+ borrow.exit.cooldownSecs
63666
+ )} after opening or topping up: the position cannot be closed, deleveraged or migrated out in that window, and an incoming liquidation cannot be averted by repaying (adding collateral still works).`
63667
+ });
63668
+ }
63636
63669
  if (liq.seizure === "full-collateral") {
63637
63670
  out.push({
63638
63671
  severity: "critical",
@@ -64924,7 +64957,7 @@ function validateTermSheet(sheet) {
64924
64957
  "full-collateral seizure must surface a tag or an implication"
64925
64958
  );
64926
64959
  }
64927
- if (l.penalty < 0 || l.penalty > 1)
64960
+ if (l.penalty != null && (l.penalty < 0 || l.penalty > 1))
64928
64961
  fail("penalty-range", `liquidation.penalty ${l.penalty} outside 0..1`);
64929
64962
  if (l.closeFactor <= 0 || l.closeFactor > 1)
64930
64963
  fail(