@1delta/margin-fetcher 5.0.0 → 5.0.1

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
@@ -8767,7 +8767,16 @@ interface AvailabilityTerms {
8767
8767
  gating: Open<'permissionless' | 'whitelist' | 'attestation' | 'kyc' | 'allowlist-contract'>;
8768
8768
  /** Absent ⇒ no window applies. NOT the same as `closed`. */
8769
8769
  window?: AuctionWindow;
8770
- /** Raw base units. */
8770
+ /**
8771
+ * Minimum position size to OPEN, in RAW base units of THIS side's asset.
8772
+ *
8773
+ * On the borrow side this is a minimum DEBT (Comet `baseBorrowMin`, Liquity
8774
+ * `minDebt`, Maker-fork `dust`). On the supply/collateral side it is a
8775
+ * minimum COLLATERAL — a genuinely different gate, and the only one some
8776
+ * lenders have: Frankencoin caps nothing on the debt but refuses a position
8777
+ * below `minimumCollateral`, and dropping under it closes the position
8778
+ * permanently.
8779
+ */
8771
8780
  minSize?: string;
8772
8781
  cap?: string;
8773
8782
  /** 0..1 — how full the cap is. */
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { parseAbi, keccak256, encodeAbiParameters, formatEther, BaseError, erc20Abi, stringToHex, pad, encodeFunctionData, formatUnits, isAddress, getAddress, decodeFunctionResult, decodeAbiParameters, concat, AbiEncodingLengthMismatchError, concatHex, toBytes, slice, hexToString, InvalidAddressError, boolToHex, integerRegex, numberToHex, bytesRegex, BytesSizeMismatchError, arrayRegex, UnsupportedPackedAbiType } from './chunk-YILYOOYB.js';
2
2
  import './chunk-BYTNVMX7.js';
3
3
  import './chunk-PR4QN5HX.js';
4
- import { Lender, isExactly, isMidnight, isTerm, isTermMax, isTeller, isListaDao, isLiquityFamily, isRiver, isInverse, isUsdd, isCompoundV3Type, isFluid, isGearboxV3, isLlamaLend, LIQUITY_FAMILY_LENDERS, isAaveType, isCompoundV3, isMultiMarket, isSiloV2Type, isSiloV3Type, isDolomite, isResupply, isFrankencoin, hasCrossMarginRisk, isEulerType, isAaveV4Type, isInit, isMorphoType, isCompoundV2Type, isVenusType, isSumerType, AAVE_V3_LENDERS, AAVE_V2_LENDERS, isAaveV2Type, isAaveV32Type, isAaveV3Type, isYLDR, isLista, hasSubAccounts, isTectonicType, isKineticType, isBenqiType } from '@1delta/lender-registry';
4
+ import { Lender, isExactly, isMidnight, isTerm, isTermMax, isTeller, isListaDao, isLiquityFamily, isRiver, isInverse, isUsdd, isCompoundV3Type, isFluid, isGearboxV3, isLlamaLend, isFrankencoin, isResupply, LIQUITY_FAMILY_LENDERS, isAaveType, isCompoundV3, isMultiMarket, isSiloV2Type, isSiloV3Type, isDolomite, hasCrossMarginRisk, isEulerType, isAaveV4Type, isInit, isMorphoType, isCompoundV2Type, isVenusType, isSumerType, AAVE_V3_LENDERS, AAVE_V2_LENDERS, isAaveV2Type, isAaveV32Type, isAaveV3Type, isYLDR, isLista, hasSubAccounts, isTectonicType, isKineticType, isBenqiType } from '@1delta/lender-registry';
5
5
  export { hasCrossMarginRisk, isAaveType, isAaveV2Type, isAaveV32Type, isAaveV3Type, isCompoundV3, isCompoundV3Type, isInit, isMorphoType, isMultiMarket, isYLDR } from '@1delta/lender-registry';
6
6
  import { fetchTokenLists, fetchTokenList, aavePools, compoundV3Pools, initConfig, morphoPools, compoundV2Pools, eulerVaults, siloMarkets, siloV2LenderKey, siloMarketsV3, siloV3LenderKey, aaveV4Spokes, aaveV4SpokeLenderKey, fluidResolvers, gearboxResolvers, dolomiteConfigs, midnightConfig, termConfig, termMarketsByChain, exactlyConfig, liquityLendersByChain, riverLendersByChain, inverseLendersByChain, llamaLendLendersByChain, usddLendersByChain, frankencoinLendersByChain, resupplyLendersByChain, tellerConfigByChain, tellerPoolsByChain, termMaxConfigByChain, aaveReserves, compoundV3Reserves, liquityConfigFor, liquityBranchesByChain, riverConfigFor, riverChainData, termMaxApiBaseUrl, termMaxLenderKey, inverseConfigFor, inverseChainData, llamaLendConfigFor, llamaLendChainData, llamaLendApiBaseUrl, resupplyConfigFor, usddConfigFor, usddChainData, frankencoinConfigFor, frankencoinChainData, aaveOracles, morphoTypeMarkets, resolveMidnightApiBase, resolveTermApiBase, llamaLendMarketByVault, getSiloV2MarketEntry, getSiloV3MarketEntry, dolomiteEmode, aaveTokens, morphoTypeVaults, eulerConfigs, gearboxMarketCompressor, gearboxMarketConfigurators, gearboxV3LenderKey, midnightMarketsByChain, parseAaveV4SpokeLenderKey, gearboxAccountCompressor, compoundV2Tokens, exactlyMarketsByChain, aaveOraclesConfig, compoundV2Oracles, compoundV2TokenArray, compoundV2OraclesConfig, compoundV3OraclesData, compoundV3BaseData, compoundV2Reserves, aaveV4Oracles } from '@1delta/data-sdk';
7
7
  import lodash from 'lodash';
@@ -61581,6 +61581,32 @@ var TERM_PROFILES = [
61581
61581
  ]
61582
61582
  }
61583
61583
  }),
61584
+ P({
61585
+ id: "frankencoin.challenge@v1",
61586
+ name: "Frankencoin position",
61587
+ family: "frankencoin",
61588
+ borrow: {
61589
+ description: "A per-position CDP minting ZCHF. There is no price oracle: you DECLARE the liquidation price yourself, and anyone may challenge it with a Dutch auction.",
61590
+ implications: [
61591
+ "Liquidation is NOT driven by your health factor. A challenge succeeds when the market price falls below the price YOU declared, regardless of how much you have borrowed \u2014 repaying does not reduce that risk, only re-declaring the price does.",
61592
+ "You owe more than you received: a 10\u201340 % reserve contribution is withheld at mint, plus an upfront fee. The debt is not the amount you got.",
61593
+ "The position EXPIRES on a fixed date; past it the position can be liquidated."
61594
+ ]
61595
+ }
61596
+ }),
61597
+ P({
61598
+ id: "resupply.cdp@v1",
61599
+ name: "Resupply pair",
61600
+ family: "resupply",
61601
+ borrow: {
61602
+ description: "A CDP minting reUSD against a supply position in ANOTHER lending market, held as a staked ERC-4626 share.",
61603
+ implications: [
61604
+ "Redemptions are COMMUNAL: collateral is skimmed pro-rata from every borrower in the pair, so being well-collateralised does not exempt you and there is no rate to raise in defence.",
61605
+ "Your collateral is a lending position elsewhere \u2014 bad debt in that market is bad debt here.",
61606
+ "There is a hard minimum position size: a smaller borrow, or a repay that would leave less than it, reverts."
61607
+ ]
61608
+ }
61609
+ }),
61584
61610
  // ── Vaults ───────────────────────────────────────────────────────────────
61585
61611
  P({
61586
61612
  id: "vault.lending@v1",
@@ -61851,7 +61877,9 @@ function resolveMinDebt(input) {
61851
61877
  // USDD / Maker-fork: `dust` IS the per-vault debt floor.
61852
61878
  m.usdd?.dust,
61853
61879
  // Gearbox credit facade.
61854
- m.gearbox?.minDebt
61880
+ m.gearbox?.minDebt,
61881
+ // Resupply — a hard per-position floor that also bounds partial repayment.
61882
+ m.resupply?.minimumBorrowAmount
61855
61883
  ];
61856
61884
  for (const c of candidates) {
61857
61885
  if (c == null) continue;
@@ -61862,6 +61890,14 @@ function resolveMinDebt(input) {
61862
61890
  }
61863
61891
  return void 0;
61864
61892
  }
61893
+ function resolveMinCollateral(input) {
61894
+ const m = input.market ?? {};
61895
+ const raw = m.frankencoin?.minimumCollateral;
61896
+ if (raw == null) return void 0;
61897
+ const str3 = String(raw);
61898
+ if (str3 === "0" || str3 === "" || !/^\d+$/.test(str3)) return void 0;
61899
+ return str3;
61900
+ }
61865
61901
  function capUtil(total, cap) {
61866
61902
  if (!isNum(cap) || cap <= 0 || !isNum(total)) return void 0;
61867
61903
  return clamp01(total / cap);
@@ -61893,7 +61929,7 @@ function buildAvailability(input, side) {
61893
61929
  gating: "permissionless",
61894
61930
  // A borrow below the protocol's floor REVERTS, so gate the CTA on it
61895
61931
  // rather than letting the transaction fail.
61896
- minSize: side === "borrow" ? resolveMinDebt(input) : void 0,
61932
+ minSize: side === "borrow" ? resolveMinDebt(input) : resolveMinCollateral(input),
61897
61933
  cap: isNum(cap) && cap > 0 ? String(cap) : void 0,
61898
61934
  capUtilization,
61899
61935
  requires: side === "supply" ? ["token-approval"] : void 0
@@ -63060,6 +63096,158 @@ var llamaLendAdapter = {
63060
63096
  };
63061
63097
  }
63062
63098
  };
63099
+ var ppmToPercent = (v) => {
63100
+ if (v == null) return void 0;
63101
+ const n = Number(v);
63102
+ return Number.isFinite(n) ? n / 1e4 : void 0;
63103
+ };
63104
+ var frankencoinAdapter = {
63105
+ id: "frankencoin",
63106
+ matches: isFrankencoin,
63107
+ profileId: () => "frankencoin.challenge@v1",
63108
+ build: (input) => {
63109
+ const fc = input.market?.frankencoin ?? {};
63110
+ const reservePct = ppmToPercent(fc.reserveContributionPPM);
63111
+ const upfrontPct = ppmToPercent(fc.upfrontFeePPM);
63112
+ const challengerPct = ppmToPercent(fc.challengerRewardPPM);
63113
+ const expiration = Number(fc.expiration ?? 0);
63114
+ const challengePeriod = Number(fc.challengePeriod ?? 0);
63115
+ const fees = [
63116
+ reservePct != null && reservePct > 0 ? {
63117
+ id: "reserve-contribution",
63118
+ label: "Equity-reserve contribution",
63119
+ when: "entry",
63120
+ unit: "percent",
63121
+ basis: "principal",
63122
+ value: reservePct,
63123
+ payee: "protocol",
63124
+ description: "Withheld at mint into the FPS equity reserve. You owe the full amount but receive this much less, so the debt is NOT what you got."
63125
+ } : null,
63126
+ upfrontPct != null && upfrontPct > 0 ? {
63127
+ id: "origination",
63128
+ label: "Upfront interest",
63129
+ when: "entry",
63130
+ unit: "percent",
63131
+ basis: "principal",
63132
+ value: upfrontPct,
63133
+ payee: "protocol",
63134
+ description: "Charged once at mint, pro-rated over the time to expiry."
63135
+ } : null
63136
+ ].filter(Boolean);
63137
+ return {
63138
+ // Not a coverage hole: the protocol has no price feed by design.
63139
+ oracle: { kind: "none" },
63140
+ borrow: {
63141
+ debtShape: "accruing",
63142
+ maturity: expiration > 0 ? {
63143
+ kind: "fixed-date",
63144
+ maturity: expiration,
63145
+ maturityIso: new Date(expiration * 1e3).toISOString(),
63146
+ atMaturity: "liquidatable"
63147
+ } : void 0,
63148
+ exit: {
63149
+ // Repaying is not free here: the reserve haircut means the net cost
63150
+ // is `faceDelta − assignedReserve`, and it is resolved at repay time.
63151
+ earlyRepay: "discount",
63152
+ atMaturityCost: "accrued",
63153
+ lateBehaviour: "liquidatable",
63154
+ partialAllowed: true
63155
+ },
63156
+ liquidation: {
63157
+ // A Dutch-auction challenge, not a repay-and-seize.
63158
+ model: "auction",
63159
+ absorber: "liquidator",
63160
+ // Price-triggered, but against the OWNER-DECLARED price — and with
63161
+ // no health gate whatsoever.
63162
+ trigger: "price",
63163
+ seizure: "proportional",
63164
+ permissioned: false,
63165
+ windowSecs: challengePeriod > 0 ? challengePeriod : void 0,
63166
+ penalty: challengerPct != null ? challengerPct / 100 : 0,
63167
+ penalties: challengerPct != null ? [
63168
+ {
63169
+ id: "challenger-reward",
63170
+ label: "Challenger reward",
63171
+ value: challengerPct / 100,
63172
+ description: "Paid to whoever successfully challenges the declared price."
63173
+ }
63174
+ ] : void 0,
63175
+ badDebt: "socialized"
63176
+ },
63177
+ fees,
63178
+ counterparty: { kind: "cdp", solvency: "overcollateralized" }
63179
+ },
63180
+ constraints: {
63181
+ crossMargin: false,
63182
+ // Each position is its own CONTRACT, cloned from an "original".
63183
+ positionModel: "account",
63184
+ positionIdMeaning: "Each position is its own contract, cloned from the market\u2019s original position; you own the clone."
63185
+ }
63186
+ };
63187
+ }
63188
+ };
63189
+ var e5ToFraction = (v) => {
63190
+ if (v == null) return void 0;
63191
+ const n = Number(v);
63192
+ return Number.isFinite(n) ? n / 1e5 : void 0;
63193
+ };
63194
+ var resupplyAdapter = {
63195
+ id: "resupply",
63196
+ matches: isResupply,
63197
+ profileId: () => "resupply.cdp@v1",
63198
+ build: (input) => {
63199
+ const rs = input.market?.resupply ?? {};
63200
+ const liquidationFee = e5ToFraction(rs.liquidationFee);
63201
+ const mintFee = e5ToFraction(rs.mintFee);
63202
+ const minBorrow = rs.minimumBorrowAmount != null && String(rs.minimumBorrowAmount) !== "0" ? String(rs.minimumBorrowAmount) : void 0;
63203
+ return {
63204
+ borrow: {
63205
+ debtShape: "accruing",
63206
+ exit: {
63207
+ earlyRepay: "free",
63208
+ atMaturityCost: "accrued",
63209
+ lateBehaviour: "none",
63210
+ partialAllowed: true,
63211
+ // The floor bounds repayment too: a partial repay may not leave the
63212
+ // position under it.
63213
+ minDebt: minBorrow
63214
+ },
63215
+ liquidation: {
63216
+ model: "repay-seize",
63217
+ absorber: "liquidator",
63218
+ trigger: "price",
63219
+ seizure: "proportional",
63220
+ penalty: liquidationFee ?? 0,
63221
+ // Every borrower in the pair is exposed, not just the marginal one.
63222
+ redeemable: true,
63223
+ badDebt: "socialized"
63224
+ },
63225
+ fees: mintFee != null && mintFee > 0 ? [
63226
+ {
63227
+ id: "origination",
63228
+ label: "Mint fee",
63229
+ when: "entry",
63230
+ unit: "percent",
63231
+ basis: "principal",
63232
+ value: mintFee * 100,
63233
+ payee: "protocol"
63234
+ }
63235
+ ] : void 0,
63236
+ availability: minBorrow ? { minSize: minBorrow } : void 0,
63237
+ counterparty: { kind: "cdp", solvency: "overcollateralized" }
63238
+ },
63239
+ supply: {
63240
+ // The collateral is itself a lending position elsewhere.
63241
+ principal: { protected: false, risks: ["bad-debt"] }
63242
+ },
63243
+ constraints: {
63244
+ crossMargin: false,
63245
+ positionModel: "account",
63246
+ positionIdMeaning: "One position per address per pair \u2014 address-keyed, no discovery."
63247
+ }
63248
+ };
63249
+ }
63250
+ };
63063
63251
 
63064
63252
  // src/terms/adapters/index.ts
63065
63253
  var TERM_ADAPTERS = [
@@ -63076,7 +63264,9 @@ var TERM_ADAPTERS = [
63076
63264
  compoundV3Adapter,
63077
63265
  fluidAdapter,
63078
63266
  gearboxAdapter,
63079
- llamaLendAdapter
63267
+ llamaLendAdapter,
63268
+ frankencoinAdapter,
63269
+ resupplyAdapter
63080
63270
  ];
63081
63271
  function resolveAdapter(lender) {
63082
63272
  return TERM_ADAPTERS.find((a) => a.matches(lender));
@@ -63470,11 +63660,15 @@ function validateTermSheet(sheet) {
63470
63660
  if (o) {
63471
63661
  if (o.kind === "none" && o.address)
63472
63662
  fail("oracle-none-address", "oracle.kind none must not carry an address");
63473
- if (o.kind === "none" && sheet.borrow && sheet.borrow.liquidation.trigger !== "time")
63474
- fail(
63475
- "no-oracle-non-time",
63476
- `oracle.kind none but liquidation.trigger is ${sheet.borrow.liquidation.trigger}`
63477
- );
63663
+ if (o.kind === "none" && sheet.borrow) {
63664
+ const t = sheet.borrow.liquidation.trigger;
63665
+ const m = sheet.borrow.liquidation.model;
63666
+ if (t !== "time" && t !== "none" && m !== "auction")
63667
+ fail(
63668
+ "no-oracle-non-time",
63669
+ `oracle.kind none but liquidation.trigger is ${t} with model ${m ?? "unset"}`
63670
+ );
63671
+ }
63478
63672
  if (o.address && o.address !== o.address.toLowerCase())
63479
63673
  fail("oracle-lowercase", "oracle.address must be lowercased");
63480
63674
  }