@1delta/margin-fetcher 5.0.61 → 5.0.63

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.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { parseAbi, keccak256, encodeAbiParameters, formatEther, BaseError, erc20Abi, stringToHex, isAddress, pad, encodeFunctionData, formatUnits, getAddress, decodeFunctionResult, decodeAbiParameters, concat, parseAbiParameters, AbiEncodingLengthMismatchError, concatHex, toBytes, slice, hexToString, InvalidAddressError, boolToHex, integerRegex, numberToHex, bytesRegex, BytesSizeMismatchError, arrayRegex, UnsupportedPackedAbiType } from './chunk-JUYF2XLF.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, isCooler, isDssType, isCompoundV3Type, isFluid, isGearboxV3, isLlamaLend, isFrankencoin, isResupply, isCurvance, isTwyne, isFraxlend, LIQUITY_FAMILY_LENDERS, isAaveType, isCompoundV3, isMultiMarket, isSiloV2Type, isSiloV3Type, isDolomite, isMorphoType, isUsdd, isSky, hasCrossMarginRisk, isEulerType, isAaveV4Type, isInit, isCompoundV2Type, isVenusType, isSumerType, AAVE_V3_LENDERS, AAVE_V2_LENDERS, isMorphoBlue, 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, isCooler, isDssType, isCompoundV3Type, isFluid, isGearboxV3, isLlamaLend, isFrankencoin, isResupply, isCurvance, isTwyne, isFraxlend, isAaveV4Type, LIQUITY_FAMILY_LENDERS, isAaveType, isCompoundV3, isCompoundV2Type, isMultiMarket, isSiloV2Type, isSiloV3Type, isDolomite, isMorphoType, isUsdd, isSky, hasCrossMarginRisk, isEulerType, isInit, isVenusType, isSumerType, AAVE_V3_LENDERS, AAVE_V2_LENDERS, isMorphoBlue, 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
- 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, coolerLendersByChain, llamaLendLendersByChain, usddLendersByChain, skyLendersByChain, frankencoinLendersByChain, resupplyLendersByChain, curvanceLendersByChain, fraxlendLendersByChain, twyneLendersByChain, tellerConfigByChain, tellerPoolsByChain, termMaxConfigByChain, aaveReserves, compoundV3Reserves, resolveTermApiBase, liquityConfigFor, liquityBranchesByChain, riverConfigFor, riverChainData, termMaxApiBaseUrl, termMaxLenderKey, inverseConfigFor, inverseChainData, coolerConfigFor, llamaLendConfigFor, llamaLendChainData, llamaLendApiBaseUrl, resupplyConfigFor, fraxlendConfigFor, dssConfigFor, dssChainData, dssDebtToken, dssDebtJoin, frankencoinConfigFor, frankencoinChainData, aaveOracles, morphoTypeMarkets, resolveMidnightApiBase, llamaLendMarketByVault, getSiloV2MarketEntry, getSiloV3MarketEntry, dolomiteEmode, curvanceConfigFor, aaveTokens, morphoTypeVaults, listaCollateralProvider, eulerConfigs, gearboxMarketCompressor, gearboxMarketConfigurators, gearboxV3LenderKey, midnightMarketsByChain, twyneConfigFor, twyneChainData, parseAaveV4SpokeLenderKey, fraxlendPairsFor, gearboxAccountCompressor, compoundV2Tokens, exactlyMarketsByChain, aaveOraclesConfig, compoundV2Oracles, compoundV2TokenArray, compoundV2OraclesConfig, compoundV3OraclesData, compoundV3BaseData, compoundV2Reserves, aaveV4Oracles, morphoTypeOracles } from '@1delta/data-sdk';
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, coolerLendersByChain, llamaLendLendersByChain, usddLendersByChain, skyLendersByChain, frankencoinLendersByChain, resupplyLendersByChain, curvanceLendersByChain, fraxlendLendersByChain, twyneLendersByChain, tellerConfigByChain, tellerPoolsByChain, termMaxConfigByChain, aaveReserves, compoundV3Reserves, resolveTermApiBase, liquityConfigFor, liquityBranchesByChain, riverConfigFor, riverChainData, termMaxApiBaseUrl, termMaxLenderKey, inverseConfigFor, inverseChainData, coolerConfigFor, llamaLendConfigFor, llamaLendChainData, llamaLendApiBaseUrl, resupplyConfigFor, fraxlendConfigFor, dssConfigFor, dssChainData, dssDebtToken, dssDebtJoin, frankencoinConfigFor, frankencoinChainData, aaveOracles, morphoTypeMarkets, resolveMidnightApiBase, llamaLendMarketByVault, getSiloV2MarketEntry, getSiloV3MarketEntry, dolomiteEmode, curvanceConfigFor, aaveTokens, morphoTypeVaults, listaCollateralProvider, eulerConfigs, gearboxMarketCompressor, gearboxMarketConfigurators, compoundV2TokenArray, gearboxV3LenderKey, midnightMarketsByChain, twyneConfigFor, twyneChainData, parseAaveV4SpokeLenderKey, fraxlendPairsFor, gearboxAccountCompressor, compoundV2Tokens, exactlyMarketsByChain, aaveOraclesConfig, compoundV2Oracles, compoundV2OraclesConfig, compoundV3OraclesData, compoundV3BaseData, compoundV2Reserves, aaveV4Oracles, morphoTypeOracles } from '@1delta/data-sdk';
7
7
  import lodash from 'lodash';
8
8
  import { Chain } from '@1delta/chain-registry';
9
9
  import { multicallRetryUniversal, getEvmClient, getEvmChain, getEvmClientUniversal } from '@1delta/providers';
@@ -7103,18 +7103,15 @@ var formatAaveYieldToNumber = (n) => {
7103
7103
  if (!n) return 0;
7104
7104
  return Number(formatEther(BigInt(n) / BigInt(1e7)));
7105
7105
  };
7106
- var calculateRateForCompoundType2 = (n) => {
7106
+ var calculateRateForCompoundType2 = (n, unitSeconds) => {
7107
7107
  const rate = Number(formatEther(BigInt(n)));
7108
- return (Math.pow(rate * 60 * 60 * 24 / 12 + 1, 365) - 1) * 100;
7108
+ const perUnit = unitSeconds && unitSeconds > 0 ? unitSeconds : 12;
7109
+ return (Math.pow(rate * 60 * 60 * 24 / perUnit + 1, 365) - 1) * 100;
7109
7110
  };
7110
- var calculateRateForCompoundType = (n, chainId, scale3 = 1 /* SECOND */) => {
7111
+ var calculateRateForCompoundType = (n, chainId, scale3 = 1 /* SECOND */, unitSeconds) => {
7111
7112
  const rate = Number(formatEther(BigInt(n)));
7112
- if (scale3 === 0 /* BLOCK */)
7113
- return (Math.pow(
7114
- rate * 60 * 60 * 24 / (BLOCK_TIMES[chainId] ?? "1") + 1,
7115
- 365
7116
- ) - 1) * 100;
7117
- return (Math.pow(rate * 60 * 60 * 24 + 1, 365) - 1) * 100;
7113
+ const perUnit = unitSeconds && unitSeconds > 0 ? unitSeconds : scale3 === 0 /* BLOCK */ ? Number(BLOCK_TIMES[chainId] ?? 1) : 1;
7114
+ return (Math.pow(rate * 60 * 60 * 24 / perUnit + 1, 365) - 1) * 100;
7118
7115
  };
7119
7116
  var formatAaveRawApyToApr = (raw) => {
7120
7117
  const apy = formatAaveYieldToNumber(raw);
@@ -15305,9 +15302,102 @@ function getCall(lender, chainId) {
15305
15302
  params: [tk]
15306
15303
  });
15307
15304
  }
15305
+ var L1_BLOCK_SECONDS = 12;
15306
+ var L1_MERGE_BLOCK = 15537394;
15307
+ var L1_MERGE_TIMESTAMP = 1663224162;
15308
+ var estimateL1Height = (nowSeconds) => L1_MERGE_BLOCK + (nowSeconds - L1_MERGE_TIMESTAMP) / L1_BLOCK_SECONDS;
15309
+ var CADENCE_SPAN = 20000n;
15310
+ var TTL_MS = 24 * 60 * 60 * 1e3;
15311
+ var clockCache = /* @__PURE__ */ new Map();
15312
+ var cadenceCache = /* @__PURE__ */ new Map();
15313
+ var fresh = (e) => !!e && Date.now() - e.at < TTL_MS;
15314
+ function classifyRateClock(accrualBlockNumber, headBlockNumber, headTimestamp) {
15315
+ const v = Number(accrualBlockNumber);
15316
+ if (!Number.isFinite(v) || v <= 0) return void 0;
15317
+ const candidates = [
15318
+ ["timestamp", Number(headTimestamp)],
15319
+ ["block", Number(headBlockNumber)],
15320
+ // The L1 height is not readable from this chain; derive it from the L2
15321
+ // head's timestamp, which is the same wall clock Ethereum runs on.
15322
+ ["l1-block", estimateL1Height(Number(headTimestamp))]
15323
+ ];
15324
+ let best;
15325
+ let bestRatio = Infinity;
15326
+ for (const [kind, expected] of candidates) {
15327
+ if (!Number.isFinite(expected) || expected <= 0) continue;
15328
+ const ratio = v > expected ? v / expected : expected / v;
15329
+ if (ratio < bestRatio) {
15330
+ bestRatio = ratio;
15331
+ best = kind;
15332
+ }
15333
+ }
15334
+ return bestRatio <= 2 ? best : void 0;
15335
+ }
15336
+ async function chainBlockSeconds(chainId) {
15337
+ const hit = cadenceCache.get(chainId);
15338
+ if (fresh(hit)) return hit.value;
15339
+ let value;
15340
+ try {
15341
+ const client = await getEvmClient(chainId);
15342
+ const head = await client.getBlockNumber();
15343
+ const span = head > CADENCE_SPAN ? CADENCE_SPAN : head / 2n;
15344
+ const [b1, b0] = await Promise.all([
15345
+ client.getBlock({ blockNumber: head }),
15346
+ client.getBlock({ blockNumber: head - span })
15347
+ ]);
15348
+ const secs = Number(b1.timestamp - b0.timestamp) / Number(span);
15349
+ if (secs >= 0.05 && secs <= 60) value = secs;
15350
+ } catch {
15351
+ value = void 0;
15352
+ }
15353
+ cadenceCache.set(chainId, { at: Date.now(), value });
15354
+ return value;
15355
+ }
15356
+ async function resolveRateUnitSeconds(chainId, lender) {
15357
+ const key3 = `${chainId}:${lender}`;
15358
+ let clock = clockCache.get(key3);
15359
+ if (!fresh(clock)) {
15360
+ let value;
15361
+ try {
15362
+ const sample = getCompoundV2Tokens(chainId, lender)[0]?.cToken;
15363
+ if (sample) {
15364
+ const client = await getEvmClient(chainId);
15365
+ const head = await client.getBlock();
15366
+ const accrual = await client.readContract({
15367
+ address: sample,
15368
+ abi: [
15369
+ {
15370
+ inputs: [],
15371
+ name: "accrualBlockNumber",
15372
+ outputs: [{ name: "", type: "uint256" }],
15373
+ stateMutability: "view",
15374
+ type: "function"
15375
+ }
15376
+ ],
15377
+ functionName: "accrualBlockNumber"
15378
+ });
15379
+ value = classifyRateClock(accrual, head.number, head.timestamp);
15380
+ }
15381
+ } catch {
15382
+ value = void 0;
15383
+ }
15384
+ clock = { at: Date.now(), value };
15385
+ clockCache.set(key3, clock);
15386
+ }
15387
+ switch (clock.value) {
15388
+ case "timestamp":
15389
+ return 1;
15390
+ case "l1-block":
15391
+ return L1_BLOCK_SECONDS;
15392
+ case "block":
15393
+ return await chainBlockSeconds(chainId);
15394
+ default:
15395
+ return void 0;
15396
+ }
15397
+ }
15308
15398
 
15309
15399
  // src/lending/public-data/compound-v2/getters/venus.ts
15310
- function readVTokenMetadata(meta, token) {
15400
+ function readVTokenMetadata(meta, token, unitSeconds) {
15311
15401
  const pick3 = (...values) => values.find((v) => v !== void 0);
15312
15402
  return {
15313
15403
  vToken: meta.vToken ?? "",
@@ -15343,7 +15433,8 @@ function readVTokenMetadata(meta, token) {
15343
15433
  meta.supplyRatePerBlockOrTimestamp
15344
15434
  )?.toString() ?? "0",
15345
15435
  token.chainId,
15346
- 0 /* BLOCK */
15436
+ 0 /* BLOCK */,
15437
+ unitSeconds
15347
15438
  ) / 100
15348
15439
  ) * 100,
15349
15440
  variableBorrowRate: apyToApr(
@@ -15353,7 +15444,8 @@ function readVTokenMetadata(meta, token) {
15353
15444
  meta.borrowRatePerBlockOrTimestamp
15354
15445
  )?.toString() ?? "0",
15355
15446
  token.chainId,
15356
- 0 /* BLOCK */
15447
+ 0 /* BLOCK */,
15448
+ unitSeconds
15357
15449
  ) / 100
15358
15450
  ) * 100,
15359
15451
  supplyCap: 0,
@@ -15572,20 +15664,24 @@ function parseTectonicMarketData(input) {
15572
15664
  isBoosted: toBool3(field3(input, "isBoosted", 22)),
15573
15665
  baseRatePerBlock: toBigInt3(field3(input, "baseRatePerBlock", 23)),
15574
15666
  multiplierPerBlock: toBigInt3(field3(input, "multiplierPerBlock", 24)),
15575
- jumpMultiplierPerBlock: toBigInt3(field3(input, "jumpMultiplierPerBlock", 25)),
15667
+ jumpMultiplierPerBlock: toBigInt3(
15668
+ field3(input, "jumpMultiplierPerBlock", 25)
15669
+ ),
15576
15670
  kink: toBigInt3(field3(input, "kink", 26))
15577
15671
  };
15578
15672
  }
15579
- function getTectonicRates(info, chainId) {
15673
+ function getTectonicRates(info, chainId, unitSeconds) {
15580
15674
  const supplyApy = calculateRateForCompoundType(
15581
15675
  info.supplyRatePerBlock.toString(),
15582
15676
  chainId,
15583
- 0 /* BLOCK */
15677
+ 0 /* BLOCK */,
15678
+ unitSeconds
15584
15679
  );
15585
15680
  const borrowApy = calculateRateForCompoundType(
15586
15681
  info.borrowRatePerBlock.toString(),
15587
15682
  chainId,
15588
- 0 /* BLOCK */
15683
+ 0 /* BLOCK */,
15684
+ unitSeconds
15589
15685
  );
15590
15686
  return {
15591
15687
  depositRate: apyToApr(supplyApy / 100) * 100,
@@ -15631,7 +15727,9 @@ function parseKineticMarketMetadata(input) {
15631
15727
  totalBorrows: toBigInt4(field4(input, "totalBorrows", 9)),
15632
15728
  collateralFactor: toBigInt4(field4(input, "collateralFactor", 10)),
15633
15729
  underlyingToken: String(field4(input, "underlyingToken", 11)),
15634
- underlyingTokenDecimals: Number(field4(input, "underlyingTokenDecimals", 12)),
15730
+ underlyingTokenDecimals: Number(
15731
+ field4(input, "underlyingTokenDecimals", 12)
15732
+ ),
15635
15733
  cTokenDecimals: Number(field4(input, "cTokenDecimals", 13)),
15636
15734
  totalReserves: toBigInt4(field4(input, "totalReserves", 16)),
15637
15735
  cash: toBigInt4(field4(input, "cash", 17)),
@@ -16116,9 +16214,7 @@ function parseBenqiEcoMarketMetadata(input) {
16116
16214
  borrowCap: toBigInt5(field5(input, "borrowCap", 6)),
16117
16215
  supplyCap: toBigInt5(field5(input, "supplyCap", 7)),
16118
16216
  totalSupply: toBigInt5(field5(input, "totalSupply", 8)),
16119
- totalUnderlyingSupply: toBigInt5(
16120
- field5(input, "totalUnderlyingSupply", 9)
16121
- ),
16217
+ totalUnderlyingSupply: toBigInt5(field5(input, "totalUnderlyingSupply", 9)),
16122
16218
  totalBorrows: toBigInt5(field5(input, "totalBorrows", 10)),
16123
16219
  collateralFactor: toBigInt5(field5(input, "collateralFactor", 11)),
16124
16220
  underlying: parseToken(field5(input, "underlying", 12)),
@@ -16129,16 +16225,18 @@ function parseBenqiEcoMarketMetadata(input) {
16129
16225
  borrowPaused: toBool5(field5(input, "borrowPaused", 17))
16130
16226
  };
16131
16227
  }
16132
- function getBenqiRates(info, chainId) {
16228
+ function getBenqiRates(info, chainId, unitSeconds) {
16133
16229
  const supplyApy = calculateRateForCompoundType(
16134
16230
  info.supplyRate.toString(),
16135
16231
  chainId,
16136
- 0 /* BLOCK */
16232
+ 0 /* BLOCK */,
16233
+ unitSeconds
16137
16234
  );
16138
16235
  const borrowApy = calculateRateForCompoundType(
16139
16236
  info.borrowRate.toString(),
16140
16237
  chainId,
16141
- 0 /* BLOCK */
16238
+ 0 /* BLOCK */,
16239
+ unitSeconds
16142
16240
  );
16143
16241
  return {
16144
16242
  depositRate: apyToApr(supplyApy / 100) * 100,
@@ -16339,27 +16437,29 @@ function parseBaseFields(meta) {
16339
16437
  pausedActions: DEFAULT_PAUSED_ACTIONS
16340
16438
  };
16341
16439
  }
16342
- function readLodestarCTokenMetadata(meta, token) {
16440
+ function readLodestarCTokenMetadata(meta, token, unitSeconds) {
16343
16441
  const base = parseBaseFields(meta);
16344
16442
  return {
16345
16443
  ...base,
16346
16444
  depositRate: capRate(
16347
16445
  apyToApr(
16348
16446
  calculateRateForCompoundType2(
16349
- base.supplyRatePerBlock?.toString() ?? "0"
16447
+ base.supplyRatePerBlock?.toString() ?? "0",
16448
+ unitSeconds
16350
16449
  ) / 100
16351
16450
  ) * 100
16352
16451
  ),
16353
16452
  variableBorrowRate: capRate(
16354
16453
  apyToApr(
16355
16454
  calculateRateForCompoundType2(
16356
- base.borrowRatePerBlock?.toString() ?? "0"
16455
+ base.borrowRatePerBlock?.toString() ?? "0",
16456
+ unitSeconds
16357
16457
  ) / 100
16358
16458
  ) * 100
16359
16459
  )
16360
16460
  };
16361
16461
  }
16362
- function readCompoundV2CTokenMetadata(meta, token) {
16462
+ function readCompoundV2CTokenMetadata(meta, token, unitSeconds) {
16363
16463
  const base = parseBaseFields(meta);
16364
16464
  return {
16365
16465
  ...base,
@@ -16368,7 +16468,8 @@ function readCompoundV2CTokenMetadata(meta, token) {
16368
16468
  calculateRateForCompoundType(
16369
16469
  base.supplyRatePerBlock?.toString() ?? "0",
16370
16470
  token.chainId,
16371
- 0 /* BLOCK */
16471
+ 0 /* BLOCK */,
16472
+ unitSeconds
16372
16473
  ) / 100
16373
16474
  ) * 100
16374
16475
  ),
@@ -16377,7 +16478,8 @@ function readCompoundV2CTokenMetadata(meta, token) {
16377
16478
  calculateRateForCompoundType(
16378
16479
  base.borrowRatePerBlock?.toString() ?? "0",
16379
16480
  token.chainId,
16380
- 0 /* BLOCK */
16481
+ 0 /* BLOCK */,
16482
+ unitSeconds
16381
16483
  ) / 100
16382
16484
  ) * 100
16383
16485
  )
@@ -16406,7 +16508,7 @@ function parseCreamV2BaseFields(meta) {
16406
16508
  pausedActions: DEFAULT_PAUSED_ACTIONS
16407
16509
  };
16408
16510
  }
16409
- function readCreamV2CTokenMetadata(meta, token) {
16511
+ function readCreamV2CTokenMetadata(meta, token, unitSeconds) {
16410
16512
  const base = parseCreamV2BaseFields(meta);
16411
16513
  return {
16412
16514
  ...base,
@@ -16415,7 +16517,8 @@ function readCreamV2CTokenMetadata(meta, token) {
16415
16517
  calculateRateForCompoundType(
16416
16518
  base.supplyRatePerBlock?.toString() ?? "0",
16417
16519
  token.chainId,
16418
- 0 /* BLOCK */
16520
+ 0 /* BLOCK */,
16521
+ unitSeconds
16419
16522
  ) / 100
16420
16523
  ) * 100
16421
16524
  ),
@@ -16424,7 +16527,8 @@ function readCreamV2CTokenMetadata(meta, token) {
16424
16527
  calculateRateForCompoundType(
16425
16528
  base.borrowRatePerBlock?.toString() ?? "0",
16426
16529
  token.chainId,
16427
- 0 /* BLOCK */
16530
+ 0 /* BLOCK */,
16531
+ unitSeconds
16428
16532
  ) / 100
16429
16533
  ) * 100
16430
16534
  )
@@ -16557,13 +16661,13 @@ var REPAIR_BACKOFF_JITTER_MS = 60;
16557
16661
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
16558
16662
  var backoffForRound = (round) => REPAIR_BACKOFF_BASE_MS * 2 ** round + Math.random() * REPAIR_BACKOFF_JITTER_MS;
16559
16663
  var endpointUrl = (client, rpcId) => client?.transport?.url ?? `rpc#${rpcId}`;
16560
- var resolveEndpoint = (chainId, getEvmClient17, startRpcId, tried, maxProbe = 12, timeoutMs) => {
16664
+ var resolveEndpoint = (chainId, getEvmClient18, startRpcId, tried, maxProbe = 12, timeoutMs) => {
16561
16665
  let fallback = null;
16562
16666
  for (let probe = 0; probe < maxProbe; probe++) {
16563
16667
  const rpcId = startRpcId + probe;
16564
16668
  let client;
16565
16669
  try {
16566
- client = getEvmClient17(chainId, rpcId, { timeoutMs });
16670
+ client = getEvmClient18(chainId, rpcId, { timeoutMs });
16567
16671
  } catch {
16568
16672
  break;
16569
16673
  }
@@ -16592,7 +16696,7 @@ var recordPermanentFailures = (slots, offset, collector) => {
16592
16696
  if (slots[i].permanent) collector.add(offset + i);
16593
16697
  }
16594
16698
  };
16595
- var repairFailedSlots = async (chainId, contracts, slots, getEvmClient17, nextRpcId, batchSize, logs, rounds = MULTICALL_REPAIR_ROUNDS, options) => {
16699
+ var repairFailedSlots = async (chainId, contracts, slots, getEvmClient18, nextRpcId, batchSize, logs, rounds = MULTICALL_REPAIR_ROUNDS, options) => {
16596
16700
  let searchFrom = nextRpcId;
16597
16701
  for (let round = 0; round < rounds; round++) {
16598
16702
  const failedIdx = [];
@@ -16602,7 +16706,7 @@ var repairFailedSlots = async (chainId, contracts, slots, getEvmClient17, nextRp
16602
16706
  if (failedIdx.length === 0) return slots;
16603
16707
  const endpoint = resolveEndpoint(
16604
16708
  chainId,
16605
- getEvmClient17,
16709
+ getEvmClient18,
16606
16710
  searchFrom,
16607
16711
  options?.tried
16608
16712
  );
@@ -16654,7 +16758,7 @@ var repairFailedSlots = async (chainId, contracts, slots, getEvmClient17, nextRp
16654
16758
  }
16655
16759
  return slots;
16656
16760
  };
16657
- var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient17, poolSize, retries = maxRetries, allowFailure = true, batchSize = MULTICALL_DEFAULT_BATCH_SIZE, logs = false, retryFailed = false, permanentFailures, options) => {
16761
+ var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient18, poolSize, retries = maxRetries, allowFailure = true, batchSize = MULTICALL_DEFAULT_BATCH_SIZE, logs = false, retryFailed = false, permanentFailures, options) => {
16658
16762
  const abiIsArray = isArray(abi[0]);
16659
16763
  const contracts = calls.map(({ address, name, params }, i) => ({
16660
16764
  abi: abiIsArray ? abi?.[i] : abi,
@@ -16728,7 +16832,7 @@ var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient17, poolS
16728
16832
  const attemptHedged = async (items, startRpcId, tried) => {
16729
16833
  const primary = resolveEndpoint(
16730
16834
  chainId,
16731
- getEvmClient17,
16835
+ getEvmClient18,
16732
16836
  startRpcId,
16733
16837
  tried,
16734
16838
  12,
@@ -16752,7 +16856,7 @@ var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient17, poolS
16752
16856
  clearTimeout(timer);
16753
16857
  const alt = resolveEndpoint(
16754
16858
  chainId,
16755
- getEvmClient17,
16859
+ getEvmClient18,
16756
16860
  primary.rpcId + 1,
16757
16861
  tried,
16758
16862
  12,
@@ -16804,7 +16908,7 @@ var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient17, poolS
16804
16908
  chainId,
16805
16909
  batch.items,
16806
16910
  slots,
16807
- getEvmClient17,
16911
+ getEvmClient18,
16808
16912
  won.rpcId + 1,
16809
16913
  requestBytes,
16810
16914
  logs,
@@ -16826,7 +16930,7 @@ var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient17, poolS
16826
16930
  const tried = /* @__PURE__ */ new Set();
16827
16931
  const start = resolveEndpoint(
16828
16932
  chainId,
16829
- getEvmClient17,
16933
+ getEvmClient18,
16830
16934
  workerId,
16831
16935
  void 0,
16832
16936
  12,
@@ -16873,7 +16977,7 @@ var parseMantissaWad = (raw) => {
16873
16977
  return void 0;
16874
16978
  }
16875
16979
  };
16876
- var getCompoundV2DataConverter = (lender, chainId, prices, additionalYields, tokenList = {}) => {
16980
+ var getCompoundV2DataConverter = (lender, chainId, prices, additionalYields, tokenList = {}, unitSeconds) => {
16877
16981
  if (lender === Lender.TAKARA) {
16878
16982
  return [
16879
16983
  (data) => {
@@ -16995,7 +17099,7 @@ var getCompoundV2DataConverter = (lender, chainId, prices, additionalYields, tok
16995
17099
  const pauseReads = Boolean(stdComptroller) && usesComptrollerPauseReads(lender);
16996
17100
  const pauseCalls = pauseReads ? tokenCount * 2 : 0;
16997
17101
  const expectedNumberOfCalls = isSumer ? tokenCount + SUMER_GROUP_COUNT : tokenCount + stdComptrollerCalls + pauseCalls;
16998
- const reader = getReader(lender, chainId);
17102
+ const reader = getReader(lender, chainId, unitSeconds);
16999
17103
  return [
17000
17104
  (data) => {
17001
17105
  if (data.length !== expectedNumberOfCalls) return void 0;
@@ -17065,9 +17169,9 @@ var getCompoundV2DataConverter = (lender, chainId, prices, additionalYields, tok
17065
17169
  expectedNumberOfCalls
17066
17170
  ];
17067
17171
  };
17068
- function getReader(lender, chainId) {
17172
+ function getReader(lender, chainId, unitSeconds) {
17069
17173
  if (isVenusType(lender)) {
17070
- return (x, t) => readVTokenMetadata(x, t);
17174
+ return (x, t) => readVTokenMetadata(x, t, unitSeconds);
17071
17175
  }
17072
17176
  if (lender === Lender.MOONWELL) {
17073
17177
  return (x, t) => parseMoonwellMarket(x, t);
@@ -17076,10 +17180,10 @@ function getReader(lender, chainId) {
17076
17180
  return (x, t) => readSumerCTokenMetadata(x, t);
17077
17181
  }
17078
17182
  if (lender === Lender.LODESTAR || lender === Lender.TENDER) {
17079
- return (x, t) => readLodestarCTokenMetadata(x);
17183
+ return (x, t) => readLodestarCTokenMetadata(x, t, unitSeconds);
17080
17184
  }
17081
17185
  if (lender === Lender.CREAM_FINANCE && (chainId === Chain.POLYGON_MAINNET || chainId === Chain.ARBITRUM_ONE)) {
17082
- return (x, t) => readCreamV2CTokenMetadata(x, t);
17186
+ return (x, t) => readCreamV2CTokenMetadata(x, t, unitSeconds);
17083
17187
  }
17084
17188
  if (lender === Lender.COMPOUND_V2 || lender === Lender.FLUX_FINANCE || lender === Lender.WE_PIGGY || lender === Lender.CREAM_FINANCE || lender === Lender.CAPY_FI || lender === Lender.GAMMA || // long-tail forks read through a shared 14-field lens — MUST mirror the
17085
17189
  // `CreamLensAbi` branch in publicCallBuild.ts `getCall`. A lender in that
@@ -17087,7 +17191,7 @@ function getReader(lender, chainId) {
17087
17191
  // `reader is not a function`, which is not a length mismatch and so is not
17088
17192
  // caught by the parser's own guard.
17089
17193
  lender === Lender.DEMETER || lender === Lender.LANDER || lender === Lender.FILDA || lender === Lender.SOLIDLIZARD || lender === Lender.IRON_BANK || lender === Lender.MENDI || lender === Lender.PAXO || lender === Lender.SONNE || lender === Lender.ASO_FINANCE || lender === Lender.BLUME || lender === Lender.KAWA || lender === Lender.XPERT || lender === Lender.KEOM || lender === Lender.REACTOR_FUSION) {
17090
- return (x, t) => readCompoundV2CTokenMetadata(x, t);
17194
+ return (x, t) => readCompoundV2CTokenMetadata(x, t, unitSeconds);
17091
17195
  }
17092
17196
  }
17093
17197
 
@@ -18802,7 +18906,9 @@ function normalizeAaveV4(spokeDataList, chainId, spokeLenderKeyArg, prices, addi
18802
18906
  // >= PERCENTAGE_FACTOR (README: "bonus ≥ 100% (= ≥ 0% bonus)"). Same
18803
18907
  // semantics as V3's `liquidationBonus`, so penalty = bps/BPS - 1.
18804
18908
  liquidationPenalty: Number.isFinite(dynCfg.maxLiquidationBonus) && dynCfg.maxLiquidationBonus > 0 ? dynCfg.maxLiquidationBonus / BPS - 1 : 0,
18805
- closeFactor: 0.5,
18909
+ // No close factor in V4 — the ceiling is the whole debt. See the
18910
+ // field docstring on `AaveV4TokenEntry.closeFactor`.
18911
+ closeFactor: 1,
18806
18912
  targetHealthFactor,
18807
18913
  // _validateSetUsingAsCollateral: !paused && !frozen (frozen blocks enabling)
18808
18914
  collateralDisabled: dynCfg.collateralFactor === 0 || reserve.config.paused || reserve.config.frozen,
@@ -18818,7 +18924,9 @@ function normalizeAaveV4(spokeDataList, chainId, spokeLenderKeyArg, prices, addi
18818
18924
  collateralFactor: 0,
18819
18925
  borrowFactor: 1,
18820
18926
  liquidationPenalty: 0,
18821
- closeFactor: 0.5,
18927
+ // No close factor in V4 — the ceiling is the whole debt. See the
18928
+ // field docstring on `AaveV4TokenEntry.closeFactor`.
18929
+ closeFactor: 1,
18822
18930
  targetHealthFactor,
18823
18931
  collateralDisabled: true,
18824
18932
  debtDisabled: !reserve.config.borrowable || reserve.config.paused || reserve.config.frozen
@@ -18887,7 +18995,8 @@ function normalizeAaveV4(spokeDataList, chainId, spokeLenderKeyArg, prices, addi
18887
18995
  borrowCap,
18888
18996
  supplyCap,
18889
18997
  debtCeiling: 0,
18890
- closeFactor: 0.5,
18998
+ // No close factor in V4 — the ceiling is the whole debt.
18999
+ closeFactor: 1,
18891
19000
  targetHealthFactor,
18892
19001
  spokeActive,
18893
19002
  spokeHalted,
@@ -18906,13 +19015,20 @@ function normalizeAaveV4(spokeDataList, chainId, spokeLenderKeyArg, prices, addi
18906
19015
  };
18907
19016
  }
18908
19017
  if (Object.keys(data).length === 0) return void 0;
19018
+ const liqCfg = spokeData.liquidationConfig;
19019
+ const wad = (v) => v != null && v > 0n ? Number(v) / 1e18 : void 0;
18909
19020
  return {
18910
19021
  data,
18911
19022
  chainId,
18912
19023
  params: {
18913
19024
  spoke: spokeAddr,
18914
19025
  oracle: spokeData.oracle.toLowerCase(),
18915
- label: spokeData.label
19026
+ label: spokeData.label,
19027
+ liquidation: {
19028
+ targetHealthFactor: wad(liqCfg?.targetHealthFactor),
19029
+ healthFactorForMaxBonus: wad(liqCfg?.healthFactorForMaxBonus),
19030
+ liquidationBonusFactorBps: liqCfg?.liquidationBonusFactor || void 0
19031
+ }
18916
19032
  }
18917
19033
  };
18918
19034
  }
@@ -19676,7 +19792,7 @@ var buildFluidCall = (chainId, _lender) => {
19676
19792
  // src/lending/public-data/fluid/apiData.ts
19677
19793
  var FLUID_API_BASE = "https://api.fluid.instadapp.io/v2";
19678
19794
  var SUPPORTED_CHAINS = /* @__PURE__ */ new Set(["1", "56", "137", "8453", "9745", "42161"]);
19679
- var TTL_MS = 6e4;
19795
+ var TTL_MS2 = 6e4;
19680
19796
  var ZERO_ADDRESS4 = "0x0000000000000000000000000000000000000000";
19681
19797
  var cache = {};
19682
19798
  var inflight = /* @__PURE__ */ new Map();
@@ -19724,7 +19840,7 @@ var parseChainPayload = (raw) => {
19724
19840
  var fetchFluidApiData = async (chainId) => {
19725
19841
  if (!SUPPORTED_CHAINS.has(chainId)) return void 0;
19726
19842
  const hit = cache[chainId];
19727
- if (hit && Date.now() - hit.at < TTL_MS) return hit.data;
19843
+ if (hit && Date.now() - hit.at < TTL_MS2) return hit.data;
19728
19844
  const pending = inflight.get(chainId);
19729
19845
  if (pending) return pending;
19730
19846
  const run = (async () => {
@@ -19758,14 +19874,14 @@ var DEX_RESOLVER_FALLBACK = {
19758
19874
  };
19759
19875
  var dexResolverFor = (chainId) => fluidResolvers()?.[chainId]?.dexResolver ?? DEX_RESOLVER_FALLBACK[chainId];
19760
19876
  var ZERO_ADDRESS5 = "0x0000000000000000000000000000000000000000";
19761
- var TTL_MS2 = 6e4;
19877
+ var TTL_MS3 = 6e4;
19762
19878
  var cache2 = {};
19763
19879
  var inflight2 = /* @__PURE__ */ new Map();
19764
19880
  var fetchFluidDexState = async (chainId, multicallRetry, extraDexAddresses = []) => {
19765
19881
  const resolver = dexResolverFor(chainId);
19766
19882
  if (!resolver) return void 0;
19767
19883
  const hit = cache2[chainId];
19768
- if (hit && Date.now() - hit.at < TTL_MS2) return hit.data;
19884
+ if (hit && Date.now() - hit.at < TTL_MS3) return hit.data;
19769
19885
  const pending = inflight2.get(chainId);
19770
19886
  if (pending) return pending;
19771
19887
  const run = (async () => {
@@ -20768,7 +20884,7 @@ function buildLenderCall(chainId, lender) {
20768
20884
  if (isGearboxV3(lender)) return buildGearboxCall(chainId);
20769
20885
  return [];
20770
20886
  }
20771
- function getLenderDataConverter(lender, chainId, prices, additionalYields, tokenList = {}) {
20887
+ function getLenderDataConverter(lender, chainId, prices, additionalYields, tokenList = {}, unitSeconds) {
20772
20888
  if (isAaveV2Type(lender))
20773
20889
  return getAaveV2ReservesDataConverter(
20774
20890
  lender,
@@ -20816,7 +20932,8 @@ function getLenderDataConverter(lender, chainId, prices, additionalYields, token
20816
20932
  chainId,
20817
20933
  prices,
20818
20934
  additionalYields,
20819
- tokenList
20935
+ tokenList,
20936
+ unitSeconds
20820
20937
  );
20821
20938
  if (isEulerType(lender))
20822
20939
  return getEulerV2ReservesDataConverter(
@@ -20957,6 +21074,16 @@ var getLenderPublicData = async (chainId, lenders, prices, additionalYields, mul
20957
21074
  }
20958
21075
  const invalidLenders = [];
20959
21076
  let lenderData = {};
21077
+ const rateUnitSeconds = /* @__PURE__ */ new Map();
21078
+ await Promise.all(
21079
+ validLenders.filter((l) => isCompoundV2Type(l)).map(async (l) => {
21080
+ try {
21081
+ rateUnitSeconds.set(l, await resolveRateUnitSeconds(chainId, l));
21082
+ } catch {
21083
+ rateUnitSeconds.set(l, void 0);
21084
+ }
21085
+ })
21086
+ );
20960
21087
  let currentSlice = 0;
20961
21088
  for (const lender of validLenders) {
20962
21089
  const [converter, sliceLength] = getLenderDataConverter(
@@ -20964,7 +21091,8 @@ var getLenderPublicData = async (chainId, lenders, prices, additionalYields, mul
20964
21091
  chainId,
20965
21092
  prices,
20966
21093
  additionalYields,
20967
- list
21094
+ list,
21095
+ rateUnitSeconds.get(lender)
20968
21096
  );
20969
21097
  if (sliceLength > 0) {
20970
21098
  try {
@@ -21173,16 +21301,16 @@ var LKG_TTL_SEC = 30 * 60;
21173
21301
  var lastGood = /* @__PURE__ */ new Map();
21174
21302
  async function fetchTopAndBookWithFallback(source, chainId, marketId, nowSec9) {
21175
21303
  const key3 = `${chainId}:${marketId}`;
21176
- let fresh = null;
21304
+ let fresh2 = null;
21177
21305
  if (source.getTopAndBook) {
21178
- fresh = await source.getTopAndBook(marketId, MIDNIGHT_BOOK_LEVELS).catch(() => null);
21306
+ fresh2 = await source.getTopAndBook(marketId, MIDNIGHT_BOOK_LEVELS).catch(() => null);
21179
21307
  } else {
21180
21308
  const top = await source.getBookTop(marketId).catch(() => null);
21181
- if (top) fresh = { top, book: { bids: [], asks: [] } };
21309
+ if (top) fresh2 = { top, book: { bids: [], asks: [] } };
21182
21310
  }
21183
- if (fresh) {
21184
- lastGood.set(key3, { top: fresh.top, book: fresh.book, at: nowSec9 });
21185
- return { top: fresh.top, book: fresh.book };
21311
+ if (fresh2) {
21312
+ lastGood.set(key3, { top: fresh2.top, book: fresh2.book, at: nowSec9 });
21313
+ return { top: fresh2.top, book: fresh2.book };
21186
21314
  }
21187
21315
  const cached = lastGood.get(key3);
21188
21316
  if (cached && nowSec9 - cached.at <= LKG_TTL_SEC) {
@@ -21954,21 +22082,21 @@ var LKG_TTL_SEC2 = 30 * 60;
21954
22082
  var lastGood2 = /* @__PURE__ */ new Map();
21955
22083
  async function fetchTopAndBookWithFallback2(source, chainId, config, nowSec9) {
21956
22084
  const key3 = `${chainId}:${config.termRepoId}`;
21957
- let fresh = null;
22085
+ let fresh2 = null;
21958
22086
  if (source.getTopAndBook) {
21959
- fresh = await source.getTopAndBook(config, TERM_BOOK_LEVELS).catch(() => null);
22087
+ fresh2 = await source.getTopAndBook(config, TERM_BOOK_LEVELS).catch(() => null);
21960
22088
  } else {
21961
22089
  const top = await source.getBookTop(config).catch(() => null);
21962
- if (top) fresh = { top, book: { bids: [], asks: [] }, auction: null };
22090
+ if (top) fresh2 = { top, book: { bids: [], asks: [] }, auction: null };
21963
22091
  }
21964
- if (fresh) {
22092
+ if (fresh2) {
21965
22093
  lastGood2.set(key3, {
21966
- top: fresh.top,
21967
- book: fresh.book,
21968
- auction: fresh.auction ?? null,
22094
+ top: fresh2.top,
22095
+ book: fresh2.book,
22096
+ auction: fresh2.auction ?? null,
21969
22097
  at: nowSec9
21970
22098
  });
21971
- return { top: fresh.top, book: fresh.book, auction: fresh.auction ?? null };
22099
+ return { top: fresh2.top, book: fresh2.book, auction: fresh2.auction ?? null };
21972
22100
  }
21973
22101
  const cached = lastGood2.get(key3);
21974
22102
  if (cached && nowSec9 - cached.at <= LKG_TTL_SEC2) {
@@ -23156,7 +23284,7 @@ async function fetchTellerMarkets(chainId) {
23156
23284
  }
23157
23285
  return null;
23158
23286
  };
23159
- const num18 = (i) => {
23287
+ const num19 = (i) => {
23160
23288
  const b = big22(i);
23161
23289
  return b === null ? null : Number(b);
23162
23290
  };
@@ -23166,9 +23294,9 @@ async function fetchTellerMarkets(chainId) {
23166
23294
  config,
23167
23295
  available: big22(base),
23168
23296
  committed: big22(base + 1),
23169
- minRateBps: num18(base + 2),
23297
+ minRateBps: num19(base + 2),
23170
23298
  collateralPerPrincipal: big22(base + 3),
23171
- maxLoanDuration: num18(base + 4),
23299
+ maxLoanDuration: num19(base + 4),
23172
23300
  marketId: big22(base + 5),
23173
23301
  totalAssets: big22(base + 6)
23174
23302
  };
@@ -23578,10 +23706,10 @@ function refreshLiveMarkets(chainId, source) {
23578
23706
  const running = refreshInFlight.get(chainId);
23579
23707
  if (running) return running;
23580
23708
  lastRefreshAttempt.set(chainId, Date.now());
23581
- const p = source.getChainMarkets(chainId).then((fresh) => {
23582
- if (!fresh) return;
23709
+ const p = source.getChainMarkets(chainId).then((fresh2) => {
23710
+ if (!fresh2) return;
23583
23711
  const nowSec9 = Math.floor(Date.now() / 1e3);
23584
- const live = fresh.filter((m) => m.config.maturity > nowSec9);
23712
+ const live = fresh2.filter((m) => m.config.maturity > nowSec9);
23585
23713
  lastGood3.set(chainId, { markets: live, at: nowSec9 });
23586
23714
  rememberMarkets(chainId, live);
23587
23715
  }).catch(() => void 0).finally(() => refreshInFlight.delete(chainId));
@@ -24301,20 +24429,20 @@ async function fetchInverseMarkets(lender, chainId) {
24301
24429
  if (Object.keys(byAddr).length > 0) {
24302
24430
  const rows = markets.map((market) => {
24303
24431
  const m = byAddr[market.address.toLowerCase()];
24304
- const num18 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
24432
+ const num19 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
24305
24433
  return {
24306
24434
  market,
24307
- totalDebt: num18(m?.totalDebt),
24308
- dolaLiquidity: num18(m?.dolaLiquidity),
24309
- leftToBorrow: num18(m?.leftToBorrow),
24310
- price: num18(m?.price),
24435
+ totalDebt: num19(m?.totalDebt),
24436
+ dolaLiquidity: num19(m?.dolaLiquidity),
24437
+ leftToBorrow: num19(m?.leftToBorrow),
24438
+ price: num19(m?.price),
24311
24439
  borrowPaused: typeof m?.borrowPaused === "boolean" ? m.borrowPaused : null,
24312
- dailyBorrows: num18(m?.dailyBorrows),
24440
+ dailyBorrows: num19(m?.dailyBorrows),
24313
24441
  // The API serves it as a DECIMAL (`replenishmentIncentive: 0.1`),
24314
24442
  // unlike the on-chain bps — normalize here, and fall back to the
24315
24443
  // curated metadata value when the field is missing.
24316
24444
  replenishmentIncentiveBps: (() => {
24317
- const dec2 = num18(m?.replenishmentIncentive);
24445
+ const dec2 = num19(m?.replenishmentIncentive);
24318
24446
  if (dec2 !== null) return Math.round(dec2 * 1e4);
24319
24447
  const meta = Number(market.replenishmentIncentiveBps);
24320
24448
  return Number.isFinite(meta) ? meta : null;
@@ -41769,7 +41897,7 @@ function unflattenLenderData(pools) {
41769
41897
  }
41770
41898
  return result;
41771
41899
  }
41772
- var getLenderUserDataResult = async (chainId, queriesRaw, getEvmClient17, allowFailure = true, batchSize = MULTICALL_DEFAULT_BATCH_SIZE, retries = 3, logs = false, concurrency = 1, permanentFailures, onEndpointFailure) => {
41900
+ var getLenderUserDataResult = async (chainId, queriesRaw, getEvmClient18, allowFailure = true, batchSize = MULTICALL_DEFAULT_BATCH_SIZE, retries = 3, logs = false, concurrency = 1, permanentFailures, onEndpointFailure) => {
41773
41901
  const queries = organizeUserQueries(queriesRaw);
41774
41902
  const builtCalls = await Promise.all(
41775
41903
  queries.map(async (query3) => {
@@ -41779,7 +41907,7 @@ var getLenderUserDataResult = async (chainId, queriesRaw, getEvmClient17, allowF
41779
41907
  query3.lender,
41780
41908
  query3.account,
41781
41909
  query3.params,
41782
- getEvmClient17
41910
+ getEvmClient18
41783
41911
  );
41784
41912
  return callData.map((call) => ({ call, abi: call.abi ?? abi }));
41785
41913
  })
@@ -41789,7 +41917,7 @@ var getLenderUserDataResult = async (chainId, queriesRaw, getEvmClient17, allowF
41789
41917
  chainId,
41790
41918
  calls.map((call) => call.abi),
41791
41919
  calls.map((call) => call.call),
41792
- getEvmClient17,
41920
+ getEvmClient18,
41793
41921
  concurrency,
41794
41922
  retries,
41795
41923
  allowFailure,
@@ -59478,7 +59606,7 @@ var fetchSiloVaults = async (chainId, prices = {}, tokenList = {}, options) => {
59478
59606
 
59479
59607
  // src/vaults/euler-earn/entityLabels.ts
59480
59608
  var LABELS_BASE = "https://raw.githubusercontent.com/euler-xyz/euler-labels/master";
59481
- var TTL_MS3 = 30 * 60 * 1e3;
59609
+ var TTL_MS4 = 30 * 60 * 1e3;
59482
59610
  var cache3 = /* @__PURE__ */ new Map();
59483
59611
  var inflight4 = /* @__PURE__ */ new Map();
59484
59612
  async function fetchChainEntities(chainId) {
@@ -59504,7 +59632,7 @@ async function fetchChainEntities(chainId) {
59504
59632
  }
59505
59633
  async function fetchEulerEntityNames(chainId) {
59506
59634
  const hit = cache3.get(chainId);
59507
- if (hit && Date.now() - hit.at <= TTL_MS3) return hit.map;
59635
+ if (hit && Date.now() - hit.at <= TTL_MS4) return hit.map;
59508
59636
  const running = inflight4.get(chainId);
59509
59637
  if (running) return running;
59510
59638
  const p = fetchChainEntities(chainId).then((map) => {
@@ -69815,6 +69943,12 @@ function borrowDescription(b) {
69815
69943
  parts.push(
69816
69944
  "Liquidation is a public auction rather than a loan-to-value threshold" + (win ? `, open for ${duration(win)} once started` : "") + (pen != null ? `, and the liquidator is paid ${pct(pen * 100)}` : "") + "."
69817
69945
  );
69946
+ } else if (b.liquidation.model === "repay-to-target-hf") {
69947
+ const l = b.liquidation;
69948
+ const start = l.liquidationLtv != null && l.liquidationLtv > 0 ? `Liquidation starts at ${pct(l.liquidationLtv * 100)} LTV and` : "A liquidation";
69949
+ const sized = l.targetHealthFactor != null ? ` repays as much of your debt as it takes to restore a health factor of ${l.targetHealthFactor.toFixed(2)}` : " repays as much of your debt as it takes to restore the spoke\u2019s target health factor";
69950
+ const bonus = l.penalty != null && l.penalty > 0 ? ` \u2014 the liquidator\u2019s bonus scales up to ${pct(l.penalty * 100)}` + (l.healthFactorForMaxBonus != null ? ` at a health factor of ${l.healthFactorForMaxBonus.toFixed(2)}` : "") + "." : ".";
69951
+ parts.push(`${start}${sized} rather than a fixed share of it${bonus}`);
69818
69952
  } else if (b.liquidation.liquidationLtv != null && b.liquidation.liquidationLtv > 0) {
69819
69953
  const pen = b.liquidation.penalty;
69820
69954
  parts.push(
@@ -69848,6 +69982,27 @@ var TERM_PROFILES = [
69848
69982
  ]
69849
69983
  }
69850
69984
  }),
69985
+ P({
69986
+ id: "aave-v4.spoke@v1",
69987
+ name: "Aave V4 spoke (liquidated to a target health factor)",
69988
+ family: "pool",
69989
+ supply: {
69990
+ description: "A shared liquidity pool on an Aave V4 spoke. Deposits earn interest paid by borrowers of the hub asset, at a rate that moves with utilization. Withdrawals are instant up to the cash left in the pool.",
69991
+ implications: [
69992
+ "Your yield is not fixed \u2014 it rises and falls with borrowing demand.",
69993
+ "If utilization reaches 100 %, withdrawals are blocked until borrowers repay or new deposits arrive.",
69994
+ "Each spoke is its own market: the same asset on another spoke is a separate position with its own risk parameters."
69995
+ ]
69996
+ },
69997
+ borrow: {
69998
+ description: "Borrow against collateral at a rate that moves with utilization. Liquidation here is sized to a target health factor rather than capped at a fixed share of your debt: a liquidator repays whatever it takes to bring the position back up to the target, and their bonus grows the further under water it is.",
69999
+ implications: [
70000
+ "There is no 50 % close-factor protection \u2014 how much of your debt is taken depends on how far the position fell, and a deep enough drop takes all of it.",
70001
+ "The published liquidation penalty is the MAXIMUM bonus, charged only once the position is far enough under water; a shallow liquidation costs less.",
70002
+ "V4 uses ONE loan-to-value per asset for both borrowing and liquidation \u2014 there is no buffer between the LTV you can borrow to and the one that gets you liquidated."
70003
+ ]
70004
+ }
70005
+ }),
69851
70006
  // ── Fixed-term families ──────────────────────────────────────────────────
69852
70007
  P({
69853
70008
  id: "exactly.fixed@v1",
@@ -73624,8 +73779,18 @@ function toTermSheetInput(row, ctx = {}) {
73624
73779
  "flags.variableBorrowDisabled"
73625
73780
  ),
73626
73781
  config,
73627
- closeFactor: num15(pick2(row, "closeFactor")),
73628
- targetHealthFactor: num15(pick2(row, "targetHealthFactor")),
73782
+ // `caps.` is NOT optional here. `/pools/latest` serializes both of these as
73783
+ // TOP-LEVEL row fields, while `/lending/latest` — the endpoint the lending
73784
+ // term sheets are actually stamped on — nests them under `caps` alongside
73785
+ // the supply/borrow caps (yield-tracer `utils/lending.ts`). Reading only
73786
+ // the flat key made every sheet on that endpoint fall through to the
73787
+ // builder's `?? 1`, so Aave and Compound V2 published "the whole debt can
73788
+ // be repaid in one liquidation" against a real close factor of 0.5, and
73789
+ // Aave V4's `targetHealthFactor` vanished entirely.
73790
+ closeFactor: num15(pick2(row, "closeFactor", "caps.closeFactor")),
73791
+ targetHealthFactor: num15(
73792
+ pick2(row, "targetHealthFactor", "caps.targetHealthFactor")
73793
+ ),
73629
73794
  fixedTerm: pick2(row, "fixedTerm") ?? ctx.fixedTerm,
73630
73795
  terms: pick2(row, "terms"),
73631
73796
  market: resolveMarketDescriptors(row, ctx.market)
@@ -73657,6 +73822,11 @@ var ROW_DESCRIPTOR_KEYS = [
73657
73822
  // Fluid: distinguishes a SMART (T2/T3/T4) vault, whose collateral and/or
73658
73823
  // debt is a two-token DEX LP position, from an ordinary T1 pool row.
73659
73824
  "fluid",
73825
+ // Aave V4: the liquidation BONUS CURVE (`healthFactorForMaxBonus`,
73826
+ // `liquidationBonusFactor`). The sizing knob `targetHealthFactor` is a
73827
+ // market-level column and arrives without this; the curve is what turns the
73828
+ // published max bonus from a flat penalty into a bound.
73829
+ "aaveV4",
73660
73830
  "broker",
73661
73831
  "collateralProvider",
73662
73832
  "loanProvider",
@@ -75231,6 +75401,32 @@ var morphoBlueAdapter = {
75231
75401
  };
75232
75402
  }
75233
75403
  };
75404
+ var aaveV4Adapter = {
75405
+ id: "aave-v4",
75406
+ matches: isAaveV4Type,
75407
+ profileId: () => "aave-v4.spoke@v1",
75408
+ build: (input) => {
75409
+ const target = input.targetHealthFactor;
75410
+ const desc = input.market?.aaveV4 ?? {};
75411
+ const maxBonusHf = num17(desc.healthFactorForMaxBonus);
75412
+ return {
75413
+ borrow: {
75414
+ liquidation: {
75415
+ model: "repay-to-target-hf",
75416
+ // NOT a close factor — the ceiling. See the header.
75417
+ closeFactor: 1,
75418
+ targetHealthFactor: target,
75419
+ healthFactorForMaxBonus: maxBonusHf
75420
+ }
75421
+ }
75422
+ };
75423
+ }
75424
+ };
75425
+ function num17(v) {
75426
+ if (v == null) return void 0;
75427
+ const n = typeof v === "string" ? Number(v) : v;
75428
+ return Number.isFinite(n) && n > 0 ? n : void 0;
75429
+ }
75234
75430
 
75235
75431
  // src/terms/adapters/index.ts
75236
75432
  var TERM_ADAPTERS = [
@@ -75254,7 +75450,8 @@ var TERM_ADAPTERS = [
75254
75450
  curvanceAdapter,
75255
75451
  twyneAdapter,
75256
75452
  fraxlendAdapter,
75257
- morphoBlueAdapter
75453
+ morphoBlueAdapter,
75454
+ aaveV4Adapter
75258
75455
  ];
75259
75456
  function resolveAdapter(lender) {
75260
75457
  return TERM_ADAPTERS.find((a) => a.matches(lender));
@@ -76220,7 +76417,7 @@ function legEarnUid(marketUid) {
76220
76417
  if (isVaultVenue(parts[0])) return void 0;
76221
76418
  return marketUid;
76222
76419
  }
76223
- function num17(v) {
76420
+ function num18(v) {
76224
76421
  const n = typeof v === "number" ? v : Number(v);
76225
76422
  return Number.isFinite(n) ? n : 0;
76226
76423
  }
@@ -76242,10 +76439,10 @@ function legAsset(pos) {
76242
76439
  };
76243
76440
  }
76244
76441
  function toLeg(pos) {
76245
- const depositsUsd = num17(pos.depositsUSD);
76246
- const debtUsd = num17(pos.debtUSD) + num17(pos.debtStableUSD);
76247
- const hasSupply = depositsUsd !== 0 || num17(pos.deposits) !== 0;
76248
- const hasDebt = debtUsd !== 0 || num17(pos.debt) !== 0 || num17(pos.debtStable) !== 0;
76442
+ const depositsUsd = num18(pos.depositsUSD);
76443
+ const debtUsd = num18(pos.debtUSD) + num18(pos.debtStableUSD);
76444
+ const hasSupply = depositsUsd !== 0 || num18(pos.deposits) !== 0;
76445
+ const hasDebt = debtUsd !== 0 || num18(pos.debt) !== 0 || num18(pos.debtStable) !== 0;
76249
76446
  return {
76250
76447
  earnUid: legEarnUid(pos.marketUid),
76251
76448
  marketUid: pos.marketUid,
@@ -76285,12 +76482,12 @@ function earnPositionFromLenderEntry(entry) {
76285
76482
  (s) => s.legs.some((l) => l.side !== "none") || s.netUsd !== 0
76286
76483
  );
76287
76484
  const crossMargin = active.length <= 1;
76288
- const suppliedUsd = num17(entry.balanceData?.deposits);
76289
- const borrowedUsd = num17(entry.balanceData?.debt);
76485
+ const suppliedUsd = num18(entry.balanceData?.deposits);
76486
+ const borrowedUsd = num18(entry.balanceData?.debt);
76290
76487
  const aprBreakdown = {
76291
- market: num17(entry.aprData?.apr),
76292
- rewards: num17(entry.aprData?.rewardApr),
76293
- intrinsic: num17(entry.aprData?.intrinsicApr)
76488
+ market: num18(entry.aprData?.apr),
76489
+ rewards: num18(entry.aprData?.rewardApr),
76490
+ intrinsic: num18(entry.aprData?.intrinsicApr)
76294
76491
  };
76295
76492
  return {
76296
76493
  positionUid: buildLendingPositionUid(entry.lender, entry.chainId),
@@ -76304,15 +76501,15 @@ function earnPositionFromLenderEntry(entry) {
76304
76501
  ...entry.lenderInfo?.logoUri ? { logoURI: entry.lenderInfo.logoUri } : {},
76305
76502
  suppliedUsd,
76306
76503
  borrowedUsd,
76307
- netUsd: num17(entry.balanceData?.nav),
76504
+ netUsd: num18(entry.balanceData?.nav),
76308
76505
  apr: aprBreakdown.market + aprBreakdown.rewards + aprBreakdown.intrinsic,
76309
76506
  aprBreakdown,
76310
- depositApr: num17(entry.aprData?.depositApr),
76311
- borrowApr: num17(entry.aprData?.borrowApr),
76507
+ depositApr: num18(entry.aprData?.depositApr),
76508
+ borrowApr: num18(entry.aprData?.borrowApr),
76312
76509
  // Only a cross-margin account HAS one health factor. Publishing the first
76313
76510
  // sub-account's as the row's would be a number about a different position.
76314
76511
  health: crossMargin ? active[0]?.health ?? null : null,
76315
- leverage: num17(entry.leverage),
76512
+ leverage: num18(entry.leverage),
76316
76513
  crossMargin,
76317
76514
  legs: subAccounts.flatMap((s) => s.legs),
76318
76515
  subAccounts: active,