@1delta/margin-fetcher 0.0.412 → 0.0.414

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,8 +1,8 @@
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, LIQUITY_FAMILY_LENDERS, isAaveType, isCompoundV3, isMultiMarket, isSiloV2Type, isSiloV3Type, isDolomite, isMidnight, isTerm, isExactly, isLiquityFamily, isRiver, isTeller, isInverse, isTermMax, isResupply, isUsdd, isFrankencoin, isLlamaLend, isEulerType, isAaveV4Type, isInit, isMorphoType, isCompoundV2Type, isVenusType, isSumerType, AAVE_V3_LENDERS, AAVE_V2_LENDERS, isAaveV2Type, isAaveV32Type, isAaveV3Type, isFluid, isGearboxV3, isYLDR, isCompoundV3Type, isLista, isTectonicType, isKineticType, isBenqiType } from '@1delta/lender-registry';
5
- export { isAaveType, isAaveV2Type, isAaveV32Type, isAaveV3Type, isCompoundV3, isCompoundV3Type, isInit, isMorphoType, isMultiMarket, isYLDR } from '@1delta/lender-registry';
4
+ import { Lender, isExactly, isMidnight, isTerm, isTermMax, isTeller, isListaDao, isLiquityFamily, isRiver, isInverse, isUsdd, isCompoundV3Type, isFluid, isGearboxV3, LIQUITY_FAMILY_LENDERS, isAaveType, isCompoundV3, isMultiMarket, isSiloV2Type, isSiloV3Type, isDolomite, isResupply, isFrankencoin, isLlamaLend, 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
+ 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';
8
8
  import { multicallRetryUniversal, getEvmClient, getEvmChain, getEvmClientUniversal } from '@1delta/providers';
@@ -16139,12 +16139,12 @@ function getCall(lender, chainId) {
16139
16139
 
16140
16140
  // src/lending/public-data/compound-v2/getters/venus.ts
16141
16141
  function readVTokenMetadata(meta, token) {
16142
- const pick2 = (...values) => values.find((v) => v !== void 0);
16142
+ const pick3 = (...values) => values.find((v) => v !== void 0);
16143
16143
  return {
16144
16144
  vToken: meta.vToken ?? "",
16145
16145
  exchangeRateCurrent: meta.exchangeRateCurrent ?? 0n,
16146
- supplyRatePerBlock: pick2(meta.supplyRatePerBlock, meta.supplyRatePerBlockOrTimestamp) ?? 0n,
16147
- borrowRatePerBlock: pick2(meta.borrowRatePerBlock, meta.borrowRatePerBlockOrTimestamp) ?? 0n,
16146
+ supplyRatePerBlock: pick3(meta.supplyRatePerBlock, meta.supplyRatePerBlockOrTimestamp) ?? 0n,
16147
+ borrowRatePerBlock: pick3(meta.borrowRatePerBlock, meta.borrowRatePerBlockOrTimestamp) ?? 0n,
16148
16148
  reserveFactorMantissa: meta.reserveFactorMantissa ?? 0n,
16149
16149
  supplyCaps: meta.supplyCaps ?? 0n,
16150
16150
  borrowCaps: meta.borrowCaps ?? 0n,
@@ -16169,7 +16169,7 @@ function readVTokenMetadata(meta, token) {
16169
16169
  pausedActions: decodePausedActions(meta.pausedActions ?? 0n),
16170
16170
  depositRate: apyToApr(
16171
16171
  calculateRateForCompoundType(
16172
- pick2(
16172
+ pick3(
16173
16173
  meta.supplyRatePerBlock,
16174
16174
  meta.supplyRatePerBlockOrTimestamp
16175
16175
  )?.toString() ?? "0",
@@ -16179,7 +16179,7 @@ function readVTokenMetadata(meta, token) {
16179
16179
  ) * 100,
16180
16180
  variableBorrowRate: apyToApr(
16181
16181
  calculateRateForCompoundType(
16182
- pick2(
16182
+ pick3(
16183
16183
  meta.borrowRatePerBlock,
16184
16184
  meta.borrowRatePerBlockOrTimestamp
16185
16185
  )?.toString() ?? "0",
@@ -22904,7 +22904,7 @@ async function fetchTellerMarkets(chainId) {
22904
22904
  }
22905
22905
  return null;
22906
22906
  };
22907
- const num10 = (i) => {
22907
+ const num11 = (i) => {
22908
22908
  const b = big13(i);
22909
22909
  return b === null ? null : Number(b);
22910
22910
  };
@@ -22914,9 +22914,9 @@ async function fetchTellerMarkets(chainId) {
22914
22914
  config,
22915
22915
  available: big13(base),
22916
22916
  committed: big13(base + 1),
22917
- minRateBps: num10(base + 2),
22917
+ minRateBps: num11(base + 2),
22918
22918
  collateralPerPrincipal: big13(base + 3),
22919
- maxLoanDuration: num10(base + 4),
22919
+ maxLoanDuration: num11(base + 4),
22920
22920
  marketId: big13(base + 5),
22921
22921
  totalAssets: big13(base + 6)
22922
22922
  };
@@ -23879,20 +23879,20 @@ async function fetchInverseMarkets(lender, chainId) {
23879
23879
  if (Object.keys(byAddr).length > 0) {
23880
23880
  const rows = markets.map((market) => {
23881
23881
  const m = byAddr[market.address.toLowerCase()];
23882
- const num10 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
23882
+ const num11 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
23883
23883
  return {
23884
23884
  market,
23885
- totalDebt: num10(m?.totalDebt),
23886
- dolaLiquidity: num10(m?.dolaLiquidity),
23887
- leftToBorrow: num10(m?.leftToBorrow),
23888
- price: num10(m?.price),
23885
+ totalDebt: num11(m?.totalDebt),
23886
+ dolaLiquidity: num11(m?.dolaLiquidity),
23887
+ leftToBorrow: num11(m?.leftToBorrow),
23888
+ price: num11(m?.price),
23889
23889
  borrowPaused: typeof m?.borrowPaused === "boolean" ? m.borrowPaused : null,
23890
- dailyBorrows: num10(m?.dailyBorrows),
23890
+ dailyBorrows: num11(m?.dailyBorrows),
23891
23891
  // The API serves it as a DECIMAL (`replenishmentIncentive: 0.1`),
23892
23892
  // unlike the on-chain bps — normalize here, and fall back to the
23893
23893
  // curated metadata value when the field is missing.
23894
23894
  replenishmentIncentiveBps: (() => {
23895
- const dec = num10(m?.replenishmentIncentive);
23895
+ const dec = num11(m?.replenishmentIncentive);
23896
23896
  if (dec !== null) return Math.round(dec * 1e4);
23897
23897
  const meta = Number(market.replenishmentIncentiveBps);
23898
23898
  return Number.isFinite(meta) ? meta : null;
@@ -26866,30 +26866,138 @@ var { isArray } = lodash;
26866
26866
  var maxRetries = 3;
26867
26867
  var MULTICALL_FAILURE = "0x";
26868
26868
  var isFailedCall = (value) => value === void 0 || value === null || value === MULTICALL_FAILURE;
26869
- var retryFailedSlots = async (chainId, contracts, mapped, getEvmClient17, nextRpcId, batchSize, logs) => {
26870
- const failedIdx = [];
26871
- for (let i = 0; i < mapped.length; i++) {
26872
- if (isFailedCall(mapped[i])) failedIdx.push(i);
26869
+ var MULTICALL_REPAIR_ROUNDS = 3;
26870
+ var MAX_CALLS_PER_SHARD = 400;
26871
+ var DETERMINISTIC_ERROR_NAMES = /* @__PURE__ */ new Set([
26872
+ "ContractFunctionRevertedError",
26873
+ "RawContractError",
26874
+ "AbiDecodingZeroDataError",
26875
+ "AbiDecodingDataSizeTooSmallError",
26876
+ "AbiFunctionNotFoundError"
26877
+ ]);
26878
+ var isDeterministicCallError = (error) => {
26879
+ let err = error;
26880
+ for (let depth = 0; err && depth < 8; depth++) {
26881
+ if (DETERMINISTIC_ERROR_NAMES.has(err.name)) return true;
26882
+ const message = String(err.shortMessage ?? err.message ?? "").toLowerCase();
26883
+ if (message.includes("execution reverted") || message.includes("returned no data"))
26884
+ return true;
26885
+ err = err.cause;
26873
26886
  }
26874
- if (failedIdx.length === 0) return mapped;
26875
- try {
26876
- const provider = getEvmClient17(chainId, nextRpcId);
26877
- const returnData = await provider.multicall({
26878
- allowFailure: true,
26879
- batchSize,
26880
- contracts: failedIdx.map((i) => contracts[i])
26881
- });
26882
- returnData.forEach((entry, k) => {
26883
- if (entry?.status === "success") mapped[failedIdx[k]] = entry.result;
26884
- });
26885
- } catch (error) {
26886
- if (logs) console.log("error in multicall failure-retry", error);
26887
+ return false;
26888
+ };
26889
+ var REPAIR_BACKOFF_BASE_MS = 100;
26890
+ var REPAIR_BACKOFF_JITTER_MS = 60;
26891
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
26892
+ var backoffForRound = (round) => REPAIR_BACKOFF_BASE_MS * 2 ** round + Math.random() * REPAIR_BACKOFF_JITTER_MS;
26893
+ var endpointUrl = (client, rpcId) => client?.transport?.url ?? `rpc#${rpcId}`;
26894
+ var resolveEndpoint = (chainId, getEvmClient17, startRpcId, tried, maxProbe = 12) => {
26895
+ let fallback = null;
26896
+ for (let probe = 0; probe < maxProbe; probe++) {
26897
+ const rpcId = startRpcId + probe;
26898
+ let client;
26899
+ try {
26900
+ client = getEvmClient17(chainId, rpcId);
26901
+ } catch {
26902
+ break;
26903
+ }
26904
+ const url = endpointUrl(client, rpcId);
26905
+ if (!tried?.has(url)) {
26906
+ tried?.add(url);
26907
+ return { client, rpcId, url, reused: false };
26908
+ }
26909
+ fallback ??= { client, rpcId, url, reused: true };
26910
+ }
26911
+ return fallback;
26912
+ };
26913
+ var toSlots = (returnData, allowFailure) => {
26914
+ if (!allowFailure)
26915
+ return returnData.map((value) => ({ value, permanent: false }));
26916
+ return returnData.map(
26917
+ ({ result, status, error }) => status === "success" ? { value: result, permanent: false } : {
26918
+ value: MULTICALL_FAILURE,
26919
+ permanent: isDeterministicCallError(error)
26920
+ }
26921
+ );
26922
+ };
26923
+ var slotValues = (slots) => slots.map((s) => s.value);
26924
+ var recordPermanentFailures = (slots, offset, collector) => {
26925
+ if (!collector) return;
26926
+ for (let i = 0; i < slots.length; i++) {
26927
+ if (slots[i].permanent) collector.add(offset + i);
26928
+ }
26929
+ };
26930
+ var repairFailedSlots = async (chainId, contracts, slots, getEvmClient17, nextRpcId, batchSize, logs, rounds = MULTICALL_REPAIR_ROUNDS, options) => {
26931
+ let searchFrom = nextRpcId;
26932
+ for (let round = 0; round < rounds; round++) {
26933
+ const failedIdx = [];
26934
+ for (let i = 0; i < slots.length; i++) {
26935
+ if (isFailedCall(slots[i].value) && !slots[i].permanent) failedIdx.push(i);
26936
+ }
26937
+ if (failedIdx.length === 0) return slots;
26938
+ const endpoint = resolveEndpoint(
26939
+ chainId,
26940
+ getEvmClient17,
26941
+ searchFrom,
26942
+ options?.tried
26943
+ );
26944
+ if (!endpoint) {
26945
+ if (logs)
26946
+ console.log(
26947
+ `multicall repair: no usable endpoint for chain ${chainId}, giving up with ${failedIdx.length} failed slots`
26948
+ );
26949
+ return slots;
26950
+ }
26951
+ searchFrom = endpoint.rpcId + 1;
26952
+ await sleep(backoffForRound(round));
26953
+ try {
26954
+ const returnData = await endpoint.client.multicall({
26955
+ allowFailure: true,
26956
+ batchSize,
26957
+ contracts: failedIdx.map((i) => contracts[i])
26958
+ });
26959
+ let repaired = 0;
26960
+ returnData.forEach((entry, k) => {
26961
+ const idx = failedIdx[k];
26962
+ if (entry?.status === "success") {
26963
+ slots[idx] = { value: entry.result, permanent: false };
26964
+ repaired++;
26965
+ } else if (isDeterministicCallError(entry?.error)) {
26966
+ slots[idx].permanent = true;
26967
+ }
26968
+ });
26969
+ if (repaired === 0)
26970
+ options?.onEndpointFailure?.({
26971
+ chainId,
26972
+ url: endpoint.url,
26973
+ rpcId: endpoint.rpcId,
26974
+ kind: "slots"
26975
+ });
26976
+ } catch (error) {
26977
+ options?.onEndpointFailure?.({
26978
+ chainId,
26979
+ url: endpoint.url,
26980
+ rpcId: endpoint.rpcId,
26981
+ kind: "transport"
26982
+ });
26983
+ if (logs)
26984
+ console.log(
26985
+ `error in multicall failure-repair round ${round} chain ${chainId}`,
26986
+ error
26987
+ );
26988
+ }
26887
26989
  }
26888
- return mapped;
26990
+ return slots;
26889
26991
  };
26890
- var multicallViemAbiArray = async (chainId, abi, calls, getEvmClient17, retry = true, providerId = 0, retries = maxRetries, allowFailure = true, batchSize = MULTICALL_DEFAULT_BATCH_SIZE, logs = false, retryFailed = false) => {
26992
+ var multicallViemAbiArray = async (chainId, abi, calls, getEvmClient17, retry = true, providerId = 0, retries = maxRetries, allowFailure = true, batchSize = MULTICALL_DEFAULT_BATCH_SIZE, logs = false, retryFailed = false, permanentFailures, options) => {
26993
+ const tried = options?.tried ?? /* @__PURE__ */ new Set();
26994
+ const endpoint = resolveEndpoint(chainId, getEvmClient17, providerId, tried);
26995
+ if (!endpoint) throw new Error("No usable RPC endpoint for " + chainId);
26996
+ const endpointOptions = {
26997
+ tried,
26998
+ onEndpointFailure: options?.onEndpointFailure
26999
+ };
26891
27000
  try {
26892
- const provider = getEvmClient17(chainId, providerId);
26893
27001
  const abiIsArray = isArray(abi[0]);
26894
27002
  const contracts = calls.map(({ address, name, params }, i) => ({
26895
27003
  abi: abiIsArray ? abi?.[i] : abi,
@@ -26897,46 +27005,83 @@ var multicallViemAbiArray = async (chainId, abi, calls, getEvmClient17, retry =
26897
27005
  functionName: name,
26898
27006
  args: params
26899
27007
  }));
26900
- const returnData = await provider.multicall({
27008
+ const returnData = await endpoint.client.multicall({
26901
27009
  allowFailure,
26902
27010
  batchSize,
26903
27011
  contracts
26904
27012
  });
26905
27013
  if (!allowFailure) return returnData;
26906
- const res = returnData.map(
26907
- ({ result, status }) => status !== "success" ? MULTICALL_FAILURE : result
26908
- );
26909
- if (!retryFailed) return res;
26910
- return retryFailedSlots(
27014
+ const slots = toSlots(returnData, true);
27015
+ if (retries > 0 && slots.length > 0 && slots.every((s) => isFailedCall(s.value) && !s.permanent)) {
27016
+ if (logs)
27017
+ console.log(
27018
+ `multicall fully failed on rpc ${endpoint.rpcId} for chain ${chainId}, failing over`
27019
+ );
27020
+ options?.onEndpointFailure?.({
27021
+ chainId,
27022
+ url: endpoint.url,
27023
+ rpcId: endpoint.rpcId,
27024
+ kind: "slots"
27025
+ });
27026
+ await sleep(backoffForRound(0));
27027
+ return multicallViemAbiArray(
27028
+ chainId,
27029
+ abi,
27030
+ calls,
27031
+ getEvmClient17,
27032
+ retry,
27033
+ endpoint.rpcId + 1,
27034
+ retries - 1,
27035
+ allowFailure,
27036
+ batchSize,
27037
+ logs,
27038
+ retryFailed,
27039
+ permanentFailures,
27040
+ endpointOptions
27041
+ );
27042
+ }
27043
+ const finalSlots = retryFailed ? await repairFailedSlots(
26911
27044
  chainId,
26912
27045
  contracts,
26913
- res,
27046
+ slots,
26914
27047
  getEvmClient17,
26915
- providerId + 1,
27048
+ endpoint.rpcId + 1,
26916
27049
  batchSize,
26917
- logs
26918
- );
27050
+ logs,
27051
+ MULTICALL_REPAIR_ROUNDS,
27052
+ endpointOptions
27053
+ ) : slots;
27054
+ recordPermanentFailures(finalSlots, 0, permanentFailures);
27055
+ return slotValues(finalSlots);
26919
27056
  } catch (error) {
26920
27057
  if (logs) console.log("error in multicall", error);
27058
+ options?.onEndpointFailure?.({
27059
+ chainId,
27060
+ url: endpoint.url,
27061
+ rpcId: endpoint.rpcId,
27062
+ kind: "transport"
27063
+ });
26921
27064
  retries--;
26922
27065
  if (retries < 0) throw new Error("Failed too often");
26923
- providerId++;
27066
+ await sleep(backoffForRound(0));
26924
27067
  return multicallViemAbiArray(
26925
27068
  chainId,
26926
27069
  abi,
26927
27070
  calls,
26928
27071
  getEvmClient17,
26929
27072
  retry,
26930
- providerId,
27073
+ endpoint.rpcId + 1,
26931
27074
  retries,
26932
27075
  allowFailure,
26933
27076
  batchSize,
26934
27077
  logs,
26935
- retryFailed
27078
+ retryFailed,
27079
+ permanentFailures,
27080
+ endpointOptions
26936
27081
  );
26937
27082
  }
26938
27083
  };
26939
- var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient17, poolSize, retries = maxRetries, allowFailure = true, batchSize = MULTICALL_DEFAULT_BATCH_SIZE, logs = false, retryFailed = false) => {
27084
+ var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient17, poolSize, retries = maxRetries, allowFailure = true, batchSize = MULTICALL_DEFAULT_BATCH_SIZE, logs = false, retryFailed = false, permanentFailures, options) => {
26940
27085
  if (poolSize <= 1) {
26941
27086
  return multicallViemAbiArray(
26942
27087
  chainId,
@@ -26949,7 +27094,9 @@ var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient17, poolS
26949
27094
  allowFailure,
26950
27095
  batchSize,
26951
27096
  logs,
26952
- retryFailed
27097
+ retryFailed,
27098
+ permanentFailures,
27099
+ options
26953
27100
  );
26954
27101
  }
26955
27102
  const abiIsArray = isArray(abi[0]);
@@ -26959,45 +27106,88 @@ var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient17, poolS
26959
27106
  functionName: name,
26960
27107
  args: params
26961
27108
  }));
26962
- const shardSize = Math.max(1, Math.ceil(contracts.length / poolSize));
27109
+ const shardSize = Math.max(
27110
+ 1,
27111
+ Math.min(MAX_CALLS_PER_SHARD, Math.ceil(contracts.length / poolSize))
27112
+ );
26963
27113
  const batches = [];
26964
27114
  for (let i = 0; i < contracts.length; i += shardSize) {
26965
27115
  batches.push({ start: i, items: contracts.slice(i, i + shardSize) });
26966
27116
  }
26967
27117
  const results = new Array(contracts.length);
26968
- const runBatch = async (batch, rpcId, attemptsLeft, size) => {
27118
+ const runBatch = async (batch, rpcId, attemptsLeft, size, tried) => {
27119
+ const endpoint = resolveEndpoint(chainId, getEvmClient17, rpcId, tried);
27120
+ if (!endpoint) {
27121
+ if (logs)
27122
+ console.log(
27123
+ `sharded multicall: no untried endpoint left for chain ${chainId}`
27124
+ );
27125
+ return;
27126
+ }
27127
+ const shardOptions = {
27128
+ tried,
27129
+ onEndpointFailure: options?.onEndpointFailure
27130
+ };
26969
27131
  try {
26970
- const provider = getEvmClient17(chainId, rpcId);
26971
- const returnData = await provider.multicall({
27132
+ const returnData = await endpoint.client.multicall({
26972
27133
  allowFailure,
26973
27134
  batchSize: size,
26974
27135
  contracts: batch.items
26975
27136
  });
26976
- let mapped = allowFailure ? returnData.map(
26977
- ({ result, status }) => status !== "success" ? MULTICALL_FAILURE : result
26978
- ) : returnData;
27137
+ let slots = toSlots(returnData, allowFailure);
27138
+ if (allowFailure && attemptsLeft > 0 && slots.length > 0 && slots.every((s) => isFailedCall(s.value) && !s.permanent)) {
27139
+ if (logs)
27140
+ console.log(
27141
+ `sharded multicall shard fully failed on rpc ${endpoint.rpcId}, failing over`
27142
+ );
27143
+ options?.onEndpointFailure?.({
27144
+ chainId,
27145
+ url: endpoint.url,
27146
+ rpcId: endpoint.rpcId,
27147
+ kind: "slots"
27148
+ });
27149
+ await sleep(backoffForRound(0));
27150
+ return runBatch(
27151
+ batch,
27152
+ endpoint.rpcId + 1,
27153
+ attemptsLeft - 1,
27154
+ size,
27155
+ tried
27156
+ );
27157
+ }
26979
27158
  if (allowFailure && retryFailed) {
26980
- mapped = await retryFailedSlots(
27159
+ slots = await repairFailedSlots(
26981
27160
  chainId,
26982
27161
  batch.items,
26983
- mapped,
27162
+ slots,
26984
27163
  getEvmClient17,
26985
- rpcId + 1,
27164
+ endpoint.rpcId + 1,
26986
27165
  size,
26987
- logs
27166
+ logs,
27167
+ MULTICALL_REPAIR_ROUNDS,
27168
+ shardOptions
26988
27169
  );
26989
27170
  }
26990
- for (let j = 0; j < mapped.length; j++) {
26991
- results[batch.start + j] = mapped[j];
27171
+ recordPermanentFailures(slots, batch.start, permanentFailures);
27172
+ for (let j = 0; j < slots.length; j++) {
27173
+ results[batch.start + j] = slots[j].value;
26992
27174
  }
26993
27175
  } catch (error) {
26994
27176
  if (logs) console.log("error in sharded multicall batch", error);
27177
+ options?.onEndpointFailure?.({
27178
+ chainId,
27179
+ url: endpoint.url,
27180
+ rpcId: endpoint.rpcId,
27181
+ kind: "transport"
27182
+ });
26995
27183
  if (attemptsLeft <= 0) throw error;
27184
+ await sleep(backoffForRound(0));
26996
27185
  return runBatch(
26997
27186
  batch,
26998
- rpcId + 1,
27187
+ endpoint.rpcId + 1,
26999
27188
  attemptsLeft - 1,
27000
- Math.max(1, Math.floor(size / 2))
27189
+ Math.max(1, Math.floor(size / 2)),
27190
+ tried
27001
27191
  );
27002
27192
  }
27003
27193
  };
@@ -27007,7 +27197,7 @@ var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient17, poolS
27007
27197
  while (true) {
27008
27198
  const idx = cursor++;
27009
27199
  if (idx >= batches.length) break;
27010
- await runBatch(batches[idx], workerId, retries, batchSize);
27200
+ await runBatch(batches[idx], workerId, retries, batchSize, /* @__PURE__ */ new Set());
27011
27201
  }
27012
27202
  };
27013
27203
  await Promise.all(Array.from({ length: workers }, (_3, w) => worker(w)));
@@ -27037,7 +27227,11 @@ var getCompoundV2UserDataConverter = (lender, chainId, account, metaMap) => {
27037
27227
  const lendingPositions = {};
27038
27228
  let totalDebt24h = 0;
27039
27229
  let totalDeposits24h = 0;
27040
- const asstsIn = data[data.length - 1].map((a) => a.toLowerCase());
27230
+ const assetsInRaw = data[data.length - 1];
27231
+ if (isFailedCall(assetsInRaw) || !Array.isArray(assetsInRaw)) {
27232
+ return void 0;
27233
+ }
27234
+ const asstsIn = assetsInRaw.map((a) => a.toLowerCase());
27041
27235
  for (let i = 0; i < tokens.length; i++) {
27042
27236
  const { cToken } = tokens[i];
27043
27237
  const key = createMarketUid(chainId, lender, cToken);
@@ -28520,7 +28714,11 @@ var getAaveV3UserDataConverter = (lender, chainId, account, metaMap) => {
28520
28714
  if (data.length !== expectedNumberOfCalls) {
28521
28715
  return void 0;
28522
28716
  }
28523
- const userEMode = Number(data[expectedNumberOfCalls - 1]);
28717
+ const rawEMode = Number(data[expectedNumberOfCalls - 1]);
28718
+ if (isFailedCall(data[expectedNumberOfCalls - 1]) || !Number.isFinite(rawEMode)) {
28719
+ return void 0;
28720
+ }
28721
+ const userEMode = rawEMode;
28524
28722
  const lendingPositions = {};
28525
28723
  let totalDebt24h = 0;
28526
28724
  let totalDeposits24h = 0;
@@ -32358,6 +32556,85 @@ var getTermMaxUserDataConverter = (_lender, chainId, account, meta) => {
32358
32556
  ];
32359
32557
  };
32360
32558
 
32559
+ // src/lending/user-data/fetch-balances/validate.ts
32560
+ var BALANCE_FIELDS = [
32561
+ "deposits",
32562
+ "debt",
32563
+ "nav",
32564
+ "collateral",
32565
+ "adjustedDebt",
32566
+ "borrowDiscountedCollateral"
32567
+ ];
32568
+ var APR_FIELDS = ["apr", "borrowApr", "depositApr"];
32569
+ var isBadNumber = (v) => typeof v === "number" && !Number.isFinite(v);
32570
+ function checkSubAccount(entry) {
32571
+ const violations = [];
32572
+ const accountId = entry?.accountId ?? "0";
32573
+ const balance = entry?.balanceData;
32574
+ const apr = entry?.aprData;
32575
+ for (const field12 of BALANCE_FIELDS) {
32576
+ if (isBadNumber(balance?.[field12]))
32577
+ violations.push({
32578
+ accountId,
32579
+ code: "non-finite",
32580
+ detail: `balanceData.${field12} = ${balance[field12]}`,
32581
+ requiresFailedReads: false
32582
+ });
32583
+ }
32584
+ for (const field12 of APR_FIELDS) {
32585
+ if (isBadNumber(apr?.[field12]))
32586
+ violations.push({
32587
+ accountId,
32588
+ code: "non-finite",
32589
+ detail: `aprData.${field12} = ${apr[field12]}`,
32590
+ requiresFailedReads: false
32591
+ });
32592
+ }
32593
+ if (isBadNumber(entry?.health))
32594
+ violations.push({
32595
+ accountId,
32596
+ code: "non-finite",
32597
+ detail: `health = ${entry.health}`,
32598
+ requiresFailedReads: false
32599
+ });
32600
+ if (isBadNumber(entry?.borrowCapacityUSD))
32601
+ violations.push({
32602
+ accountId,
32603
+ code: "non-finite",
32604
+ detail: `borrowCapacityUSD = ${entry.borrowCapacityUSD}`,
32605
+ requiresFailedReads: false
32606
+ });
32607
+ const mode = entry?.userConfig?.selectedMode;
32608
+ if (mode !== void 0 && mode !== null && String(mode).includes("NaN"))
32609
+ violations.push({
32610
+ accountId,
32611
+ code: "invalid-mode",
32612
+ detail: `userConfig.selectedMode = ${String(mode)}`,
32613
+ requiresFailedReads: false
32614
+ });
32615
+ if ((balance?.debt ?? 0) > 0 && (balance?.deposits ?? 0) <= 0)
32616
+ violations.push({
32617
+ accountId,
32618
+ code: "debt-without-collateral",
32619
+ detail: `debt = ${balance?.debt}, deposits = ${balance?.deposits}`,
32620
+ requiresFailedReads: true
32621
+ });
32622
+ return violations;
32623
+ }
32624
+ function validateUserData(userData, hadFailedReads) {
32625
+ const kept = [];
32626
+ const dropped = [];
32627
+ const violations = [];
32628
+ for (const entry of userData?.data ?? []) {
32629
+ const found = checkSubAccount(entry);
32630
+ violations.push(...found);
32631
+ const fatal = found.filter((v) => !v.requiresFailedReads || hadFailedReads);
32632
+ if (fatal.length > 0) dropped.push(entry?.accountId ?? "0");
32633
+ else kept.push(entry);
32634
+ }
32635
+ return { kept, violations, dropped };
32636
+ }
32637
+
32361
32638
  // src/lending/user-data/fetch-balances/parse.ts
32362
32639
  function getUserDataConverter(lender, chainId, account, params, meta) {
32363
32640
  if (isAaveV4Type(lender))
@@ -32452,6 +32729,7 @@ var filterEmptyUserData = (userData, keepActiveControllerMode = false) => {
32452
32729
  if (filteredData.length === 0) return null;
32453
32730
  return { ...userData, data: filteredData };
32454
32731
  };
32732
+ var getReadFailurePolicy = (lender) => isMultiMarket(lender) && !hasCrossMarginRisk(lender) ? "lenient" : "strict";
32455
32733
  var convertLenderUserDataResult = (chainId, queriesRaw, rawResults, lenderState, options) => {
32456
32734
  const queries = organizeUserQueries(queriesRaw);
32457
32735
  const lenderData = {};
@@ -32464,51 +32742,94 @@ var convertLenderUserDataResult = (chainId, queriesRaw, rawResults, lenderState,
32464
32742
  query3.params,
32465
32743
  lenderState
32466
32744
  );
32745
+ const data = rawResults.slice(currentSlice, currentSlice + sliceLength);
32746
+ let failedCalls = 0;
32747
+ let retryableFailedCalls = 0;
32748
+ for (let i = 0; i < data.length; i++) {
32749
+ if (!isFailedCall(data[i])) continue;
32750
+ failedCalls++;
32751
+ if (!options?.permanentFailures?.has(currentSlice + i))
32752
+ retryableFailedCalls++;
32753
+ }
32754
+ currentSlice += sliceLength;
32755
+ const allFailed = data.length > 0 && failedCalls === data.length;
32756
+ const policy = getReadFailurePolicy(query3.lender);
32757
+ const voided = allFailed || policy === "strict" && retryableFailedCalls > 0;
32758
+ if (failedCalls > 0) {
32759
+ options?.onIncomplete?.({
32760
+ lender: query3.lender,
32761
+ failedCalls,
32762
+ retryableFailedCalls,
32763
+ totalCalls: data.length,
32764
+ dropped: voided,
32765
+ reason: allFailed ? "all-reads-failed" : voided ? "partial-read-cross-margin" : "partial-read"
32766
+ });
32767
+ }
32768
+ if (voided) continue;
32769
+ let convertedData;
32467
32770
  try {
32468
- const data = rawResults.slice(currentSlice, currentSlice + sliceLength);
32469
- let failedCalls = 0;
32470
- for (const entry of data) if (isFailedCall(entry)) failedCalls++;
32471
- const allFailed = data.length > 0 && failedCalls === data.length;
32472
- if (failedCalls > 0) {
32771
+ convertedData = converter(data);
32772
+ } catch (e) {
32773
+ options?.onIncomplete?.({
32774
+ lender: query3.lender,
32775
+ failedCalls,
32776
+ retryableFailedCalls,
32777
+ totalCalls: data.length,
32778
+ dropped: true,
32779
+ reason: "converter-error",
32780
+ detail: e instanceof Error ? e.message : String(e)
32781
+ });
32782
+ continue;
32783
+ }
32784
+ if (!convertedData) continue;
32785
+ const incomplete = retryableFailedCalls > 0;
32786
+ const publish = (key, entry) => {
32787
+ if (!entry) return;
32788
+ const { kept, violations, dropped } = validateUserData(entry, incomplete);
32789
+ if (dropped.length > 0) {
32473
32790
  options?.onIncomplete?.({
32474
- lender: query3.lender,
32791
+ lender: key,
32475
32792
  failedCalls,
32793
+ retryableFailedCalls,
32476
32794
  totalCalls: data.length,
32477
- dropped: allFailed
32795
+ dropped: kept.length === 0,
32796
+ reason: "invariant-violation",
32797
+ detail: violations.map((v) => `${v.code}: ${v.detail}`).join("; ")
32478
32798
  });
32479
32799
  }
32480
- if (allFailed) {
32481
- currentSlice += sliceLength;
32482
- continue;
32483
- }
32484
- const convertedData = converter(data);
32485
- if (convertedData) {
32486
- if (isMultiMarket(query3.lender)) {
32487
- Object.keys(convertedData).forEach((market) => {
32488
- const filtered = filterEmptyUserData(convertedData[market]);
32489
- if (filtered) {
32490
- filtered.lender = market;
32491
- if (failedCalls > 0) filtered.incomplete = true;
32492
- lenderData[market] = filtered;
32493
- }
32494
- });
32495
- } else {
32496
- const filtered = filterEmptyUserData(
32800
+ if (kept.length === 0) return;
32801
+ entry.data = kept;
32802
+ entry.lender = key;
32803
+ if (incomplete) entry.incomplete = true;
32804
+ lenderData[key] = entry;
32805
+ };
32806
+ try {
32807
+ if (isMultiMarket(query3.lender)) {
32808
+ for (const market of Object.keys(convertedData)) {
32809
+ publish(market, filterEmptyUserData(convertedData[market]));
32810
+ }
32811
+ } else {
32812
+ publish(
32813
+ query3.lender,
32814
+ filterEmptyUserData(
32497
32815
  convertedData,
32498
32816
  // Euler: retain a controller-only (empty) sub-account so its active
32499
32817
  // controller mode reaches the leverage-open builder.
32500
32818
  isEulerType(query3.lender)
32501
- );
32502
- if (filtered) {
32503
- filtered.lender = query3.lender;
32504
- if (failedCalls > 0) filtered.incomplete = true;
32505
- lenderData[query3.lender] = filtered;
32506
- }
32507
- }
32819
+ )
32820
+ );
32508
32821
  }
32509
32822
  } catch (e) {
32823
+ options?.onIncomplete?.({
32824
+ lender: query3.lender,
32825
+ failedCalls,
32826
+ retryableFailedCalls,
32827
+ totalCalls: data.length,
32828
+ dropped: true,
32829
+ reason: "converter-error",
32830
+ detail: e instanceof Error ? e.message : String(e)
32831
+ });
32510
32832
  }
32511
- currentSlice += sliceLength;
32512
32833
  }
32513
32834
  return lenderData;
32514
32835
  };
@@ -37131,7 +37452,7 @@ function unflattenLenderData(pools) {
37131
37452
  }
37132
37453
  return result;
37133
37454
  }
37134
- var getLenderUserDataResult = async (chainId, queriesRaw, getEvmClient17, allowFailure = true, batchSize = MULTICALL_DEFAULT_BATCH_SIZE, retries = 3, logs = false, concurrency = 1) => {
37455
+ var getLenderUserDataResult = async (chainId, queriesRaw, getEvmClient17, allowFailure = true, batchSize = MULTICALL_DEFAULT_BATCH_SIZE, retries = 3, logs = false, concurrency = 1, permanentFailures, onEndpointFailure) => {
37135
37456
  const queries = organizeUserQueries(queriesRaw);
37136
37457
  const builtCalls = await Promise.all(
37137
37458
  queries.map(async (query3) => {
@@ -37158,8 +37479,11 @@ var getLenderUserDataResult = async (chainId, queriesRaw, getEvmClient17, allowF
37158
37479
  batchSize,
37159
37480
  logs,
37160
37481
  // Retry individual failed slots: a dropped call here is indistinguishable
37161
- // from "user has no position" downstream, so it is worth one extra pass.
37162
- true
37482
+ // from "user has no position" downstream, so it is worth re-reading on
37483
+ // other endpoints before giving up.
37484
+ true,
37485
+ permanentFailures,
37486
+ onEndpointFailure ? { onEndpointFailure } : void 0
37163
37487
  );
37164
37488
  };
37165
37489
  var prepareLenderUserDataRpcCalls = async (chainId, queriesRaw, batchSize = MULTICALL_DEFAULT_BATCH_SIZE, blockTag = "latest", allowFailure = true) => {
@@ -37224,8 +37548,11 @@ async function getLenderUserDataMulti(account, chainQueries, lenderState) {
37224
37548
  };
37225
37549
  }
37226
37550
  );
37551
+ const permanentFailuresByChain = cleanChainQueries.map(
37552
+ () => /* @__PURE__ */ new Set()
37553
+ );
37227
37554
  const promises = cleanChainQueries.map(
37228
- async ({ chainId, queries, providerOptions }) => {
37555
+ async ({ chainId, queries, providerOptions }, i) => {
37229
37556
  return await getLenderUserDataResult(
37230
37557
  chainId,
37231
37558
  queries,
@@ -37233,7 +37560,9 @@ async function getLenderUserDataMulti(account, chainQueries, lenderState) {
37233
37560
  providerOptions.allowFailure,
37234
37561
  providerOptions.batchSize,
37235
37562
  providerOptions.retries,
37236
- providerOptions.logs
37563
+ providerOptions.logs,
37564
+ 1,
37565
+ permanentFailuresByChain[i]
37237
37566
  );
37238
37567
  }
37239
37568
  );
@@ -37245,7 +37574,8 @@ async function getLenderUserDataMulti(account, chainQueries, lenderState) {
37245
37574
  chainId,
37246
37575
  queries,
37247
37576
  data[i],
37248
- parsedLenderState
37577
+ parsedLenderState,
37578
+ { permanentFailures: permanentFailuresByChain[i] }
37249
37579
  );
37250
37580
  returndata[chainId] = dataForChain;
37251
37581
  } catch (e) {
@@ -38032,7 +38362,14 @@ function fuseLenderData(userDataResult, summary) {
38032
38362
  lender,
38033
38363
  chainId,
38034
38364
  account: userData.account,
38035
- data: userData.data
38365
+ data: userData.data,
38366
+ // Carry the partial-read marker onto the entry the client renders — the
38367
+ // top-level `incompleteLenders` list alone leaves a consumer to re-join it
38368
+ // by key, which no client did, so broken aggregates rendered as fact.
38369
+ ...userData.incomplete ? { incomplete: true } : {},
38370
+ // Same reasoning for the stale marker: a client that cannot see it will
38371
+ // render a cached position as a live one.
38372
+ ...userData.stale ? { stale: true, staleAgeMs: userData.staleAgeMs } : {}
38036
38373
  });
38037
38374
  }
38038
38375
  entries.sort(
@@ -40083,13 +40420,66 @@ var monadLstFetcher = {
40083
40420
  return out;
40084
40421
  }
40085
40422
  };
40423
+
40424
+ // src/yields/intrinsic/linearRewards.ts
40425
+ var YEAR_SECONDS4 = 31536000n;
40426
+ var ONE_E186 = 10n ** 18n;
40427
+ var LINEAR_REWARDS_APR_ABI = [
40428
+ {
40429
+ name: "rewardsCycleData",
40430
+ type: "function",
40431
+ stateMutability: "view",
40432
+ inputs: [],
40433
+ outputs: [
40434
+ { name: "cycleEnd", type: "uint40" },
40435
+ { name: "lastSync", type: "uint40" },
40436
+ { name: "rewardCycleAmount", type: "uint216" }
40437
+ ]
40438
+ },
40439
+ {
40440
+ name: "maxDistributionPerSecondPerAsset",
40441
+ type: "function",
40442
+ stateMutability: "view",
40443
+ inputs: [],
40444
+ outputs: [{ type: "uint256" }]
40445
+ },
40446
+ {
40447
+ name: "storedTotalAssets",
40448
+ type: "function",
40449
+ stateMutability: "view",
40450
+ inputs: [],
40451
+ outputs: [{ type: "uint256" }]
40452
+ }
40453
+ ];
40454
+ var linearRewardsCalls = (address) => [
40455
+ { address, name: "rewardsCycleData", params: [] },
40456
+ { address, name: "maxDistributionPerSecondPerAsset", params: [] },
40457
+ { address, name: "storedTotalAssets", params: [] }
40458
+ ];
40459
+ var LINEAR_REWARDS_READS = 3;
40460
+ function linearRewardsApr(results, offset = 0) {
40461
+ const cycle = results[offset];
40462
+ const maxPerSecondPerAsset = results[offset + 1];
40463
+ const storedTotalAssets = results[offset + 2];
40464
+ if (!Array.isArray(cycle) || typeof maxPerSecondPerAsset !== "bigint" || typeof storedTotalAssets !== "bigint" || storedTotalAssets === 0n) {
40465
+ return 0;
40466
+ }
40467
+ const [cycleEnd, lastSync, rewardCycleAmount] = cycle;
40468
+ const window = BigInt(cycleEnd) - BigInt(lastSync);
40469
+ const uncapped = window > 0n ? rewardCycleAmount / window : 0n;
40470
+ const cap = maxPerSecondPerAsset * storedTotalAssets / ONE_E186;
40471
+ const perSecond = uncapped < cap ? uncapped : cap;
40472
+ return Number(perSecond * YEAR_SECONDS4 * ONE_E186 / storedTotalAssets) / 1e16;
40473
+ }
40474
+
40475
+ // src/yields/intrinsic/fetchers/frax.ts
40086
40476
  var CHAIN_ID7 = Chain.ETHEREUM_MAINNET;
40087
40477
  var FRXETH_SUMMARY_URL = "https://api.frax.finance/v2/frxeth/summary/latest";
40088
40478
  var SFRXETH = "0xac3e018457b222d93114458476f3e3416abbe38f";
40089
40479
  var SFRXUSD = "0xcf62f905562626cfcdd2261162a51fd02fc9c5b6";
40090
40480
  var SFRAX = "0xa663b02cf0a4b149d2ad41910cb81e23e1c41c32";
40091
- var YEAR_SECONDS4 = 31536000n;
40092
- var ONE_E186 = 10n ** 18n;
40481
+ var YEAR_SECONDS5 = 31536000n;
40482
+ var ONE_E187 = 10n ** 18n;
40093
40483
  var SFRXETH_KEY = "SFRXETH";
40094
40484
  var SFRXUSD_KEY = "SFRXUSD";
40095
40485
  var SFRXUSD_GROUP_KEY = "Staked Frax USD::sfrxUSD";
@@ -40125,33 +40515,6 @@ var XERC4626_ABI = [
40125
40515
  outputs: [{ type: "uint256" }]
40126
40516
  }
40127
40517
  ];
40128
- var LINEAR_REWARDS_ABI = [
40129
- {
40130
- name: "rewardsCycleData",
40131
- type: "function",
40132
- stateMutability: "view",
40133
- inputs: [],
40134
- outputs: [
40135
- { type: "uint40", name: "cycleEnd" },
40136
- { type: "uint40", name: "lastSync" },
40137
- { type: "uint216", name: "rewardCycleAmount" }
40138
- ]
40139
- },
40140
- {
40141
- name: "maxDistributionPerSecondPerAsset",
40142
- type: "function",
40143
- stateMutability: "view",
40144
- inputs: [],
40145
- outputs: [{ type: "uint256" }]
40146
- },
40147
- {
40148
- name: "storedTotalAssets",
40149
- type: "function",
40150
- stateMutability: "view",
40151
- inputs: [],
40152
- outputs: [{ type: "uint256" }]
40153
- }
40154
- ];
40155
40518
  var robustFetchJson2 = async (url, { timeoutMs = 5e3, attempts = 3 } = {}) => {
40156
40519
  let lastErr;
40157
40520
  for (let i = 0; i < attempts; i++) {
@@ -40188,7 +40551,7 @@ var fetchSfrxEthAprOnChain = async () => {
40188
40551
  const window = BigInt(cycleEnd) - BigInt(lastSync);
40189
40552
  if (window <= 0n || totalAssets === 0n) return 0;
40190
40553
  const perSecond = lastRewardAmount / window;
40191
- return Number(perSecond * YEAR_SECONDS4 * ONE_E186 / totalAssets) / 1e16;
40554
+ return Number(perSecond * YEAR_SECONDS5 * ONE_E187 / totalAssets) / 1e16;
40192
40555
  };
40193
40556
  var fetchSfrxEthApr = async () => {
40194
40557
  try {
@@ -40203,26 +40566,14 @@ var fetchSfrxEthApr = async () => {
40203
40566
  var fetchFraxSavingsAprs = async () => {
40204
40567
  const results = await multicallRetryUniversal({
40205
40568
  chain: CHAIN_ID7,
40206
- abi: LINEAR_REWARDS_ABI,
40207
- calls: [SFRXUSD, SFRAX].flatMap((address) => [
40208
- { address, name: "rewardsCycleData", params: [] },
40209
- { address, name: "maxDistributionPerSecondPerAsset", params: [] },
40210
- { address, name: "storedTotalAssets", params: [] }
40211
- ]),
40569
+ abi: LINEAR_REWARDS_APR_ABI,
40570
+ calls: [SFRXUSD, SFRAX].flatMap((address) => linearRewardsCalls(address)),
40212
40571
  allowFailure: false
40213
40572
  });
40214
- const aprAt = (offset) => {
40215
- const [cycleEnd, lastSync, rewardCycleAmount] = results[offset];
40216
- const maxPerSecondPerAsset = results[offset + 1];
40217
- const storedTotalAssets = results[offset + 2];
40218
- if (storedTotalAssets === 0n) return 0;
40219
- const window = BigInt(cycleEnd) - BigInt(lastSync);
40220
- const uncapped = window > 0n ? rewardCycleAmount / window : 0n;
40221
- const cap = maxPerSecondPerAsset * storedTotalAssets / ONE_E186;
40222
- const perSecond = uncapped < cap ? uncapped : cap;
40223
- return Number(perSecond * YEAR_SECONDS4 * ONE_E186 / storedTotalAssets) / 1e16;
40573
+ return {
40574
+ sfrxUsd: linearRewardsApr(results, 0),
40575
+ sfrax: linearRewardsApr(results, LINEAR_REWARDS_READS)
40224
40576
  };
40225
- return { sfrxUsd: aprAt(0), sfrax: aprAt(3) };
40226
40577
  };
40227
40578
  var sfrxEthFetcher = {
40228
40579
  label: "SFRXETH",
@@ -41849,7 +42200,7 @@ var hastraPrimeFetcher = {
41849
42200
  }
41850
42201
  };
41851
42202
  var RAY5 = 10n ** 27n;
41852
- var YEAR_SECONDS5 = 31536e3;
42203
+ var YEAR_SECONDS6 = 31536e3;
41853
42204
  var SPARK_VAULTS_V2 = [
41854
42205
  {
41855
42206
  chainId: Chain.ETHEREUM_MAINNET,
@@ -41927,7 +42278,7 @@ var SSR_ABI = [
41927
42278
  var aprFromVsr = (vsr) => {
41928
42279
  if (vsr <= RAY5) return 0;
41929
42280
  const perSecond = Number(vsr - RAY5) / 1e27;
41930
- return perSecond * YEAR_SECONDS5 * 100;
42281
+ return perSecond * YEAR_SECONDS6 * 100;
41931
42282
  };
41932
42283
  var chainsOf = (vaults) => [
41933
42284
  ...new Set(vaults.map((v) => v.chainId))
@@ -41992,6 +42343,23 @@ var sparkSavingsFetcher = {
41992
42343
  return out;
41993
42344
  }
41994
42345
  };
42346
+ var CHAIN_ID8 = Chain.ETHEREUM_MAINNET;
42347
+ var SREUSD = "0x557ab1e003951a73c12d16f0fea8490e39c33c35";
42348
+ var SREUSD_KEY = "SREUSD";
42349
+ var SREUSD_GROUP_KEY = "Savings reUSD::sreUSD";
42350
+ var sreusdSavingsFetcher = {
42351
+ label: "SREUSD",
42352
+ fetch: async () => {
42353
+ const results = await multicallRetryUniversal({
42354
+ chain: CHAIN_ID8,
42355
+ abi: LINEAR_REWARDS_APR_ABI,
42356
+ calls: linearRewardsCalls(SREUSD),
42357
+ allowFailure: false
42358
+ });
42359
+ const apr = linearRewardsApr(results);
42360
+ return { [SREUSD_KEY]: apr, [SREUSD_GROUP_KEY]: apr };
42361
+ }
42362
+ };
41995
42363
  var RATE_ABI = [
41996
42364
  {
41997
42365
  name: "rate",
@@ -42019,9 +42387,9 @@ var SUSDP_VAULTS = [
42019
42387
  address: "0x472ed57b376fe400259fb28e5c46eb53f0e3e7e7"
42020
42388
  }
42021
42389
  ];
42022
- var YEAR_SECONDS6 = 31536e3;
42390
+ var YEAR_SECONDS7 = 31536e3;
42023
42391
  var BASE_27 = 1e27;
42024
- var aprFromRate = (rate) => Number(rate) / BASE_27 * YEAR_SECONDS6 * 100;
42392
+ var aprFromRate = (rate) => Number(rate) / BASE_27 * YEAR_SECONDS7 * 100;
42025
42393
  var SUSDP_KEY = "SUSDP";
42026
42394
  var SUSDP_GROUP_KEY = "Staked USDp::sUSDp";
42027
42395
  var parallelFetcher = {
@@ -42241,10 +42609,10 @@ var toriFetcher = {
42241
42609
  return { [STRUSD_KEY]: apr, [STRUSD_GROUP_KEY]: apr };
42242
42610
  }
42243
42611
  };
42244
- var CHAIN_ID8 = Chain.ETHEREUM_MAINNET;
42612
+ var CHAIN_ID9 = Chain.ETHEREUM_MAINNET;
42245
42613
  var SNUSD = "0x08efcc2f3e61185d0ea7f8830b3fec9bfa2ee313";
42246
- var YEAR_SECONDS7 = 31536000n;
42247
- var ONE_E187 = 10n ** 18n;
42614
+ var YEAR_SECONDS8 = 31536000n;
42615
+ var ONE_E188 = 10n ** 18n;
42248
42616
  var SNUSD_KEY = "SNUSD";
42249
42617
  var SNUSD_GROUP_KEY = "Staked NUSD::sNUSD";
42250
42618
  var SNUSD_LEGACY_GROUP_KEY = "sNUSD::Staked NUSD";
@@ -42280,7 +42648,7 @@ var STAKED_NUSD_ABI = [
42280
42648
  ];
42281
42649
  var fetchSnusdAprOnChain = async () => {
42282
42650
  const [vestingAmount, lastDistribution, vestingPeriod, totalAssets] = await multicallRetryUniversal({
42283
- chain: CHAIN_ID8,
42651
+ chain: CHAIN_ID9,
42284
42652
  abi: STAKED_NUSD_ABI,
42285
42653
  calls: [
42286
42654
  { address: SNUSD, name: "vestingAmount", params: [] },
@@ -42295,7 +42663,7 @@ var fetchSnusdAprOnChain = async () => {
42295
42663
  const window = elapsed > vestingPeriod ? elapsed : vestingPeriod;
42296
42664
  if (window <= 0n) return 0;
42297
42665
  const perSecond = vestingAmount / window;
42298
- return Number(perSecond * YEAR_SECONDS7 * ONE_E187 / totalAssets) / 1e16;
42666
+ return Number(perSecond * YEAR_SECONDS8 * ONE_E188 / totalAssets) / 1e16;
42299
42667
  };
42300
42668
  var snusdFetcher = {
42301
42669
  label: "SNUSD",
@@ -42308,13 +42676,13 @@ var snusdFetcher = {
42308
42676
  };
42309
42677
  }
42310
42678
  };
42311
- var CHAIN_ID9 = Chain.ETHEREUM_MAINNET;
42679
+ var CHAIN_ID10 = Chain.ETHEREUM_MAINNET;
42312
42680
  var APYUSD = "0x38eeb52f0771140d10c4e9a9a72349a329fe8a6a";
42313
42681
  var APYX_LINEAR_VEST = "0x0d62b4cc02b4b51ed19ddf41d7a7979cf394c99f";
42314
42682
  var APYX_DISCOVER_URL = "https://api.apyx.fi/v1/rewards/seasons/2/discover";
42315
42683
  var APYX_DEFILLAMA_POOL = "cb6139f9-4a68-4efd-8245-0312a92aee55";
42316
- var YEAR_SECONDS8 = 31536000n;
42317
- var ONE_E188 = 10n ** 18n;
42684
+ var YEAR_SECONDS9 = 31536000n;
42685
+ var ONE_E189 = 10n ** 18n;
42318
42686
  var APYUSD_KEY = "APYUSD";
42319
42687
  var APYUSD_GROUP_KEY = "apyUSD::APYUSD";
42320
42688
  var APYUSD_LEGACY_GROUP_KEY = "apyUSD::apyUSD";
@@ -42343,7 +42711,7 @@ var APYX_READ_ABI = [
42343
42711
  ];
42344
42712
  var fetchApyusdAprOnChain = async () => {
42345
42713
  const [totalAssets, unvested, periodRemaining] = await multicallRetryUniversal({
42346
- chain: CHAIN_ID9,
42714
+ chain: CHAIN_ID10,
42347
42715
  abi: APYX_READ_ABI,
42348
42716
  calls: [
42349
42717
  { address: APYUSD, name: "totalAssets", params: [] },
@@ -42360,7 +42728,7 @@ var fetchApyusdAprOnChain = async () => {
42360
42728
  throw new Error("apyx vesting state empty");
42361
42729
  }
42362
42730
  const perSecond = unvested / periodRemaining;
42363
- return Number(perSecond * YEAR_SECONDS8 * ONE_E188 / totalAssets) / 1e16;
42731
+ return Number(perSecond * YEAR_SECONDS9 * ONE_E189 / totalAssets) / 1e16;
42364
42732
  };
42365
42733
  var fetchApyusdApyFromApi = async () => {
42366
42734
  const res = await fetch(APYX_DISCOVER_URL, {
@@ -42480,7 +42848,7 @@ var strataFetcher = {
42480
42848
  }
42481
42849
  };
42482
42850
  var RAY6 = 10n ** 27n;
42483
- var YEAR_SECONDS9 = 31536e3;
42851
+ var YEAR_SECONDS10 = 31536e3;
42484
42852
  var DSR_ABI = [
42485
42853
  {
42486
42854
  name: "dsr",
@@ -42508,7 +42876,7 @@ var SUSDD_GROUP_KEY = "Savings Usdd::sUSDD";
42508
42876
  var aprFromDsr = (dsr) => {
42509
42877
  if (dsr <= RAY6) return 0;
42510
42878
  const perSecond = Number(dsr - RAY6) / 1e27;
42511
- return perSecond * YEAR_SECONDS9 * 100;
42879
+ return perSecond * YEAR_SECONDS10 * 100;
42512
42880
  };
42513
42881
  var fetchChainDsr = async (chainId, pot) => {
42514
42882
  const [dsr] = await multicallRetryUniversal({
@@ -44444,6 +44812,30 @@ var SINGLE_CHAIN_ENTRIES = {
44444
44812
  yieldFetcher: fraxSavingsFetcher,
44445
44813
  yieldKey: "SFRXUSD"
44446
44814
  },
44815
+ {
44816
+ // Resupply sreUSD — the savings leg of the reUSD CDP, and the same
44817
+ // `LinearRewardsErc4626` family as the two Frax rows below (so it
44818
+ // shares `linearRewards.ts`). Plain instant 4626: `maxDeposit =
44819
+ // uint.max`, no cooldown, no exit fee (verified on-chain 2026-08).
44820
+ //
44821
+ // The rate is deliberately modest and that is not an error: sreUSD is
44822
+ // funded by Resupply's revenue share plus the DYNAMIC OFF-PEG component
44823
+ // of the borrow rate, not by the whole borrow rate — 0.74% APR against
44824
+ // a 2.27% borrow rate at integration, cross-checked against DefiLlama
44825
+ // (0.711%) and the protocol's own `Utilities.sreusdRates()` lens.
44826
+ address: "0x557ab1e003951a73c12d16f0fea8490e39c33c35",
44827
+ underlying: "0x57ab1e0003f623289cd798b1824be09a793e4bec",
44828
+ // reUSD
44829
+ symbol: "sreUSD",
44830
+ brand: "Resupply",
44831
+ description: "reUSD is Resupply\u2019s CDP stablecoin, minted against other lenders\u2019 supply positions (Curve Lend / Fraxlend). sreUSD streams the protocol\u2019s revenue share plus the off-peg borrow-rate amplifier to depositors on a 7-day linear cycle. Instant exit.",
44832
+ decimals: 18,
44833
+ isRebasing: false,
44834
+ isMintable: true,
44835
+ withdrawalMode: "instant",
44836
+ yieldFetcher: sreusdSavingsFetcher,
44837
+ yieldKey: "SREUSD"
44838
+ },
44447
44839
  {
44448
44840
  // Frax sFRAX — the pre-rename sibling of sfrxUSD, over legacy
44449
44841
  // FRAX. Still live and roughly 2x sfrxUSD's size, so it stays a
@@ -52814,7 +53206,7 @@ var Erc4626PreviewRedeemAbi = [
52814
53206
  ];
52815
53207
 
52816
53208
  // src/vaults/lst/readers/shared.ts
52817
- var ONE_E189 = 10n ** 18n;
53209
+ var ONE_E1810 = 10n ** 18n;
52818
53210
  var rescaleDecimals = (v, fromDec, toDec) => toDec >= fromDec ? v * 10n ** BigInt(toDec - fromDec) : v / 10n ** BigInt(fromDec - toDec);
52819
53211
  var MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
52820
53212
  var Multicall3BalanceAbi = [
@@ -52857,7 +53249,7 @@ var readerBeetsStS = (entry) => ({
52857
53249
  }
52858
53250
  const liquidity = toBigInt13(pool);
52859
53251
  return {
52860
- totalAssets: totalSupply * exchangeRate / ONE_E189,
53252
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
52861
53253
  totalSupply,
52862
53254
  exchangeRate,
52863
53255
  liquidity
@@ -52890,7 +53282,7 @@ var readerBenqiSavax = (entry) => ({
52890
53282
  {
52891
53283
  address: entry.address,
52892
53284
  name: "getPooledAvaxByShares",
52893
- params: [ONE_E189]
53285
+ params: [ONE_E1810]
52894
53286
  },
52895
53287
  { address: entry.address, name: "totalPooledAvax", params: [] }
52896
53288
  ],
@@ -52901,7 +53293,7 @@ var readerBenqiSavax = (entry) => ({
52901
53293
  if (totalSupply === void 0 || exchangeRate === void 0) {
52902
53294
  return void 0;
52903
53295
  }
52904
- const totalAssets = toBigInt13(totalPooled) ?? totalSupply * exchangeRate / ONE_E189;
53296
+ const totalAssets = toBigInt13(totalPooled) ?? totalSupply * exchangeRate / ONE_E1810;
52905
53297
  return {
52906
53298
  totalAssets,
52907
53299
  totalSupply,
@@ -52920,7 +53312,7 @@ var readerBgtWrapper1to1 = (entry) => ({
52920
53312
  return {
52921
53313
  totalAssets: totalSupply,
52922
53314
  totalSupply,
52923
- exchangeRate: ONE_E189
53315
+ exchangeRate: ONE_E1810
52924
53316
  };
52925
53317
  }
52926
53318
  });
@@ -52950,7 +53342,7 @@ var readerDineroBeraEth = (entry) => ({
52950
53342
  return void 0;
52951
53343
  }
52952
53344
  return {
52953
- totalAssets: totalSupply * exchangeRate / ONE_E189,
53345
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
52954
53346
  totalSupply,
52955
53347
  exchangeRate
52956
53348
  };
@@ -52962,7 +53354,7 @@ var readerErc4626 = (entry) => ({
52962
53354
  calls: [
52963
53355
  { address: entry.address, name: "totalAssets", params: [] },
52964
53356
  { address: entry.address, name: "totalSupply", params: [] },
52965
- { address: entry.address, name: "convertToAssets", params: [ONE_E189] }
53357
+ { address: entry.address, name: "convertToAssets", params: [ONE_E1810] }
52966
53358
  ],
52967
53359
  abis: [Erc4626ReadAbi, TotalSupplyAbi, Erc4626ReadAbi],
52968
53360
  parse: ([assets, supply, rate]) => {
@@ -52979,7 +53371,7 @@ var readerErc4626PreviewRedeem = (entry) => ({
52979
53371
  calls: [
52980
53372
  { address: entry.address, name: "totalAssets", params: [] },
52981
53373
  { address: entry.address, name: "totalSupply", params: [] },
52982
- { address: entry.address, name: "previewRedeem", params: [ONE_E189] }
53374
+ { address: entry.address, name: "previewRedeem", params: [ONE_E1810] }
52983
53375
  ],
52984
53376
  abis: [Erc4626PreviewRedeemAbi, TotalSupplyAbi, Erc4626PreviewRedeemAbi],
52985
53377
  parse: ([assets, supply, rate]) => {
@@ -53059,7 +53451,7 @@ var readerEtherFiWeEth = (entry) => {
53059
53451
  }
53060
53452
  }
53061
53453
  return {
53062
- totalAssets: totalSupply * exchangeRate / ONE_E189,
53454
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
53063
53455
  totalSupply,
53064
53456
  exchangeRate,
53065
53457
  liquidity
@@ -53092,7 +53484,7 @@ var readerHyperbeatBeHype = (entry) => {
53092
53484
  return {
53093
53485
  totalAssets: totalSupply,
53094
53486
  totalSupply,
53095
- exchangeRate: ONE_E189
53487
+ exchangeRate: ONE_E1810
53096
53488
  };
53097
53489
  }
53098
53490
  };
@@ -53100,7 +53492,7 @@ var readerHyperbeatBeHype = (entry) => {
53100
53492
  return {
53101
53493
  calls: [
53102
53494
  { address: entry.address, name: "totalSupply", params: [] },
53103
- { address: stakingCore, name: "BeHYPEToHYPE", params: [ONE_E189] }
53495
+ { address: stakingCore, name: "BeHYPEToHYPE", params: [ONE_E1810] }
53104
53496
  ],
53105
53497
  abis: [TotalSupplyAbi, HyperbeatStakingCoreAbi],
53106
53498
  parse: ([supply, rate]) => {
@@ -53110,7 +53502,7 @@ var readerHyperbeatBeHype = (entry) => {
53110
53502
  return void 0;
53111
53503
  }
53112
53504
  return {
53113
- totalAssets: totalSupply * exchangeRate / ONE_E189,
53505
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
53114
53506
  totalSupply,
53115
53507
  exchangeRate
53116
53508
  };
@@ -53142,7 +53534,7 @@ var readerKelpRsEth = (entry) => {
53142
53534
  return {
53143
53535
  totalAssets: totalSupply,
53144
53536
  totalSupply,
53145
- exchangeRate: ONE_E189
53537
+ exchangeRate: ONE_E1810
53146
53538
  };
53147
53539
  }
53148
53540
  };
@@ -53160,7 +53552,7 @@ var readerKelpRsEth = (entry) => {
53160
53552
  return void 0;
53161
53553
  }
53162
53554
  return {
53163
- totalAssets: totalSupply * exchangeRate / ONE_E189,
53555
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
53164
53556
  totalSupply,
53165
53557
  exchangeRate
53166
53558
  };
@@ -53192,7 +53584,7 @@ var readerKinetiqKHype = (entry) => {
53192
53584
  return {
53193
53585
  totalAssets: totalSupply,
53194
53586
  totalSupply,
53195
- exchangeRate: ONE_E189
53587
+ exchangeRate: ONE_E1810
53196
53588
  };
53197
53589
  }
53198
53590
  };
@@ -53200,7 +53592,7 @@ var readerKinetiqKHype = (entry) => {
53200
53592
  return {
53201
53593
  calls: [
53202
53594
  { address: entry.address, name: "totalSupply", params: [] },
53203
- { address: accountant, name: "kHYPEToHYPE", params: [ONE_E189] }
53595
+ { address: accountant, name: "kHYPEToHYPE", params: [ONE_E1810] }
53204
53596
  ],
53205
53597
  abis: [TotalSupplyAbi, KinetiqStakingAccountantAbi],
53206
53598
  parse: ([supply, rate]) => {
@@ -53210,7 +53602,7 @@ var readerKinetiqKHype = (entry) => {
53210
53602
  return void 0;
53211
53603
  }
53212
53604
  return {
53213
- totalAssets: totalSupply * exchangeRate / ONE_E189,
53605
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
53214
53606
  totalSupply,
53215
53607
  exchangeRate
53216
53608
  };
@@ -53250,7 +53642,7 @@ var readerLairStKaia = (entry) => ({
53250
53642
  if (totalSupply === void 0 || exchangeRate === void 0) {
53251
53643
  return void 0;
53252
53644
  }
53253
- const totalAssets = toBigInt13(totalStaking) ?? totalSupply * exchangeRate / ONE_E189;
53645
+ const totalAssets = toBigInt13(totalStaking) ?? totalSupply * exchangeRate / ONE_E1810;
53254
53646
  return {
53255
53647
  totalAssets,
53256
53648
  totalSupply,
@@ -53284,7 +53676,7 @@ var readerLidoWstEth = (entry) => ({
53284
53676
  return void 0;
53285
53677
  }
53286
53678
  return {
53287
- totalAssets: totalSupply * exchangeRate / ONE_E189,
53679
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
53288
53680
  totalSupply,
53289
53681
  exchangeRate
53290
53682
  };
@@ -53322,7 +53714,7 @@ var readerListaSlisBnb = (entry) => {
53322
53714
  return {
53323
53715
  totalAssets: totalSupply,
53324
53716
  totalSupply,
53325
- exchangeRate: ONE_E189
53717
+ exchangeRate: ONE_E1810
53326
53718
  };
53327
53719
  }
53328
53720
  };
@@ -53330,7 +53722,7 @@ var readerListaSlisBnb = (entry) => {
53330
53722
  return {
53331
53723
  calls: [
53332
53724
  { address: entry.address, name: "totalSupply", params: [] },
53333
- { address: manager, name: "convertSnBnbToBnb", params: [ONE_E189] },
53725
+ { address: manager, name: "convertSnBnbToBnb", params: [ONE_E1810] },
53334
53726
  { address: manager, name: "getTotalPooledBnb", params: [] }
53335
53727
  ],
53336
53728
  abis: [TotalSupplyAbi, ListaStakeManagerReadAbi, ListaStakeManagerReadAbi],
@@ -53341,7 +53733,7 @@ var readerListaSlisBnb = (entry) => {
53341
53733
  return void 0;
53342
53734
  }
53343
53735
  const pooledBnb = toBigInt13(pooled);
53344
- const totalAssets = pooledBnb ?? totalSupply * exchangeRate / ONE_E189;
53736
+ const totalAssets = pooledBnb ?? totalSupply * exchangeRate / ONE_E1810;
53345
53737
  return { totalAssets, totalSupply, exchangeRate };
53346
53738
  }
53347
53739
  };
@@ -53371,7 +53763,7 @@ var readerMantleMEth = (entry) => {
53371
53763
  return {
53372
53764
  totalAssets: totalSupply,
53373
53765
  totalSupply,
53374
- exchangeRate: ONE_E189
53766
+ exchangeRate: ONE_E1810
53375
53767
  };
53376
53768
  }
53377
53769
  };
@@ -53379,7 +53771,7 @@ var readerMantleMEth = (entry) => {
53379
53771
  return {
53380
53772
  calls: [
53381
53773
  { address: entry.address, name: "totalSupply", params: [] },
53382
- { address: staking, name: "mETHToETH", params: [ONE_E189] }
53774
+ { address: staking, name: "mETHToETH", params: [ONE_E1810] }
53383
53775
  ],
53384
53776
  abis: [TotalSupplyAbi, MantleStakingAbi],
53385
53777
  parse: ([supply, rate]) => {
@@ -53389,7 +53781,7 @@ var readerMantleMEth = (entry) => {
53389
53781
  return void 0;
53390
53782
  }
53391
53783
  return {
53392
- totalAssets: totalSupply * exchangeRate / ONE_E189,
53784
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
53393
53785
  totalSupply,
53394
53786
  exchangeRate
53395
53787
  };
@@ -53410,7 +53802,7 @@ var readerOffChain = (entry) => {
53410
53802
  return {
53411
53803
  totalAssets: rescaleDecimals(totalSupply, shareDec, underlyingDec),
53412
53804
  totalSupply,
53413
- exchangeRate: ONE_E189
53805
+ exchangeRate: ONE_E1810
53414
53806
  };
53415
53807
  }
53416
53808
  };
@@ -53444,7 +53836,7 @@ var readerRenzoEzEth = (entry) => {
53444
53836
  return {
53445
53837
  totalAssets: totalSupply,
53446
53838
  totalSupply,
53447
- exchangeRate: ONE_E189
53839
+ exchangeRate: ONE_E1810
53448
53840
  };
53449
53841
  }
53450
53842
  };
@@ -53463,7 +53855,7 @@ var readerRenzoEzEth = (entry) => {
53463
53855
  return {
53464
53856
  totalAssets: totalTvl,
53465
53857
  totalSupply,
53466
- exchangeRate: totalTvl * ONE_E189 / totalSupply
53858
+ exchangeRate: totalTvl * ONE_E1810 / totalSupply
53467
53859
  };
53468
53860
  }
53469
53861
  };
@@ -53519,7 +53911,7 @@ var readerRocketReth = (entry) => {
53519
53911
  }
53520
53912
  const liquidity = depositPool ? toBigInt13(slice2[2]) : void 0;
53521
53913
  return {
53522
- totalAssets: totalSupply * exchangeRate / ONE_E189,
53914
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
53523
53915
  totalSupply,
53524
53916
  exchangeRate,
53525
53917
  liquidity
@@ -53561,7 +53953,7 @@ var readerStaderEthx = (entry) => {
53561
53953
  return {
53562
53954
  totalAssets: totalSupply,
53563
53955
  totalSupply,
53564
- exchangeRate: ONE_E189
53956
+ exchangeRate: ONE_E1810
53565
53957
  };
53566
53958
  }
53567
53959
  };
@@ -53579,7 +53971,7 @@ var readerStaderEthx = (entry) => {
53579
53971
  return void 0;
53580
53972
  }
53581
53973
  return {
53582
- totalAssets: totalSupply * exchangeRate / ONE_E189,
53974
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
53583
53975
  totalSupply,
53584
53976
  exchangeRate
53585
53977
  };
@@ -53596,7 +53988,7 @@ var readerStaderMaticX = (entry) => {
53596
53988
  {
53597
53989
  address: rateAddress,
53598
53990
  name: "convertMaticXToMatic",
53599
- params: [ONE_E189],
53991
+ params: [ONE_E1810],
53600
53992
  chainId: homeChainId
53601
53993
  }
53602
53994
  ],
@@ -53611,7 +54003,7 @@ var readerStaderMaticX = (entry) => {
53611
54003
  }
53612
54004
  const isCrossChain = homeContract !== void 0;
53613
54005
  return {
53614
- totalAssets: isCrossChain ? totalSupply * amountInMatic / ONE_E189 : totalPooledMatic ?? totalSupply * amountInMatic / ONE_E189,
54006
+ totalAssets: isCrossChain ? totalSupply * amountInMatic / ONE_E1810 : totalPooledMatic ?? totalSupply * amountInMatic / ONE_E1810,
53615
54007
  totalSupply,
53616
54008
  exchangeRate: amountInMatic
53617
54009
  };
@@ -53643,7 +54035,7 @@ var readerStakeWiseOsEth = (entry) => {
53643
54035
  return {
53644
54036
  totalAssets: totalSupply,
53645
54037
  totalSupply,
53646
- exchangeRate: ONE_E189
54038
+ exchangeRate: ONE_E1810
53647
54039
  };
53648
54040
  }
53649
54041
  };
@@ -53651,7 +54043,7 @@ var readerStakeWiseOsEth = (entry) => {
53651
54043
  return {
53652
54044
  calls: [
53653
54045
  { address: entry.address, name: "totalSupply", params: [] },
53654
- { address: controller, name: "convertToAssets", params: [ONE_E189] }
54046
+ { address: controller, name: "convertToAssets", params: [ONE_E1810] }
53655
54047
  ],
53656
54048
  abis: [TotalSupplyAbi, StakeWiseOsTokenAbi],
53657
54049
  parse: ([supply, rate]) => {
@@ -53661,7 +54053,7 @@ var readerStakeWiseOsEth = (entry) => {
53661
54053
  return void 0;
53662
54054
  }
53663
54055
  return {
53664
- totalAssets: totalSupply * exchangeRate / ONE_E189,
54056
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
53665
54057
  totalSupply,
53666
54058
  exchangeRate
53667
54059
  };
@@ -53693,7 +54085,7 @@ var readerStCelo = (entry) => {
53693
54085
  return {
53694
54086
  totalAssets: totalSupply,
53695
54087
  totalSupply,
53696
- exchangeRate: ONE_E189
54088
+ exchangeRate: ONE_E1810
53697
54089
  };
53698
54090
  }
53699
54091
  };
@@ -53701,7 +54093,7 @@ var readerStCelo = (entry) => {
53701
54093
  return {
53702
54094
  calls: [
53703
54095
  { address: entry.address, name: "totalSupply", params: [] },
53704
- { address: manager, name: "toCelo", params: [ONE_E189] }
54096
+ { address: manager, name: "toCelo", params: [ONE_E1810] }
53705
54097
  ],
53706
54098
  abis: [TotalSupplyAbi, StCeloManagerAbi],
53707
54099
  parse: ([supply, rate]) => {
@@ -53711,7 +54103,7 @@ var readerStCelo = (entry) => {
53711
54103
  return void 0;
53712
54104
  }
53713
54105
  return {
53714
- totalAssets: totalSupply * exchangeRate / ONE_E189,
54106
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
53715
54107
  totalSupply,
53716
54108
  exchangeRate
53717
54109
  };
@@ -53744,7 +54136,7 @@ var readerSwellGetRate = (entry) => ({
53744
54136
  return void 0;
53745
54137
  }
53746
54138
  return {
53747
- totalAssets: totalSupply * exchangeRate / ONE_E189,
54139
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
53748
54140
  totalSupply,
53749
54141
  exchangeRate
53750
54142
  };
@@ -53775,7 +54167,7 @@ var readerValantisWstHype = (entry) => {
53775
54167
  return {
53776
54168
  totalAssets: totalSupply,
53777
54169
  totalSupply,
53778
- exchangeRate: ONE_E189
54170
+ exchangeRate: ONE_E1810
53779
54171
  };
53780
54172
  }
53781
54173
  };
@@ -53793,7 +54185,7 @@ var readerValantisWstHype = (entry) => {
53793
54185
  return void 0;
53794
54186
  }
53795
54187
  return {
53796
- totalAssets: totalSupply * exchangeRate / ONE_E189,
54188
+ totalAssets: totalSupply * exchangeRate / ONE_E1810,
53797
54189
  totalSupply,
53798
54190
  exchangeRate
53799
54191
  };
@@ -53827,7 +54219,7 @@ var readerVedaAccountant = (entry) => {
53827
54219
  return {
53828
54220
  totalAssets: rescaleDecimals(totalSupply, shareDec, underlyingDec),
53829
54221
  totalSupply,
53830
- exchangeRate: ONE_E189
54222
+ exchangeRate: ONE_E1810
53831
54223
  };
53832
54224
  }
53833
54225
  };
@@ -53846,7 +54238,7 @@ var readerVedaAccountant = (entry) => {
53846
54238
  const exchangeRate = rawRate * scale;
53847
54239
  return {
53848
54240
  totalAssets: rescaleDecimals(
53849
- totalSupply * exchangeRate / ONE_E189,
54241
+ totalSupply * exchangeRate / ONE_E1810,
53850
54242
  shareDec,
53851
54243
  underlyingDec
53852
54244
  ),
@@ -53882,9 +54274,9 @@ var readerAnkrRatio = (entry) => ({
53882
54274
  return void 0;
53883
54275
  }
53884
54276
  return {
53885
- totalAssets: totalSupply * ONE_E189 / r,
54277
+ totalAssets: totalSupply * ONE_E1810 / r,
53886
54278
  totalSupply,
53887
- exchangeRate: ONE_E189 * ONE_E189 / r
54279
+ exchangeRate: ONE_E1810 * ONE_E1810 / r
53888
54280
  };
53889
54281
  }
53890
54282
  });
@@ -53928,7 +54320,7 @@ var readerCoreEarnRate = (entry) => {
53928
54320
  return {
53929
54321
  totalAssets: totalSupply * r / CORE_RATE_DENOM,
53930
54322
  totalSupply,
53931
- exchangeRate: r * ONE_E189 / CORE_RATE_DENOM
54323
+ exchangeRate: r * ONE_E1810 / CORE_RATE_DENOM
53932
54324
  };
53933
54325
  }
53934
54326
  };
@@ -53950,7 +54342,7 @@ var readerCoreStakedRatio = (entry) => {
53950
54342
  return {
53951
54343
  totalAssets: totalStaked,
53952
54344
  totalSupply,
53953
- exchangeRate: totalStaked * ONE_E189 / totalSupply
54345
+ exchangeRate: totalStaked * ONE_E1810 / totalSupply
53954
54346
  };
53955
54347
  }
53956
54348
  };
@@ -53981,7 +54373,7 @@ var readerStellaStDot = (entry) => ({
53981
54373
  {
53982
54374
  address: entry.address,
53983
54375
  name: "getPooledTokenByShares",
53984
- params: [ONE_E189]
54376
+ params: [ONE_E1810]
53985
54377
  },
53986
54378
  { address: entry.address, name: "getTotalPooledToken", params: [] }
53987
54379
  ],
@@ -53992,7 +54384,7 @@ var readerStellaStDot = (entry) => ({
53992
54384
  if (totalSupply === void 0 || exchangeRate === void 0) {
53993
54385
  return void 0;
53994
54386
  }
53995
- const totalAssets = toBigInt13(pooled) ?? totalSupply * exchangeRate / ONE_E189;
54387
+ const totalAssets = toBigInt13(pooled) ?? totalSupply * exchangeRate / ONE_E1810;
53996
54388
  return {
53997
54389
  totalAssets,
53998
54390
  totalSupply,
@@ -54030,7 +54422,7 @@ var readerKintsuSMon = (entry) => ({
54030
54422
  const totalAssets = toBigInt13(pooled);
54031
54423
  const totalSupply = toBigInt13(shares);
54032
54424
  if (totalAssets === void 0 || totalSupply === void 0) return void 0;
54033
- const exchangeRate = totalSupply > 0n ? totalAssets * ONE_E189 / totalSupply : ONE_E189;
54425
+ const exchangeRate = totalSupply > 0n ? totalAssets * ONE_E1810 / totalSupply : ONE_E1810;
54034
54426
  return { totalAssets, totalSupply, exchangeRate };
54035
54427
  }
54036
54428
  });
@@ -54466,7 +54858,7 @@ var getLstValidators = async (chainId, shareToken) => {
54466
54858
  };
54467
54859
 
54468
54860
  // src/vaults/lst/fetchPublic.ts
54469
- var ONE_E1810 = 10n ** 18n;
54861
+ var ONE_E1811 = 10n ** 18n;
54470
54862
  var ERC20_BALANCE_ABI = parseAbi([
54471
54863
  "function balanceOf(address) view returns (uint256)"
54472
54864
  ]);
@@ -54581,8 +54973,8 @@ var fetchLstShareTokens = async (chainId, multicallRetry, prices = {}, tokenList
54581
54973
  const underlyingUnit = 10n ** BigInt(underlyingDec);
54582
54974
  const totalAssetsFormatted = Number(state.totalAssets) / 10 ** underlyingDec;
54583
54975
  const totalAssetsUsd = priceUsd !== void 0 ? totalAssetsFormatted * priceUsd : 0;
54584
- const convertToAssets = state.exchangeRate * underlyingUnit / ONE_E1810;
54585
- const convertToShares = state.exchangeRate > 0n ? ONE_E1810 * shareUnit / state.exchangeRate : 0n;
54976
+ const convertToAssets = state.exchangeRate * underlyingUnit / ONE_E1811;
54977
+ const convertToShares = state.exchangeRate > 0n ? ONE_E1811 * shareUnit / state.exchangeRate : 0n;
54586
54978
  let liquidityRaw;
54587
54979
  if (state.liquidity !== void 0) {
54588
54980
  liquidityRaw = state.liquidity;
@@ -57987,7 +58379,7 @@ var NavOracleReadAbi = [
57987
58379
  ];
57988
58380
 
57989
58381
  // src/vaults/savings/readers/shared.ts
57990
- var ONE_E1811 = 10n ** 18n;
58382
+ var ONE_E1812 = 10n ** 18n;
57991
58383
  var toBigInt15 = (v) => {
57992
58384
  if (v === void 0 || v === null) return void 0;
57993
58385
  if (typeof v === "bigint") return v;
@@ -58028,7 +58420,7 @@ var readerErc46262 = (entry) => {
58028
58420
  return {
58029
58421
  totalAssets,
58030
58422
  totalSupply,
58031
- exchangeRate: convertToAssetsRaw * ONE_E1811 / underlyingUnit
58423
+ exchangeRate: convertToAssetsRaw * ONE_E1812 / underlyingUnit
58032
58424
  };
58033
58425
  }
58034
58426
  };
@@ -58150,7 +58542,7 @@ var readerErc4626Idle = (entry) => {
58150
58542
  return {
58151
58543
  totalAssets,
58152
58544
  totalSupply,
58153
- exchangeRate: convertToAssetsRaw * ONE_E1811 / underlyingUnit,
58545
+ exchangeRate: convertToAssetsRaw * ONE_E1812 / underlyingUnit,
58154
58546
  ...capacity !== void 0 ? {
58155
58547
  instantRedeemCapacity: capacity,
58156
58548
  instantRedeemEnabled: true,
@@ -58199,7 +58591,7 @@ var readerErc4626WithdrawLimit = (entry) => {
58199
58591
  return {
58200
58592
  totalAssets,
58201
58593
  totalSupply,
58202
- exchangeRate: convertToAssetsRaw * ONE_E1811 / underlyingUnit,
58594
+ exchangeRate: convertToAssetsRaw * ONE_E1812 / underlyingUnit,
58203
58595
  ...capacity !== void 0 ? {
58204
58596
  instantRedeemCapacity: capacity,
58205
58597
  instantRedeemEnabled: true,
@@ -58228,7 +58620,7 @@ var readerFrankencoinSavings = (entry) => ({
58228
58620
  // `fetchPublic` derives `convertToAssets` / `convertToShares` from
58229
58621
  // `exchangeRate`, and 1e18 makes them the identity.
58230
58622
  totalSupply: deposits,
58231
- exchangeRate: ONE_E1811
58623
+ exchangeRate: ONE_E1812
58232
58624
  };
58233
58625
  }
58234
58626
  });
@@ -58254,7 +58646,7 @@ var readerNavOracle = (entry) => {
58254
58646
  const exchangeRate = toBigInt15(raw);
58255
58647
  if (exchangeRate === void 0 || exchangeRate <= 0n) return void 0;
58256
58648
  return {
58257
- totalAssets: totalSupply * exchangeRate * underlyingUnit / (ONE_E1811 * shareUnit),
58649
+ totalAssets: totalSupply * exchangeRate * underlyingUnit / (ONE_E1812 * shareUnit),
58258
58650
  totalSupply,
58259
58651
  exchangeRate
58260
58652
  };
@@ -58268,7 +58660,7 @@ var readerNativeWnlp = (entry) => {
58268
58660
  return {
58269
58661
  calls: [
58270
58662
  { address, name: "totalSupply", params: [] },
58271
- { address, name: "getNlpByWnlp", params: [ONE_E1811] },
58663
+ { address, name: "getNlpByWnlp", params: [ONE_E1812] },
58272
58664
  { address, name: "instantRedeemFeeBips", params: [] },
58273
58665
  { address, name: "instantRedeemEnabled", params: [] },
58274
58666
  // Falls back to the vault itself when no CreditVault is pinned —
@@ -58302,7 +58694,7 @@ var readerNativeWnlp = (entry) => {
58302
58694
  const windowSeconds = toBigInt15(window);
58303
58695
  const bips = toBigInt15(feeBips);
58304
58696
  return {
58305
- totalAssets: totalSupply * exchangeRate / ONE_E1811,
58697
+ totalAssets: totalSupply * exchangeRate / ONE_E1812,
58306
58698
  totalSupply,
58307
58699
  exchangeRate,
58308
58700
  // `instantRedeemFeeBips` on-chain is already basis points, so it
@@ -58342,7 +58734,7 @@ var buildReader2 = (entry) => {
58342
58734
  var resolveYieldApr2 = async (entries) => (await resolveEntryApr(entries)).apr;
58343
58735
 
58344
58736
  // src/vaults/savings/fetchPublic.ts
58345
- var ONE_E1812 = 10n ** 18n;
58737
+ var ONE_E1813 = 10n ** 18n;
58346
58738
  var fetchSavingsVaults = async (chainId, multicallRetry, prices = {}, tokenList = {}) => {
58347
58739
  const entries = getSavingsRegistry(chainId);
58348
58740
  if (entries.length === 0) return {};
@@ -58391,8 +58783,8 @@ var fetchSavingsVaults = async (chainId, multicallRetry, prices = {}, tokenList
58391
58783
  1,
58392
58784
  Number(liquidityAmount * 1000000n / state.totalAssets) / 1e6
58393
58785
  ) : 1;
58394
- const convertToAssets = state.exchangeRate * underlyingUnit / ONE_E1812;
58395
- const convertToShares = state.exchangeRate > 0n ? ONE_E1812 * shareUnit / state.exchangeRate : 0n;
58786
+ const convertToAssets = state.exchangeRate * underlyingUnit / ONE_E1813;
58787
+ const convertToShares = state.exchangeRate > 0n ? ONE_E1813 * shareUnit / state.exchangeRate : 0n;
58396
58788
  const displayName = composeVaultDisplayName(
58397
58789
  entry.brand,
58398
58790
  entry.brand,
@@ -59354,8 +59746,8 @@ var postInfo2 = async (url, body) => {
59354
59746
  };
59355
59747
  var latestAccountValue = (portfolio) => {
59356
59748
  if (!Array.isArray(portfolio) || portfolio.length === 0) return void 0;
59357
- const pick2 = portfolio.find((p) => p[0] === "allTime") ?? portfolio.find((p) => p[0] === "day") ?? portfolio[0];
59358
- const hist = pick2?.[1]?.accountValueHistory;
59749
+ const pick3 = portfolio.find((p) => p[0] === "allTime") ?? portfolio.find((p) => p[0] === "day") ?? portfolio[0];
59750
+ const hist = pick3?.[1]?.accountValueHistory;
59359
59751
  const last = hist?.[hist.length - 1];
59360
59752
  return last?.[1];
59361
59753
  };
@@ -60725,7 +61117,7 @@ var readVaultSharePrices = async (chainId, addresses, multicallRetry) => {
60725
61117
  };
60726
61118
 
60727
61119
  // src/vaults/yield/annualize.ts
60728
- var YEAR_SECONDS10 = 365 * 24 * 60 * 60;
61120
+ var YEAR_SECONDS11 = 365 * 24 * 60 * 60;
60729
61121
  var SCALE = 10n ** 18n;
60730
61122
  var appendSnapshot = (points, snap, options) => {
60731
61123
  const maxPoints = options?.maxPoints ?? 90;
@@ -60754,7 +61146,7 @@ var computeVaultApr = (points, options) => {
60754
61146
  if (pThen === 0n) return void 0;
60755
61147
  const ratioScaled = BigInt(now.p) * SCALE / pThen;
60756
61148
  const ratio = Number(ratioScaled) / 1e18;
60757
- const apr = (ratio - 1) * (YEAR_SECONDS10 / windowSeconds);
61149
+ const apr = (ratio - 1) * (YEAR_SECONDS11 / windowSeconds);
60758
61150
  return {
60759
61151
  apr,
60760
61152
  sharePriceNow: now.p,
@@ -61228,7 +61620,7 @@ function parseRawRpcBatchResponses(batches, batchResponses, allowFailure = true)
61228
61620
  });
61229
61621
  return allResults;
61230
61622
  }
61231
- function parseMulticallRpcResponses(responses, batches, allowFailure = true) {
61623
+ function parseMulticallRpcResponses(responses, batches, allowFailure = true, permanentFailures) {
61232
61624
  const allResults = [];
61233
61625
  const sortedResponses = [...responses].sort((a, b) => a.id - b.id);
61234
61626
  sortedResponses.forEach((response, batchIdx) => {
@@ -61261,6 +61653,7 @@ function parseMulticallRpcResponses(responses, batches, allowFailure = true) {
61261
61653
  if (!allowFailure) {
61262
61654
  throw new Error(`Call failed for ${metadata.functionName}`);
61263
61655
  }
61656
+ permanentFailures?.add(allResults.length);
61264
61657
  allResults.push("0x");
61265
61658
  return;
61266
61659
  }
@@ -61277,6 +61670,7 @@ function parseMulticallRpcResponses(responses, batches, allowFailure = true) {
61277
61670
  `Failed to decode result for ${metadata.functionName}: ${error}`
61278
61671
  );
61279
61672
  }
61673
+ permanentFailures?.add(allResults.length);
61280
61674
  allResults.push("0x");
61281
61675
  }
61282
61676
  });
@@ -61557,6 +61951,2493 @@ async function fetchTokenBalances(chainId, account, tokens, options = {}) {
61557
61951
  return parseTokenBalanceResult(rawResult, prepared.query);
61558
61952
  }
61559
61953
 
61560
- export { ApiBookSource, DEFAULT_MIDNIGHT_API, DEFAULT_OUTLIER_GUARD, DEFAULT_STALE_REJECT_SECONDS, DEFAULT_TERMMAX_API, EMPTY_BALANCE, EXACTLY_LENDER_KEY, GMX_API_HOSTS, GMX_READ_CONTRACTS, GMX_SUPPORTED_CHAINS, HYPERCORE_VAULT_REGISTRY, IDLE_MARKET_ID, INTERFACE_IDS, LAGOON_API_URL, LAGOON_CHAIN_IDS, MORPHO_LENS, MaxParamThresholds, STABLECOIN_SYMBOLS, STCELO_MANAGER_ADDRESS, TELLER_CALLS_PER_BID, TERMMAX_CALLS_PER_ACCOUNT, DECIMAL_BASE as TERMMAX_DECIMAL_BASE, TERMMAX_LIQUIDATION_PENALTY, TERMMAX_LIQUIDATION_WINDOW_SECS, TERMMAX_LIQUIDATOR_BONUS, TERMMAX_PARTIAL_CLOSE_FACTOR, TERMMAX_PARTIAL_LIQUIDATION_THRESHOLD_USD, TermMaxApiSource, TermSubgraphSource, UPSHIFT_CHAIN_IDS, UPSHIFT_VAULTS_URL, VAULT_SHARE_PRICE_PROBE, VOLATILE_VAULT_OVERRIDES, YEARN_CHAIN_IDS, YEARN_YDAEMON_BASE, __resetResupplyUserCaches, accountDepositListKey, accountWithdrawalListKey, appendSnapshot, applyPositionDelta, attachPricesToFlashLiquidity, buildFluidFTokensCall, buildLiquityUserCall, buildLoopResult, buildMorphoTypeCall, buildMorphoTypeUserCallWithLens, buildPortfolioTotals, buildSumerAccumulators, buildSummaries, buildTellerUserCall, buildTermMaxUserCall, buildVaultLookup, calculateLeverage, calculateNetApr, calculateOverallNetApr, calculateWeightedAverage, classifyFreshness, classifyReliability, classifyVault, collectFeedObservations, computeBorrowDelta2 as computeBorrowDelta, computeCloseTradeDeltas, computeCollateralSwapDeltas, computeDebtSwapDeltas, computeDepositDelta2 as computeDepositDelta, computeEModeAnalysis, computeOpenTradeDeltas, computePostTradeMetrics, computeRepayDelta2 as computeRepayDelta, computeSumerBorrowDelta, computeSumerDepositDelta, computeSumerRepayDelta, computeSumerWaterfall, computeSumerWithdrawDelta, computeVaultApr, computeWithdrawDelta2 as computeWithdrawDelta, computeZapTradeDeltas, consensusReference, convertExactlyMarketsToResponse, convertFrankencoinMarketsToResponse, convertInverseMarketsToResponse, convertLenderUserDataResult, convertLiquityMarketsToResponse, convertLlamaLendMarketsToResponse, convertResupplyMarketsToResponse, convertRiverMarketsToResponse, convertTellerMarketsToResponse, convertTermMarketsToResponse, convertTermMaxMarketsToResponse, convertUsddMarketsToResponse, createMarketUid, createMidnightBookSource, createMulticallRpcCall, createRawRpcCalls, createTermBookSource, createTermMaxDataSource, decodeListaMarkets, decodeMarkets, decodePackedListaUserDataset, decodePackedMorphoUserDataset, detectInterfaceKinds, encodeBalanceFetcherCalldata, exactlyPairLtv, exactlyPenaltyRateToAprPercent, exactlyWadRateToPercent, feedKeyOf, feedStatKey, fetchDolomiteAccountNumbers, fetchEulerEarnVaults, fetchEulerEarnVaultsFromSubgraph, fetchEulerSubAccountIndexes, fetchExactlyMarkets, fetchFlashLiquidityForChain, fetchFluidFTokens, fetchFrankencoinMarkets, fetchGeneralYields, fetchGeneralYieldsByMarketUid, fetchGmxExecutionFees, fetchGmxTickerPrices, fetchGmxVaults, fetchHypercoreVaults, fetchInverseMarkets, fetchLagoonApiVaults, fetchLagoonVaults, fetchLiquityMarkets, fetchListaVaultsFromChain, fetchLlamaLendMarkets, fetchMorphoUserBalances, fetchMorphoUserPositionMarkets, fetchMorphoVaults, fetchMorphoVaultsFromApi, fetchMorphoVaultsFromChain, fetchOraclePrices, fetchPendlePrices, fetchResupplyMarkets, fetchRiverMarkets, fetchSiloVaults, fetchTellerMarkets, fetchTermMarkets, fetchTermMaxMarkets, fetchTokenBalances, fetchTokenMetadata, fetchUpshiftApiVaults, fetchUpshiftVaults, fetchUsddMarkets, fetchYearnApiVaults, fetchYearnVaults, filterActiveLenders, filterLendersByProtocol, frankencoinKeyParts, frankencoinLenderKey, fuseLenderData, generateLendingPools, getAavesForChain, getAssetConfig, getBalanceForMarketUid, getBorrowCapacity, getCachedLiquityTroves, getCachedTellerBids, getCachedTermMaxDiscovery, getCachedTermMaxMarket, getCachedTermMaxMarkets, getCoreValidators, getFluidFTokensConverter, getGmxApiHost, getGmxReadContracts, getGmxUserPositions, getHealthFactor, getHypercoreUserPositions, getHypercoreVaultRegistry, getLenderAssets, getLenderPublicData, getLenderPublicDataAll, getLenderPublicDataViaApi, getLenderUserDataMulti, getLenderUserDataResult, getLendersForChain, getLstDelegation, getLstValidators, getLstWithdrawalRegistry, getLstWithdrawalRequests, getMaxAmountClose, getMaxAmountCollateralSwap, getMaxAmountDebtSwap, getMaxAmountOpen, getMergedUserData, getMorphoTypeMarketConverter, getResolvedDolomiteAccountNumbers, getStCeloValidatorGroups, getSubAccountAddress, getSubAccountIndex, getVaultPublicDataAll, getVaultWithdrawalRequests, hasEulerEarnVaultSubgraph, hasLagoonVaults, hasMorphoPositionIndex, hasMorphoUserApi, hasMorphoUserSubgraph, hasUpshiftVaults, hasYearnVaults, inverseKeyParts, inverseLenderKey, isStablecoinSymbol, isYearnV3, keysFromMaps, lenderApiOnly, lenderFamily, liquityCandidateTroveIds, liquityKeyParts, liquityLenderKey, llamaLendKeyParts, llamaLendLenderKey, multicall3Abi2 as multicall3Abi, nanTo, needsLenderApproval, needsTokenApproval, noOpResult, normalizeToBytes, parseBalanceFetcherResult, parseMergedResult, parseMulticallRpcResponses, parseRawRpcBatchResponses, parseRawRpcResponses, parseTermMaxLtv, parseTokenBalanceResult, positivePart2 as positivePart, predictInverseEscrow, prepareLenderUserDataRpcCalls, prepareMergedMulticallParams, prepareMergedRpcCalls, prepareMulticallInputs, prepareTokenBalanceRpcCalls, priceGlvVaults, priceGmMarkets, probeAaveFeedTimestamps, pruneFeedStats, readVaultSharePrices, rejectOutliers as rejectPriceOutliers, resolveDerivation, resolveStCeloDepositGroup, resupplyKeyParts, resupplyLenderKey, riverKeyParts, riverLenderKey, selectAssetGroupPrices, stampVaultClassification, tellerBpsToPercent, tellerImpliedLtv, tellerLenderKey, tellerPoolFromLenderKey, termLenderKey, termMaxApiBase, borrowNif as termMaxBorrowNif, curveApr as termMaxCurveApr, curveAprNumber as termMaxCurveAprNumber, daysToMaturity as termMaxDaysToMaturity, lendNif as termMaxLendNif, mintGtFeeRatio as termMaxMintGtFeeRatio, ratioToNumber as termMaxRatioToNumber, ratioToPercent as termMaxRatioToPercent, tickToAprNumber, tickToPrice, unflattenLenderData, updateFeedStats, usddIlkBytes32, usddKeyParts, usddLenderKey };
61954
+ // src/terms/types.ts
61955
+ var TERM_SHEET_SCHEMA_VERSION = 1;
61956
+
61957
+ // src/terms/input.ts
61958
+ var num10 = (v) => {
61959
+ if (v == null) return void 0;
61960
+ const n = typeof v === "string" ? Number(v) : v;
61961
+ return Number.isFinite(n) ? n : void 0;
61962
+ };
61963
+ function pick2(src, ...keys) {
61964
+ for (const k of keys) {
61965
+ if (k.includes(".")) {
61966
+ let cur = src;
61967
+ for (const seg of k.split(".")) {
61968
+ if (cur == null) break;
61969
+ cur = cur[seg];
61970
+ }
61971
+ if (cur != null) return cur;
61972
+ } else if (src[k] != null) {
61973
+ return src[k];
61974
+ }
61975
+ }
61976
+ return void 0;
61977
+ }
61978
+ function toTermSheetInput(row, ctx = {}) {
61979
+ const marketUid = ctx.marketUid ?? pick2(row, "marketUid", "poolId") ?? "";
61980
+ const lender = ctx.lender ?? pick2(row, "lender", "lenderKey", "lenderInfo.key") ?? marketUid.split(":")[0] ?? "";
61981
+ const chainId = ctx.chainId ?? pick2(row, "chainId") ?? marketUid.split(":")[1] ?? "";
61982
+ const rawConfig = pick2(row, "config") ?? void 0;
61983
+ const config = rawConfig ? Object.fromEntries(
61984
+ Object.entries(rawConfig).map(([k, v]) => [
61985
+ k,
61986
+ {
61987
+ category: v?.category ?? k,
61988
+ label: v?.label,
61989
+ borrowCollateralFactor: num10(v?.borrowCollateralFactor),
61990
+ collateralFactor: num10(v?.collateralFactor),
61991
+ borrowFactor: num10(v?.borrowFactor),
61992
+ liquidationPenalty: num10(v?.liquidationPenalty),
61993
+ closeFactor: num10(v?.closeFactor),
61994
+ targetHealthFactor: num10(v?.targetHealthFactor),
61995
+ collateralDisabled: v?.collateralDisabled === true,
61996
+ debtDisabled: v?.debtDisabled === true
61997
+ }
61998
+ ])
61999
+ ) : void 0;
62000
+ const rewards = pick2(row, "rewards")?.map((r) => ({
62001
+ asset: r?.asset,
62002
+ depositRate: num10(r?.depositRate),
62003
+ variableBorrowRate: num10(r?.variableBorrowRate),
62004
+ stableBorrowRate: num10(r?.stableBorrowRate),
62005
+ kind: r?.kind,
62006
+ endsAt: num10(r?.endsAt),
62007
+ claim: r?.claim
62008
+ }));
62009
+ return {
62010
+ marketUid,
62011
+ lender,
62012
+ chainId,
62013
+ asset: pick2(
62014
+ row,
62015
+ "asset",
62016
+ "underlyingInfo.asset"
62017
+ ),
62018
+ underlying: pick2(row, "underlying"),
62019
+ decimals: num10(pick2(row, "decimals")),
62020
+ depositRate: num10(pick2(row, "depositRate")),
62021
+ variableBorrowRate: num10(pick2(row, "variableBorrowRate")),
62022
+ stableBorrowRate: num10(pick2(row, "stableBorrowRate")),
62023
+ intrinsicYield: num10(pick2(row, "intrinsicYield")),
62024
+ rewards,
62025
+ rateModel: pick2(row, "rateModel"),
62026
+ originationFee: num10(pick2(row, "originationFee")),
62027
+ totalDeposits: num10(pick2(row, "totalDeposits")),
62028
+ totalDebt: num10(pick2(row, "totalDebt")),
62029
+ totalDebtStable: num10(pick2(row, "totalDebtStable")),
62030
+ totalLiquidity: num10(pick2(row, "totalLiquidity")),
62031
+ borrowLiquidity: num10(pick2(row, "borrowLiquidity")),
62032
+ totalDepositsUsd: num10(pick2(row, "totalDepositsUSD", "totalDepositsUsd")),
62033
+ totalDebtUsd: num10(pick2(row, "totalDebtUSD", "totalDebtUsd")),
62034
+ totalLiquidityUsd: num10(pick2(row, "totalLiquidityUSD", "totalLiquidityUsd")),
62035
+ utilization: num10(pick2(row, "utilization")),
62036
+ irmTotalDeposits: num10(pick2(row, "irmTotalDeposits")),
62037
+ irmTotalDebt: num10(pick2(row, "irmTotalDebt")),
62038
+ lockupRatio: num10(pick2(row, "lockupRatio")),
62039
+ supplyCap: num10(pick2(row, "supplyCap", "caps.supplyCap")),
62040
+ borrowCap: num10(pick2(row, "borrowCap", "caps.borrowCap")),
62041
+ debtCeiling: pick2(row, "debtCeiling", "caps.debtCeiling"),
62042
+ isActive: pick2(row, "isActive", "flags.isActive"),
62043
+ isFrozen: pick2(row, "isFrozen", "flags.isFrozen"),
62044
+ borrowingEnabled: pick2(
62045
+ row,
62046
+ "borrowingEnabled",
62047
+ "flags.borrowingEnabled"
62048
+ ),
62049
+ depositsEnabled: pick2(
62050
+ row,
62051
+ "depositsEnabled",
62052
+ "flags.depositsEnabled"
62053
+ ),
62054
+ collateralActive: pick2(
62055
+ row,
62056
+ "collateralActive",
62057
+ "flags.collateralActive"
62058
+ ),
62059
+ hasStable: pick2(row, "hasStable", "flags.hasStable"),
62060
+ variableBorrowDisabled: pick2(
62061
+ row,
62062
+ "variableBorrowDisabled",
62063
+ "flags.variableBorrowDisabled"
62064
+ ),
62065
+ config,
62066
+ closeFactor: num10(pick2(row, "closeFactor")),
62067
+ targetHealthFactor: num10(pick2(row, "targetHealthFactor")),
62068
+ fixedTerm: pick2(row, "fixedTerm") ?? ctx.fixedTerm,
62069
+ terms: pick2(row, "terms"),
62070
+ market: resolveMarketDescriptors(row, ctx.market)
62071
+ };
62072
+ }
62073
+ var ROW_DESCRIPTOR_KEYS = [
62074
+ "liquity",
62075
+ "river",
62076
+ "teller",
62077
+ "inverse",
62078
+ "exactly",
62079
+ "usdd",
62080
+ "termmax",
62081
+ "broker",
62082
+ "collateralProvider",
62083
+ "loanProvider"
62084
+ ];
62085
+ function resolveMarketDescriptors(row, ctxMarket) {
62086
+ const fromParams = pick2(row, "params.market");
62087
+ const rowLevel = {};
62088
+ for (const k of ROW_DESCRIPTOR_KEYS) {
62089
+ if (row[k] != null) rowLevel[k] = row[k];
62090
+ }
62091
+ const merged = { ...ctxMarket ?? {}, ...fromParams ?? {}, ...rowLevel };
62092
+ return Object.keys(merged).length > 0 ? merged : void 0;
62093
+ }
62094
+
62095
+ // src/terms/tags.ts
62096
+ var push = (set, ...tags) => {
62097
+ for (const t of tags) set.add(t);
62098
+ };
62099
+ function marketTags(sheet) {
62100
+ const out = /* @__PURE__ */ new Set();
62101
+ const { governance: g, oracle: o } = sheet;
62102
+ if (g) {
62103
+ if (g.mutability === "immutable") push(out, "immutable");
62104
+ if (g.mutability === "governed") {
62105
+ const isTimelockRoot = g.controllerKind === "TIMELOCK";
62106
+ if (!isTimelockRoot) push(out, "no-timelock");
62107
+ else if (!g.timelockSecs && !g.timelockUnknown) push(out, "no-timelock");
62108
+ if (g.controllerKind === "EOA") push(out, "eoa-controlled");
62109
+ }
62110
+ }
62111
+ if (o) {
62112
+ if (o.kind === "none") push(out, "no-oracle");
62113
+ if (o.kind === "nav-attested") push(out, "nav-attested");
62114
+ if (o.band === "HIGH" || o.band === "CRITICAL") push(out, "oracle-flagged");
62115
+ if (o.flags?.some((f) => f === "wrong-asset" || f === "correlated-proxy"))
62116
+ push(out, "oracle-flagged");
62117
+ }
62118
+ return [...out];
62119
+ }
62120
+ function rateTags(rate) {
62121
+ const out = /* @__PURE__ */ new Set();
62122
+ switch (rate.kind) {
62123
+ case "fixed-term":
62124
+ case "fixed-open":
62125
+ push(out, "fixed-rate");
62126
+ break;
62127
+ case "user-set":
62128
+ push(out, "user-set-rate");
62129
+ break;
62130
+ case "zero-interest":
62131
+ push(out, "zero-interest");
62132
+ break;
62133
+ case "prepaid":
62134
+ push(out, "prepaid-interest");
62135
+ break;
62136
+ case "nav-accrual":
62137
+ push(out, "nav-accrual");
62138
+ break;
62139
+ case "variable-curve":
62140
+ case "variable-managed":
62141
+ push(out, "variable-rate");
62142
+ break;
62143
+ }
62144
+ if (rate.rewards?.some((r) => r.indicative || r.kind === "points"))
62145
+ push(out, "points-rewards");
62146
+ return [...out];
62147
+ }
62148
+ function maturityTags(maturity) {
62149
+ const out = /* @__PURE__ */ new Set();
62150
+ if (maturity.kind === "fixed-date") push(out, "has-maturity");
62151
+ if (maturity.kind === "perpetual") push(out, "perpetual");
62152
+ if (maturity.kind === "rolling-duration") push(out, "rolling-duration");
62153
+ if (maturity.atMaturity === "physical-delivery")
62154
+ push(out, "physical-delivery");
62155
+ return [...out];
62156
+ }
62157
+ function availabilityTags(a) {
62158
+ const out = /* @__PURE__ */ new Set();
62159
+ if (a.gating !== "permissionless") push(out, "permissioned");
62160
+ if (a.cap != null || a.capUtilization != null) push(out, "capped");
62161
+ if (a.blockedBy === "cap-full") push(out, "cap-full");
62162
+ return [...out];
62163
+ }
62164
+ function counterpartyTags(c) {
62165
+ const out = /* @__PURE__ */ new Set();
62166
+ if (c.solvency === "undercollateralized") push(out, "undercollateralized");
62167
+ if (c.solvency === "tranched-junior") push(out, "first-loss");
62168
+ if (c.solvency === "nav-attested") push(out, "nav-attested");
62169
+ if (c.socializedLoss) push(out, "socialized-loss");
62170
+ return [...out];
62171
+ }
62172
+ function deriveSupplyTags(supply, market = {}) {
62173
+ const out = /* @__PURE__ */ new Set();
62174
+ push(out, ...rateTags(supply.rate));
62175
+ push(out, ...maturityTags(supply.maturity));
62176
+ push(out, ...availabilityTags(supply.availability));
62177
+ push(out, ...counterpartyTags(supply.counterparty));
62178
+ push(out, ...marketTags(market));
62179
+ switch (supply.exit.mode) {
62180
+ case "instant":
62181
+ push(out, "exit-instant");
62182
+ break;
62183
+ case "instant-capped":
62184
+ case "instant-or-queued":
62185
+ push(out, "exit-capped");
62186
+ break;
62187
+ case "fixed-cooldown":
62188
+ push(out, "exit-cooldown");
62189
+ break;
62190
+ case "queued":
62191
+ case "request-based":
62192
+ case "fee-or-queued":
62193
+ push(out, "exit-queued");
62194
+ break;
62195
+ case "market-sale":
62196
+ push(out, "exit-market-sale");
62197
+ break;
62198
+ }
62199
+ if (supply.exit.priceRisk === "may-be-impossible")
62200
+ push(out, "exit-may-be-impossible");
62201
+ for (const r of supply.principal.risks) {
62202
+ if (r === "first-loss") push(out, "first-loss");
62203
+ if (r === "physical-delivery") push(out, "physical-delivery");
62204
+ }
62205
+ return [...out];
62206
+ }
62207
+ function deriveBorrowTags(borrow, market = {}) {
62208
+ const out = /* @__PURE__ */ new Set();
62209
+ push(out, ...rateTags(borrow.rate));
62210
+ push(out, ...maturityTags(borrow.maturity));
62211
+ push(out, ...availabilityTags(borrow.availability));
62212
+ push(out, ...counterpartyTags(borrow.counterparty));
62213
+ push(out, ...marketTags(market));
62214
+ push(out, borrow.debtShape === "accruing" ? "accruing-debt" : "static-debt");
62215
+ const trigger = borrow.liquidation.trigger;
62216
+ if (trigger === "price") push(out, "price-liquidation");
62217
+ if (trigger === "time") push(out, "time-liquidation");
62218
+ if (trigger === "price-and-time")
62219
+ push(out, "price-liquidation", "time-liquidation");
62220
+ if (trigger === "redemption") push(out, "redeemable");
62221
+ if (trigger === "none") push(out, "no-liquidation");
62222
+ if (borrow.liquidation.redeemable) push(out, "redeemable");
62223
+ if (borrow.liquidation.seizure === "full-collateral")
62224
+ push(out, "full-collateral-seizure");
62225
+ switch (borrow.exit.earlyRepay) {
62226
+ case "free":
62227
+ push(out, "early-exit-free");
62228
+ break;
62229
+ case "penalty":
62230
+ push(out, "early-exit-penalty");
62231
+ break;
62232
+ case "discount":
62233
+ push(out, "early-exit-discount");
62234
+ break;
62235
+ }
62236
+ if (borrow.maturity.atMaturity === "default-seizure")
62237
+ push(out, "full-collateral-seizure");
62238
+ return [...out];
62239
+ }
62240
+
62241
+ // src/terms/format.ts
62242
+ var SECS_PER_DAY = 86400;
62243
+ function pct(value, dp = 2) {
62244
+ if (value == null || !Number.isFinite(value)) return "\u2014";
62245
+ const fixed = value.toFixed(dp);
62246
+ const trimmed = fixed.includes(".") ? fixed.replace(/0+$/, "").replace(/\.$/, "") : fixed;
62247
+ return `${trimmed === "" || trimmed === "-" ? "0" : trimmed}%`;
62248
+ }
62249
+ function duration(secs) {
62250
+ if (secs == null || !Number.isFinite(secs) || secs < 0) return "\u2014";
62251
+ if (secs === 0) return "none";
62252
+ if (secs >= SECS_PER_DAY) {
62253
+ const d = secs / SECS_PER_DAY;
62254
+ return `${d % 1 === 0 ? d : d.toFixed(1)} day${d === 1 ? "" : "s"}`;
62255
+ }
62256
+ if (secs >= 3600) {
62257
+ const h = secs / 3600;
62258
+ return `${h % 1 === 0 ? h : h.toFixed(1)} hour${h === 1 ? "" : "s"}`;
62259
+ }
62260
+ if (secs >= 60) {
62261
+ const m = Math.round(secs / 60);
62262
+ return `${m} minute${m === 1 ? "" : "s"}`;
62263
+ }
62264
+ return `${Math.round(secs)} second${Math.round(secs) === 1 ? "" : "s"}`;
62265
+ }
62266
+ function shortDate(unixSecs) {
62267
+ if (unixSecs == null || !Number.isFinite(unixSecs)) return "\u2014";
62268
+ const d = new Date(unixSecs * 1e3);
62269
+ const months = [
62270
+ "Jan",
62271
+ "Feb",
62272
+ "Mar",
62273
+ "Apr",
62274
+ "May",
62275
+ "Jun",
62276
+ "Jul",
62277
+ "Aug",
62278
+ "Sep",
62279
+ "Oct",
62280
+ "Nov",
62281
+ "Dec"
62282
+ ];
62283
+ return `${d.getUTCDate()} ${months[d.getUTCMonth()]} ${d.getUTCFullYear()}`;
62284
+ }
62285
+ function feePhrase(fee) {
62286
+ const magnitude = Math.abs(fee.value);
62287
+ let amount;
62288
+ switch (fee.unit) {
62289
+ case "bps":
62290
+ amount = `${magnitude} bps`;
62291
+ break;
62292
+ case "apr-percent":
62293
+ amount = `${pct(magnitude)}/yr`;
62294
+ break;
62295
+ case "percent":
62296
+ amount = pct(magnitude);
62297
+ break;
62298
+ default:
62299
+ amount = String(magnitude);
62300
+ }
62301
+ const rebate = fee.value < 0 ? " rebate" : "";
62302
+ const qualifier = fee.indicative ? " (estimated, resolved at execution)" : "";
62303
+ return `${fee.label}: ${amount}${rebate}${qualifier}`;
62304
+ }
62305
+ function maturityPhrase(m) {
62306
+ if (m.kind === "fixed-date" && m.maturity)
62307
+ return `until ${shortDate(m.maturity)}`;
62308
+ if (m.kind === "rolling-duration") {
62309
+ if (m.maxDurationSecs) return `for up to ${duration(m.maxDurationSecs)}`;
62310
+ return "for a term you choose";
62311
+ }
62312
+ return "with no maturity";
62313
+ }
62314
+ function rateLabel(sheet) {
62315
+ switch (sheet.rate.kind) {
62316
+ case "fixed-term":
62317
+ case "fixed-open":
62318
+ return "Fixed";
62319
+ case "user-set":
62320
+ return "You set";
62321
+ case "zero-interest":
62322
+ return "No interest";
62323
+ case "prepaid":
62324
+ return "Prepaid";
62325
+ case "nav-accrual":
62326
+ return "NAV";
62327
+ case "none":
62328
+ return "No yield";
62329
+ default:
62330
+ return "Variable";
62331
+ }
62332
+ }
62333
+ var earlyRepayPhrase = {
62334
+ free: "repay any time at no extra cost",
62335
+ discount: "repaying early earns a rebate",
62336
+ penalty: "repaying early costs a penalty",
62337
+ "market-price": "exit early only at the market price",
62338
+ "not-allowed": "no early repayment"
62339
+ };
62340
+ var exitPhrase = {
62341
+ instant: "withdraw any time",
62342
+ "instant-capped": "withdraw up to available liquidity",
62343
+ "instant-or-queued": "withdraw instantly or queue",
62344
+ "fee-or-queued": "instant exit for a fee, or queue for free",
62345
+ "fixed-cooldown": "withdraw after a cooldown",
62346
+ queued: "withdrawals are queued",
62347
+ "request-based": "withdrawals need a request",
62348
+ "market-sale": "exit by selling on the market",
62349
+ "at-maturity": "locked until maturity",
62350
+ "off-chain": "exit off-chain",
62351
+ "dex-only": "exit only via a DEX"
62352
+ };
62353
+ function supplyHeadline(s) {
62354
+ if (s.role === "collateral") {
62355
+ return `Collateral only \xB7 ${maturityPhrase(s.maturity)}`;
62356
+ }
62357
+ const rate = `${rateLabel(s)} ${pct(s.rate.aprTotal)}`;
62358
+ const exit = exitPhrase[String(s.exit.mode)] ?? (s.exit.settlement === "sync" ? "withdraw any time" : "delayed withdrawal");
62359
+ const cooldown = s.exit.cooldownSecs ? ` (${duration(s.exit.cooldownSecs)})` : "";
62360
+ const mat = s.maturity.kind === "perpetual" ? "" : ` ${maturityPhrase(s.maturity)}`;
62361
+ return `${rate}${mat} \xB7 ${exit}${cooldown}`;
62362
+ }
62363
+ function borrowHeadline(b) {
62364
+ const rate = b.rate.kind === "zero-interest" ? "No ongoing interest" : b.rate.kind === "user-set" ? `You set the rate (${pct(b.rate.minApr)}\u2013${pct(b.rate.maxApr)})` : `${rateLabel(b)} ${pct(b.rate.apr)}`;
62365
+ const mat = b.maturity.kind === "perpetual" ? "" : ` ${maturityPhrase(b.maturity)}`;
62366
+ const exit = earlyRepayPhrase[String(b.exit.earlyRepay)] ?? "see repay terms";
62367
+ return `${rate}${mat} \xB7 ${exit}`;
62368
+ }
62369
+ function supplyDescription(s, sheet = {}) {
62370
+ const parts = [];
62371
+ if (s.role === "collateral") {
62372
+ parts.push(
62373
+ "Deposits here are collateral only \u2014 they secure borrowing and do not earn interest."
62374
+ );
62375
+ } else {
62376
+ const comp = [`${pct(s.rate.components.base)} base`];
62377
+ if (s.rate.components.rewards)
62378
+ comp.push(`${pct(s.rate.components.rewards)} rewards`);
62379
+ if (s.rate.components.intrinsic)
62380
+ comp.push(`${pct(s.rate.components.intrinsic)} from the asset itself`);
62381
+ parts.push(
62382
+ `You earn ${pct(s.rate.aprTotal)} (${comp.join(" + ")}), ${s.rate.isLocked ? "locked for the term" : "floating with utilization"}.`
62383
+ );
62384
+ }
62385
+ if (s.exit.mode === "instant") {
62386
+ const util = sheet.utilization?.utilization;
62387
+ parts.push(
62388
+ util != null && util >= 0.95 ? `Withdrawals are instant in principle, but utilization is ${pct(util * 100)} \u2014 there may be little or no liquidity to withdraw right now.` : "Withdrawals are instant, up to the liquidity available in the market."
62389
+ );
62390
+ } else if (s.exit.cooldownSecs) {
62391
+ parts.push(
62392
+ `Getting out takes ${duration(s.exit.cooldownSecs)} \u2014 request first, then claim.`
62393
+ );
62394
+ } else if (s.exit.priceRisk === "market-price") {
62395
+ parts.push(
62396
+ "There is no redemption before maturity; exiting early means selling the position on a market at whatever price is bid."
62397
+ );
62398
+ } else if (s.exit.priceRisk === "may-be-impossible") {
62399
+ parts.push(
62400
+ "Exiting early requires a buyer on the order book, and there may not be one at any price."
62401
+ );
62402
+ }
62403
+ if (s.backedBy && s.backedBy.count > 0) {
62404
+ parts.push(
62405
+ s.backedBy.weightBasis === "unweighted" ? `Your deposit is lent against ${s.backedBy.count} accepted collateral asset${s.backedBy.count === 1 ? "" : "s"}; the protocol does not record which one backs which borrow.` : `Your deposit is lent against ${s.backedBy.count} collateral market${s.backedBy.count === 1 ? "" : "s"}${s.backedBy.topWeightPct != null ? `, the largest being ${s.backedBy.topWeightPct.toFixed(0)} % of the total` : ""}.`
62406
+ );
62407
+ }
62408
+ return parts.join(" ");
62409
+ }
62410
+ function borrowDescription(b) {
62411
+ const parts = [];
62412
+ if (b.rate.kind === "zero-interest") {
62413
+ parts.push(
62414
+ "This market charges no ongoing interest \u2014 the borrowing cost is a one-off fee taken at open."
62415
+ );
62416
+ } else if (b.rate.kind === "prepaid") {
62417
+ parts.push(
62418
+ "Interest is prepaid by holding a separate token; the debt principal itself does not grow."
62419
+ );
62420
+ } else if (b.rate.kind === "user-set") {
62421
+ parts.push(
62422
+ `You choose your own rate between ${pct(b.rate.minApr)} and ${pct(b.rate.maxApr)}; a lower rate is cheaper but makes you redeemed first.`
62423
+ );
62424
+ } else {
62425
+ parts.push(
62426
+ `You pay ${pct(b.rate.apr)}${b.rate.isLocked ? ", locked for the term" : ", floating with utilization"}.`
62427
+ );
62428
+ }
62429
+ parts.push(
62430
+ b.debtShape === "static-face" ? "The amount owed is fixed at trade time and does not accrue." : b.debtShape === "prepaid" ? "The debt principal is static; the cost is the prepaid interest token burning down." : "The amount owed grows continuously as interest accrues."
62431
+ );
62432
+ if (b.maturity.kind === "fixed-date" && b.maturity.maturity) {
62433
+ const consequence = {
62434
+ "penalty-accrues": "a late penalty starts accruing",
62435
+ liquidatable: "the loan can be liquidated regardless of its health",
62436
+ "default-seizure": "the loan defaults and the collateral is forfeit",
62437
+ "physical-delivery": "the collateral is delivered to lenders",
62438
+ refinanced: "a keeper refinances it into the variable position",
62439
+ "stops-earning": "it simply stops accruing"
62440
+ };
62441
+ const what = consequence[String(b.maturity.atMaturity)];
62442
+ parts.push(
62443
+ `It matures on ${shortDate(b.maturity.maturity)}${what ? `; if nothing is done then, ${what}` : ""}.`
62444
+ );
62445
+ }
62446
+ if (b.liquidation.trigger === "time") {
62447
+ parts.push(
62448
+ "Liquidation here is triggered by TIME, not price \u2014 being late is the trigger, and being over-collateralised does not protect you."
62449
+ );
62450
+ } else if (b.liquidation.liquidationLtv != null) {
62451
+ parts.push(
62452
+ `Liquidation starts at ${pct(b.liquidation.liquidationLtv * 100)} LTV, with a ${pct(b.liquidation.penalty * 100)} penalty.`
62453
+ );
62454
+ }
62455
+ return parts.join(" ");
62456
+ }
62457
+
62458
+ // src/terms/profiles.ts
62459
+ var P = (p) => p;
62460
+ var TERM_PROFILES = [
62461
+ // ── Variable-rate pool lenders ───────────────────────────────────────────
62462
+ P({
62463
+ id: "pool.variable@v1",
62464
+ name: "Variable-rate pool market",
62465
+ family: "pool",
62466
+ supply: {
62467
+ description: "A shared liquidity pool. Deposits earn interest paid by borrowers, at a rate that moves with utilization. Withdrawals are instant up to the cash left in the pool.",
62468
+ implications: [
62469
+ "Your yield is not fixed \u2014 it rises and falls with borrowing demand.",
62470
+ "If utilization reaches 100 %, withdrawals are blocked until borrowers repay or new deposits arrive.",
62471
+ "Your deposit is exposed to every collateral asset the market accepts, not just one."
62472
+ ]
62473
+ },
62474
+ borrow: {
62475
+ description: "Borrow against collateral at a rate that moves with utilization. The debt accrues continuously and can be repaid at any time.",
62476
+ implications: [
62477
+ "The rate can change block to block \u2014 a cheap borrow can become expensive.",
62478
+ "If the collateral value falls past the liquidation threshold, a liquidator repays part of your debt and takes collateral plus a penalty."
62479
+ ]
62480
+ }
62481
+ }),
62482
+ // ── Fixed-term families ──────────────────────────────────────────────────
62483
+ P({
62484
+ id: "exactly.fixed@v1",
62485
+ name: "Exactly fixed-rate pool",
62486
+ family: "exactly",
62487
+ supply: {
62488
+ description: "A fixed-rate deposit into a maturity-dated pool, backed by the floating pool. The rate is locked when you deposit.",
62489
+ implications: [
62490
+ "Withdrawing before maturity sells the position back at the current curve rate \u2014 the haircut worsens as rates rise.",
62491
+ "After maturity the deposit stops earning but is never penalised and never expires."
62492
+ ]
62493
+ },
62494
+ borrow: {
62495
+ description: "A fixed-rate borrow with a static face value (principal + fee) locked at trade time, under a shared cross-margin risk module.",
62496
+ implications: [
62497
+ "Repaying early is REBATED, not penalised \u2014 but the rebate can be zero when the pool has no unassigned earnings left.",
62498
+ "Repaying late accrues a linear penalty of roughly 164 %/yr on the face value, which drives the position toward liquidation on its own.",
62499
+ "A liquidator settles at full face value \u2014 only you, repaying voluntarily, get the early-repay rebate."
62500
+ ]
62501
+ }
62502
+ }),
62503
+ P({
62504
+ id: "midnight.orderbook@v1",
62505
+ name: "Morpho Midnight order book",
62506
+ family: "midnight",
62507
+ supply: {
62508
+ description: "A fixed-rate lend filled from an order book of maker offers rather than a pool. Interest lives entirely in the entry price: you pay a discount now and are owed the face value at maturity.",
62509
+ implications: [
62510
+ "Exiting before maturity means selling your units into the bid side at a discount \u2014 there may be no bid.",
62511
+ "Two ongoing fees are charged to the LENDER side and reduce your net position.",
62512
+ "Bad debt is socialised across lenders."
62513
+ ]
62514
+ },
62515
+ borrow: {
62516
+ description: "A fixed-rate borrow of zero-coupon units. The debt is a static unit count that accrues nothing; you receive the discounted value now and owe the face at maturity.",
62517
+ implications: [
62518
+ "Once maturity passes the loan is in default and can be liquidated regardless of health or LTV \u2014 being past due IS the trigger.",
62519
+ "Over-repaying REVERTS: the repayment must be sized to the exact debt.",
62520
+ "Repaying early costs face value with neither fee nor rebate, so there is no saving in paying sooner."
62521
+ ]
62522
+ }
62523
+ }),
62524
+ P({
62525
+ id: "term.auction@v1",
62526
+ name: "Term Finance repo",
62527
+ family: "term",
62528
+ supply: {
62529
+ description: "A fixed-rate, fixed-maturity tri-party repo. Lending happens either in a scheduled sealed-bid auction or by buying repo tokens on the secondary market between rounds.",
62530
+ implications: [
62531
+ "Lending is possible between auction rounds via the secondary book, even when borrowing is not.",
62532
+ "Exiting early means selling the repo token on the secondary market at whatever price is bid."
62533
+ ]
62534
+ },
62535
+ borrow: {
62536
+ description: "A repurchase obligation with a static face value, originated only inside a scheduled sealed-bid auction round.",
62537
+ implications: [
62538
+ "Borrowing is ONLY possible inside an open auction round \u2014 most repos sit between rounds and cannot be borrowed even though they quote a rate.",
62539
+ "Any rate shown outside an open round is the previous round\u2019s clearing rate: historical, not obtainable.",
62540
+ "Repaying early costs the full face value with no rebate, so early repayment is strictly worse than holding to maturity."
62541
+ ]
62542
+ }
62543
+ }),
62544
+ P({
62545
+ id: "termmax.fixed@v1",
62546
+ name: "TermMax fixed-maturity market",
62547
+ family: "termmax",
62548
+ supply: {
62549
+ description: "Lending is buying a zero-coupon bond token (FT) on a fixed-maturity AMM. You pay a discount now and redeem the face value at maturity.",
62550
+ implications: [
62551
+ "You are NOT principal-protected the way a pool supply is: after maturity, unpaid collateral is distributed pro-rata to all bond holders \u2014 you can be settled in collateral instead of the asset you lent.",
62552
+ "That loss is socialised across the whole bond supply, so a default anywhere in the market affects you regardless of which loan defaulted.",
62553
+ "Exiting early requires a maker order to sell into, and one may not exist."
62554
+ ]
62555
+ },
62556
+ borrow: {
62557
+ description: "A fixed-maturity loan represented by an NFT, with a static face-value debt that accrues nothing.",
62558
+ implications: [
62559
+ "After maturity plus a two-hour liquidation window, unpaid collateral is delivered to lenders.",
62560
+ "The debt is a static face value \u2014 repaying early does not reduce the amount owed."
62561
+ ]
62562
+ }
62563
+ }),
62564
+ P({
62565
+ id: "teller.pool@v1",
62566
+ name: "Teller V2 pooled loan",
62567
+ family: "teller",
62568
+ supply: {
62569
+ description: "A deposit into a lender-commitment pool that funds fixed-term, fixed-APR loans. There is no price oracle anywhere in this market.",
62570
+ implications: [
62571
+ "Borrowers are defaulted on TIME, not price \u2014 the pool\u2019s protection is collateral seizure after a missed payment, not a margin call.",
62572
+ "Withdrawals sit behind a delay."
62573
+ ]
62574
+ },
62575
+ borrow: {
62576
+ description: "A fixed-term, fixed-APR loan with escrowed collateral and TIME-based default. There is no oracle and no health factor in the liquidation trigger.",
62577
+ implications: [
62578
+ "On default a liquidator seizes your ENTIRE escrowed collateral, not just the amount owed \u2014 at 50 % LTV that is roughly twice the borrowed value.",
62579
+ "The grace period after the payment window can be extremely short \u2014 as little as five minutes.",
62580
+ "Being over-collateralised does not protect you: a perfectly healthy loan is defaulted for being late.",
62581
+ "An upfront origination fee is charged at borrow time."
62582
+ ]
62583
+ }
62584
+ }),
62585
+ P({
62586
+ id: "lista.broker@v1",
62587
+ name: "Lista brokered fixed term",
62588
+ family: "lista",
62589
+ borrow: {
62590
+ description: "A fixed-rate loan fronted by a single market broker over an underlying Morpho-style market. Interest accrues linearly and freezes at maturity.",
62591
+ implications: [
62592
+ "Repaying early costs roughly half the remaining-term interest as a penalty.",
62593
+ "If the loan is not repaid at maturity, a keeper refinances it into the variable position."
62594
+ ]
62595
+ }
62596
+ }),
62597
+ // ── CDP families ─────────────────────────────────────────────────────────
62598
+ P({
62599
+ id: "liquity.cdp@v1",
62600
+ name: "Liquity V2 family trove",
62601
+ family: "liquity",
62602
+ supply: {
62603
+ description: "The Stability Pool absorbs liquidated debt in exchange for discounted collateral. It is an earn position, not collateral.",
62604
+ implications: [
62605
+ "Your deposit is converted into collateral when liquidations occur \u2014 the balance is meant to change composition."
62606
+ ]
62607
+ },
62608
+ borrow: {
62609
+ description: "A CDP where YOU set the interest rate. A one-off upfront fee is charged at open, and there is a minimum debt size.",
62610
+ implications: [
62611
+ "Your collateral can be REDEEMED at par while the position is perfectly healthy, if you carry the lowest interest rate on the branch. Raising your rate is the only defence.",
62612
+ "You pay a one-off upfront fee equal to roughly a week of interest at open, and again whenever you adjust the rate.",
62613
+ "There is a minimum debt size; you cannot leave a position below it."
62614
+ ]
62615
+ }
62616
+ }),
62617
+ P({
62618
+ id: "river.cdp@v1",
62619
+ name: "River (Satoshi) trove",
62620
+ family: "river",
62621
+ borrow: {
62622
+ description: "A CDP with NO ongoing interest. The entire borrowing cost is a one-off decaying mint fee charged at open.",
62623
+ implications: [
62624
+ "There is no APR \u2014 the cost is the mint fee, so a short-held loan is expensive and a long-held one is cheap.",
62625
+ "Your collateral can be redeemed at par while healthy."
62626
+ ]
62627
+ }
62628
+ }),
62629
+ P({
62630
+ id: "inverse.firm@v1",
62631
+ name: "Inverse FiRM market",
62632
+ family: "inverse",
62633
+ borrow: {
62634
+ description: "A CDP where interest is PREPAID by holding DBR tokens. The debt principal is static; you burn one DBR per borrowed dollar per year.",
62635
+ implications: [
62636
+ "Running out of DBR triggers a permissionless forced top-up at roughly 54.75 %/yr, repeatable every block, and freezes withdrawals.",
62637
+ "The debt principal never grows \u2014 the cost is the DBR balance burning down.",
62638
+ "Only governance-approved contracts may borrow, which blocks leverage and other routed flows."
62639
+ ]
62640
+ }
62641
+ }),
62642
+ P({
62643
+ id: "usdd.cdp@v1",
62644
+ name: "USDD CDP ilk",
62645
+ family: "usdd",
62646
+ borrow: {
62647
+ description: "A Maker-style CDP with a governance-set stability fee and no utilization curve.",
62648
+ implications: [
62649
+ "The rate is set by governance, not by a market \u2014 it can change without any utilization signal."
62650
+ ]
62651
+ }
62652
+ }),
62653
+ // ── Vaults ───────────────────────────────────────────────────────────────
62654
+ P({
62655
+ id: "vault.lending@v1",
62656
+ name: "Curated lending vault",
62657
+ family: "vault",
62658
+ supply: {
62659
+ description: "A curated vault that allocates deposits across underlying lending markets. A curator chooses which markets and how much.",
62660
+ implications: [
62661
+ "You are exposed to every market the curator allocates into, and the allocation can change.",
62662
+ "Withdrawals are limited to the vault\u2019s liquid cash \u2014 if allocations are fully utilised, exits queue."
62663
+ ]
62664
+ }
62665
+ }),
62666
+ P({
62667
+ id: "vault.savings@v1",
62668
+ name: "Savings vault",
62669
+ family: "savings",
62670
+ supply: {
62671
+ description: "A yield-bearing wrapper whose share price accrues. There is no borrow side and no utilization.",
62672
+ implications: [
62673
+ "The exit mechanism, not the APR, is the load-bearing term here \u2014 check the cooldown before depositing."
62674
+ ]
62675
+ }
62676
+ }),
62677
+ P({
62678
+ id: "vault.nav@v1",
62679
+ name: "NAV-attested vault",
62680
+ family: "savings-nav",
62681
+ supply: {
62682
+ description: "A vault whose share price is published by an operator from an off-chain net asset value rather than derived from on-chain holdings.",
62683
+ implications: [
62684
+ "There is no on-chain solvency invariant \u2014 the share price is a trusted input, not a computed one.",
62685
+ "The published value can be stale; some feeds keep no round history at all."
62686
+ ]
62687
+ }
62688
+ }),
62689
+ P({
62690
+ id: "vault.tranche-junior@v1",
62691
+ name: "Junior tranche",
62692
+ family: "tranche",
62693
+ supply: {
62694
+ description: "The junior slice of a tranched structure. It pays a higher rate precisely because it absorbs losses first.",
62695
+ implications: [
62696
+ "This is FIRST-LOSS capital \u2014 a drawdown hits your position before it touches the senior tranche.",
62697
+ "Redemptions can be paused when senior coverage falls below its minimum."
62698
+ ]
62699
+ }
62700
+ }),
62701
+ P({
62702
+ id: "vault.tranche-senior@v1",
62703
+ name: "Senior tranche",
62704
+ family: "tranche",
62705
+ supply: {
62706
+ description: "The senior slice of a tranched structure, protected by junior capital up to the size of that junior slice.",
62707
+ implications: [
62708
+ "Protection is finite \u2014 it ends when the junior tranche is exhausted."
62709
+ ]
62710
+ }
62711
+ }),
62712
+ P({
62713
+ id: "vault.credit@v1",
62714
+ name: "Undercollateralized credit pool",
62715
+ family: "credit",
62716
+ supply: {
62717
+ description: "A pool lending to borrowers who are not fully collateralised on-chain. Repayment depends on off-chain credit arrangements.",
62718
+ implications: [
62719
+ "There is no on-chain collateral to liquidate if a borrower does not repay.",
62720
+ "Exits are delayed or capped by the pool\u2019s own inventory."
62721
+ ]
62722
+ }
62723
+ })
62724
+ ];
62725
+ var BY_ID = new Map(TERM_PROFILES.map((p) => [p.id, p]));
62726
+ function getTermProfile(id) {
62727
+ return BY_ID.get(id);
62728
+ }
62729
+ var DEFAULT_PROFILE_ID = "pool.variable@v1";
62730
+
62731
+ // src/terms/buildTermSheet.ts
62732
+ var isNum = (v) => typeof v === "number" && Number.isFinite(v);
62733
+ var clamp01 = (v) => Math.max(0, Math.min(1, v));
62734
+ function assetRef(input) {
62735
+ const a = input.asset ?? {};
62736
+ return {
62737
+ chainId: String(a.chainId ?? input.chainId),
62738
+ address: String(a.address ?? input.underlying ?? "").toLowerCase(),
62739
+ symbol: a.symbol,
62740
+ name: a.name,
62741
+ decimals: a.decimals ?? input.decimals,
62742
+ assetGroup: a.assetGroup,
62743
+ logoURI: a.logoURI
62744
+ };
62745
+ }
62746
+ function defaultConfig(input) {
62747
+ const cfg = input.config;
62748
+ if (!cfg) return void 0;
62749
+ return cfg["0"] ?? Object.values(cfg)[0];
62750
+ }
62751
+ var RATE_MODEL_TO_KIND = {
62752
+ variable: "variable-curve",
62753
+ fixedTerm: "fixed-term",
62754
+ userSet: "user-set",
62755
+ zeroInterest: "zero-interest",
62756
+ dbr: "prepaid",
62757
+ protocolSet: "variable-managed"
62758
+ };
62759
+ function rateKindOf(input) {
62760
+ const mapped = input.rateModel ? RATE_MODEL_TO_KIND[input.rateModel] : void 0;
62761
+ return mapped ?? "variable-curve";
62762
+ }
62763
+ function rewardTerms(input, side) {
62764
+ if (!input.rewards?.length) return void 0;
62765
+ const out = [];
62766
+ for (const r of input.rewards) {
62767
+ const apr = side === "supply" ? r.depositRate ?? 0 : r.variableBorrowRate ?? r.stableBorrowRate ?? 0;
62768
+ if (!apr) continue;
62769
+ const isPoints = r.kind === "points" || !r.asset;
62770
+ out.push({
62771
+ asset: r.asset ? { chainId: input.chainId, address: String(r.asset).toLowerCase() } : void 0,
62772
+ kind: isPoints ? "points" : "token",
62773
+ apr,
62774
+ side,
62775
+ claim: r.claim ?? (isPoints ? "none" : "accrual"),
62776
+ endsAt: r.endsAt,
62777
+ // Points have no priceable value, so they must not inflate `aprTotal`.
62778
+ indicative: isPoints || void 0
62779
+ });
62780
+ }
62781
+ return out.length ? out : void 0;
62782
+ }
62783
+ function buildRate(input, side) {
62784
+ const kind = rateKindOf(input);
62785
+ const rewards = rewardTerms(input, side);
62786
+ const priceableRewards = (rewards ?? []).filter((r) => !r.indicative).reduce((s, r) => s + r.apr, 0);
62787
+ const menuBest = input.terms?.length ? Math.min(...input.terms.map((t) => t.apr)) : void 0;
62788
+ let base;
62789
+ if (side === "supply") {
62790
+ base = input.depositRate ?? 0;
62791
+ } else if (kind === "fixed-term") {
62792
+ base = menuBest ?? input.stableBorrowRate ?? input.variableBorrowRate ?? 0;
62793
+ } else {
62794
+ base = input.variableBorrowRate ?? 0;
62795
+ }
62796
+ const intrinsic = side === "supply" ? input.intrinsicYield ?? 0 : 0;
62797
+ return {
62798
+ kind,
62799
+ apr: base,
62800
+ components: {
62801
+ base,
62802
+ rewards: priceableRewards || void 0,
62803
+ intrinsic: intrinsic || void 0
62804
+ },
62805
+ aprTotal: base + priceableRewards + intrinsic,
62806
+ basis: "apr-nominal",
62807
+ compounding: kind === "zero-interest" || kind === "none" ? "none" : "per-second",
62808
+ source: kind === "fixed-term" ? input.fixedTerm?.provider?.kind === "orderbook" ? "orderbook" : input.fixedTerm?.provider?.kind === "auction" ? "auction" : "derived" : kind === "variable-managed" ? "governance" : kind === "user-set" ? "borrower" : kind === "nav-accrual" ? "oracle" : "utilization-curve",
62809
+ isLocked: kind === "fixed-term" || kind === "fixed-open",
62810
+ rewards,
62811
+ menu: input.terms?.length ? input.terms.map((t) => ({ ...t })) : void 0
62812
+ };
62813
+ }
62814
+ function buildMaturity(input, now) {
62815
+ const ft = input.fixedTerm;
62816
+ const maturity = ft?.maturity;
62817
+ if (isNum(maturity) && maturity > 0) {
62818
+ return {
62819
+ kind: "fixed-date",
62820
+ maturity,
62821
+ maturityIso: new Date(maturity * 1e3).toISOString(),
62822
+ secondsToMaturity: Math.max(0, maturity - now)
62823
+ };
62824
+ }
62825
+ if (input.terms?.length) {
62826
+ const durations = input.terms.map((t) => t.durationSecs).filter(isNum);
62827
+ return {
62828
+ kind: "rolling-duration",
62829
+ minDurationSecs: durations.length ? Math.min(...durations) : void 0,
62830
+ maxDurationSecs: durations.length ? Math.max(...durations) : void 0
62831
+ };
62832
+ }
62833
+ return { kind: "perpetual" };
62834
+ }
62835
+ function buildFees(input, side) {
62836
+ const out = [];
62837
+ const ft = input.fixedTerm;
62838
+ if (side === "borrow") {
62839
+ const origination = input.originationFee ?? ft?.fees?.originationFeePercent ?? void 0;
62840
+ if (isNum(origination) && origination !== 0) {
62841
+ out.push({
62842
+ id: "origination",
62843
+ label: "Origination fee",
62844
+ when: "entry",
62845
+ unit: "percent",
62846
+ basis: "principal",
62847
+ value: origination,
62848
+ payee: "protocol",
62849
+ description: "Charged once at borrow time as a percent of the amount borrowed. Not an APR \u2014 never add it to the rate."
62850
+ });
62851
+ }
62852
+ if (isNum(ft?.fees?.latePenaltyApr) && ft.fees.latePenaltyApr !== 0) {
62853
+ out.push({
62854
+ id: "late-penalty",
62855
+ label: "Late-repayment penalty",
62856
+ when: "late",
62857
+ unit: "apr-percent",
62858
+ basis: "face-value",
62859
+ value: ft.fees.latePenaltyApr,
62860
+ payee: "lenders",
62861
+ mutable: true,
62862
+ description: "Accrues on overdue debt after maturity until repaid. Simple interest on the face value, not compounding."
62863
+ });
62864
+ }
62865
+ if (ft?.earlyRepay?.kind === "discount") {
62866
+ out.push({
62867
+ id: "early-repay-discount",
62868
+ label: "Early-repayment rebate",
62869
+ when: "exit",
62870
+ unit: "percent",
62871
+ basis: "face-value",
62872
+ // Negative = rebate. Sign is load-bearing.
62873
+ value: -0,
62874
+ indicative: true,
62875
+ payee: "protocol",
62876
+ description: "Repaying before maturity costs LESS than face value. The exact rebate is resolved at repay time and can be zero."
62877
+ });
62878
+ }
62879
+ }
62880
+ if (side === "supply") {
62881
+ if (isNum(ft?.fees?.continuousFeeApr) && ft.fees.continuousFeeApr !== 0) {
62882
+ out.push({
62883
+ id: "continuous",
62884
+ label: "Continuous lender fee",
62885
+ when: "ongoing",
62886
+ unit: "apr-percent",
62887
+ basis: "principal",
62888
+ value: ft.fees.continuousFeeApr,
62889
+ payee: "protocol",
62890
+ description: "An ongoing haircut taken from the lender side."
62891
+ });
62892
+ }
62893
+ if (isNum(ft?.fees?.settlementFee) && ft.fees.settlementFee !== 0) {
62894
+ out.push({
62895
+ id: "settlement",
62896
+ label: "Settlement fee",
62897
+ when: "exit",
62898
+ unit: "percent",
62899
+ basis: "principal",
62900
+ value: ft.fees.settlementFee * 100,
62901
+ payee: "protocol"
62902
+ });
62903
+ }
62904
+ }
62905
+ return out;
62906
+ }
62907
+ function capUtil(total, cap) {
62908
+ if (!isNum(cap) || cap <= 0 || !isNum(total)) return void 0;
62909
+ return clamp01(total / cap);
62910
+ }
62911
+ function buildAvailability(input, side) {
62912
+ const frozen = input.isFrozen === true;
62913
+ const inactive = input.isActive === false;
62914
+ const enabled = side === "supply" ? input.depositsEnabled !== false : input.borrowingEnabled !== false;
62915
+ const cap = side === "supply" ? input.supplyCap : input.borrowCap;
62916
+ const total = side === "supply" ? input.totalDeposits : input.totalDebt;
62917
+ const capUtilization = capUtil(total, cap);
62918
+ const capFull = capUtilization != null && capUtilization >= 1;
62919
+ const noLiquidity = side === "borrow" && isNum(input.borrowLiquidity) && input.borrowLiquidity <= 0;
62920
+ const canOpen = enabled && !frozen && !inactive && !capFull && !noLiquidity;
62921
+ let blockedBy;
62922
+ if (!canOpen) {
62923
+ if (inactive) blockedBy = "shutdown";
62924
+ else if (frozen) blockedBy = "frozen";
62925
+ else if (!enabled) blockedBy = "disabled";
62926
+ else if (capFull) blockedBy = "cap-full";
62927
+ else if (noLiquidity) blockedBy = "no-liquidity";
62928
+ }
62929
+ return {
62930
+ canOpen,
62931
+ // Closing a position stays possible in every wind-down we have seen —
62932
+ // freezes stop entry, not exit. Adapters override where that is false.
62933
+ canClose: true,
62934
+ blockedBy,
62935
+ gating: "permissionless",
62936
+ cap: isNum(cap) && cap > 0 ? String(cap) : void 0,
62937
+ capUtilization,
62938
+ requires: side === "supply" ? ["token-approval"] : void 0
62939
+ };
62940
+ }
62941
+ function buildUtilization(input) {
62942
+ if (!isNum(input.utilization)) return void 0;
62943
+ const shared = isNum(input.irmTotalDeposits) && isNum(input.irmTotalDebt);
62944
+ return {
62945
+ utilization: clamp01(input.utilization),
62946
+ basis: shared ? "pool" : "market",
62947
+ irmTotalDeposits: input.irmTotalDeposits,
62948
+ irmTotalDebt: input.irmTotalDebt,
62949
+ supplyCapUtilization: capUtil(input.totalDeposits, input.supplyCap),
62950
+ borrowCapUtilization: capUtil(input.totalDebt, input.borrowCap),
62951
+ lockupRatio: input.lockupRatio
62952
+ };
62953
+ }
62954
+ function buildConstraints(input, siblings, acceptedCollateral) {
62955
+ const ceiling = input.debtCeiling;
62956
+ const ceilingNum = isNum(Number(ceiling)) ? Number(ceiling) : 0;
62957
+ const lender = input.lender ?? "";
62958
+ const collateralAssetCount = acceptedCollateral?.count ?? 0;
62959
+ const marketCount = siblings.filter((s) => s.marketUid).length || 1;
62960
+ return {
62961
+ isolation: ceilingNum > 0 ? { enabled: true, debtCeiling: String(ceiling) } : void 0,
62962
+ crossMargin: hasCrossMarginRisk(lender),
62963
+ // A sub-account lender gets the honest generic answer; its adapter refines
62964
+ // it to the concrete shape (`nft` / `cdp-id` / `loan-id` / `escrow`).
62965
+ positionModel: hasSubAccounts(lender) ? "sub-account" : "account",
62966
+ observed: {
62967
+ collateralAssetCount,
62968
+ marketCount,
62969
+ multiMarketKey: isMultiMarket(lender)
62970
+ }
62971
+ };
62972
+ }
62973
+ function liquidationFrom(cfg, input) {
62974
+ return {
62975
+ trigger: "price",
62976
+ ltv: cfg?.borrowCollateralFactor,
62977
+ liquidationLtv: cfg?.collateralFactor,
62978
+ penalty: cfg?.liquidationPenalty ?? 0,
62979
+ closeFactor: cfg?.closeFactor ?? input.closeFactor ?? 1,
62980
+ targetHealthFactor: cfg?.targetHealthFactor ?? input.targetHealthFactor,
62981
+ seizure: "proportional"
62982
+ };
62983
+ }
62984
+ function buildModes(input) {
62985
+ const cfg = input.config;
62986
+ if (!cfg) return void 0;
62987
+ const keys = Object.keys(cfg);
62988
+ if (keys.length <= 1) return void 0;
62989
+ const out = [];
62990
+ for (const key of keys) {
62991
+ const c = cfg[key];
62992
+ if (!c) continue;
62993
+ const isDefault = key === "0";
62994
+ out.push({
62995
+ modeId: key,
62996
+ label: c.label,
62997
+ isDefault,
62998
+ entry: isDefault ? void 0 : "user-selected",
62999
+ liquidation: {
63000
+ ltv: c.borrowCollateralFactor,
63001
+ liquidationLtv: c.collateralFactor,
63002
+ penalty: c.liquidationPenalty ?? 0,
63003
+ closeFactor: c.closeFactor ?? input.closeFactor ?? 1,
63004
+ targetHealthFactor: c.targetHealthFactor ?? input.targetHealthFactor
63005
+ },
63006
+ availability: c.debtDisabled ? { canOpen: false } : void 0
63007
+ });
63008
+ }
63009
+ return out.length > 1 ? out : void 0;
63010
+ }
63011
+ function buildExposures2(input, siblings, direction) {
63012
+ const cfg = input.config;
63013
+ if (!cfg || siblings.length === 0) return void 0;
63014
+ const categories = Object.keys(cfg);
63015
+ const items = [];
63016
+ for (const sib of siblings) {
63017
+ if (sib.marketUid === input.marketUid) continue;
63018
+ const sibCfg = sib.config;
63019
+ if (!sibCfg) continue;
63020
+ let best;
63021
+ for (const cat of categories) {
63022
+ const here = cfg[cat];
63023
+ const there = sibCfg[cat];
63024
+ if (!here || !there) continue;
63025
+ if (direction === "accepted" || direction === "backing") {
63026
+ if (here.debtDisabled === true) continue;
63027
+ if (there.collateralDisabled === true) continue;
63028
+ }
63029
+ if (!best || (there.collateralFactor ?? 0) > (best.collateralFactor ?? 0))
63030
+ best = there;
63031
+ }
63032
+ if (!best) continue;
63033
+ items.push({
63034
+ asset: assetRef(sib),
63035
+ marketUid: sib.marketUid,
63036
+ via: "collateral",
63037
+ ltv: best.borrowCollateralFactor,
63038
+ liquidationLtv: best.collateralFactor,
63039
+ liquidationPenalty: best.liquidationPenalty
63040
+ });
63041
+ }
63042
+ if (items.length === 0) return void 0;
63043
+ return {
63044
+ count: items.length,
63045
+ weightBasis: "unweighted",
63046
+ items
63047
+ };
63048
+ }
63049
+ function buildSupply(input, now, siblings) {
63050
+ const canBeCollateral = input.collateralActive !== false;
63051
+ const earns = (input.depositRate ?? 0) > 0 || (input.intrinsicYield ?? 0) > 0;
63052
+ if (input.depositsEnabled === false && !canBeCollateral && !earns)
63053
+ return void 0;
63054
+ const rate = buildRate(input, "supply");
63055
+ const maturity = buildMaturity(input, now);
63056
+ const fees = buildFees(input, "supply");
63057
+ const availability = buildAvailability(input, "supply");
63058
+ const liquidity = isNum(input.totalLiquidity) ? {
63059
+ assets: input.totalLiquidity,
63060
+ assetsUsd: input.totalLiquidityUsd,
63061
+ ratio: isNum(input.totalDeposits) && input.totalDeposits > 0 ? clamp01(input.totalLiquidity / input.totalDeposits) : void 0
63062
+ } : void 0;
63063
+ const supply = {
63064
+ role: canBeCollateral ? earns ? "both" : "collateral" : "yield",
63065
+ rate,
63066
+ maturity,
63067
+ exit: {
63068
+ mode: "instant",
63069
+ settlement: "sync",
63070
+ liquidity,
63071
+ partialAllowed: true,
63072
+ priceRisk: "none",
63073
+ fees: fees.filter((f) => f.when === "exit" || f.when === "performance")
63074
+ },
63075
+ fees,
63076
+ backedBy: buildExposures2(input, siblings, "backing"),
63077
+ modes: buildModes(input),
63078
+ counterparty: {
63079
+ kind: "pool",
63080
+ solvency: "overcollateralized"
63081
+ },
63082
+ availability,
63083
+ principal: { protected: true, risks: [] },
63084
+ info: { headline: "", description: "", tags: [] }
63085
+ };
63086
+ return supply;
63087
+ }
63088
+ function buildBorrow(input, now, siblings) {
63089
+ const cfg = defaultConfig(input);
63090
+ const debtDisabled = cfg?.debtDisabled === true;
63091
+ if (input.borrowingEnabled === false && debtDisabled) return void 0;
63092
+ if (debtDisabled && (input.totalDebt ?? 0) === 0 && input.borrowingEnabled !== true)
63093
+ return void 0;
63094
+ const rate = buildRate(input, "borrow");
63095
+ const maturity = buildMaturity(input, now);
63096
+ const fees = buildFees(input, "borrow");
63097
+ const borrow = {
63098
+ rate,
63099
+ maturity,
63100
+ debtShape: rate.kind === "prepaid" ? "prepaid" : rate.kind === "fixed-term" ? "static-face" : "accruing",
63101
+ exit: {
63102
+ earlyRepay: "free",
63103
+ atMaturityCost: rate.kind === "fixed-term" ? "face" : "accrued",
63104
+ lateBehaviour: "none",
63105
+ partialAllowed: true,
63106
+ fees: fees.filter((f) => f.when === "exit" || f.when === "late")
63107
+ },
63108
+ liquidation: liquidationFrom(cfg, input),
63109
+ acceptedCollateral: buildExposures2(input, siblings, "accepted"),
63110
+ modes: buildModes(input),
63111
+ fees,
63112
+ counterparty: { kind: "pool", solvency: "overcollateralized" },
63113
+ availability: buildAvailability(input, "borrow"),
63114
+ info: { headline: "", description: "", tags: [] }
63115
+ };
63116
+ return borrow;
63117
+ }
63118
+ function isPlainObject(v) {
63119
+ return typeof v === "object" && v !== null && !Array.isArray(v);
63120
+ }
63121
+ function mergeDeep(base, patch) {
63122
+ if (!patch) return base;
63123
+ if (!isPlainObject(base) || !isPlainObject(patch)) return patch ?? base;
63124
+ const out = { ...base };
63125
+ for (const [k, v] of Object.entries(patch)) {
63126
+ if (v === void 0) continue;
63127
+ const cur = out[k];
63128
+ out[k] = isPlainObject(v) && isPlainObject(cur) ? mergeDeep(cur, v) : v;
63129
+ }
63130
+ return out;
63131
+ }
63132
+ function finalizeInfo(sheet) {
63133
+ const market = { governance: sheet.governance, oracle: sheet.oracle };
63134
+ if (sheet.supply && !sheet.supply.info)
63135
+ sheet.supply.info = { headline: "", description: "", tags: [] };
63136
+ if (sheet.borrow && !sheet.borrow.info)
63137
+ sheet.borrow.info = { headline: "", description: "", tags: [] };
63138
+ if (sheet.supply) {
63139
+ sheet.supply.info = {
63140
+ headline: sheet.supply.info.headline || supplyHeadline(sheet.supply),
63141
+ description: sheet.supply.info.description || supplyDescription(sheet.supply, sheet),
63142
+ implications: sheet.supply.info.implications,
63143
+ tags: deriveSupplyTags(sheet.supply, market)
63144
+ };
63145
+ }
63146
+ if (sheet.borrow) {
63147
+ sheet.borrow.info = {
63148
+ headline: sheet.borrow.info.headline || borrowHeadline(sheet.borrow),
63149
+ description: sheet.borrow.info.description || borrowDescription(sheet.borrow),
63150
+ implications: sheet.borrow.info.implications,
63151
+ tags: deriveBorrowTags(sheet.borrow, market)
63152
+ };
63153
+ }
63154
+ return sheet;
63155
+ }
63156
+ function buildCoverage(sheet) {
63157
+ const present = [];
63158
+ const notApplicable = {};
63159
+ const pending = {};
63160
+ if (sheet.supply) present.push("supply");
63161
+ else notApplicable.supply = "no-supply-side";
63162
+ if (sheet.borrow) present.push("borrow");
63163
+ else notApplicable.borrow = "no-borrow-side";
63164
+ if (sheet.utilization) present.push("utilization");
63165
+ if (sheet.constraints) present.push("constraints");
63166
+ if (sheet.oracle) present.push("oracle");
63167
+ else if (sheet.borrow?.liquidation.trigger === "time")
63168
+ notApplicable.oracle = "time-based-liquidation";
63169
+ else pending.oracle = "not-classified-for-this-lender";
63170
+ if (sheet.governance) present.push("governance");
63171
+ else pending.governance = "screener-has-no-rows-for-this-market";
63172
+ return {
63173
+ present,
63174
+ notApplicable: Object.keys(notApplicable).length ? notApplicable : void 0,
63175
+ pending: Object.keys(pending).length ? pending : void 0
63176
+ };
63177
+ }
63178
+ function buildTermSheet(input, opts = {}) {
63179
+ const now = opts.now ?? Math.floor(Date.now() / 1e3);
63180
+ const siblings = opts.siblings ?? [];
63181
+ const base = {
63182
+ schemaVersion: TERM_SHEET_SCHEMA_VERSION,
63183
+ asOf: now,
63184
+ profileId: opts.profileId ?? DEFAULT_PROFILE_ID,
63185
+ marketUid: input.marketUid,
63186
+ lender: input.lender,
63187
+ chainId: input.chainId,
63188
+ supply: buildSupply(input, now, siblings),
63189
+ borrow: buildBorrow(input, now, siblings),
63190
+ utilization: buildUtilization(input),
63191
+ constraints: void 0
63192
+ };
63193
+ base.constraints = buildConstraints(
63194
+ input,
63195
+ siblings,
63196
+ base.borrow?.acceptedCollateral ?? base.supply?.backedBy
63197
+ );
63198
+ const merged = mergeDeep(base, opts.patch);
63199
+ for (const side of ["supply", "borrow"]) {
63200
+ if (merged[side] && !base[side]) merged[side] = void 0;
63201
+ }
63202
+ const withInfo = finalizeInfo(merged);
63203
+ withInfo.coverage = buildCoverage(withInfo);
63204
+ return withInfo;
63205
+ }
63206
+
63207
+ // src/terms/severity.ts
63208
+ var SEVERITY_ORDER = {
63209
+ critical: 0,
63210
+ warn: 1,
63211
+ info: 2
63212
+ };
63213
+ function rankFindings(findings) {
63214
+ return [...findings].sort(
63215
+ (a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity]
63216
+ );
63217
+ }
63218
+ function fmtSecs(secs) {
63219
+ if (secs >= 86400) {
63220
+ const d = secs / 86400;
63221
+ return `${d % 1 === 0 ? d : d.toFixed(1)} day${d === 1 ? "" : "s"}`;
63222
+ }
63223
+ if (secs >= 3600) {
63224
+ const h = secs / 3600;
63225
+ return `${h % 1 === 0 ? h : h.toFixed(1)} hour${h === 1 ? "" : "s"}`;
63226
+ }
63227
+ if (secs >= 60) {
63228
+ const m = Math.round(secs / 60);
63229
+ return `${m} minute${m === 1 ? "" : "s"}`;
63230
+ }
63231
+ return `${secs} second${secs === 1 ? "" : "s"}`;
63232
+ }
63233
+ function marketFindings(sheet) {
63234
+ const out = [];
63235
+ const { governance: g, oracle: o, utilization: u } = sheet;
63236
+ if (o?.band === "CRITICAL") {
63237
+ out.push({
63238
+ severity: "critical",
63239
+ id: "oracle-critical",
63240
+ side: "market",
63241
+ message: `The price oracle for this market scores CRITICAL for feed correctness${o.priceDescription ? ` (reports ${o.priceDescription})` : ""}.`
63242
+ });
63243
+ } else if (o?.band === "HIGH") {
63244
+ out.push({
63245
+ severity: "warn",
63246
+ id: "oracle-high",
63247
+ side: "market",
63248
+ message: "The price oracle for this market scores HIGH risk."
63249
+ });
63250
+ }
63251
+ if (o?.flags?.includes("wrong-asset")) {
63252
+ out.push({
63253
+ severity: "critical",
63254
+ id: "oracle-wrong-asset",
63255
+ side: "market",
63256
+ message: `The oracle does not price the intended asset${o.intendedPair ? ` (expected ${o.intendedPair})` : ""}.`
63257
+ });
63258
+ }
63259
+ if (o?.mutability?.mutable && !o.mutability.timelockSecs) {
63260
+ out.push({
63261
+ severity: "warn",
63262
+ id: "oracle-swappable",
63263
+ side: "market",
63264
+ message: "The price oracle can be replaced with no delay by its controller."
63265
+ });
63266
+ }
63267
+ if (g?.mutability === "governed") {
63268
+ if (g.controllerKind === "EOA") {
63269
+ out.push({
63270
+ severity: "critical",
63271
+ id: "governance-eoa",
63272
+ side: "market",
63273
+ message: "A single private key (an EOA, not a multisig or timelock) can change this market\u2019s parameters."
63274
+ });
63275
+ } else if (g.controllerKind === "TIMELOCK" && g.timelockUnknown) {
63276
+ out.push({
63277
+ severity: "info",
63278
+ id: "governance-timelock-unknown",
63279
+ side: "market",
63280
+ message: "Parameter changes go through a timelock, but its delay could not be read."
63281
+ });
63282
+ } else if (!g.timelockSecs) {
63283
+ out.push({
63284
+ severity: "warn",
63285
+ id: "governance-no-timelock",
63286
+ side: "market",
63287
+ message: `Parameters can be changed with no notice period${g.controllerKind ? ` by a ${g.controllerKind.toLowerCase()}` : ""}.`
63288
+ });
63289
+ }
63290
+ }
63291
+ if (u && u.utilization >= 0.98) {
63292
+ out.push({
63293
+ severity: "warn",
63294
+ id: "utilization-full",
63295
+ side: "market",
63296
+ message: `Utilization is ${(u.utilization * 100).toFixed(1)} % \u2014 withdrawals may not be possible until borrowers repay.`
63297
+ });
63298
+ }
63299
+ return out;
63300
+ }
63301
+ function supplyFindings(supply) {
63302
+ const out = [];
63303
+ if (supply.principal.risks.includes("first-loss")) {
63304
+ out.push({
63305
+ severity: "critical",
63306
+ id: "first-loss",
63307
+ side: "supply",
63308
+ message: "This is a junior/first-loss position \u2014 it absorbs losses before other depositors and can be marked down sharply."
63309
+ });
63310
+ }
63311
+ if (supply.principal.risks.includes("physical-delivery")) {
63312
+ out.push({
63313
+ severity: "critical",
63314
+ id: "physical-delivery",
63315
+ side: "supply",
63316
+ message: "On default you can be settled in collateral rather than the asset you lent."
63317
+ });
63318
+ }
63319
+ if (supply.counterparty.socializedLoss) {
63320
+ out.push({
63321
+ severity: "warn",
63322
+ id: "socialized-loss",
63323
+ side: "supply",
63324
+ message: "Bad debt is socialised across all lenders in this market, not borne by the defaulting borrower alone."
63325
+ });
63326
+ }
63327
+ if (supply.counterparty.solvency === "undercollateralized") {
63328
+ out.push({
63329
+ severity: "critical",
63330
+ id: "undercollateralized",
63331
+ side: "supply",
63332
+ message: "Borrowers here are not fully collateralised on-chain \u2014 repayment depends on off-chain credit."
63333
+ });
63334
+ }
63335
+ if (supply.counterparty.solvency === "nav-attested") {
63336
+ out.push({
63337
+ severity: "warn",
63338
+ id: "nav-attested",
63339
+ side: "supply",
63340
+ message: "The share price is published by an operator; there is no on-chain solvency invariant."
63341
+ });
63342
+ }
63343
+ if (supply.exit.priceRisk === "may-be-impossible") {
63344
+ out.push({
63345
+ severity: "critical",
63346
+ id: "exit-may-be-impossible",
63347
+ side: "supply",
63348
+ message: "Exiting early means selling on a market that can be empty \u2014 there may be no exit before maturity at any price."
63349
+ });
63350
+ } else if (supply.exit.priceRisk === "market-price") {
63351
+ out.push({
63352
+ severity: "warn",
63353
+ id: "exit-market-price",
63354
+ side: "supply",
63355
+ message: "Exiting early means selling at the prevailing market price, which can be below par."
63356
+ });
63357
+ }
63358
+ if (supply.exit.cooldownSecs) {
63359
+ out.push({
63360
+ severity: "warn",
63361
+ id: "exit-cooldown",
63362
+ side: "supply",
63363
+ message: `Withdrawals require a ${fmtSecs(supply.exit.cooldownSecs)} waiting period.`
63364
+ });
63365
+ } else if (supply.exit.settlement === "async" && supply.exit.mode !== "instant") {
63366
+ out.push({
63367
+ severity: "warn",
63368
+ id: "exit-async",
63369
+ side: "supply",
63370
+ message: "Withdrawals are not instant \u2014 they queue or need a request."
63371
+ });
63372
+ }
63373
+ const exitFee = supply.exit.fees.find(
63374
+ (f) => f.when === "exit" && f.value > 0 && !f.indicative
63375
+ );
63376
+ if (exitFee) {
63377
+ out.push({
63378
+ severity: "warn",
63379
+ id: "exit-fee",
63380
+ side: "supply",
63381
+ message: `Exiting costs ${exitFee.label.toLowerCase()}.`
63382
+ });
63383
+ }
63384
+ if (supply.exit.liquidity && supply.exit.liquidity.assets <= 0 && supply.exit.settlement === "sync") {
63385
+ out.push({
63386
+ severity: "warn",
63387
+ id: "no-liquidity",
63388
+ side: "supply",
63389
+ message: "There is no withdrawable liquidity right now \u2014 your deposit cannot be taken out until borrowers repay."
63390
+ });
63391
+ }
63392
+ if (supply.backedBy) {
63393
+ if ((supply.backedBy.worstRiskScore ?? 0) >= 4) {
63394
+ out.push({
63395
+ severity: "warn",
63396
+ id: "backing-quality",
63397
+ side: "supply",
63398
+ message: "Some collateral backing this market is low quality or thinly traded."
63399
+ });
63400
+ }
63401
+ if (supply.backedBy.worstOracleBand === "HIGH" || supply.backedBy.worstOracleBand === "CRITICAL") {
63402
+ out.push({
63403
+ severity: "warn",
63404
+ id: "backing-oracle",
63405
+ side: "supply",
63406
+ message: "Collateral backing this market is priced by an oracle flagged as risky."
63407
+ });
63408
+ }
63409
+ }
63410
+ const points = supply.rate.rewards?.filter((r) => r.indicative) ?? [];
63411
+ if (points.length > 0) {
63412
+ out.push({
63413
+ severity: "info",
63414
+ id: "points-rewards",
63415
+ side: "supply",
63416
+ message: "Part of the advertised return is a points program with no priceable value."
63417
+ });
63418
+ }
63419
+ if (!supply.availability.canOpen) {
63420
+ out.push({
63421
+ severity: "warn",
63422
+ id: "supply-closed",
63423
+ side: "supply",
63424
+ message: `Deposits are currently unavailable${supply.availability.blockedBy ? ` (${supply.availability.blockedBy.replace(/-/g, " ")})` : ""}.`
63425
+ });
63426
+ }
63427
+ return out;
63428
+ }
63429
+ function borrowFindings(borrow) {
63430
+ const out = [];
63431
+ const liq = borrow.liquidation;
63432
+ if (liq.seizure === "full-collateral") {
63433
+ out.push({
63434
+ severity: "critical",
63435
+ id: "full-collateral-seizure",
63436
+ side: "borrow",
63437
+ message: "On default a liquidator takes your ENTIRE collateral, not just the amount owed \u2014 at 50 % LTV that is roughly twice the borrowed value."
63438
+ });
63439
+ }
63440
+ if (liq.trigger === "time" || liq.trigger === "price-and-time") {
63441
+ out.push({
63442
+ severity: "critical",
63443
+ id: "time-liquidation",
63444
+ side: "borrow",
63445
+ message: `This loan can be liquidated for being LATE, regardless of how over-collateralised it is${borrow.maturity.graceSecs ? ` \u2014 the grace period is ${fmtSecs(borrow.maturity.graceSecs)}` : ""}. A healthy health factor does not protect you.`
63446
+ });
63447
+ }
63448
+ if (liq.redeemable || liq.trigger === "redemption") {
63449
+ out.push({
63450
+ severity: "critical",
63451
+ id: "redeemable",
63452
+ side: "borrow",
63453
+ message: "Your collateral can be redeemed at par while the position is perfectly healthy, if you carry the lowest interest rate on this branch."
63454
+ });
63455
+ }
63456
+ if (borrow.maturity.atMaturity === "physical-delivery") {
63457
+ out.push({
63458
+ severity: "critical",
63459
+ id: "physical-delivery",
63460
+ side: "borrow",
63461
+ message: "After maturity plus the liquidation window, unpaid collateral is delivered to lenders."
63462
+ });
63463
+ }
63464
+ if (borrow.maturity.atMaturity === "default-seizure") {
63465
+ out.push({
63466
+ severity: "critical",
63467
+ id: "default-seizure",
63468
+ side: "borrow",
63469
+ message: `Missing the payment window puts the loan in default and forfeits the collateral${borrow.maturity.graceSecs ? ` \u2014 the window is only ${fmtSecs(borrow.maturity.graceSecs)}` : ""}.`
63470
+ });
63471
+ }
63472
+ if (borrow.maturity.atMaturity === "liquidatable") {
63473
+ out.push({
63474
+ severity: "critical",
63475
+ id: "past-due-liquidatable",
63476
+ side: "borrow",
63477
+ message: "Once maturity passes the loan is in default and can be liquidated regardless of its health or LTV."
63478
+ });
63479
+ }
63480
+ const latePenalty = borrow.fees.find((f) => f.when === "late" && f.value > 0);
63481
+ if (latePenalty) {
63482
+ out.push({
63483
+ severity: "warn",
63484
+ id: "late-penalty",
63485
+ side: "borrow",
63486
+ message: `Repaying late accrues ${latePenalty.label.toLowerCase()}.`
63487
+ });
63488
+ }
63489
+ if (borrow.exit.earlyRepay === "penalty") {
63490
+ out.push({
63491
+ severity: "warn",
63492
+ id: "early-repay-penalty",
63493
+ side: "borrow",
63494
+ message: "Repaying before maturity costs a penalty."
63495
+ });
63496
+ }
63497
+ if (borrow.exit.earlyRepay === "not-allowed") {
63498
+ out.push({
63499
+ severity: "warn",
63500
+ id: "no-early-repay",
63501
+ side: "borrow",
63502
+ message: "This loan cannot be repaid before maturity."
63503
+ });
63504
+ }
63505
+ if (borrow.debtShape === "static-face" && borrow.exit.atMaturityCost === "face") {
63506
+ out.push({
63507
+ severity: "info",
63508
+ id: "static-debt",
63509
+ side: "borrow",
63510
+ message: "The amount owed is fixed at trade time and does not accrue \u2014 repaying early does not reduce it."
63511
+ });
63512
+ }
63513
+ if (borrow.exit.overRepayReverts) {
63514
+ out.push({
63515
+ severity: "warn",
63516
+ id: "over-repay-reverts",
63517
+ side: "borrow",
63518
+ message: "Sending more than the exact debt REVERTS \u2014 the repayment must be sized precisely."
63519
+ });
63520
+ }
63521
+ if (borrow.debtShape === "prepaid") {
63522
+ out.push({
63523
+ severity: "warn",
63524
+ id: "prepaid-interest",
63525
+ side: "borrow",
63526
+ message: "Interest is prepaid in a separate token; running out triggers a forced top-up at a punitive rate and freezes withdrawals."
63527
+ });
63528
+ }
63529
+ const origination = borrow.fees.find((f) => f.when === "entry" && f.value > 0);
63530
+ if (origination) {
63531
+ out.push({
63532
+ severity: "info",
63533
+ id: "origination-fee",
63534
+ side: "borrow",
63535
+ message: `An upfront ${origination.label.toLowerCase()} is charged at borrow time.`
63536
+ });
63537
+ }
63538
+ if (borrow.acceptedCollateral) {
63539
+ if (borrow.acceptedCollateral.worstOracleBand === "HIGH" || borrow.acceptedCollateral.worstOracleBand === "CRITICAL") {
63540
+ out.push({
63541
+ severity: "warn",
63542
+ id: "collateral-oracle",
63543
+ side: "borrow",
63544
+ message: "Collateral you can post here is priced by an oracle flagged as risky \u2014 a mispricing can liquidate you."
63545
+ });
63546
+ }
63547
+ }
63548
+ if (!borrow.availability.canOpen) {
63549
+ out.push({
63550
+ severity: "warn",
63551
+ id: "borrow-closed",
63552
+ side: "borrow",
63553
+ message: `Borrowing is currently unavailable${borrow.availability.blockedBy ? ` (${borrow.availability.blockedBy.replace(/-/g, " ")})` : ""}.`
63554
+ });
63555
+ }
63556
+ if (borrow.availability.requires?.includes("caller-allowlist")) {
63557
+ out.push({
63558
+ severity: "warn",
63559
+ id: "caller-allowlist",
63560
+ side: "borrow",
63561
+ message: "Only governance-approved contracts may borrow here, which blocks leverage and other routed flows."
63562
+ });
63563
+ }
63564
+ return out;
63565
+ }
63566
+ function findingsFor(sheet, side) {
63567
+ const sideFindings = side === "supply" ? sheet.supply ? supplyFindings(sheet.supply) : [] : sheet.borrow ? borrowFindings(sheet.borrow) : [];
63568
+ return rankFindings([...sideFindings, ...marketFindings(sheet)]);
63569
+ }
63570
+ function hasCritical(sheet, side) {
63571
+ return findingsFor(sheet, side).some((f) => f.severity === "critical");
63572
+ }
63573
+ var exactlyAdapter = {
63574
+ id: "exactly",
63575
+ matches: isExactly,
63576
+ profileId: () => "exactly.fixed@v1",
63577
+ build: (input) => ({
63578
+ supply: {
63579
+ // Fixed deposits have no late penalty and no expiry — an unclaimed
63580
+ // matured deposit simply stops earning.
63581
+ maturity: { atMaturity: "stops-earning" },
63582
+ exit: {
63583
+ mode: "market-sale",
63584
+ settlement: "sync",
63585
+ // `withdrawAtMaturity` before maturity pays face / (1 + curve rate),
63586
+ // so the cost is deterministic but worsens as rates rise.
63587
+ priceRisk: "haircut-formula",
63588
+ partialAllowed: true
63589
+ },
63590
+ counterparty: { kind: "pool", solvency: "overcollateralized" }
63591
+ },
63592
+ borrow: {
63593
+ debtShape: "static-face",
63594
+ exit: {
63595
+ earlyRepay: "discount",
63596
+ atMaturityCost: "face",
63597
+ lateBehaviour: "penalty-accrues",
63598
+ partialAllowed: true
63599
+ },
63600
+ maturity: { atMaturity: "penalty-accrues" },
63601
+ liquidation: {
63602
+ // Price-based, but coupled to time through the penalty above.
63603
+ trigger: "price-and-time",
63604
+ seizure: "proportional"
63605
+ },
63606
+ counterparty: { kind: "pool", solvency: "overcollateralized" }
63607
+ },
63608
+ constraints: {
63609
+ // Exactly reuses `loanId` as the MATURITY, stringified — not a loan
63610
+ // counter. This is the trap the field exists to defuse.
63611
+ positionModel: "loan-id",
63612
+ positionIdMeaning: "The unix maturity timestamp of the fixed pool, as a string."
63613
+ }
63614
+ })
63615
+ };
63616
+ var midnightAdapter = {
63617
+ id: "midnight",
63618
+ matches: isMidnight,
63619
+ profileId: () => "midnight.orderbook@v1",
63620
+ build: () => ({
63621
+ supply: {
63622
+ exit: {
63623
+ mode: "market-sale",
63624
+ settlement: "sync",
63625
+ // Selling units into the bid side — there may be no bid.
63626
+ priceRisk: "market-price",
63627
+ partialAllowed: true
63628
+ },
63629
+ counterparty: {
63630
+ kind: "orderbook",
63631
+ solvency: "overcollateralized",
63632
+ // Bad debt is socialised to lenders via `lossFactor`.
63633
+ socializedLoss: true
63634
+ },
63635
+ principal: { protected: false, risks: ["bad-debt"] },
63636
+ maturity: { atMaturity: "stops-earning" }
63637
+ },
63638
+ borrow: {
63639
+ debtShape: "static-face",
63640
+ exit: {
63641
+ // Face value, 1:1, neither fee nor rebate — so paying early is not a
63642
+ // saving, it just gives up the time value.
63643
+ earlyRepay: "free",
63644
+ atMaturityCost: "face",
63645
+ lateBehaviour: "liquidatable",
63646
+ partialAllowed: true,
63647
+ overRepayReverts: true
63648
+ },
63649
+ maturity: { atMaturity: "liquidatable" },
63650
+ liquidation: { trigger: "price-and-time", seizure: "proportional" },
63651
+ counterparty: { kind: "orderbook", solvency: "overcollateralized" }
63652
+ },
63653
+ constraints: { positionModel: "account" }
63654
+ })
63655
+ };
63656
+ var termAdapter = {
63657
+ id: "term",
63658
+ matches: isTerm,
63659
+ profileId: () => "term.auction@v1",
63660
+ build: (input) => {
63661
+ const auction = input.fixedTerm?.auction;
63662
+ return {
63663
+ supply: {
63664
+ exit: {
63665
+ mode: "market-sale",
63666
+ settlement: "sync",
63667
+ priceRisk: "market-price",
63668
+ partialAllowed: true
63669
+ },
63670
+ counterparty: { kind: "auction", solvency: "overcollateralized" },
63671
+ availability: auction ? { canOpen: auction.canLend === true, window: auction } : void 0,
63672
+ maturity: { atMaturity: "stops-earning" }
63673
+ },
63674
+ borrow: {
63675
+ debtShape: "static-face",
63676
+ exit: {
63677
+ // No rebate — on a debt that never accrues, early repayment is
63678
+ // strictly worse than holding to maturity.
63679
+ earlyRepay: "free",
63680
+ atMaturityCost: "face",
63681
+ lateBehaviour: "liquidatable",
63682
+ partialAllowed: true
63683
+ },
63684
+ liquidation: { trigger: "price", seizure: "proportional" },
63685
+ counterparty: { kind: "auction", solvency: "overcollateralized" },
63686
+ availability: auction ? {
63687
+ canOpen: auction.canBorrow === true,
63688
+ blockedBy: auction.canBorrow ? void 0 : "auction-closed",
63689
+ window: auction,
63690
+ minSize: auction.minBorrowAmount
63691
+ } : void 0
63692
+ },
63693
+ constraints: { positionModel: "account" }
63694
+ };
63695
+ }
63696
+ };
63697
+ var termMaxAdapter = {
63698
+ id: "termmax",
63699
+ matches: isTermMax,
63700
+ profileId: () => "termmax.fixed@v1",
63701
+ build: () => ({
63702
+ supply: {
63703
+ exit: {
63704
+ mode: "market-sale",
63705
+ settlement: "sync",
63706
+ // Selling FT needs a maker order on the other side, which may not exist.
63707
+ priceRisk: "may-be-impossible",
63708
+ partialAllowed: true
63709
+ },
63710
+ counterparty: {
63711
+ kind: "orderbook",
63712
+ solvency: "overcollateralized",
63713
+ socializedLoss: true
63714
+ },
63715
+ principal: {
63716
+ protected: false,
63717
+ risks: ["physical-delivery", "bad-debt"]
63718
+ },
63719
+ maturity: { atMaturity: "physical-delivery" }
63720
+ },
63721
+ borrow: {
63722
+ // A raw uint128 face value with zero accrual.
63723
+ debtShape: "static-face",
63724
+ exit: {
63725
+ earlyRepay: "free",
63726
+ atMaturityCost: "face",
63727
+ lateBehaviour: "liquidatable",
63728
+ partialAllowed: true
63729
+ },
63730
+ maturity: { atMaturity: "physical-delivery", graceSecs: 7200 },
63731
+ liquidation: { trigger: "price-and-time", seizure: "proportional" },
63732
+ counterparty: { kind: "orderbook", solvency: "overcollateralized" }
63733
+ },
63734
+ constraints: {
63735
+ positionModel: "nft",
63736
+ positionIdMeaning: "The GT (loan) ERC-721 token id."
63737
+ }
63738
+ })
63739
+ };
63740
+ var tellerAdapter = {
63741
+ id: "teller",
63742
+ matches: isTeller,
63743
+ profileId: () => "teller.pool@v1",
63744
+ build: (input) => {
63745
+ const teller = input.market?.teller ?? {};
63746
+ const graceSecs = typeof teller.paymentDefaultDuration === "number" ? teller.paymentDefaultDuration : void 0;
63747
+ const attested = teller.requiresBorrowerAttestation === true;
63748
+ return {
63749
+ // Not a coverage hole: Teller genuinely has no price input.
63750
+ oracle: { kind: "none" },
63751
+ supply: {
63752
+ exit: { mode: "queued", settlement: "async", partialAllowed: true },
63753
+ counterparty: { kind: "pool", solvency: "undercollateralized" },
63754
+ principal: { protected: false, risks: ["bad-debt"] }
63755
+ },
63756
+ borrow: {
63757
+ debtShape: "accruing",
63758
+ exit: {
63759
+ // Free, pro-rata early repayment — but only a FULL close is
63760
+ // supported; there is no partial collateral withdrawal.
63761
+ earlyRepay: "free",
63762
+ atMaturityCost: "accrued",
63763
+ lateBehaviour: "default-seizure",
63764
+ partialAllowed: false
63765
+ },
63766
+ maturity: { atMaturity: "default-seizure", graceSecs },
63767
+ liquidation: {
63768
+ trigger: "time",
63769
+ seizure: "full-collateral",
63770
+ gracePeriodSecs: graceSecs
63771
+ },
63772
+ counterparty: { kind: "pool", solvency: "undercollateralized" },
63773
+ availability: attested ? { gating: "attestation", requires: ["token-approval"] } : void 0
63774
+ },
63775
+ constraints: {
63776
+ positionModel: "loan-id",
63777
+ positionIdMeaning: "The Teller bidId of this loan."
63778
+ }
63779
+ };
63780
+ }
63781
+ };
63782
+ var listaBrokerAdapter = {
63783
+ id: "lista-broker",
63784
+ matches: isListaDao,
63785
+ profileId: (input) => input.variableBorrowDisabled && input.terms?.length ? "lista.broker@v1" : "pool.variable@v1",
63786
+ build: (input) => {
63787
+ const brokered = input.variableBorrowDisabled === true && !!input.terms?.length;
63788
+ if (!brokered) return {};
63789
+ return {
63790
+ borrow: {
63791
+ debtShape: "accruing",
63792
+ exit: {
63793
+ earlyRepay: "penalty",
63794
+ atMaturityCost: "accrued",
63795
+ // A keeper refinances an unpaid loan into the variable position
63796
+ // rather than liquidating it.
63797
+ lateBehaviour: "refinanced",
63798
+ partialAllowed: true
63799
+ },
63800
+ maturity: { atMaturity: "refinanced" },
63801
+ liquidation: { trigger: "price", seizure: "proportional" },
63802
+ counterparty: {
63803
+ kind: "broker",
63804
+ address: input.market?.broker ?? void 0,
63805
+ solvency: "overcollateralized"
63806
+ }
63807
+ },
63808
+ constraints: {
63809
+ positionModel: "loan-id",
63810
+ positionIdMeaning: "The broker posId; the dynamic (flexible) loan uses the uint128 max sentinel."
63811
+ }
63812
+ };
63813
+ }
63814
+ };
63815
+ var liquityAdapter = {
63816
+ id: "liquity",
63817
+ matches: isLiquityFamily,
63818
+ profileId: () => "liquity.cdp@v1",
63819
+ build: (input) => {
63820
+ const liquity = input.market?.liquity ?? {};
63821
+ const minDebt = liquity.minDebt != null ? String(liquity.minDebt) : void 0;
63822
+ const minApr = typeof liquity.minAnnualInterestRate === "string" || typeof liquity.minAnnualInterestRate === "number" ? Number(liquity.minAnnualInterestRate) / 1e18 * 100 : void 0;
63823
+ const maxApr = typeof liquity.maxAnnualInterestRate === "string" || typeof liquity.maxAnnualInterestRate === "number" ? Number(liquity.maxAnnualInterestRate) / 1e18 * 100 : void 0;
63824
+ return {
63825
+ supply: {
63826
+ // The Stability Pool is an earn position, never collateral.
63827
+ role: "yield",
63828
+ exit: { mode: "instant", settlement: "sync", partialAllowed: true },
63829
+ counterparty: { kind: "cdp", solvency: "overcollateralized" },
63830
+ principal: { protected: false, risks: ["bad-debt"] }
63831
+ },
63832
+ borrow: {
63833
+ rate: {
63834
+ kind: "user-set",
63835
+ isLocked: false,
63836
+ source: "borrower",
63837
+ minApr,
63838
+ maxApr
63839
+ },
63840
+ debtShape: "accruing",
63841
+ exit: {
63842
+ earlyRepay: "free",
63843
+ atMaturityCost: "accrued",
63844
+ lateBehaviour: "none",
63845
+ partialAllowed: true,
63846
+ minDebt
63847
+ },
63848
+ liquidation: {
63849
+ trigger: "price",
63850
+ seizure: "proportional",
63851
+ // The field that makes redemption visible.
63852
+ redeemable: true
63853
+ },
63854
+ counterparty: { kind: "cdp", solvency: "overcollateralized" }
63855
+ },
63856
+ constraints: {
63857
+ positionModel: "cdp-id",
63858
+ positionIdMeaning: "The trove id (a keccak of owner + branch + index)."
63859
+ }
63860
+ };
63861
+ }
63862
+ };
63863
+ var riverAdapter = {
63864
+ id: "river",
63865
+ matches: isRiver,
63866
+ profileId: () => "river.cdp@v1",
63867
+ build: () => ({
63868
+ supply: {
63869
+ role: "yield",
63870
+ exit: { mode: "instant", settlement: "sync", partialAllowed: true },
63871
+ counterparty: { kind: "cdp", solvency: "overcollateralized" },
63872
+ principal: { protected: false, risks: ["bad-debt"] }
63873
+ },
63874
+ borrow: {
63875
+ rate: { kind: "zero-interest", isLocked: true, compounding: "none" },
63876
+ debtShape: "accruing",
63877
+ exit: {
63878
+ earlyRepay: "free",
63879
+ atMaturityCost: "accrued",
63880
+ lateBehaviour: "none",
63881
+ partialAllowed: true
63882
+ },
63883
+ liquidation: {
63884
+ trigger: "price",
63885
+ seizure: "proportional",
63886
+ redeemable: true
63887
+ },
63888
+ counterparty: { kind: "cdp", solvency: "overcollateralized" }
63889
+ },
63890
+ constraints: {
63891
+ // River troves are ADDRESS-keyed — there is no id to discover.
63892
+ positionModel: "account",
63893
+ positionIdMeaning: "Troves are keyed by owner address; there is no id."
63894
+ }
63895
+ })
63896
+ };
63897
+ var inverseAdapter = {
63898
+ id: "inverse",
63899
+ matches: isInverse,
63900
+ profileId: () => "inverse.firm@v1",
63901
+ build: () => ({
63902
+ borrow: {
63903
+ rate: { kind: "prepaid", isLocked: false, source: "oracle" },
63904
+ debtShape: "prepaid",
63905
+ exit: {
63906
+ earlyRepay: "free",
63907
+ atMaturityCost: "accrued",
63908
+ lateBehaviour: "none",
63909
+ partialAllowed: true
63910
+ },
63911
+ liquidation: { trigger: "price", seizure: "proportional" },
63912
+ counterparty: { kind: "cdp", solvency: "overcollateralized" },
63913
+ availability: {
63914
+ // Every CONTRACT caller must be allowlisted by a 48h governance vote,
63915
+ // which is what blocks leverage and other routed flows.
63916
+ requires: ["token-approval", "caller-allowlist"]
63917
+ }
63918
+ },
63919
+ constraints: {
63920
+ positionModel: "escrow",
63921
+ positionIdMeaning: "A per-user CREATE2 escrow address, computed rather than discovered."
63922
+ }
63923
+ })
63924
+ };
63925
+ var usddAdapter = {
63926
+ id: "usdd",
63927
+ matches: isUsdd,
63928
+ profileId: () => "usdd.cdp@v1",
63929
+ build: () => ({
63930
+ borrow: {
63931
+ rate: { kind: "variable-managed", source: "governance", isLocked: false },
63932
+ debtShape: "accruing",
63933
+ exit: {
63934
+ earlyRepay: "free",
63935
+ atMaturityCost: "accrued",
63936
+ lateBehaviour: "none",
63937
+ partialAllowed: true
63938
+ },
63939
+ liquidation: { trigger: "price", seizure: "proportional" },
63940
+ counterparty: { kind: "cdp", solvency: "overcollateralized" }
63941
+ },
63942
+ constraints: {
63943
+ positionModel: "cdp-id",
63944
+ positionIdMeaning: "The CDP id issued by the manager for this ilk."
63945
+ }
63946
+ })
63947
+ };
63948
+ var compoundV3Adapter = {
63949
+ id: "compound-v3",
63950
+ matches: isCompoundV3Type,
63951
+ profileId: () => "pool.variable@v1",
63952
+ build: (input) => {
63953
+ const isBase = (input.totalDebt ?? 0) > 0 || input.borrowingEnabled === true;
63954
+ if (isBase) return {};
63955
+ return {
63956
+ supply: {
63957
+ role: "collateral",
63958
+ rate: { kind: "none", apr: 0, aprTotal: 0, isLocked: false },
63959
+ // Collateral earns nothing in Comet and cannot be lent out.
63960
+ exit: { mode: "instant", settlement: "sync", partialAllowed: true }
63961
+ },
63962
+ // Explicitly no borrow side on a collateral row.
63963
+ borrow: void 0
63964
+ };
63965
+ }
63966
+ };
63967
+ var fluidAdapter = {
63968
+ id: "fluid",
63969
+ matches: isFluid,
63970
+ profileId: () => "pool.variable@v1",
63971
+ build: (input) => ({
63972
+ // Only claim the liquidity-layer basis when the layer totals are actually
63973
+ // present. The origin does not select `irmTotal*` today, and asserting a
63974
+ // basis we cannot verify is worse than reporting the plain market ratio —
63975
+ // the validator rejects it for exactly that reason.
63976
+ utilization: input.irmTotalDeposits != null && input.irmTotalDebt != null ? { basis: "liquidity-layer", lockupRatio: input.lockupRatio } : { lockupRatio: input.lockupRatio },
63977
+ constraints: {
63978
+ positionModel: "nft",
63979
+ positionIdMeaning: "The Fluid vault position NFT id."
63980
+ }
63981
+ })
63982
+ };
63983
+ var gearboxAdapter = {
63984
+ id: "gearbox",
63985
+ matches: isGearboxV3,
63986
+ profileId: () => "pool.variable@v1",
63987
+ build: () => ({
63988
+ utilization: { basis: "pool" },
63989
+ constraints: {
63990
+ positionModel: "sub-account",
63991
+ positionIdMeaning: "The credit-account address opened for this user."
63992
+ }
63993
+ })
63994
+ };
63995
+
63996
+ // src/terms/adapters/index.ts
63997
+ var TERM_ADAPTERS = [
63998
+ exactlyAdapter,
63999
+ midnightAdapter,
64000
+ termAdapter,
64001
+ termMaxAdapter,
64002
+ tellerAdapter,
64003
+ listaBrokerAdapter,
64004
+ liquityAdapter,
64005
+ riverAdapter,
64006
+ inverseAdapter,
64007
+ usddAdapter,
64008
+ compoundV3Adapter,
64009
+ fluidAdapter,
64010
+ gearboxAdapter
64011
+ ];
64012
+ function resolveAdapter(lender) {
64013
+ return TERM_ADAPTERS.find((a) => a.matches(lender));
64014
+ }
64015
+
64016
+ // src/terms/stamp.ts
64017
+ function buildTermSheetsForGroup(rows, ctx = {}, opts = {}) {
64018
+ const inputs = rows.map((r) => toTermSheetInput(r, ctx));
64019
+ const out = /* @__PURE__ */ new Map();
64020
+ const index = opts.enrich === false ? void 0 : enrichmentIndexFromRows(rows);
64021
+ for (const input of inputs) {
64022
+ if (!input.marketUid) continue;
64023
+ const adapter = resolveAdapter(input.lender);
64024
+ const sheet = buildTermSheet(input, {
64025
+ now: opts.now,
64026
+ siblings: inputs,
64027
+ patch: adapter?.build(input),
64028
+ profileId: adapter?.profileId(input) ?? DEFAULT_PROFILE_ID
64029
+ });
64030
+ if (index) enrichTermSheet(sheet, index);
64031
+ if (opts.withImplications !== false) attachImplications(sheet);
64032
+ out.set(input.marketUid, sheet);
64033
+ }
64034
+ return out;
64035
+ }
64036
+ function attachImplications(sheet) {
64037
+ if (sheet.supply) {
64038
+ const found = findingsFor(sheet, "supply").filter((f) => f.severity !== "info").map((f) => f.message);
64039
+ if (found.length) sheet.supply.info.implications = found;
64040
+ }
64041
+ if (sheet.borrow) {
64042
+ const found = findingsFor(sheet, "borrow").filter((f) => f.severity !== "info").map((f) => f.message);
64043
+ if (found.length) sheet.borrow.info.implications = found;
64044
+ }
64045
+ return sheet;
64046
+ }
64047
+ var BAND_ORDER = ["LOW", "MEDIUM", "HIGH", "CRITICAL"];
64048
+ function enrichmentIndexFromRows(rows) {
64049
+ const oracleByMarketUid = /* @__PURE__ */ new Map();
64050
+ const governanceByMarketUid = /* @__PURE__ */ new Map();
64051
+ const assetRisk = {};
64052
+ for (const row of rows) {
64053
+ const uid = String(row?.marketUid ?? "");
64054
+ if (!uid) continue;
64055
+ const feeds = Array.isArray(row?.oracleInfo?.feeds) ? row.oracleInfo.feeds : [];
64056
+ if (feeds.length > 0) {
64057
+ const primary = feeds.reduce(
64058
+ (worst, f) => BAND_ORDER.indexOf(String(f?.band)) > BAND_ORDER.indexOf(String(worst?.band)) ? f : worst
64059
+ );
64060
+ oracleByMarketUid.set(uid, {
64061
+ marketUid: uid,
64062
+ oracle: primary?.oracle ?? void 0,
64063
+ provider: primary?.provider ?? void 0,
64064
+ priceDescription: primary?.priceDescription ?? void 0,
64065
+ intendedPair: primary?.intendedPair ?? void 0,
64066
+ correctOracle: primary?.correctOracle ?? void 0,
64067
+ denominatorMatch: primary?.denominatorMatch ?? void 0,
64068
+ fixedRate: primary?.fixedRate ?? void 0,
64069
+ score: primary?.score ?? row?.oracleInfo?.worstScore,
64070
+ band: primary?.band ?? row?.oracleInfo?.worstBand,
64071
+ flags: Array.isArray(primary?.flags) ? primary.flags : void 0,
64072
+ components: feeds.length > 1 ? feeds.map((f) => String(f?.oracle ?? "")).filter(Boolean) : void 0
64073
+ });
64074
+ }
64075
+ const breakdown = Array.isArray(row?.risk?.breakdown) ? row.risk.breakdown : [];
64076
+ const gov = breakdown.find((b) => b?.category === "governance");
64077
+ if (gov) {
64078
+ governanceByMarketUid.set(uid, {
64079
+ marketUid: uid,
64080
+ tier: gov.tier ?? void 0,
64081
+ score: gov.score ?? void 0,
64082
+ ownerKind: gov.ownerKind ?? void 0,
64083
+ signerThreshold: gov.signerThreshold ?? null,
64084
+ signerCount: gov.signerCount ?? null,
64085
+ mode: gov.mode ?? void 0,
64086
+ // The screen resolves the governance ROOT but does not carry the
64087
+ // timelock delay through to this shape, so the notice period stays
64088
+ // genuinely unknown — see `coverage.pending`.
64089
+ delaySeconds: gov.delaySeconds ?? null
64090
+ });
64091
+ }
64092
+ const token = breakdown.find((b) => b?.category === "token");
64093
+ const chainId = String(row?.chainId ?? uid.split(":")[1] ?? "");
64094
+ const address = String(
64095
+ row?.asset?.address ?? row?.underlying ?? uid.split(":")[2] ?? ""
64096
+ ).toLowerCase();
64097
+ if (token?.score != null && chainId && address) {
64098
+ assetRisk[chainId] ??= {};
64099
+ assetRisk[chainId][address] = {
64100
+ riskScore: Number(token.score),
64101
+ source: token.label ?? void 0
64102
+ };
64103
+ }
64104
+ }
64105
+ return { oracleByMarketUid, governanceByMarketUid, assetRisk };
64106
+ }
64107
+ function toDigest(sheet) {
64108
+ const strip = (e) => e ? (({ items, ...rest }) => rest)(e) : void 0;
64109
+ return {
64110
+ schemaVersion: sheet.schemaVersion,
64111
+ profileId: sheet.profileId,
64112
+ marketUid: sheet.marketUid,
64113
+ supply: sheet.supply ? {
64114
+ rateKind: sheet.supply.rate.kind,
64115
+ aprTotal: sheet.supply.rate.aprTotal,
64116
+ maturityKind: sheet.supply.maturity.kind,
64117
+ maturity: sheet.supply.maturity.maturity,
64118
+ exitMode: sheet.supply.exit.mode,
64119
+ settlement: sheet.supply.exit.settlement,
64120
+ canOpen: sheet.supply.availability.canOpen,
64121
+ headline: sheet.supply.info.headline,
64122
+ tags: sheet.supply.info.tags,
64123
+ backedBy: strip(sheet.supply.backedBy)
64124
+ } : void 0,
64125
+ borrow: sheet.borrow ? {
64126
+ rateKind: sheet.borrow.rate.kind,
64127
+ apr: sheet.borrow.rate.apr,
64128
+ maturityKind: sheet.borrow.maturity.kind,
64129
+ maturity: sheet.borrow.maturity.maturity,
64130
+ debtShape: sheet.borrow.debtShape,
64131
+ earlyRepay: sheet.borrow.exit.earlyRepay,
64132
+ liquidationTrigger: sheet.borrow.liquidation.trigger,
64133
+ canOpen: sheet.borrow.availability.canOpen,
64134
+ headline: sheet.borrow.info.headline,
64135
+ tags: sheet.borrow.info.tags,
64136
+ acceptedCollateral: strip(sheet.borrow.acceptedCollateral)
64137
+ } : void 0,
64138
+ oracle: sheet.oracle ? {
64139
+ kind: sheet.oracle.kind,
64140
+ address: sheet.oracle.address,
64141
+ provider: sheet.oracle.provider,
64142
+ band: sheet.oracle.band
64143
+ } : void 0,
64144
+ governance: sheet.governance ? {
64145
+ mutability: sheet.governance.mutability,
64146
+ controllerKind: sheet.governance.controllerKind,
64147
+ timelockSecs: sheet.governance.timelockSecs,
64148
+ tier: sheet.governance.tier
64149
+ } : void 0,
64150
+ utilization: sheet.utilization?.utilization
64151
+ };
64152
+ }
64153
+ function enrichTermSheet(sheet, index) {
64154
+ const uid = sheet.marketUid;
64155
+ if (!uid) return sheet;
64156
+ const oracleRow = index.oracleByMarketUid?.get(uid);
64157
+ if (oracleRow?.oracle && sheet.oracle?.kind !== "none") {
64158
+ sheet.oracle = {
64159
+ ...sheet.oracle,
64160
+ kind: "price-feed",
64161
+ address: oracleRow.oracle.toLowerCase(),
64162
+ provider: oracleRow.provider,
64163
+ priceDescription: oracleRow.priceDescription,
64164
+ intendedPair: oracleRow.intendedPair,
64165
+ correctAsset: oracleRow.correctOracle ?? void 0,
64166
+ correctNumeraire: oracleRow.denominatorMatch ?? void 0,
64167
+ fixedRate: oracleRow.fixedRate,
64168
+ score: oracleRow.score,
64169
+ band: oracleRow.band,
64170
+ flags: oracleRow.flags,
64171
+ components: oracleRow.components
64172
+ };
64173
+ }
64174
+ const govRow = index.governanceByMarketUid?.get(uid);
64175
+ if (govRow) {
64176
+ const immutable = govRow.mode === "immutable";
64177
+ sheet.governance = {
64178
+ ...sheet.governance,
64179
+ mutability: immutable ? "immutable" : "governed",
64180
+ controllerKind: govRow.ownerKind,
64181
+ safe: govRow.signerThreshold != null && govRow.signerCount != null ? { threshold: govRow.signerThreshold, owners: govRow.signerCount } : void 0,
64182
+ // Only meaningful on a TIMELOCK root — see the validator rule.
64183
+ timelockSecs: govRow.ownerKind === "TIMELOCK" && govRow.delaySeconds != null ? govRow.delaySeconds : void 0,
64184
+ timelockSource: govRow.delaySeconds != null ? "screened" : void 0,
64185
+ // Three states, not two. A TIMELOCK root whose delay we could not read
64186
+ // is NOT the same as "no notice period" — claiming the latter would be
64187
+ // a false alarm on the safest governance shape we screen for.
64188
+ timelockUnknown: govRow.ownerKind === "TIMELOCK" && govRow.delaySeconds == null ? true : void 0,
64189
+ tier: govRow.tier,
64190
+ score: govRow.score
64191
+ };
64192
+ if (immutable) {
64193
+ sheet.governance.timelockSecs = void 0;
64194
+ sheet.governance.powers = void 0;
64195
+ }
64196
+ }
64197
+ const quality = index.assetRisk;
64198
+ for (const exp of [
64199
+ sheet.supply?.backedBy,
64200
+ sheet.borrow?.acceptedCollateral
64201
+ ]) {
64202
+ if (!exp) continue;
64203
+ for (const item of exp.items) {
64204
+ const chain = item.asset.chainId;
64205
+ const addr2 = item.asset.address;
64206
+ const q = quality?.[chain]?.[addr2];
64207
+ if (q) {
64208
+ item.quality = {
64209
+ riskScore: q.riskScore,
64210
+ source: q.source,
64211
+ liquidityUsd: q.liquidityUsd,
64212
+ governanceScore: q.governanceScore,
64213
+ governanceLevel: q.governanceLevel,
64214
+ upgradeable: q.upgradeable,
64215
+ canPause: q.canPause,
64216
+ adminKind: q.adminKind
64217
+ };
64218
+ }
64219
+ const itemOracle = item.marketUid ? index.oracleByMarketUid?.get(item.marketUid) : void 0;
64220
+ if (itemOracle?.oracle) {
64221
+ item.oracle = {
64222
+ kind: "price-feed",
64223
+ address: itemOracle.oracle.toLowerCase(),
64224
+ provider: itemOracle.provider,
64225
+ band: itemOracle.band,
64226
+ score: itemOracle.score,
64227
+ flags: itemOracle.flags
64228
+ };
64229
+ }
64230
+ }
64231
+ const scores = exp.items.map((i) => i.quality?.riskScore).filter((n) => n != null);
64232
+ if (scores.length) exp.worstRiskScore = Math.max(...scores);
64233
+ const BAND_ORDER2 = ["LOW", "MEDIUM", "HIGH", "CRITICAL"];
64234
+ const bands = exp.items.map((i) => i.oracle?.band).filter((b) => !!b);
64235
+ if (bands.length) {
64236
+ exp.worstOracleBand = bands.reduce(
64237
+ (worst, b) => BAND_ORDER2.indexOf(b) > BAND_ORDER2.indexOf(worst) ? b : worst
64238
+ );
64239
+ }
64240
+ }
64241
+ if (sheet.coverage) {
64242
+ const present = new Set(sheet.coverage.present);
64243
+ const pending = { ...sheet.coverage.pending ?? {} };
64244
+ if (sheet.oracle) {
64245
+ present.add("oracle");
64246
+ delete pending.oracle;
64247
+ }
64248
+ if (sheet.governance) {
64249
+ present.add("governance");
64250
+ delete pending.governance;
64251
+ if (sheet.governance.timelockUnknown)
64252
+ pending["governance.timelockSecs"] = "delay-not-carried-by-screen";
64253
+ }
64254
+ sheet.coverage = {
64255
+ ...sheet.coverage,
64256
+ present: [...present],
64257
+ pending: Object.keys(pending).length ? pending : void 0
64258
+ };
64259
+ }
64260
+ finalizeInfo(sheet);
64261
+ attachImplications(sheet);
64262
+ return sheet;
64263
+ }
64264
+
64265
+ // src/terms/validate.ts
64266
+ var approx = (a, b, tol = 1e-6) => Math.abs(a - b) <= tol;
64267
+ function validateTermSheet(sheet) {
64268
+ const v = [];
64269
+ const uid = sheet.marketUid;
64270
+ const fail = (rule, message) => v.push({ rule, message, marketUid: uid });
64271
+ if (sheet.schemaVersion !== TERM_SHEET_SCHEMA_VERSION)
64272
+ fail("schema-version", `unexpected schemaVersion ${sheet.schemaVersion}`);
64273
+ if (!sheet.profileId) fail("profile-id", "missing profileId");
64274
+ for (const side of ["supply", "borrow"]) {
64275
+ const s = sheet[side];
64276
+ if (!s) continue;
64277
+ const c = s.rate.components;
64278
+ const expected = (c.base ?? 0) + (c.rewards ?? 0) + (c.intrinsic ?? 0);
64279
+ if (!approx(s.rate.aprTotal, expected, 1e-6))
64280
+ fail(
64281
+ "apr-total",
64282
+ `${side}.rate.aprTotal ${s.rate.aprTotal} != base+rewards+intrinsic ${expected}`
64283
+ );
64284
+ const priceable = (s.rate.rewards ?? []).filter((r) => !r.indicative).reduce((sum, r) => sum + r.apr, 0);
64285
+ if (s.rate.rewards && !approx(c.rewards ?? 0, priceable, 1e-6))
64286
+ fail(
64287
+ "rewards-priceable",
64288
+ `${side}.rate.components.rewards ${c.rewards} != sum of non-indicative rewards ${priceable}`
64289
+ );
64290
+ if (s.rate.basis !== "apr-nominal")
64291
+ fail("rate-basis", `${side}.rate.basis must be apr-nominal`);
64292
+ if (s.maturity.kind === "fixed-date" && s.maturity.maturity == null)
64293
+ fail(
64294
+ "maturity-missing",
64295
+ `${side}.maturity.kind is fixed-date with no maturity`
64296
+ );
64297
+ if ((s.rate.kind === "fixed-term" || s.rate.kind === "fixed-open") && s.rate.kind === "fixed-term" && s.maturity.kind === "perpetual")
64298
+ fail(
64299
+ "fixed-term-perpetual",
64300
+ `${side} is fixed-term but maturity.kind is perpetual`
64301
+ );
64302
+ if (s.availability.canOpen === false && !s.availability.blockedBy)
64303
+ fail(
64304
+ "blocked-reason",
64305
+ `${side}.availability.canOpen is false with no blockedBy`
64306
+ );
64307
+ if (!s.info.headline) fail("headline", `${side}.info.headline is empty`);
64308
+ if (!Array.isArray(s.info.tags))
64309
+ fail("tags", `${side}.info.tags is not an array`);
64310
+ }
64311
+ if (sheet.supply) {
64312
+ const e = sheet.supply.exit;
64313
+ if (e.mode === "instant") {
64314
+ if (e.settlement !== "sync")
64315
+ fail("instant-sync", "exit.mode instant must be settlement sync");
64316
+ if (e.cooldownSecs)
64317
+ fail("instant-cooldown", "exit.mode instant must not carry a cooldown");
64318
+ }
64319
+ }
64320
+ if (sheet.borrow) {
64321
+ const l = sheet.borrow.liquidation;
64322
+ if (l.seizure === "full-collateral") {
64323
+ const impl = sheet.borrow.info.implications ?? [];
64324
+ const tags = sheet.borrow.info.tags ?? [];
64325
+ if (!tags.includes("full-collateral-seizure") && impl.length === 0)
64326
+ fail(
64327
+ "full-seizure-warning",
64328
+ "full-collateral seizure must surface a tag or an implication"
64329
+ );
64330
+ }
64331
+ if (l.penalty < 0 || l.penalty > 1)
64332
+ fail("penalty-range", `liquidation.penalty ${l.penalty} outside 0..1`);
64333
+ if (l.closeFactor <= 0 || l.closeFactor > 1)
64334
+ fail(
64335
+ "close-factor-range",
64336
+ `liquidation.closeFactor ${l.closeFactor} outside 0..1`
64337
+ );
64338
+ }
64339
+ for (const [label, exp] of [
64340
+ ["supply.backedBy", sheet.supply?.backedBy],
64341
+ ["borrow.acceptedCollateral", sheet.borrow?.acceptedCollateral]
64342
+ ]) {
64343
+ if (!exp) continue;
64344
+ if (exp.count !== exp.items.length && exp.items.length > 0)
64345
+ fail(
64346
+ "exposure-count",
64347
+ `${label}.count ${exp.count} != items ${exp.items.length}`
64348
+ );
64349
+ if (exp.weightBasis === "unweighted") {
64350
+ if (exp.items.some((i) => i.weightPct != null))
64351
+ fail(
64352
+ "unweighted-has-weight",
64353
+ `${label} is unweighted but an item carries weightPct`
64354
+ );
64355
+ if (exp.topWeightPct != null)
64356
+ fail(
64357
+ "unweighted-top-weight",
64358
+ `${label} is unweighted but has topWeightPct`
64359
+ );
64360
+ } else if (exp.items.length > 0) {
64361
+ const sum = exp.items.reduce((s, i) => s + (i.weightPct ?? 0), 0);
64362
+ if (Math.abs(sum - 100) > 0.5)
64363
+ fail(
64364
+ "weight-sum",
64365
+ `${label} weights sum to ${sum.toFixed(2)}, expected ~100`
64366
+ );
64367
+ }
64368
+ if (exp.items.length > 0) {
64369
+ const scores = exp.items.map((i) => i.quality?.riskScore).filter((n) => n != null);
64370
+ if (scores.length > 0 && exp.worstRiskScore != null) {
64371
+ const worst = Math.max(...scores);
64372
+ if (exp.worstRiskScore !== worst)
64373
+ fail(
64374
+ "exposure-worst-score",
64375
+ `${label}.worstRiskScore ${exp.worstRiskScore} != actual worst ${worst}`
64376
+ );
64377
+ }
64378
+ }
64379
+ }
64380
+ const u = sheet.utilization;
64381
+ if (u) {
64382
+ if (u.utilization < 0 || u.utilization > 1)
64383
+ fail("utilization-range", `utilization ${u.utilization} outside 0..1`);
64384
+ if (u.basis !== "market") {
64385
+ if (u.irmTotalDeposits == null || u.irmTotalDebt == null)
64386
+ fail(
64387
+ "irm-basis-totals",
64388
+ `utilization.basis is ${u.basis} but irmTotal* are missing`
64389
+ );
64390
+ else if (u.irmTotalDeposits > 0) {
64391
+ const derived = u.irmTotalDebt / u.irmTotalDeposits;
64392
+ if (Math.abs(derived - u.utilization) > 0.02)
64393
+ fail(
64394
+ "irm-basis-mismatch",
64395
+ `irmTotalDebt/irmTotalDeposits ${derived.toFixed(4)} != utilization ${u.utilization.toFixed(4)}`
64396
+ );
64397
+ }
64398
+ }
64399
+ }
64400
+ const o = sheet.oracle;
64401
+ if (o) {
64402
+ if (o.kind === "none" && o.address)
64403
+ fail("oracle-none-address", "oracle.kind none must not carry an address");
64404
+ if (o.kind === "none" && sheet.borrow && sheet.borrow.liquidation.trigger !== "time")
64405
+ fail(
64406
+ "no-oracle-non-time",
64407
+ `oracle.kind none but liquidation.trigger is ${sheet.borrow.liquidation.trigger}`
64408
+ );
64409
+ if (o.address && o.address !== o.address.toLowerCase())
64410
+ fail("oracle-lowercase", "oracle.address must be lowercased");
64411
+ }
64412
+ const g = sheet.governance;
64413
+ if (g) {
64414
+ if (g.mutability === "immutable") {
64415
+ if (g.timelockSecs)
64416
+ fail(
64417
+ "immutable-timelock",
64418
+ "immutable governance must not carry a timelock"
64419
+ );
64420
+ if (g.powers?.length)
64421
+ fail("immutable-powers", "immutable governance must not carry powers");
64422
+ }
64423
+ if (g.timelockSecs && g.controllerKind && g.controllerKind !== "TIMELOCK")
64424
+ fail(
64425
+ "timelock-without-timelock-root",
64426
+ `timelockSecs set but controllerKind is ${g.controllerKind}`
64427
+ );
64428
+ }
64429
+ for (const side of ["supply", "borrow"]) {
64430
+ const modes = sheet[side]?.modes;
64431
+ if (!modes) continue;
64432
+ if (modes.filter((m) => m.isDefault).length > 1)
64433
+ fail("multiple-defaults", `${side}.modes has more than one isDefault`);
64434
+ }
64435
+ return v;
64436
+ }
64437
+ function validateTermSheets(sheets) {
64438
+ return sheets.flatMap((s) => validateTermSheet(s));
64439
+ }
64440
+
64441
+ export { ApiBookSource, DEFAULT_MIDNIGHT_API, DEFAULT_OUTLIER_GUARD, DEFAULT_PROFILE_ID, DEFAULT_STALE_REJECT_SECONDS, DEFAULT_TERMMAX_API, EMPTY_BALANCE, EXACTLY_LENDER_KEY, GMX_API_HOSTS, GMX_READ_CONTRACTS, GMX_SUPPORTED_CHAINS, HYPERCORE_VAULT_REGISTRY, IDLE_MARKET_ID, INTERFACE_IDS, LAGOON_API_URL, LAGOON_CHAIN_IDS, MORPHO_LENS, MULTICALL_FAILURE, MaxParamThresholds, STABLECOIN_SYMBOLS, STCELO_MANAGER_ADDRESS, TELLER_CALLS_PER_BID, TERMMAX_CALLS_PER_ACCOUNT, DECIMAL_BASE as TERMMAX_DECIMAL_BASE, TERMMAX_LIQUIDATION_PENALTY, TERMMAX_LIQUIDATION_WINDOW_SECS, TERMMAX_LIQUIDATOR_BONUS, TERMMAX_PARTIAL_CLOSE_FACTOR, TERMMAX_PARTIAL_LIQUIDATION_THRESHOLD_USD, TERM_ADAPTERS, TERM_PROFILES, TERM_SHEET_SCHEMA_VERSION, TermMaxApiSource, TermSubgraphSource, UPSHIFT_CHAIN_IDS, UPSHIFT_VAULTS_URL, VAULT_SHARE_PRICE_PROBE, VOLATILE_VAULT_OVERRIDES, YEARN_CHAIN_IDS, YEARN_YDAEMON_BASE, __resetResupplyUserCaches, accountDepositListKey, accountWithdrawalListKey, appendSnapshot, applyPositionDelta, attachImplications, attachPricesToFlashLiquidity, borrowDescription, borrowFindings, borrowHeadline, buildExposures2 as buildExposures, buildFluidFTokensCall, buildLiquityUserCall, buildLoopResult, buildMorphoTypeCall, buildMorphoTypeUserCallWithLens, buildPortfolioTotals, buildSumerAccumulators, buildSummaries, buildTellerUserCall, buildTermMaxUserCall, buildTermSheet, buildTermSheetsForGroup, buildVaultLookup, calculateLeverage, calculateNetApr, calculateOverallNetApr, calculateWeightedAverage, classifyFreshness, classifyReliability, classifyVault, collectFeedObservations, computeBorrowDelta2 as computeBorrowDelta, computeCloseTradeDeltas, computeCollateralSwapDeltas, computeDebtSwapDeltas, computeDepositDelta2 as computeDepositDelta, computeEModeAnalysis, computeOpenTradeDeltas, computePostTradeMetrics, computeRepayDelta2 as computeRepayDelta, computeSumerBorrowDelta, computeSumerDepositDelta, computeSumerRepayDelta, computeSumerWaterfall, computeSumerWithdrawDelta, computeVaultApr, computeWithdrawDelta2 as computeWithdrawDelta, computeZapTradeDeltas, consensusReference, convertExactlyMarketsToResponse, convertFrankencoinMarketsToResponse, convertInverseMarketsToResponse, convertLenderUserDataResult, convertLiquityMarketsToResponse, convertLlamaLendMarketsToResponse, convertResupplyMarketsToResponse, convertRiverMarketsToResponse, convertTellerMarketsToResponse, convertTermMarketsToResponse, convertTermMaxMarketsToResponse, convertUsddMarketsToResponse, createMarketUid, createMidnightBookSource, createMulticallRpcCall, createRawRpcCalls, createTermBookSource, createTermMaxDataSource, decodeListaMarkets, decodeMarkets, decodePackedListaUserDataset, decodePackedMorphoUserDataset, deriveBorrowTags, deriveSupplyTags, detectInterfaceKinds, duration, encodeBalanceFetcherCalldata, enrichTermSheet, enrichmentIndexFromRows, exactlyPairLtv, exactlyPenaltyRateToAprPercent, exactlyWadRateToPercent, feePhrase, feedKeyOf, feedStatKey, fetchDolomiteAccountNumbers, fetchEulerEarnVaults, fetchEulerEarnVaultsFromSubgraph, fetchEulerSubAccountIndexes, fetchExactlyMarkets, fetchFlashLiquidityForChain, fetchFluidFTokens, fetchFrankencoinMarkets, fetchGeneralYields, fetchGeneralYieldsByMarketUid, fetchGmxExecutionFees, fetchGmxTickerPrices, fetchGmxVaults, fetchHypercoreVaults, fetchInverseMarkets, fetchLagoonApiVaults, fetchLagoonVaults, fetchLiquityMarkets, fetchListaVaultsFromChain, fetchLlamaLendMarkets, fetchMorphoUserBalances, fetchMorphoUserPositionMarkets, fetchMorphoVaults, fetchMorphoVaultsFromApi, fetchMorphoVaultsFromChain, fetchOraclePrices, fetchPendlePrices, fetchResupplyMarkets, fetchRiverMarkets, fetchSiloVaults, fetchTellerMarkets, fetchTermMarkets, fetchTermMaxMarkets, fetchTokenBalances, fetchTokenMetadata, fetchUpshiftApiVaults, fetchUpshiftVaults, fetchUsddMarkets, fetchYearnApiVaults, fetchYearnVaults, filterActiveLenders, filterLendersByProtocol, finalizeInfo, findingsFor, frankencoinKeyParts, frankencoinLenderKey, fuseLenderData, generateLendingPools, getAavesForChain, getAssetConfig, getBalanceForMarketUid, getBorrowCapacity, getCachedLiquityTroves, getCachedTellerBids, getCachedTermMaxDiscovery, getCachedTermMaxMarket, getCachedTermMaxMarkets, getCoreValidators, getFluidFTokensConverter, getGmxApiHost, getGmxReadContracts, getGmxUserPositions, getHealthFactor, getHypercoreUserPositions, getHypercoreVaultRegistry, getLenderAssets, getLenderPublicData, getLenderPublicDataAll, getLenderPublicDataViaApi, getLenderUserDataMulti, getLenderUserDataResult, getLendersForChain, getLstDelegation, getLstValidators, getLstWithdrawalRegistry, getLstWithdrawalRequests, getMaxAmountClose, getMaxAmountCollateralSwap, getMaxAmountDebtSwap, getMaxAmountOpen, getMergedUserData, getMorphoTypeMarketConverter, getReadFailurePolicy, getResolvedDolomiteAccountNumbers, getStCeloValidatorGroups, getSubAccountAddress, getSubAccountIndex, getTermProfile, getVaultPublicDataAll, getVaultWithdrawalRequests, hasCritical, hasEulerEarnVaultSubgraph, hasLagoonVaults, hasMorphoPositionIndex, hasMorphoUserApi, hasMorphoUserSubgraph, hasUpshiftVaults, hasYearnVaults, inverseKeyParts, inverseLenderKey, isFailedCall, isStablecoinSymbol, isYearnV3, keysFromMaps, lenderApiOnly, lenderFamily, liquityCandidateTroveIds, liquityKeyParts, liquityLenderKey, llamaLendKeyParts, llamaLendLenderKey, mergeDeep, multicall3Abi2 as multicall3Abi, nanTo, needsLenderApproval, needsTokenApproval, noOpResult, normalizeToBytes, parseBalanceFetcherResult, parseMergedResult, parseMulticallRpcResponses, parseRawRpcBatchResponses, parseRawRpcResponses, parseTermMaxLtv, parseTokenBalanceResult, pct, positivePart2 as positivePart, predictInverseEscrow, prepareLenderUserDataRpcCalls, prepareMergedMulticallParams, prepareMergedRpcCalls, prepareMulticallInputs, prepareTokenBalanceRpcCalls, priceGlvVaults, priceGmMarkets, probeAaveFeedTimestamps, pruneFeedStats, rankFindings, readVaultSharePrices, rejectOutliers as rejectPriceOutliers, resolveAdapter, resolveDerivation, resolveStCeloDepositGroup, resupplyKeyParts, resupplyLenderKey, riverKeyParts, riverLenderKey, selectAssetGroupPrices, shortDate, stampVaultClassification, supplyDescription, supplyFindings, supplyHeadline, tellerBpsToPercent, tellerImpliedLtv, tellerLenderKey, tellerPoolFromLenderKey, termLenderKey, termMaxApiBase, borrowNif as termMaxBorrowNif, curveApr as termMaxCurveApr, curveAprNumber as termMaxCurveAprNumber, daysToMaturity as termMaxDaysToMaturity, lendNif as termMaxLendNif, mintGtFeeRatio as termMaxMintGtFeeRatio, ratioToNumber as termMaxRatioToNumber, ratioToPercent as termMaxRatioToPercent, tickToAprNumber, tickToPrice, toDigest, toTermSheetInput, unflattenLenderData, updateFeedStats, usddIlkBytes32, usddKeyParts, usddLenderKey, validateTermSheet, validateTermSheets, validateUserData };
61561
64442
  //# sourceMappingURL=index.js.map
61562
64443
  //# sourceMappingURL=index.js.map