@1delta/margin-fetcher 0.0.412 → 0.0.413
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1253 -14
- package/dist/index.js +3123 -243
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
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,
|
|
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
|
|
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:
|
|
16147
|
-
borrowRatePerBlock:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
22917
|
+
minRateBps: num11(base + 2),
|
|
22918
22918
|
collateralPerPrincipal: big13(base + 3),
|
|
22919
|
-
maxLoanDuration:
|
|
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
|
|
23882
|
+
const num11 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
23883
23883
|
return {
|
|
23884
23884
|
market,
|
|
23885
|
-
totalDebt:
|
|
23886
|
-
dolaLiquidity:
|
|
23887
|
-
leftToBorrow:
|
|
23888
|
-
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:
|
|
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 =
|
|
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
|
|
26870
|
-
|
|
26871
|
-
|
|
26872
|
-
|
|
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
|
-
|
|
26875
|
-
|
|
26876
|
-
|
|
26877
|
-
|
|
26878
|
-
|
|
26879
|
-
|
|
26880
|
-
|
|
26881
|
-
|
|
26882
|
-
|
|
26883
|
-
|
|
26884
|
-
|
|
26885
|
-
|
|
26886
|
-
|
|
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
|
|
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
|
|
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
|
|
26907
|
-
|
|
26908
|
-
|
|
26909
|
-
|
|
26910
|
-
|
|
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
|
-
|
|
27046
|
+
slots,
|
|
26914
27047
|
getEvmClient17,
|
|
26915
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
26977
|
-
|
|
26978
|
-
|
|
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
|
-
|
|
27159
|
+
slots = await repairFailedSlots(
|
|
26981
27160
|
chainId,
|
|
26982
27161
|
batch.items,
|
|
26983
|
-
|
|
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
|
-
|
|
26991
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
32469
|
-
|
|
32470
|
-
|
|
32471
|
-
|
|
32472
|
-
|
|
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:
|
|
32791
|
+
lender: key,
|
|
32475
32792
|
failedCalls,
|
|
32793
|
+
retryableFailedCalls,
|
|
32476
32794
|
totalCalls: data.length,
|
|
32477
|
-
dropped:
|
|
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 (
|
|
32481
|
-
|
|
32482
|
-
|
|
32483
|
-
|
|
32484
|
-
|
|
32485
|
-
|
|
32486
|
-
|
|
32487
|
-
|
|
32488
|
-
|
|
32489
|
-
|
|
32490
|
-
|
|
32491
|
-
|
|
32492
|
-
|
|
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
|
-
|
|
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
|
|
37162
|
-
|
|
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
|
|
40092
|
-
var
|
|
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 *
|
|
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:
|
|
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
|
-
|
|
40215
|
-
|
|
40216
|
-
|
|
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
|
|
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 *
|
|
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
|
|
42390
|
+
var YEAR_SECONDS7 = 31536e3;
|
|
42023
42391
|
var BASE_27 = 1e27;
|
|
42024
|
-
var aprFromRate = (rate) => Number(rate) / BASE_27 *
|
|
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
|
|
42612
|
+
var CHAIN_ID9 = Chain.ETHEREUM_MAINNET;
|
|
42245
42613
|
var SNUSD = "0x08efcc2f3e61185d0ea7f8830b3fec9bfa2ee313";
|
|
42246
|
-
var
|
|
42247
|
-
var
|
|
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:
|
|
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 *
|
|
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
|
|
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
|
|
42317
|
-
var
|
|
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:
|
|
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 *
|
|
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
|
|
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 *
|
|
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
|
|
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 /
|
|
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: [
|
|
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 /
|
|
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:
|
|
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 /
|
|
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: [
|
|
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: [
|
|
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 /
|
|
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:
|
|
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: [
|
|
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 /
|
|
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:
|
|
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 /
|
|
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:
|
|
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: [
|
|
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 /
|
|
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 /
|
|
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 /
|
|
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:
|
|
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: [
|
|
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 /
|
|
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:
|
|
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: [
|
|
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 /
|
|
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:
|
|
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:
|
|
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 *
|
|
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 /
|
|
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:
|
|
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 /
|
|
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: [
|
|
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 /
|
|
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:
|
|
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: [
|
|
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 /
|
|
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:
|
|
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: [
|
|
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 /
|
|
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 /
|
|
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:
|
|
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 /
|
|
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:
|
|
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 /
|
|
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 *
|
|
54277
|
+
totalAssets: totalSupply * ONE_E1810 / r,
|
|
53886
54278
|
totalSupply,
|
|
53887
|
-
exchangeRate:
|
|
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 *
|
|
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 *
|
|
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: [
|
|
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 /
|
|
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 *
|
|
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
|
|
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 /
|
|
54585
|
-
const convertToShares = 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
|
|
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 *
|
|
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 *
|
|
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 *
|
|
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:
|
|
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 / (
|
|
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: [
|
|
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 /
|
|
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
|
|
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 /
|
|
58395
|
-
const convertToShares = 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
|
|
59358
|
-
const hist =
|
|
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
|
|
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) * (
|
|
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,2492 @@ async function fetchTokenBalances(chainId, account, tokens, options = {}) {
|
|
|
61557
61951
|
return parseTokenBalanceResult(rawResult, prepared.query);
|
|
61558
61952
|
}
|
|
61559
61953
|
|
|
61560
|
-
|
|
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 s = value.toFixed(dp).replace(/\.?0+$/, "");
|
|
62246
|
+
return `${s === "" || s === "-" ? "0" : s} %`;
|
|
62247
|
+
}
|
|
62248
|
+
function duration(secs) {
|
|
62249
|
+
if (secs == null || !Number.isFinite(secs) || secs < 0) return "\u2014";
|
|
62250
|
+
if (secs === 0) return "none";
|
|
62251
|
+
if (secs >= SECS_PER_DAY) {
|
|
62252
|
+
const d = secs / SECS_PER_DAY;
|
|
62253
|
+
return `${d % 1 === 0 ? d : d.toFixed(1)} day${d === 1 ? "" : "s"}`;
|
|
62254
|
+
}
|
|
62255
|
+
if (secs >= 3600) {
|
|
62256
|
+
const h = secs / 3600;
|
|
62257
|
+
return `${h % 1 === 0 ? h : h.toFixed(1)} hour${h === 1 ? "" : "s"}`;
|
|
62258
|
+
}
|
|
62259
|
+
if (secs >= 60) {
|
|
62260
|
+
const m = Math.round(secs / 60);
|
|
62261
|
+
return `${m} minute${m === 1 ? "" : "s"}`;
|
|
62262
|
+
}
|
|
62263
|
+
return `${Math.round(secs)} second${Math.round(secs) === 1 ? "" : "s"}`;
|
|
62264
|
+
}
|
|
62265
|
+
function shortDate(unixSecs) {
|
|
62266
|
+
if (unixSecs == null || !Number.isFinite(unixSecs)) return "\u2014";
|
|
62267
|
+
const d = new Date(unixSecs * 1e3);
|
|
62268
|
+
const months = [
|
|
62269
|
+
"Jan",
|
|
62270
|
+
"Feb",
|
|
62271
|
+
"Mar",
|
|
62272
|
+
"Apr",
|
|
62273
|
+
"May",
|
|
62274
|
+
"Jun",
|
|
62275
|
+
"Jul",
|
|
62276
|
+
"Aug",
|
|
62277
|
+
"Sep",
|
|
62278
|
+
"Oct",
|
|
62279
|
+
"Nov",
|
|
62280
|
+
"Dec"
|
|
62281
|
+
];
|
|
62282
|
+
return `${d.getUTCDate()} ${months[d.getUTCMonth()]} ${d.getUTCFullYear()}`;
|
|
62283
|
+
}
|
|
62284
|
+
function feePhrase(fee) {
|
|
62285
|
+
const magnitude = Math.abs(fee.value);
|
|
62286
|
+
let amount;
|
|
62287
|
+
switch (fee.unit) {
|
|
62288
|
+
case "bps":
|
|
62289
|
+
amount = `${magnitude} bps`;
|
|
62290
|
+
break;
|
|
62291
|
+
case "apr-percent":
|
|
62292
|
+
amount = `${pct(magnitude)}/yr`;
|
|
62293
|
+
break;
|
|
62294
|
+
case "percent":
|
|
62295
|
+
amount = pct(magnitude);
|
|
62296
|
+
break;
|
|
62297
|
+
default:
|
|
62298
|
+
amount = String(magnitude);
|
|
62299
|
+
}
|
|
62300
|
+
const rebate = fee.value < 0 ? " rebate" : "";
|
|
62301
|
+
const qualifier = fee.indicative ? " (estimated, resolved at execution)" : "";
|
|
62302
|
+
return `${fee.label}: ${amount}${rebate}${qualifier}`;
|
|
62303
|
+
}
|
|
62304
|
+
function maturityPhrase(m) {
|
|
62305
|
+
if (m.kind === "fixed-date" && m.maturity)
|
|
62306
|
+
return `until ${shortDate(m.maturity)}`;
|
|
62307
|
+
if (m.kind === "rolling-duration") {
|
|
62308
|
+
if (m.maxDurationSecs) return `for up to ${duration(m.maxDurationSecs)}`;
|
|
62309
|
+
return "for a term you choose";
|
|
62310
|
+
}
|
|
62311
|
+
return "with no maturity";
|
|
62312
|
+
}
|
|
62313
|
+
function rateLabel(sheet) {
|
|
62314
|
+
switch (sheet.rate.kind) {
|
|
62315
|
+
case "fixed-term":
|
|
62316
|
+
case "fixed-open":
|
|
62317
|
+
return "Fixed";
|
|
62318
|
+
case "user-set":
|
|
62319
|
+
return "You set";
|
|
62320
|
+
case "zero-interest":
|
|
62321
|
+
return "No interest";
|
|
62322
|
+
case "prepaid":
|
|
62323
|
+
return "Prepaid";
|
|
62324
|
+
case "nav-accrual":
|
|
62325
|
+
return "NAV";
|
|
62326
|
+
case "none":
|
|
62327
|
+
return "No yield";
|
|
62328
|
+
default:
|
|
62329
|
+
return "Variable";
|
|
62330
|
+
}
|
|
62331
|
+
}
|
|
62332
|
+
var earlyRepayPhrase = {
|
|
62333
|
+
free: "repay any time at no extra cost",
|
|
62334
|
+
discount: "repaying early earns a rebate",
|
|
62335
|
+
penalty: "repaying early costs a penalty",
|
|
62336
|
+
"market-price": "exit early only at the market price",
|
|
62337
|
+
"not-allowed": "no early repayment"
|
|
62338
|
+
};
|
|
62339
|
+
var exitPhrase = {
|
|
62340
|
+
instant: "withdraw any time",
|
|
62341
|
+
"instant-capped": "withdraw up to available liquidity",
|
|
62342
|
+
"instant-or-queued": "withdraw instantly or queue",
|
|
62343
|
+
"fee-or-queued": "instant exit for a fee, or queue for free",
|
|
62344
|
+
"fixed-cooldown": "withdraw after a cooldown",
|
|
62345
|
+
queued: "withdrawals are queued",
|
|
62346
|
+
"request-based": "withdrawals need a request",
|
|
62347
|
+
"market-sale": "exit by selling on the market",
|
|
62348
|
+
"at-maturity": "locked until maturity",
|
|
62349
|
+
"off-chain": "exit off-chain",
|
|
62350
|
+
"dex-only": "exit only via a DEX"
|
|
62351
|
+
};
|
|
62352
|
+
function supplyHeadline(s) {
|
|
62353
|
+
if (s.role === "collateral") {
|
|
62354
|
+
return `Collateral only \xB7 ${maturityPhrase(s.maturity)}`;
|
|
62355
|
+
}
|
|
62356
|
+
const rate = `${rateLabel(s)} ${pct(s.rate.aprTotal)}`;
|
|
62357
|
+
const exit = exitPhrase[String(s.exit.mode)] ?? (s.exit.settlement === "sync" ? "withdraw any time" : "delayed withdrawal");
|
|
62358
|
+
const cooldown = s.exit.cooldownSecs ? ` (${duration(s.exit.cooldownSecs)})` : "";
|
|
62359
|
+
const mat = s.maturity.kind === "perpetual" ? "" : ` ${maturityPhrase(s.maturity)}`;
|
|
62360
|
+
return `${rate}${mat} \xB7 ${exit}${cooldown}`;
|
|
62361
|
+
}
|
|
62362
|
+
function borrowHeadline(b) {
|
|
62363
|
+
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)}`;
|
|
62364
|
+
const mat = b.maturity.kind === "perpetual" ? "" : ` ${maturityPhrase(b.maturity)}`;
|
|
62365
|
+
const exit = earlyRepayPhrase[String(b.exit.earlyRepay)] ?? "see repay terms";
|
|
62366
|
+
return `${rate}${mat} \xB7 ${exit}`;
|
|
62367
|
+
}
|
|
62368
|
+
function supplyDescription(s, sheet = {}) {
|
|
62369
|
+
const parts = [];
|
|
62370
|
+
if (s.role === "collateral") {
|
|
62371
|
+
parts.push(
|
|
62372
|
+
"Deposits here are collateral only \u2014 they secure borrowing and do not earn interest."
|
|
62373
|
+
);
|
|
62374
|
+
} else {
|
|
62375
|
+
const comp = [`${pct(s.rate.components.base)} base`];
|
|
62376
|
+
if (s.rate.components.rewards)
|
|
62377
|
+
comp.push(`${pct(s.rate.components.rewards)} rewards`);
|
|
62378
|
+
if (s.rate.components.intrinsic)
|
|
62379
|
+
comp.push(`${pct(s.rate.components.intrinsic)} from the asset itself`);
|
|
62380
|
+
parts.push(
|
|
62381
|
+
`You earn ${pct(s.rate.aprTotal)} (${comp.join(" + ")}), ${s.rate.isLocked ? "locked for the term" : "floating with utilization"}.`
|
|
62382
|
+
);
|
|
62383
|
+
}
|
|
62384
|
+
if (s.exit.mode === "instant") {
|
|
62385
|
+
const util = sheet.utilization?.utilization;
|
|
62386
|
+
parts.push(
|
|
62387
|
+
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."
|
|
62388
|
+
);
|
|
62389
|
+
} else if (s.exit.cooldownSecs) {
|
|
62390
|
+
parts.push(
|
|
62391
|
+
`Getting out takes ${duration(s.exit.cooldownSecs)} \u2014 request first, then claim.`
|
|
62392
|
+
);
|
|
62393
|
+
} else if (s.exit.priceRisk === "market-price") {
|
|
62394
|
+
parts.push(
|
|
62395
|
+
"There is no redemption before maturity; exiting early means selling the position on a market at whatever price is bid."
|
|
62396
|
+
);
|
|
62397
|
+
} else if (s.exit.priceRisk === "may-be-impossible") {
|
|
62398
|
+
parts.push(
|
|
62399
|
+
"Exiting early requires a buyer on the order book, and there may not be one at any price."
|
|
62400
|
+
);
|
|
62401
|
+
}
|
|
62402
|
+
if (s.backedBy && s.backedBy.count > 0) {
|
|
62403
|
+
parts.push(
|
|
62404
|
+
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` : ""}.`
|
|
62405
|
+
);
|
|
62406
|
+
}
|
|
62407
|
+
return parts.join(" ");
|
|
62408
|
+
}
|
|
62409
|
+
function borrowDescription(b) {
|
|
62410
|
+
const parts = [];
|
|
62411
|
+
if (b.rate.kind === "zero-interest") {
|
|
62412
|
+
parts.push(
|
|
62413
|
+
"This market charges no ongoing interest \u2014 the borrowing cost is a one-off fee taken at open."
|
|
62414
|
+
);
|
|
62415
|
+
} else if (b.rate.kind === "prepaid") {
|
|
62416
|
+
parts.push(
|
|
62417
|
+
"Interest is prepaid by holding a separate token; the debt principal itself does not grow."
|
|
62418
|
+
);
|
|
62419
|
+
} else if (b.rate.kind === "user-set") {
|
|
62420
|
+
parts.push(
|
|
62421
|
+
`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.`
|
|
62422
|
+
);
|
|
62423
|
+
} else {
|
|
62424
|
+
parts.push(
|
|
62425
|
+
`You pay ${pct(b.rate.apr)}${b.rate.isLocked ? ", locked for the term" : ", floating with utilization"}.`
|
|
62426
|
+
);
|
|
62427
|
+
}
|
|
62428
|
+
parts.push(
|
|
62429
|
+
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."
|
|
62430
|
+
);
|
|
62431
|
+
if (b.maturity.kind === "fixed-date" && b.maturity.maturity) {
|
|
62432
|
+
const consequence = {
|
|
62433
|
+
"penalty-accrues": "a late penalty starts accruing",
|
|
62434
|
+
liquidatable: "the loan can be liquidated regardless of its health",
|
|
62435
|
+
"default-seizure": "the loan defaults and the collateral is forfeit",
|
|
62436
|
+
"physical-delivery": "the collateral is delivered to lenders",
|
|
62437
|
+
refinanced: "a keeper refinances it into the variable position",
|
|
62438
|
+
"stops-earning": "it simply stops accruing"
|
|
62439
|
+
};
|
|
62440
|
+
const what = consequence[String(b.maturity.atMaturity)];
|
|
62441
|
+
parts.push(
|
|
62442
|
+
`It matures on ${shortDate(b.maturity.maturity)}${what ? `; if nothing is done then, ${what}` : ""}.`
|
|
62443
|
+
);
|
|
62444
|
+
}
|
|
62445
|
+
if (b.liquidation.trigger === "time") {
|
|
62446
|
+
parts.push(
|
|
62447
|
+
"Liquidation here is triggered by TIME, not price \u2014 being late is the trigger, and being over-collateralised does not protect you."
|
|
62448
|
+
);
|
|
62449
|
+
} else if (b.liquidation.liquidationLtv != null) {
|
|
62450
|
+
parts.push(
|
|
62451
|
+
`Liquidation starts at ${pct(b.liquidation.liquidationLtv * 100)} LTV, with a ${pct(b.liquidation.penalty * 100)} penalty.`
|
|
62452
|
+
);
|
|
62453
|
+
}
|
|
62454
|
+
return parts.join(" ");
|
|
62455
|
+
}
|
|
62456
|
+
|
|
62457
|
+
// src/terms/profiles.ts
|
|
62458
|
+
var P = (p) => p;
|
|
62459
|
+
var TERM_PROFILES = [
|
|
62460
|
+
// ── Variable-rate pool lenders ───────────────────────────────────────────
|
|
62461
|
+
P({
|
|
62462
|
+
id: "pool.variable@v1",
|
|
62463
|
+
name: "Variable-rate pool market",
|
|
62464
|
+
family: "pool",
|
|
62465
|
+
supply: {
|
|
62466
|
+
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.",
|
|
62467
|
+
implications: [
|
|
62468
|
+
"Your yield is not fixed \u2014 it rises and falls with borrowing demand.",
|
|
62469
|
+
"If utilization reaches 100 %, withdrawals are blocked until borrowers repay or new deposits arrive.",
|
|
62470
|
+
"Your deposit is exposed to every collateral asset the market accepts, not just one."
|
|
62471
|
+
]
|
|
62472
|
+
},
|
|
62473
|
+
borrow: {
|
|
62474
|
+
description: "Borrow against collateral at a rate that moves with utilization. The debt accrues continuously and can be repaid at any time.",
|
|
62475
|
+
implications: [
|
|
62476
|
+
"The rate can change block to block \u2014 a cheap borrow can become expensive.",
|
|
62477
|
+
"If the collateral value falls past the liquidation threshold, a liquidator repays part of your debt and takes collateral plus a penalty."
|
|
62478
|
+
]
|
|
62479
|
+
}
|
|
62480
|
+
}),
|
|
62481
|
+
// ── Fixed-term families ──────────────────────────────────────────────────
|
|
62482
|
+
P({
|
|
62483
|
+
id: "exactly.fixed@v1",
|
|
62484
|
+
name: "Exactly fixed-rate pool",
|
|
62485
|
+
family: "exactly",
|
|
62486
|
+
supply: {
|
|
62487
|
+
description: "A fixed-rate deposit into a maturity-dated pool, backed by the floating pool. The rate is locked when you deposit.",
|
|
62488
|
+
implications: [
|
|
62489
|
+
"Withdrawing before maturity sells the position back at the current curve rate \u2014 the haircut worsens as rates rise.",
|
|
62490
|
+
"After maturity the deposit stops earning but is never penalised and never expires."
|
|
62491
|
+
]
|
|
62492
|
+
},
|
|
62493
|
+
borrow: {
|
|
62494
|
+
description: "A fixed-rate borrow with a static face value (principal + fee) locked at trade time, under a shared cross-margin risk module.",
|
|
62495
|
+
implications: [
|
|
62496
|
+
"Repaying early is REBATED, not penalised \u2014 but the rebate can be zero when the pool has no unassigned earnings left.",
|
|
62497
|
+
"Repaying late accrues a linear penalty of roughly 164 %/yr on the face value, which drives the position toward liquidation on its own.",
|
|
62498
|
+
"A liquidator settles at full face value \u2014 only you, repaying voluntarily, get the early-repay rebate."
|
|
62499
|
+
]
|
|
62500
|
+
}
|
|
62501
|
+
}),
|
|
62502
|
+
P({
|
|
62503
|
+
id: "midnight.orderbook@v1",
|
|
62504
|
+
name: "Morpho Midnight order book",
|
|
62505
|
+
family: "midnight",
|
|
62506
|
+
supply: {
|
|
62507
|
+
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.",
|
|
62508
|
+
implications: [
|
|
62509
|
+
"Exiting before maturity means selling your units into the bid side at a discount \u2014 there may be no bid.",
|
|
62510
|
+
"Two ongoing fees are charged to the LENDER side and reduce your net position.",
|
|
62511
|
+
"Bad debt is socialised across lenders."
|
|
62512
|
+
]
|
|
62513
|
+
},
|
|
62514
|
+
borrow: {
|
|
62515
|
+
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.",
|
|
62516
|
+
implications: [
|
|
62517
|
+
"Once maturity passes the loan is in default and can be liquidated regardless of health or LTV \u2014 being past due IS the trigger.",
|
|
62518
|
+
"Over-repaying REVERTS: the repayment must be sized to the exact debt.",
|
|
62519
|
+
"Repaying early costs face value with neither fee nor rebate, so there is no saving in paying sooner."
|
|
62520
|
+
]
|
|
62521
|
+
}
|
|
62522
|
+
}),
|
|
62523
|
+
P({
|
|
62524
|
+
id: "term.auction@v1",
|
|
62525
|
+
name: "Term Finance repo",
|
|
62526
|
+
family: "term",
|
|
62527
|
+
supply: {
|
|
62528
|
+
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.",
|
|
62529
|
+
implications: [
|
|
62530
|
+
"Lending is possible between auction rounds via the secondary book, even when borrowing is not.",
|
|
62531
|
+
"Exiting early means selling the repo token on the secondary market at whatever price is bid."
|
|
62532
|
+
]
|
|
62533
|
+
},
|
|
62534
|
+
borrow: {
|
|
62535
|
+
description: "A repurchase obligation with a static face value, originated only inside a scheduled sealed-bid auction round.",
|
|
62536
|
+
implications: [
|
|
62537
|
+
"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.",
|
|
62538
|
+
"Any rate shown outside an open round is the previous round\u2019s clearing rate: historical, not obtainable.",
|
|
62539
|
+
"Repaying early costs the full face value with no rebate, so early repayment is strictly worse than holding to maturity."
|
|
62540
|
+
]
|
|
62541
|
+
}
|
|
62542
|
+
}),
|
|
62543
|
+
P({
|
|
62544
|
+
id: "termmax.fixed@v1",
|
|
62545
|
+
name: "TermMax fixed-maturity market",
|
|
62546
|
+
family: "termmax",
|
|
62547
|
+
supply: {
|
|
62548
|
+
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.",
|
|
62549
|
+
implications: [
|
|
62550
|
+
"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.",
|
|
62551
|
+
"That loss is socialised across the whole bond supply, so a default anywhere in the market affects you regardless of which loan defaulted.",
|
|
62552
|
+
"Exiting early requires a maker order to sell into, and one may not exist."
|
|
62553
|
+
]
|
|
62554
|
+
},
|
|
62555
|
+
borrow: {
|
|
62556
|
+
description: "A fixed-maturity loan represented by an NFT, with a static face-value debt that accrues nothing.",
|
|
62557
|
+
implications: [
|
|
62558
|
+
"After maturity plus a two-hour liquidation window, unpaid collateral is delivered to lenders.",
|
|
62559
|
+
"The debt is a static face value \u2014 repaying early does not reduce the amount owed."
|
|
62560
|
+
]
|
|
62561
|
+
}
|
|
62562
|
+
}),
|
|
62563
|
+
P({
|
|
62564
|
+
id: "teller.pool@v1",
|
|
62565
|
+
name: "Teller V2 pooled loan",
|
|
62566
|
+
family: "teller",
|
|
62567
|
+
supply: {
|
|
62568
|
+
description: "A deposit into a lender-commitment pool that funds fixed-term, fixed-APR loans. There is no price oracle anywhere in this market.",
|
|
62569
|
+
implications: [
|
|
62570
|
+
"Borrowers are defaulted on TIME, not price \u2014 the pool\u2019s protection is collateral seizure after a missed payment, not a margin call.",
|
|
62571
|
+
"Withdrawals sit behind a delay."
|
|
62572
|
+
]
|
|
62573
|
+
},
|
|
62574
|
+
borrow: {
|
|
62575
|
+
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.",
|
|
62576
|
+
implications: [
|
|
62577
|
+
"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.",
|
|
62578
|
+
"The grace period after the payment window can be extremely short \u2014 as little as five minutes.",
|
|
62579
|
+
"Being over-collateralised does not protect you: a perfectly healthy loan is defaulted for being late.",
|
|
62580
|
+
"An upfront origination fee is charged at borrow time."
|
|
62581
|
+
]
|
|
62582
|
+
}
|
|
62583
|
+
}),
|
|
62584
|
+
P({
|
|
62585
|
+
id: "lista.broker@v1",
|
|
62586
|
+
name: "Lista brokered fixed term",
|
|
62587
|
+
family: "lista",
|
|
62588
|
+
borrow: {
|
|
62589
|
+
description: "A fixed-rate loan fronted by a single market broker over an underlying Morpho-style market. Interest accrues linearly and freezes at maturity.",
|
|
62590
|
+
implications: [
|
|
62591
|
+
"Repaying early costs roughly half the remaining-term interest as a penalty.",
|
|
62592
|
+
"If the loan is not repaid at maturity, a keeper refinances it into the variable position."
|
|
62593
|
+
]
|
|
62594
|
+
}
|
|
62595
|
+
}),
|
|
62596
|
+
// ── CDP families ─────────────────────────────────────────────────────────
|
|
62597
|
+
P({
|
|
62598
|
+
id: "liquity.cdp@v1",
|
|
62599
|
+
name: "Liquity V2 family trove",
|
|
62600
|
+
family: "liquity",
|
|
62601
|
+
supply: {
|
|
62602
|
+
description: "The Stability Pool absorbs liquidated debt in exchange for discounted collateral. It is an earn position, not collateral.",
|
|
62603
|
+
implications: [
|
|
62604
|
+
"Your deposit is converted into collateral when liquidations occur \u2014 the balance is meant to change composition."
|
|
62605
|
+
]
|
|
62606
|
+
},
|
|
62607
|
+
borrow: {
|
|
62608
|
+
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.",
|
|
62609
|
+
implications: [
|
|
62610
|
+
"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.",
|
|
62611
|
+
"You pay a one-off upfront fee equal to roughly a week of interest at open, and again whenever you adjust the rate.",
|
|
62612
|
+
"There is a minimum debt size; you cannot leave a position below it."
|
|
62613
|
+
]
|
|
62614
|
+
}
|
|
62615
|
+
}),
|
|
62616
|
+
P({
|
|
62617
|
+
id: "river.cdp@v1",
|
|
62618
|
+
name: "River (Satoshi) trove",
|
|
62619
|
+
family: "river",
|
|
62620
|
+
borrow: {
|
|
62621
|
+
description: "A CDP with NO ongoing interest. The entire borrowing cost is a one-off decaying mint fee charged at open.",
|
|
62622
|
+
implications: [
|
|
62623
|
+
"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.",
|
|
62624
|
+
"Your collateral can be redeemed at par while healthy."
|
|
62625
|
+
]
|
|
62626
|
+
}
|
|
62627
|
+
}),
|
|
62628
|
+
P({
|
|
62629
|
+
id: "inverse.firm@v1",
|
|
62630
|
+
name: "Inverse FiRM market",
|
|
62631
|
+
family: "inverse",
|
|
62632
|
+
borrow: {
|
|
62633
|
+
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.",
|
|
62634
|
+
implications: [
|
|
62635
|
+
"Running out of DBR triggers a permissionless forced top-up at roughly 54.75 %/yr, repeatable every block, and freezes withdrawals.",
|
|
62636
|
+
"The debt principal never grows \u2014 the cost is the DBR balance burning down.",
|
|
62637
|
+
"Only governance-approved contracts may borrow, which blocks leverage and other routed flows."
|
|
62638
|
+
]
|
|
62639
|
+
}
|
|
62640
|
+
}),
|
|
62641
|
+
P({
|
|
62642
|
+
id: "usdd.cdp@v1",
|
|
62643
|
+
name: "USDD CDP ilk",
|
|
62644
|
+
family: "usdd",
|
|
62645
|
+
borrow: {
|
|
62646
|
+
description: "A Maker-style CDP with a governance-set stability fee and no utilization curve.",
|
|
62647
|
+
implications: [
|
|
62648
|
+
"The rate is set by governance, not by a market \u2014 it can change without any utilization signal."
|
|
62649
|
+
]
|
|
62650
|
+
}
|
|
62651
|
+
}),
|
|
62652
|
+
// ── Vaults ───────────────────────────────────────────────────────────────
|
|
62653
|
+
P({
|
|
62654
|
+
id: "vault.lending@v1",
|
|
62655
|
+
name: "Curated lending vault",
|
|
62656
|
+
family: "vault",
|
|
62657
|
+
supply: {
|
|
62658
|
+
description: "A curated vault that allocates deposits across underlying lending markets. A curator chooses which markets and how much.",
|
|
62659
|
+
implications: [
|
|
62660
|
+
"You are exposed to every market the curator allocates into, and the allocation can change.",
|
|
62661
|
+
"Withdrawals are limited to the vault\u2019s liquid cash \u2014 if allocations are fully utilised, exits queue."
|
|
62662
|
+
]
|
|
62663
|
+
}
|
|
62664
|
+
}),
|
|
62665
|
+
P({
|
|
62666
|
+
id: "vault.savings@v1",
|
|
62667
|
+
name: "Savings vault",
|
|
62668
|
+
family: "savings",
|
|
62669
|
+
supply: {
|
|
62670
|
+
description: "A yield-bearing wrapper whose share price accrues. There is no borrow side and no utilization.",
|
|
62671
|
+
implications: [
|
|
62672
|
+
"The exit mechanism, not the APR, is the load-bearing term here \u2014 check the cooldown before depositing."
|
|
62673
|
+
]
|
|
62674
|
+
}
|
|
62675
|
+
}),
|
|
62676
|
+
P({
|
|
62677
|
+
id: "vault.nav@v1",
|
|
62678
|
+
name: "NAV-attested vault",
|
|
62679
|
+
family: "savings-nav",
|
|
62680
|
+
supply: {
|
|
62681
|
+
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.",
|
|
62682
|
+
implications: [
|
|
62683
|
+
"There is no on-chain solvency invariant \u2014 the share price is a trusted input, not a computed one.",
|
|
62684
|
+
"The published value can be stale; some feeds keep no round history at all."
|
|
62685
|
+
]
|
|
62686
|
+
}
|
|
62687
|
+
}),
|
|
62688
|
+
P({
|
|
62689
|
+
id: "vault.tranche-junior@v1",
|
|
62690
|
+
name: "Junior tranche",
|
|
62691
|
+
family: "tranche",
|
|
62692
|
+
supply: {
|
|
62693
|
+
description: "The junior slice of a tranched structure. It pays a higher rate precisely because it absorbs losses first.",
|
|
62694
|
+
implications: [
|
|
62695
|
+
"This is FIRST-LOSS capital \u2014 a drawdown hits your position before it touches the senior tranche.",
|
|
62696
|
+
"Redemptions can be paused when senior coverage falls below its minimum."
|
|
62697
|
+
]
|
|
62698
|
+
}
|
|
62699
|
+
}),
|
|
62700
|
+
P({
|
|
62701
|
+
id: "vault.tranche-senior@v1",
|
|
62702
|
+
name: "Senior tranche",
|
|
62703
|
+
family: "tranche",
|
|
62704
|
+
supply: {
|
|
62705
|
+
description: "The senior slice of a tranched structure, protected by junior capital up to the size of that junior slice.",
|
|
62706
|
+
implications: [
|
|
62707
|
+
"Protection is finite \u2014 it ends when the junior tranche is exhausted."
|
|
62708
|
+
]
|
|
62709
|
+
}
|
|
62710
|
+
}),
|
|
62711
|
+
P({
|
|
62712
|
+
id: "vault.credit@v1",
|
|
62713
|
+
name: "Undercollateralized credit pool",
|
|
62714
|
+
family: "credit",
|
|
62715
|
+
supply: {
|
|
62716
|
+
description: "A pool lending to borrowers who are not fully collateralised on-chain. Repayment depends on off-chain credit arrangements.",
|
|
62717
|
+
implications: [
|
|
62718
|
+
"There is no on-chain collateral to liquidate if a borrower does not repay.",
|
|
62719
|
+
"Exits are delayed or capped by the pool\u2019s own inventory."
|
|
62720
|
+
]
|
|
62721
|
+
}
|
|
62722
|
+
})
|
|
62723
|
+
];
|
|
62724
|
+
var BY_ID = new Map(TERM_PROFILES.map((p) => [p.id, p]));
|
|
62725
|
+
function getTermProfile(id) {
|
|
62726
|
+
return BY_ID.get(id);
|
|
62727
|
+
}
|
|
62728
|
+
var DEFAULT_PROFILE_ID = "pool.variable@v1";
|
|
62729
|
+
|
|
62730
|
+
// src/terms/buildTermSheet.ts
|
|
62731
|
+
var isNum = (v) => typeof v === "number" && Number.isFinite(v);
|
|
62732
|
+
var clamp01 = (v) => Math.max(0, Math.min(1, v));
|
|
62733
|
+
function assetRef(input) {
|
|
62734
|
+
const a = input.asset ?? {};
|
|
62735
|
+
return {
|
|
62736
|
+
chainId: String(a.chainId ?? input.chainId),
|
|
62737
|
+
address: String(a.address ?? input.underlying ?? "").toLowerCase(),
|
|
62738
|
+
symbol: a.symbol,
|
|
62739
|
+
name: a.name,
|
|
62740
|
+
decimals: a.decimals ?? input.decimals,
|
|
62741
|
+
assetGroup: a.assetGroup,
|
|
62742
|
+
logoURI: a.logoURI
|
|
62743
|
+
};
|
|
62744
|
+
}
|
|
62745
|
+
function defaultConfig(input) {
|
|
62746
|
+
const cfg = input.config;
|
|
62747
|
+
if (!cfg) return void 0;
|
|
62748
|
+
return cfg["0"] ?? Object.values(cfg)[0];
|
|
62749
|
+
}
|
|
62750
|
+
var RATE_MODEL_TO_KIND = {
|
|
62751
|
+
variable: "variable-curve",
|
|
62752
|
+
fixedTerm: "fixed-term",
|
|
62753
|
+
userSet: "user-set",
|
|
62754
|
+
zeroInterest: "zero-interest",
|
|
62755
|
+
dbr: "prepaid",
|
|
62756
|
+
protocolSet: "variable-managed"
|
|
62757
|
+
};
|
|
62758
|
+
function rateKindOf(input) {
|
|
62759
|
+
const mapped = input.rateModel ? RATE_MODEL_TO_KIND[input.rateModel] : void 0;
|
|
62760
|
+
return mapped ?? "variable-curve";
|
|
62761
|
+
}
|
|
62762
|
+
function rewardTerms(input, side) {
|
|
62763
|
+
if (!input.rewards?.length) return void 0;
|
|
62764
|
+
const out = [];
|
|
62765
|
+
for (const r of input.rewards) {
|
|
62766
|
+
const apr = side === "supply" ? r.depositRate ?? 0 : r.variableBorrowRate ?? r.stableBorrowRate ?? 0;
|
|
62767
|
+
if (!apr) continue;
|
|
62768
|
+
const isPoints = r.kind === "points" || !r.asset;
|
|
62769
|
+
out.push({
|
|
62770
|
+
asset: r.asset ? { chainId: input.chainId, address: String(r.asset).toLowerCase() } : void 0,
|
|
62771
|
+
kind: isPoints ? "points" : "token",
|
|
62772
|
+
apr,
|
|
62773
|
+
side,
|
|
62774
|
+
claim: r.claim ?? (isPoints ? "none" : "accrual"),
|
|
62775
|
+
endsAt: r.endsAt,
|
|
62776
|
+
// Points have no priceable value, so they must not inflate `aprTotal`.
|
|
62777
|
+
indicative: isPoints || void 0
|
|
62778
|
+
});
|
|
62779
|
+
}
|
|
62780
|
+
return out.length ? out : void 0;
|
|
62781
|
+
}
|
|
62782
|
+
function buildRate(input, side) {
|
|
62783
|
+
const kind = rateKindOf(input);
|
|
62784
|
+
const rewards = rewardTerms(input, side);
|
|
62785
|
+
const priceableRewards = (rewards ?? []).filter((r) => !r.indicative).reduce((s, r) => s + r.apr, 0);
|
|
62786
|
+
const menuBest = input.terms?.length ? Math.min(...input.terms.map((t) => t.apr)) : void 0;
|
|
62787
|
+
let base;
|
|
62788
|
+
if (side === "supply") {
|
|
62789
|
+
base = input.depositRate ?? 0;
|
|
62790
|
+
} else if (kind === "fixed-term") {
|
|
62791
|
+
base = menuBest ?? input.stableBorrowRate ?? input.variableBorrowRate ?? 0;
|
|
62792
|
+
} else {
|
|
62793
|
+
base = input.variableBorrowRate ?? 0;
|
|
62794
|
+
}
|
|
62795
|
+
const intrinsic = side === "supply" ? input.intrinsicYield ?? 0 : 0;
|
|
62796
|
+
return {
|
|
62797
|
+
kind,
|
|
62798
|
+
apr: base,
|
|
62799
|
+
components: {
|
|
62800
|
+
base,
|
|
62801
|
+
rewards: priceableRewards || void 0,
|
|
62802
|
+
intrinsic: intrinsic || void 0
|
|
62803
|
+
},
|
|
62804
|
+
aprTotal: base + priceableRewards + intrinsic,
|
|
62805
|
+
basis: "apr-nominal",
|
|
62806
|
+
compounding: kind === "zero-interest" || kind === "none" ? "none" : "per-second",
|
|
62807
|
+
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",
|
|
62808
|
+
isLocked: kind === "fixed-term" || kind === "fixed-open",
|
|
62809
|
+
rewards,
|
|
62810
|
+
menu: input.terms?.length ? input.terms.map((t) => ({ ...t })) : void 0
|
|
62811
|
+
};
|
|
62812
|
+
}
|
|
62813
|
+
function buildMaturity(input, now) {
|
|
62814
|
+
const ft = input.fixedTerm;
|
|
62815
|
+
const maturity = ft?.maturity;
|
|
62816
|
+
if (isNum(maturity) && maturity > 0) {
|
|
62817
|
+
return {
|
|
62818
|
+
kind: "fixed-date",
|
|
62819
|
+
maturity,
|
|
62820
|
+
maturityIso: new Date(maturity * 1e3).toISOString(),
|
|
62821
|
+
secondsToMaturity: Math.max(0, maturity - now)
|
|
62822
|
+
};
|
|
62823
|
+
}
|
|
62824
|
+
if (input.terms?.length) {
|
|
62825
|
+
const durations = input.terms.map((t) => t.durationSecs).filter(isNum);
|
|
62826
|
+
return {
|
|
62827
|
+
kind: "rolling-duration",
|
|
62828
|
+
minDurationSecs: durations.length ? Math.min(...durations) : void 0,
|
|
62829
|
+
maxDurationSecs: durations.length ? Math.max(...durations) : void 0
|
|
62830
|
+
};
|
|
62831
|
+
}
|
|
62832
|
+
return { kind: "perpetual" };
|
|
62833
|
+
}
|
|
62834
|
+
function buildFees(input, side) {
|
|
62835
|
+
const out = [];
|
|
62836
|
+
const ft = input.fixedTerm;
|
|
62837
|
+
if (side === "borrow") {
|
|
62838
|
+
const origination = input.originationFee ?? ft?.fees?.originationFeePercent ?? void 0;
|
|
62839
|
+
if (isNum(origination) && origination !== 0) {
|
|
62840
|
+
out.push({
|
|
62841
|
+
id: "origination",
|
|
62842
|
+
label: "Origination fee",
|
|
62843
|
+
when: "entry",
|
|
62844
|
+
unit: "percent",
|
|
62845
|
+
basis: "principal",
|
|
62846
|
+
value: origination,
|
|
62847
|
+
payee: "protocol",
|
|
62848
|
+
description: "Charged once at borrow time as a percent of the amount borrowed. Not an APR \u2014 never add it to the rate."
|
|
62849
|
+
});
|
|
62850
|
+
}
|
|
62851
|
+
if (isNum(ft?.fees?.latePenaltyApr) && ft.fees.latePenaltyApr !== 0) {
|
|
62852
|
+
out.push({
|
|
62853
|
+
id: "late-penalty",
|
|
62854
|
+
label: "Late-repayment penalty",
|
|
62855
|
+
when: "late",
|
|
62856
|
+
unit: "apr-percent",
|
|
62857
|
+
basis: "face-value",
|
|
62858
|
+
value: ft.fees.latePenaltyApr,
|
|
62859
|
+
payee: "lenders",
|
|
62860
|
+
mutable: true,
|
|
62861
|
+
description: "Accrues on overdue debt after maturity until repaid. Simple interest on the face value, not compounding."
|
|
62862
|
+
});
|
|
62863
|
+
}
|
|
62864
|
+
if (ft?.earlyRepay?.kind === "discount") {
|
|
62865
|
+
out.push({
|
|
62866
|
+
id: "early-repay-discount",
|
|
62867
|
+
label: "Early-repayment rebate",
|
|
62868
|
+
when: "exit",
|
|
62869
|
+
unit: "percent",
|
|
62870
|
+
basis: "face-value",
|
|
62871
|
+
// Negative = rebate. Sign is load-bearing.
|
|
62872
|
+
value: -0,
|
|
62873
|
+
indicative: true,
|
|
62874
|
+
payee: "protocol",
|
|
62875
|
+
description: "Repaying before maturity costs LESS than face value. The exact rebate is resolved at repay time and can be zero."
|
|
62876
|
+
});
|
|
62877
|
+
}
|
|
62878
|
+
}
|
|
62879
|
+
if (side === "supply") {
|
|
62880
|
+
if (isNum(ft?.fees?.continuousFeeApr) && ft.fees.continuousFeeApr !== 0) {
|
|
62881
|
+
out.push({
|
|
62882
|
+
id: "continuous",
|
|
62883
|
+
label: "Continuous lender fee",
|
|
62884
|
+
when: "ongoing",
|
|
62885
|
+
unit: "apr-percent",
|
|
62886
|
+
basis: "principal",
|
|
62887
|
+
value: ft.fees.continuousFeeApr,
|
|
62888
|
+
payee: "protocol",
|
|
62889
|
+
description: "An ongoing haircut taken from the lender side."
|
|
62890
|
+
});
|
|
62891
|
+
}
|
|
62892
|
+
if (isNum(ft?.fees?.settlementFee) && ft.fees.settlementFee !== 0) {
|
|
62893
|
+
out.push({
|
|
62894
|
+
id: "settlement",
|
|
62895
|
+
label: "Settlement fee",
|
|
62896
|
+
when: "exit",
|
|
62897
|
+
unit: "percent",
|
|
62898
|
+
basis: "principal",
|
|
62899
|
+
value: ft.fees.settlementFee * 100,
|
|
62900
|
+
payee: "protocol"
|
|
62901
|
+
});
|
|
62902
|
+
}
|
|
62903
|
+
}
|
|
62904
|
+
return out;
|
|
62905
|
+
}
|
|
62906
|
+
function capUtil(total, cap) {
|
|
62907
|
+
if (!isNum(cap) || cap <= 0 || !isNum(total)) return void 0;
|
|
62908
|
+
return clamp01(total / cap);
|
|
62909
|
+
}
|
|
62910
|
+
function buildAvailability(input, side) {
|
|
62911
|
+
const frozen = input.isFrozen === true;
|
|
62912
|
+
const inactive = input.isActive === false;
|
|
62913
|
+
const enabled = side === "supply" ? input.depositsEnabled !== false : input.borrowingEnabled !== false;
|
|
62914
|
+
const cap = side === "supply" ? input.supplyCap : input.borrowCap;
|
|
62915
|
+
const total = side === "supply" ? input.totalDeposits : input.totalDebt;
|
|
62916
|
+
const capUtilization = capUtil(total, cap);
|
|
62917
|
+
const capFull = capUtilization != null && capUtilization >= 1;
|
|
62918
|
+
const noLiquidity = side === "borrow" && isNum(input.borrowLiquidity) && input.borrowLiquidity <= 0;
|
|
62919
|
+
const canOpen = enabled && !frozen && !inactive && !capFull && !noLiquidity;
|
|
62920
|
+
let blockedBy;
|
|
62921
|
+
if (!canOpen) {
|
|
62922
|
+
if (inactive) blockedBy = "shutdown";
|
|
62923
|
+
else if (frozen) blockedBy = "frozen";
|
|
62924
|
+
else if (!enabled) blockedBy = "disabled";
|
|
62925
|
+
else if (capFull) blockedBy = "cap-full";
|
|
62926
|
+
else if (noLiquidity) blockedBy = "no-liquidity";
|
|
62927
|
+
}
|
|
62928
|
+
return {
|
|
62929
|
+
canOpen,
|
|
62930
|
+
// Closing a position stays possible in every wind-down we have seen —
|
|
62931
|
+
// freezes stop entry, not exit. Adapters override where that is false.
|
|
62932
|
+
canClose: true,
|
|
62933
|
+
blockedBy,
|
|
62934
|
+
gating: "permissionless",
|
|
62935
|
+
cap: isNum(cap) && cap > 0 ? String(cap) : void 0,
|
|
62936
|
+
capUtilization,
|
|
62937
|
+
requires: side === "supply" ? ["token-approval"] : void 0
|
|
62938
|
+
};
|
|
62939
|
+
}
|
|
62940
|
+
function buildUtilization(input) {
|
|
62941
|
+
if (!isNum(input.utilization)) return void 0;
|
|
62942
|
+
const shared = isNum(input.irmTotalDeposits) && isNum(input.irmTotalDebt);
|
|
62943
|
+
return {
|
|
62944
|
+
utilization: clamp01(input.utilization),
|
|
62945
|
+
basis: shared ? "pool" : "market",
|
|
62946
|
+
irmTotalDeposits: input.irmTotalDeposits,
|
|
62947
|
+
irmTotalDebt: input.irmTotalDebt,
|
|
62948
|
+
supplyCapUtilization: capUtil(input.totalDeposits, input.supplyCap),
|
|
62949
|
+
borrowCapUtilization: capUtil(input.totalDebt, input.borrowCap),
|
|
62950
|
+
lockupRatio: input.lockupRatio
|
|
62951
|
+
};
|
|
62952
|
+
}
|
|
62953
|
+
function buildConstraints(input, siblings, acceptedCollateral) {
|
|
62954
|
+
const ceiling = input.debtCeiling;
|
|
62955
|
+
const ceilingNum = isNum(Number(ceiling)) ? Number(ceiling) : 0;
|
|
62956
|
+
const lender = input.lender ?? "";
|
|
62957
|
+
const collateralAssetCount = acceptedCollateral?.count ?? 0;
|
|
62958
|
+
const marketCount = siblings.filter((s) => s.marketUid).length || 1;
|
|
62959
|
+
return {
|
|
62960
|
+
isolation: ceilingNum > 0 ? { enabled: true, debtCeiling: String(ceiling) } : void 0,
|
|
62961
|
+
crossMargin: hasCrossMarginRisk(lender),
|
|
62962
|
+
// A sub-account lender gets the honest generic answer; its adapter refines
|
|
62963
|
+
// it to the concrete shape (`nft` / `cdp-id` / `loan-id` / `escrow`).
|
|
62964
|
+
positionModel: hasSubAccounts(lender) ? "sub-account" : "account",
|
|
62965
|
+
observed: {
|
|
62966
|
+
collateralAssetCount,
|
|
62967
|
+
marketCount,
|
|
62968
|
+
multiMarketKey: isMultiMarket(lender)
|
|
62969
|
+
}
|
|
62970
|
+
};
|
|
62971
|
+
}
|
|
62972
|
+
function liquidationFrom(cfg, input) {
|
|
62973
|
+
return {
|
|
62974
|
+
trigger: "price",
|
|
62975
|
+
ltv: cfg?.borrowCollateralFactor,
|
|
62976
|
+
liquidationLtv: cfg?.collateralFactor,
|
|
62977
|
+
penalty: cfg?.liquidationPenalty ?? 0,
|
|
62978
|
+
closeFactor: cfg?.closeFactor ?? input.closeFactor ?? 1,
|
|
62979
|
+
targetHealthFactor: cfg?.targetHealthFactor ?? input.targetHealthFactor,
|
|
62980
|
+
seizure: "proportional"
|
|
62981
|
+
};
|
|
62982
|
+
}
|
|
62983
|
+
function buildModes(input) {
|
|
62984
|
+
const cfg = input.config;
|
|
62985
|
+
if (!cfg) return void 0;
|
|
62986
|
+
const keys = Object.keys(cfg);
|
|
62987
|
+
if (keys.length <= 1) return void 0;
|
|
62988
|
+
const out = [];
|
|
62989
|
+
for (const key of keys) {
|
|
62990
|
+
const c = cfg[key];
|
|
62991
|
+
if (!c) continue;
|
|
62992
|
+
const isDefault = key === "0";
|
|
62993
|
+
out.push({
|
|
62994
|
+
modeId: key,
|
|
62995
|
+
label: c.label,
|
|
62996
|
+
isDefault,
|
|
62997
|
+
entry: isDefault ? void 0 : "user-selected",
|
|
62998
|
+
liquidation: {
|
|
62999
|
+
ltv: c.borrowCollateralFactor,
|
|
63000
|
+
liquidationLtv: c.collateralFactor,
|
|
63001
|
+
penalty: c.liquidationPenalty ?? 0,
|
|
63002
|
+
closeFactor: c.closeFactor ?? input.closeFactor ?? 1,
|
|
63003
|
+
targetHealthFactor: c.targetHealthFactor ?? input.targetHealthFactor
|
|
63004
|
+
},
|
|
63005
|
+
availability: c.debtDisabled ? { canOpen: false } : void 0
|
|
63006
|
+
});
|
|
63007
|
+
}
|
|
63008
|
+
return out.length > 1 ? out : void 0;
|
|
63009
|
+
}
|
|
63010
|
+
function buildExposures2(input, siblings, direction) {
|
|
63011
|
+
const cfg = input.config;
|
|
63012
|
+
if (!cfg || siblings.length === 0) return void 0;
|
|
63013
|
+
const categories = Object.keys(cfg);
|
|
63014
|
+
const items = [];
|
|
63015
|
+
for (const sib of siblings) {
|
|
63016
|
+
if (sib.marketUid === input.marketUid) continue;
|
|
63017
|
+
const sibCfg = sib.config;
|
|
63018
|
+
if (!sibCfg) continue;
|
|
63019
|
+
let best;
|
|
63020
|
+
for (const cat of categories) {
|
|
63021
|
+
const here = cfg[cat];
|
|
63022
|
+
const there = sibCfg[cat];
|
|
63023
|
+
if (!here || !there) continue;
|
|
63024
|
+
if (direction === "accepted" || direction === "backing") {
|
|
63025
|
+
if (here.debtDisabled === true) continue;
|
|
63026
|
+
if (there.collateralDisabled === true) continue;
|
|
63027
|
+
}
|
|
63028
|
+
if (!best || (there.collateralFactor ?? 0) > (best.collateralFactor ?? 0))
|
|
63029
|
+
best = there;
|
|
63030
|
+
}
|
|
63031
|
+
if (!best) continue;
|
|
63032
|
+
items.push({
|
|
63033
|
+
asset: assetRef(sib),
|
|
63034
|
+
marketUid: sib.marketUid,
|
|
63035
|
+
via: "collateral",
|
|
63036
|
+
ltv: best.borrowCollateralFactor,
|
|
63037
|
+
liquidationLtv: best.collateralFactor,
|
|
63038
|
+
liquidationPenalty: best.liquidationPenalty
|
|
63039
|
+
});
|
|
63040
|
+
}
|
|
63041
|
+
if (items.length === 0) return void 0;
|
|
63042
|
+
return {
|
|
63043
|
+
count: items.length,
|
|
63044
|
+
weightBasis: "unweighted",
|
|
63045
|
+
items
|
|
63046
|
+
};
|
|
63047
|
+
}
|
|
63048
|
+
function buildSupply(input, now, siblings) {
|
|
63049
|
+
const canBeCollateral = input.collateralActive !== false;
|
|
63050
|
+
const earns = (input.depositRate ?? 0) > 0 || (input.intrinsicYield ?? 0) > 0;
|
|
63051
|
+
if (input.depositsEnabled === false && !canBeCollateral && !earns)
|
|
63052
|
+
return void 0;
|
|
63053
|
+
const rate = buildRate(input, "supply");
|
|
63054
|
+
const maturity = buildMaturity(input, now);
|
|
63055
|
+
const fees = buildFees(input, "supply");
|
|
63056
|
+
const availability = buildAvailability(input, "supply");
|
|
63057
|
+
const liquidity = isNum(input.totalLiquidity) ? {
|
|
63058
|
+
assets: input.totalLiquidity,
|
|
63059
|
+
assetsUsd: input.totalLiquidityUsd,
|
|
63060
|
+
ratio: isNum(input.totalDeposits) && input.totalDeposits > 0 ? clamp01(input.totalLiquidity / input.totalDeposits) : void 0
|
|
63061
|
+
} : void 0;
|
|
63062
|
+
const supply = {
|
|
63063
|
+
role: canBeCollateral ? earns ? "both" : "collateral" : "yield",
|
|
63064
|
+
rate,
|
|
63065
|
+
maturity,
|
|
63066
|
+
exit: {
|
|
63067
|
+
mode: "instant",
|
|
63068
|
+
settlement: "sync",
|
|
63069
|
+
liquidity,
|
|
63070
|
+
partialAllowed: true,
|
|
63071
|
+
priceRisk: "none",
|
|
63072
|
+
fees: fees.filter((f) => f.when === "exit" || f.when === "performance")
|
|
63073
|
+
},
|
|
63074
|
+
fees,
|
|
63075
|
+
backedBy: buildExposures2(input, siblings, "backing"),
|
|
63076
|
+
modes: buildModes(input),
|
|
63077
|
+
counterparty: {
|
|
63078
|
+
kind: "pool",
|
|
63079
|
+
solvency: "overcollateralized"
|
|
63080
|
+
},
|
|
63081
|
+
availability,
|
|
63082
|
+
principal: { protected: true, risks: [] },
|
|
63083
|
+
info: { headline: "", description: "", tags: [] }
|
|
63084
|
+
};
|
|
63085
|
+
return supply;
|
|
63086
|
+
}
|
|
63087
|
+
function buildBorrow(input, now, siblings) {
|
|
63088
|
+
const cfg = defaultConfig(input);
|
|
63089
|
+
const debtDisabled = cfg?.debtDisabled === true;
|
|
63090
|
+
if (input.borrowingEnabled === false && debtDisabled) return void 0;
|
|
63091
|
+
if (debtDisabled && (input.totalDebt ?? 0) === 0 && input.borrowingEnabled !== true)
|
|
63092
|
+
return void 0;
|
|
63093
|
+
const rate = buildRate(input, "borrow");
|
|
63094
|
+
const maturity = buildMaturity(input, now);
|
|
63095
|
+
const fees = buildFees(input, "borrow");
|
|
63096
|
+
const borrow = {
|
|
63097
|
+
rate,
|
|
63098
|
+
maturity,
|
|
63099
|
+
debtShape: rate.kind === "prepaid" ? "prepaid" : rate.kind === "fixed-term" ? "static-face" : "accruing",
|
|
63100
|
+
exit: {
|
|
63101
|
+
earlyRepay: "free",
|
|
63102
|
+
atMaturityCost: rate.kind === "fixed-term" ? "face" : "accrued",
|
|
63103
|
+
lateBehaviour: "none",
|
|
63104
|
+
partialAllowed: true,
|
|
63105
|
+
fees: fees.filter((f) => f.when === "exit" || f.when === "late")
|
|
63106
|
+
},
|
|
63107
|
+
liquidation: liquidationFrom(cfg, input),
|
|
63108
|
+
acceptedCollateral: buildExposures2(input, siblings, "accepted"),
|
|
63109
|
+
modes: buildModes(input),
|
|
63110
|
+
fees,
|
|
63111
|
+
counterparty: { kind: "pool", solvency: "overcollateralized" },
|
|
63112
|
+
availability: buildAvailability(input, "borrow"),
|
|
63113
|
+
info: { headline: "", description: "", tags: [] }
|
|
63114
|
+
};
|
|
63115
|
+
return borrow;
|
|
63116
|
+
}
|
|
63117
|
+
function isPlainObject(v) {
|
|
63118
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
63119
|
+
}
|
|
63120
|
+
function mergeDeep(base, patch) {
|
|
63121
|
+
if (!patch) return base;
|
|
63122
|
+
if (!isPlainObject(base) || !isPlainObject(patch)) return patch ?? base;
|
|
63123
|
+
const out = { ...base };
|
|
63124
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
63125
|
+
if (v === void 0) continue;
|
|
63126
|
+
const cur = out[k];
|
|
63127
|
+
out[k] = isPlainObject(v) && isPlainObject(cur) ? mergeDeep(cur, v) : v;
|
|
63128
|
+
}
|
|
63129
|
+
return out;
|
|
63130
|
+
}
|
|
63131
|
+
function finalizeInfo(sheet) {
|
|
63132
|
+
const market = { governance: sheet.governance, oracle: sheet.oracle };
|
|
63133
|
+
if (sheet.supply && !sheet.supply.info)
|
|
63134
|
+
sheet.supply.info = { headline: "", description: "", tags: [] };
|
|
63135
|
+
if (sheet.borrow && !sheet.borrow.info)
|
|
63136
|
+
sheet.borrow.info = { headline: "", description: "", tags: [] };
|
|
63137
|
+
if (sheet.supply) {
|
|
63138
|
+
sheet.supply.info = {
|
|
63139
|
+
headline: sheet.supply.info.headline || supplyHeadline(sheet.supply),
|
|
63140
|
+
description: sheet.supply.info.description || supplyDescription(sheet.supply, sheet),
|
|
63141
|
+
implications: sheet.supply.info.implications,
|
|
63142
|
+
tags: deriveSupplyTags(sheet.supply, market)
|
|
63143
|
+
};
|
|
63144
|
+
}
|
|
63145
|
+
if (sheet.borrow) {
|
|
63146
|
+
sheet.borrow.info = {
|
|
63147
|
+
headline: sheet.borrow.info.headline || borrowHeadline(sheet.borrow),
|
|
63148
|
+
description: sheet.borrow.info.description || borrowDescription(sheet.borrow),
|
|
63149
|
+
implications: sheet.borrow.info.implications,
|
|
63150
|
+
tags: deriveBorrowTags(sheet.borrow, market)
|
|
63151
|
+
};
|
|
63152
|
+
}
|
|
63153
|
+
return sheet;
|
|
63154
|
+
}
|
|
63155
|
+
function buildCoverage(sheet) {
|
|
63156
|
+
const present = [];
|
|
63157
|
+
const notApplicable = {};
|
|
63158
|
+
const pending = {};
|
|
63159
|
+
if (sheet.supply) present.push("supply");
|
|
63160
|
+
else notApplicable.supply = "no-supply-side";
|
|
63161
|
+
if (sheet.borrow) present.push("borrow");
|
|
63162
|
+
else notApplicable.borrow = "no-borrow-side";
|
|
63163
|
+
if (sheet.utilization) present.push("utilization");
|
|
63164
|
+
if (sheet.constraints) present.push("constraints");
|
|
63165
|
+
if (sheet.oracle) present.push("oracle");
|
|
63166
|
+
else if (sheet.borrow?.liquidation.trigger === "time")
|
|
63167
|
+
notApplicable.oracle = "time-based-liquidation";
|
|
63168
|
+
else pending.oracle = "not-classified-for-this-lender";
|
|
63169
|
+
if (sheet.governance) present.push("governance");
|
|
63170
|
+
else pending.governance = "screener-has-no-rows-for-this-market";
|
|
63171
|
+
return {
|
|
63172
|
+
present,
|
|
63173
|
+
notApplicable: Object.keys(notApplicable).length ? notApplicable : void 0,
|
|
63174
|
+
pending: Object.keys(pending).length ? pending : void 0
|
|
63175
|
+
};
|
|
63176
|
+
}
|
|
63177
|
+
function buildTermSheet(input, opts = {}) {
|
|
63178
|
+
const now = opts.now ?? Math.floor(Date.now() / 1e3);
|
|
63179
|
+
const siblings = opts.siblings ?? [];
|
|
63180
|
+
const base = {
|
|
63181
|
+
schemaVersion: TERM_SHEET_SCHEMA_VERSION,
|
|
63182
|
+
asOf: now,
|
|
63183
|
+
profileId: opts.profileId ?? DEFAULT_PROFILE_ID,
|
|
63184
|
+
marketUid: input.marketUid,
|
|
63185
|
+
lender: input.lender,
|
|
63186
|
+
chainId: input.chainId,
|
|
63187
|
+
supply: buildSupply(input, now, siblings),
|
|
63188
|
+
borrow: buildBorrow(input, now, siblings),
|
|
63189
|
+
utilization: buildUtilization(input),
|
|
63190
|
+
constraints: void 0
|
|
63191
|
+
};
|
|
63192
|
+
base.constraints = buildConstraints(
|
|
63193
|
+
input,
|
|
63194
|
+
siblings,
|
|
63195
|
+
base.borrow?.acceptedCollateral ?? base.supply?.backedBy
|
|
63196
|
+
);
|
|
63197
|
+
const merged = mergeDeep(base, opts.patch);
|
|
63198
|
+
for (const side of ["supply", "borrow"]) {
|
|
63199
|
+
if (merged[side] && !base[side]) merged[side] = void 0;
|
|
63200
|
+
}
|
|
63201
|
+
const withInfo = finalizeInfo(merged);
|
|
63202
|
+
withInfo.coverage = buildCoverage(withInfo);
|
|
63203
|
+
return withInfo;
|
|
63204
|
+
}
|
|
63205
|
+
|
|
63206
|
+
// src/terms/severity.ts
|
|
63207
|
+
var SEVERITY_ORDER = {
|
|
63208
|
+
critical: 0,
|
|
63209
|
+
warn: 1,
|
|
63210
|
+
info: 2
|
|
63211
|
+
};
|
|
63212
|
+
function rankFindings(findings) {
|
|
63213
|
+
return [...findings].sort(
|
|
63214
|
+
(a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity]
|
|
63215
|
+
);
|
|
63216
|
+
}
|
|
63217
|
+
function fmtSecs(secs) {
|
|
63218
|
+
if (secs >= 86400) {
|
|
63219
|
+
const d = secs / 86400;
|
|
63220
|
+
return `${d % 1 === 0 ? d : d.toFixed(1)} day${d === 1 ? "" : "s"}`;
|
|
63221
|
+
}
|
|
63222
|
+
if (secs >= 3600) {
|
|
63223
|
+
const h = secs / 3600;
|
|
63224
|
+
return `${h % 1 === 0 ? h : h.toFixed(1)} hour${h === 1 ? "" : "s"}`;
|
|
63225
|
+
}
|
|
63226
|
+
if (secs >= 60) {
|
|
63227
|
+
const m = Math.round(secs / 60);
|
|
63228
|
+
return `${m} minute${m === 1 ? "" : "s"}`;
|
|
63229
|
+
}
|
|
63230
|
+
return `${secs} second${secs === 1 ? "" : "s"}`;
|
|
63231
|
+
}
|
|
63232
|
+
function marketFindings(sheet) {
|
|
63233
|
+
const out = [];
|
|
63234
|
+
const { governance: g, oracle: o, utilization: u } = sheet;
|
|
63235
|
+
if (o?.band === "CRITICAL") {
|
|
63236
|
+
out.push({
|
|
63237
|
+
severity: "critical",
|
|
63238
|
+
id: "oracle-critical",
|
|
63239
|
+
side: "market",
|
|
63240
|
+
message: `The price oracle for this market scores CRITICAL for feed correctness${o.priceDescription ? ` (reports ${o.priceDescription})` : ""}.`
|
|
63241
|
+
});
|
|
63242
|
+
} else if (o?.band === "HIGH") {
|
|
63243
|
+
out.push({
|
|
63244
|
+
severity: "warn",
|
|
63245
|
+
id: "oracle-high",
|
|
63246
|
+
side: "market",
|
|
63247
|
+
message: "The price oracle for this market scores HIGH risk."
|
|
63248
|
+
});
|
|
63249
|
+
}
|
|
63250
|
+
if (o?.flags?.includes("wrong-asset")) {
|
|
63251
|
+
out.push({
|
|
63252
|
+
severity: "critical",
|
|
63253
|
+
id: "oracle-wrong-asset",
|
|
63254
|
+
side: "market",
|
|
63255
|
+
message: `The oracle does not price the intended asset${o.intendedPair ? ` (expected ${o.intendedPair})` : ""}.`
|
|
63256
|
+
});
|
|
63257
|
+
}
|
|
63258
|
+
if (o?.mutability?.mutable && !o.mutability.timelockSecs) {
|
|
63259
|
+
out.push({
|
|
63260
|
+
severity: "warn",
|
|
63261
|
+
id: "oracle-swappable",
|
|
63262
|
+
side: "market",
|
|
63263
|
+
message: "The price oracle can be replaced with no delay by its controller."
|
|
63264
|
+
});
|
|
63265
|
+
}
|
|
63266
|
+
if (g?.mutability === "governed") {
|
|
63267
|
+
if (g.controllerKind === "EOA") {
|
|
63268
|
+
out.push({
|
|
63269
|
+
severity: "critical",
|
|
63270
|
+
id: "governance-eoa",
|
|
63271
|
+
side: "market",
|
|
63272
|
+
message: "A single private key (an EOA, not a multisig or timelock) can change this market\u2019s parameters."
|
|
63273
|
+
});
|
|
63274
|
+
} else if (g.controllerKind === "TIMELOCK" && g.timelockUnknown) {
|
|
63275
|
+
out.push({
|
|
63276
|
+
severity: "info",
|
|
63277
|
+
id: "governance-timelock-unknown",
|
|
63278
|
+
side: "market",
|
|
63279
|
+
message: "Parameter changes go through a timelock, but its delay could not be read."
|
|
63280
|
+
});
|
|
63281
|
+
} else if (!g.timelockSecs) {
|
|
63282
|
+
out.push({
|
|
63283
|
+
severity: "warn",
|
|
63284
|
+
id: "governance-no-timelock",
|
|
63285
|
+
side: "market",
|
|
63286
|
+
message: `Parameters can be changed with no notice period${g.controllerKind ? ` by a ${g.controllerKind.toLowerCase()}` : ""}.`
|
|
63287
|
+
});
|
|
63288
|
+
}
|
|
63289
|
+
}
|
|
63290
|
+
if (u && u.utilization >= 0.98) {
|
|
63291
|
+
out.push({
|
|
63292
|
+
severity: "warn",
|
|
63293
|
+
id: "utilization-full",
|
|
63294
|
+
side: "market",
|
|
63295
|
+
message: `Utilization is ${(u.utilization * 100).toFixed(1)} % \u2014 withdrawals may not be possible until borrowers repay.`
|
|
63296
|
+
});
|
|
63297
|
+
}
|
|
63298
|
+
return out;
|
|
63299
|
+
}
|
|
63300
|
+
function supplyFindings(supply) {
|
|
63301
|
+
const out = [];
|
|
63302
|
+
if (supply.principal.risks.includes("first-loss")) {
|
|
63303
|
+
out.push({
|
|
63304
|
+
severity: "critical",
|
|
63305
|
+
id: "first-loss",
|
|
63306
|
+
side: "supply",
|
|
63307
|
+
message: "This is a junior/first-loss position \u2014 it absorbs losses before other depositors and can be marked down sharply."
|
|
63308
|
+
});
|
|
63309
|
+
}
|
|
63310
|
+
if (supply.principal.risks.includes("physical-delivery")) {
|
|
63311
|
+
out.push({
|
|
63312
|
+
severity: "critical",
|
|
63313
|
+
id: "physical-delivery",
|
|
63314
|
+
side: "supply",
|
|
63315
|
+
message: "On default you can be settled in collateral rather than the asset you lent."
|
|
63316
|
+
});
|
|
63317
|
+
}
|
|
63318
|
+
if (supply.counterparty.socializedLoss) {
|
|
63319
|
+
out.push({
|
|
63320
|
+
severity: "warn",
|
|
63321
|
+
id: "socialized-loss",
|
|
63322
|
+
side: "supply",
|
|
63323
|
+
message: "Bad debt is socialised across all lenders in this market, not borne by the defaulting borrower alone."
|
|
63324
|
+
});
|
|
63325
|
+
}
|
|
63326
|
+
if (supply.counterparty.solvency === "undercollateralized") {
|
|
63327
|
+
out.push({
|
|
63328
|
+
severity: "critical",
|
|
63329
|
+
id: "undercollateralized",
|
|
63330
|
+
side: "supply",
|
|
63331
|
+
message: "Borrowers here are not fully collateralised on-chain \u2014 repayment depends on off-chain credit."
|
|
63332
|
+
});
|
|
63333
|
+
}
|
|
63334
|
+
if (supply.counterparty.solvency === "nav-attested") {
|
|
63335
|
+
out.push({
|
|
63336
|
+
severity: "warn",
|
|
63337
|
+
id: "nav-attested",
|
|
63338
|
+
side: "supply",
|
|
63339
|
+
message: "The share price is published by an operator; there is no on-chain solvency invariant."
|
|
63340
|
+
});
|
|
63341
|
+
}
|
|
63342
|
+
if (supply.exit.priceRisk === "may-be-impossible") {
|
|
63343
|
+
out.push({
|
|
63344
|
+
severity: "critical",
|
|
63345
|
+
id: "exit-may-be-impossible",
|
|
63346
|
+
side: "supply",
|
|
63347
|
+
message: "Exiting early means selling on a market that can be empty \u2014 there may be no exit before maturity at any price."
|
|
63348
|
+
});
|
|
63349
|
+
} else if (supply.exit.priceRisk === "market-price") {
|
|
63350
|
+
out.push({
|
|
63351
|
+
severity: "warn",
|
|
63352
|
+
id: "exit-market-price",
|
|
63353
|
+
side: "supply",
|
|
63354
|
+
message: "Exiting early means selling at the prevailing market price, which can be below par."
|
|
63355
|
+
});
|
|
63356
|
+
}
|
|
63357
|
+
if (supply.exit.cooldownSecs) {
|
|
63358
|
+
out.push({
|
|
63359
|
+
severity: "warn",
|
|
63360
|
+
id: "exit-cooldown",
|
|
63361
|
+
side: "supply",
|
|
63362
|
+
message: `Withdrawals require a ${fmtSecs(supply.exit.cooldownSecs)} waiting period.`
|
|
63363
|
+
});
|
|
63364
|
+
} else if (supply.exit.settlement === "async" && supply.exit.mode !== "instant") {
|
|
63365
|
+
out.push({
|
|
63366
|
+
severity: "warn",
|
|
63367
|
+
id: "exit-async",
|
|
63368
|
+
side: "supply",
|
|
63369
|
+
message: "Withdrawals are not instant \u2014 they queue or need a request."
|
|
63370
|
+
});
|
|
63371
|
+
}
|
|
63372
|
+
const exitFee = supply.exit.fees.find(
|
|
63373
|
+
(f) => f.when === "exit" && f.value > 0 && !f.indicative
|
|
63374
|
+
);
|
|
63375
|
+
if (exitFee) {
|
|
63376
|
+
out.push({
|
|
63377
|
+
severity: "warn",
|
|
63378
|
+
id: "exit-fee",
|
|
63379
|
+
side: "supply",
|
|
63380
|
+
message: `Exiting costs ${exitFee.label.toLowerCase()}.`
|
|
63381
|
+
});
|
|
63382
|
+
}
|
|
63383
|
+
if (supply.exit.liquidity && supply.exit.liquidity.assets <= 0 && supply.exit.settlement === "sync") {
|
|
63384
|
+
out.push({
|
|
63385
|
+
severity: "warn",
|
|
63386
|
+
id: "no-liquidity",
|
|
63387
|
+
side: "supply",
|
|
63388
|
+
message: "There is no withdrawable liquidity right now \u2014 your deposit cannot be taken out until borrowers repay."
|
|
63389
|
+
});
|
|
63390
|
+
}
|
|
63391
|
+
if (supply.backedBy) {
|
|
63392
|
+
if ((supply.backedBy.worstRiskScore ?? 0) >= 4) {
|
|
63393
|
+
out.push({
|
|
63394
|
+
severity: "warn",
|
|
63395
|
+
id: "backing-quality",
|
|
63396
|
+
side: "supply",
|
|
63397
|
+
message: "Some collateral backing this market is low quality or thinly traded."
|
|
63398
|
+
});
|
|
63399
|
+
}
|
|
63400
|
+
if (supply.backedBy.worstOracleBand === "HIGH" || supply.backedBy.worstOracleBand === "CRITICAL") {
|
|
63401
|
+
out.push({
|
|
63402
|
+
severity: "warn",
|
|
63403
|
+
id: "backing-oracle",
|
|
63404
|
+
side: "supply",
|
|
63405
|
+
message: "Collateral backing this market is priced by an oracle flagged as risky."
|
|
63406
|
+
});
|
|
63407
|
+
}
|
|
63408
|
+
}
|
|
63409
|
+
const points = supply.rate.rewards?.filter((r) => r.indicative) ?? [];
|
|
63410
|
+
if (points.length > 0) {
|
|
63411
|
+
out.push({
|
|
63412
|
+
severity: "info",
|
|
63413
|
+
id: "points-rewards",
|
|
63414
|
+
side: "supply",
|
|
63415
|
+
message: "Part of the advertised return is a points program with no priceable value."
|
|
63416
|
+
});
|
|
63417
|
+
}
|
|
63418
|
+
if (!supply.availability.canOpen) {
|
|
63419
|
+
out.push({
|
|
63420
|
+
severity: "warn",
|
|
63421
|
+
id: "supply-closed",
|
|
63422
|
+
side: "supply",
|
|
63423
|
+
message: `Deposits are currently unavailable${supply.availability.blockedBy ? ` (${supply.availability.blockedBy.replace(/-/g, " ")})` : ""}.`
|
|
63424
|
+
});
|
|
63425
|
+
}
|
|
63426
|
+
return out;
|
|
63427
|
+
}
|
|
63428
|
+
function borrowFindings(borrow) {
|
|
63429
|
+
const out = [];
|
|
63430
|
+
const liq = borrow.liquidation;
|
|
63431
|
+
if (liq.seizure === "full-collateral") {
|
|
63432
|
+
out.push({
|
|
63433
|
+
severity: "critical",
|
|
63434
|
+
id: "full-collateral-seizure",
|
|
63435
|
+
side: "borrow",
|
|
63436
|
+
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."
|
|
63437
|
+
});
|
|
63438
|
+
}
|
|
63439
|
+
if (liq.trigger === "time" || liq.trigger === "price-and-time") {
|
|
63440
|
+
out.push({
|
|
63441
|
+
severity: "critical",
|
|
63442
|
+
id: "time-liquidation",
|
|
63443
|
+
side: "borrow",
|
|
63444
|
+
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.`
|
|
63445
|
+
});
|
|
63446
|
+
}
|
|
63447
|
+
if (liq.redeemable || liq.trigger === "redemption") {
|
|
63448
|
+
out.push({
|
|
63449
|
+
severity: "critical",
|
|
63450
|
+
id: "redeemable",
|
|
63451
|
+
side: "borrow",
|
|
63452
|
+
message: "Your collateral can be redeemed at par while the position is perfectly healthy, if you carry the lowest interest rate on this branch."
|
|
63453
|
+
});
|
|
63454
|
+
}
|
|
63455
|
+
if (borrow.maturity.atMaturity === "physical-delivery") {
|
|
63456
|
+
out.push({
|
|
63457
|
+
severity: "critical",
|
|
63458
|
+
id: "physical-delivery",
|
|
63459
|
+
side: "borrow",
|
|
63460
|
+
message: "After maturity plus the liquidation window, unpaid collateral is delivered to lenders."
|
|
63461
|
+
});
|
|
63462
|
+
}
|
|
63463
|
+
if (borrow.maturity.atMaturity === "default-seizure") {
|
|
63464
|
+
out.push({
|
|
63465
|
+
severity: "critical",
|
|
63466
|
+
id: "default-seizure",
|
|
63467
|
+
side: "borrow",
|
|
63468
|
+
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)}` : ""}.`
|
|
63469
|
+
});
|
|
63470
|
+
}
|
|
63471
|
+
if (borrow.maturity.atMaturity === "liquidatable") {
|
|
63472
|
+
out.push({
|
|
63473
|
+
severity: "critical",
|
|
63474
|
+
id: "past-due-liquidatable",
|
|
63475
|
+
side: "borrow",
|
|
63476
|
+
message: "Once maturity passes the loan is in default and can be liquidated regardless of its health or LTV."
|
|
63477
|
+
});
|
|
63478
|
+
}
|
|
63479
|
+
const latePenalty = borrow.fees.find((f) => f.when === "late" && f.value > 0);
|
|
63480
|
+
if (latePenalty) {
|
|
63481
|
+
out.push({
|
|
63482
|
+
severity: "warn",
|
|
63483
|
+
id: "late-penalty",
|
|
63484
|
+
side: "borrow",
|
|
63485
|
+
message: `Repaying late accrues ${latePenalty.label.toLowerCase()}.`
|
|
63486
|
+
});
|
|
63487
|
+
}
|
|
63488
|
+
if (borrow.exit.earlyRepay === "penalty") {
|
|
63489
|
+
out.push({
|
|
63490
|
+
severity: "warn",
|
|
63491
|
+
id: "early-repay-penalty",
|
|
63492
|
+
side: "borrow",
|
|
63493
|
+
message: "Repaying before maturity costs a penalty."
|
|
63494
|
+
});
|
|
63495
|
+
}
|
|
63496
|
+
if (borrow.exit.earlyRepay === "not-allowed") {
|
|
63497
|
+
out.push({
|
|
63498
|
+
severity: "warn",
|
|
63499
|
+
id: "no-early-repay",
|
|
63500
|
+
side: "borrow",
|
|
63501
|
+
message: "This loan cannot be repaid before maturity."
|
|
63502
|
+
});
|
|
63503
|
+
}
|
|
63504
|
+
if (borrow.debtShape === "static-face" && borrow.exit.atMaturityCost === "face") {
|
|
63505
|
+
out.push({
|
|
63506
|
+
severity: "info",
|
|
63507
|
+
id: "static-debt",
|
|
63508
|
+
side: "borrow",
|
|
63509
|
+
message: "The amount owed is fixed at trade time and does not accrue \u2014 repaying early does not reduce it."
|
|
63510
|
+
});
|
|
63511
|
+
}
|
|
63512
|
+
if (borrow.exit.overRepayReverts) {
|
|
63513
|
+
out.push({
|
|
63514
|
+
severity: "warn",
|
|
63515
|
+
id: "over-repay-reverts",
|
|
63516
|
+
side: "borrow",
|
|
63517
|
+
message: "Sending more than the exact debt REVERTS \u2014 the repayment must be sized precisely."
|
|
63518
|
+
});
|
|
63519
|
+
}
|
|
63520
|
+
if (borrow.debtShape === "prepaid") {
|
|
63521
|
+
out.push({
|
|
63522
|
+
severity: "warn",
|
|
63523
|
+
id: "prepaid-interest",
|
|
63524
|
+
side: "borrow",
|
|
63525
|
+
message: "Interest is prepaid in a separate token; running out triggers a forced top-up at a punitive rate and freezes withdrawals."
|
|
63526
|
+
});
|
|
63527
|
+
}
|
|
63528
|
+
const origination = borrow.fees.find((f) => f.when === "entry" && f.value > 0);
|
|
63529
|
+
if (origination) {
|
|
63530
|
+
out.push({
|
|
63531
|
+
severity: "info",
|
|
63532
|
+
id: "origination-fee",
|
|
63533
|
+
side: "borrow",
|
|
63534
|
+
message: `An upfront ${origination.label.toLowerCase()} is charged at borrow time.`
|
|
63535
|
+
});
|
|
63536
|
+
}
|
|
63537
|
+
if (borrow.acceptedCollateral) {
|
|
63538
|
+
if (borrow.acceptedCollateral.worstOracleBand === "HIGH" || borrow.acceptedCollateral.worstOracleBand === "CRITICAL") {
|
|
63539
|
+
out.push({
|
|
63540
|
+
severity: "warn",
|
|
63541
|
+
id: "collateral-oracle",
|
|
63542
|
+
side: "borrow",
|
|
63543
|
+
message: "Collateral you can post here is priced by an oracle flagged as risky \u2014 a mispricing can liquidate you."
|
|
63544
|
+
});
|
|
63545
|
+
}
|
|
63546
|
+
}
|
|
63547
|
+
if (!borrow.availability.canOpen) {
|
|
63548
|
+
out.push({
|
|
63549
|
+
severity: "warn",
|
|
63550
|
+
id: "borrow-closed",
|
|
63551
|
+
side: "borrow",
|
|
63552
|
+
message: `Borrowing is currently unavailable${borrow.availability.blockedBy ? ` (${borrow.availability.blockedBy.replace(/-/g, " ")})` : ""}.`
|
|
63553
|
+
});
|
|
63554
|
+
}
|
|
63555
|
+
if (borrow.availability.requires?.includes("caller-allowlist")) {
|
|
63556
|
+
out.push({
|
|
63557
|
+
severity: "warn",
|
|
63558
|
+
id: "caller-allowlist",
|
|
63559
|
+
side: "borrow",
|
|
63560
|
+
message: "Only governance-approved contracts may borrow here, which blocks leverage and other routed flows."
|
|
63561
|
+
});
|
|
63562
|
+
}
|
|
63563
|
+
return out;
|
|
63564
|
+
}
|
|
63565
|
+
function findingsFor(sheet, side) {
|
|
63566
|
+
const sideFindings = side === "supply" ? sheet.supply ? supplyFindings(sheet.supply) : [] : sheet.borrow ? borrowFindings(sheet.borrow) : [];
|
|
63567
|
+
return rankFindings([...sideFindings, ...marketFindings(sheet)]);
|
|
63568
|
+
}
|
|
63569
|
+
function hasCritical(sheet, side) {
|
|
63570
|
+
return findingsFor(sheet, side).some((f) => f.severity === "critical");
|
|
63571
|
+
}
|
|
63572
|
+
var exactlyAdapter = {
|
|
63573
|
+
id: "exactly",
|
|
63574
|
+
matches: isExactly,
|
|
63575
|
+
profileId: () => "exactly.fixed@v1",
|
|
63576
|
+
build: (input) => ({
|
|
63577
|
+
supply: {
|
|
63578
|
+
// Fixed deposits have no late penalty and no expiry — an unclaimed
|
|
63579
|
+
// matured deposit simply stops earning.
|
|
63580
|
+
maturity: { atMaturity: "stops-earning" },
|
|
63581
|
+
exit: {
|
|
63582
|
+
mode: "market-sale",
|
|
63583
|
+
settlement: "sync",
|
|
63584
|
+
// `withdrawAtMaturity` before maturity pays face / (1 + curve rate),
|
|
63585
|
+
// so the cost is deterministic but worsens as rates rise.
|
|
63586
|
+
priceRisk: "haircut-formula",
|
|
63587
|
+
partialAllowed: true
|
|
63588
|
+
},
|
|
63589
|
+
counterparty: { kind: "pool", solvency: "overcollateralized" }
|
|
63590
|
+
},
|
|
63591
|
+
borrow: {
|
|
63592
|
+
debtShape: "static-face",
|
|
63593
|
+
exit: {
|
|
63594
|
+
earlyRepay: "discount",
|
|
63595
|
+
atMaturityCost: "face",
|
|
63596
|
+
lateBehaviour: "penalty-accrues",
|
|
63597
|
+
partialAllowed: true
|
|
63598
|
+
},
|
|
63599
|
+
maturity: { atMaturity: "penalty-accrues" },
|
|
63600
|
+
liquidation: {
|
|
63601
|
+
// Price-based, but coupled to time through the penalty above.
|
|
63602
|
+
trigger: "price-and-time",
|
|
63603
|
+
seizure: "proportional"
|
|
63604
|
+
},
|
|
63605
|
+
counterparty: { kind: "pool", solvency: "overcollateralized" }
|
|
63606
|
+
},
|
|
63607
|
+
constraints: {
|
|
63608
|
+
// Exactly reuses `loanId` as the MATURITY, stringified — not a loan
|
|
63609
|
+
// counter. This is the trap the field exists to defuse.
|
|
63610
|
+
positionModel: "loan-id",
|
|
63611
|
+
positionIdMeaning: "The unix maturity timestamp of the fixed pool, as a string."
|
|
63612
|
+
}
|
|
63613
|
+
})
|
|
63614
|
+
};
|
|
63615
|
+
var midnightAdapter = {
|
|
63616
|
+
id: "midnight",
|
|
63617
|
+
matches: isMidnight,
|
|
63618
|
+
profileId: () => "midnight.orderbook@v1",
|
|
63619
|
+
build: () => ({
|
|
63620
|
+
supply: {
|
|
63621
|
+
exit: {
|
|
63622
|
+
mode: "market-sale",
|
|
63623
|
+
settlement: "sync",
|
|
63624
|
+
// Selling units into the bid side — there may be no bid.
|
|
63625
|
+
priceRisk: "market-price",
|
|
63626
|
+
partialAllowed: true
|
|
63627
|
+
},
|
|
63628
|
+
counterparty: {
|
|
63629
|
+
kind: "orderbook",
|
|
63630
|
+
solvency: "overcollateralized",
|
|
63631
|
+
// Bad debt is socialised to lenders via `lossFactor`.
|
|
63632
|
+
socializedLoss: true
|
|
63633
|
+
},
|
|
63634
|
+
principal: { protected: false, risks: ["bad-debt"] },
|
|
63635
|
+
maturity: { atMaturity: "stops-earning" }
|
|
63636
|
+
},
|
|
63637
|
+
borrow: {
|
|
63638
|
+
debtShape: "static-face",
|
|
63639
|
+
exit: {
|
|
63640
|
+
// Face value, 1:1, neither fee nor rebate — so paying early is not a
|
|
63641
|
+
// saving, it just gives up the time value.
|
|
63642
|
+
earlyRepay: "free",
|
|
63643
|
+
atMaturityCost: "face",
|
|
63644
|
+
lateBehaviour: "liquidatable",
|
|
63645
|
+
partialAllowed: true,
|
|
63646
|
+
overRepayReverts: true
|
|
63647
|
+
},
|
|
63648
|
+
maturity: { atMaturity: "liquidatable" },
|
|
63649
|
+
liquidation: { trigger: "price-and-time", seizure: "proportional" },
|
|
63650
|
+
counterparty: { kind: "orderbook", solvency: "overcollateralized" }
|
|
63651
|
+
},
|
|
63652
|
+
constraints: { positionModel: "account" }
|
|
63653
|
+
})
|
|
63654
|
+
};
|
|
63655
|
+
var termAdapter = {
|
|
63656
|
+
id: "term",
|
|
63657
|
+
matches: isTerm,
|
|
63658
|
+
profileId: () => "term.auction@v1",
|
|
63659
|
+
build: (input) => {
|
|
63660
|
+
const auction = input.fixedTerm?.auction;
|
|
63661
|
+
return {
|
|
63662
|
+
supply: {
|
|
63663
|
+
exit: {
|
|
63664
|
+
mode: "market-sale",
|
|
63665
|
+
settlement: "sync",
|
|
63666
|
+
priceRisk: "market-price",
|
|
63667
|
+
partialAllowed: true
|
|
63668
|
+
},
|
|
63669
|
+
counterparty: { kind: "auction", solvency: "overcollateralized" },
|
|
63670
|
+
availability: auction ? { canOpen: auction.canLend === true, window: auction } : void 0,
|
|
63671
|
+
maturity: { atMaturity: "stops-earning" }
|
|
63672
|
+
},
|
|
63673
|
+
borrow: {
|
|
63674
|
+
debtShape: "static-face",
|
|
63675
|
+
exit: {
|
|
63676
|
+
// No rebate — on a debt that never accrues, early repayment is
|
|
63677
|
+
// strictly worse than holding to maturity.
|
|
63678
|
+
earlyRepay: "free",
|
|
63679
|
+
atMaturityCost: "face",
|
|
63680
|
+
lateBehaviour: "liquidatable",
|
|
63681
|
+
partialAllowed: true
|
|
63682
|
+
},
|
|
63683
|
+
liquidation: { trigger: "price", seizure: "proportional" },
|
|
63684
|
+
counterparty: { kind: "auction", solvency: "overcollateralized" },
|
|
63685
|
+
availability: auction ? {
|
|
63686
|
+
canOpen: auction.canBorrow === true,
|
|
63687
|
+
blockedBy: auction.canBorrow ? void 0 : "auction-closed",
|
|
63688
|
+
window: auction,
|
|
63689
|
+
minSize: auction.minBorrowAmount
|
|
63690
|
+
} : void 0
|
|
63691
|
+
},
|
|
63692
|
+
constraints: { positionModel: "account" }
|
|
63693
|
+
};
|
|
63694
|
+
}
|
|
63695
|
+
};
|
|
63696
|
+
var termMaxAdapter = {
|
|
63697
|
+
id: "termmax",
|
|
63698
|
+
matches: isTermMax,
|
|
63699
|
+
profileId: () => "termmax.fixed@v1",
|
|
63700
|
+
build: () => ({
|
|
63701
|
+
supply: {
|
|
63702
|
+
exit: {
|
|
63703
|
+
mode: "market-sale",
|
|
63704
|
+
settlement: "sync",
|
|
63705
|
+
// Selling FT needs a maker order on the other side, which may not exist.
|
|
63706
|
+
priceRisk: "may-be-impossible",
|
|
63707
|
+
partialAllowed: true
|
|
63708
|
+
},
|
|
63709
|
+
counterparty: {
|
|
63710
|
+
kind: "orderbook",
|
|
63711
|
+
solvency: "overcollateralized",
|
|
63712
|
+
socializedLoss: true
|
|
63713
|
+
},
|
|
63714
|
+
principal: {
|
|
63715
|
+
protected: false,
|
|
63716
|
+
risks: ["physical-delivery", "bad-debt"]
|
|
63717
|
+
},
|
|
63718
|
+
maturity: { atMaturity: "physical-delivery" }
|
|
63719
|
+
},
|
|
63720
|
+
borrow: {
|
|
63721
|
+
// A raw uint128 face value with zero accrual.
|
|
63722
|
+
debtShape: "static-face",
|
|
63723
|
+
exit: {
|
|
63724
|
+
earlyRepay: "free",
|
|
63725
|
+
atMaturityCost: "face",
|
|
63726
|
+
lateBehaviour: "liquidatable",
|
|
63727
|
+
partialAllowed: true
|
|
63728
|
+
},
|
|
63729
|
+
maturity: { atMaturity: "physical-delivery", graceSecs: 7200 },
|
|
63730
|
+
liquidation: { trigger: "price-and-time", seizure: "proportional" },
|
|
63731
|
+
counterparty: { kind: "orderbook", solvency: "overcollateralized" }
|
|
63732
|
+
},
|
|
63733
|
+
constraints: {
|
|
63734
|
+
positionModel: "nft",
|
|
63735
|
+
positionIdMeaning: "The GT (loan) ERC-721 token id."
|
|
63736
|
+
}
|
|
63737
|
+
})
|
|
63738
|
+
};
|
|
63739
|
+
var tellerAdapter = {
|
|
63740
|
+
id: "teller",
|
|
63741
|
+
matches: isTeller,
|
|
63742
|
+
profileId: () => "teller.pool@v1",
|
|
63743
|
+
build: (input) => {
|
|
63744
|
+
const teller = input.market?.teller ?? {};
|
|
63745
|
+
const graceSecs = typeof teller.paymentDefaultDuration === "number" ? teller.paymentDefaultDuration : void 0;
|
|
63746
|
+
const attested = teller.requiresBorrowerAttestation === true;
|
|
63747
|
+
return {
|
|
63748
|
+
// Not a coverage hole: Teller genuinely has no price input.
|
|
63749
|
+
oracle: { kind: "none" },
|
|
63750
|
+
supply: {
|
|
63751
|
+
exit: { mode: "queued", settlement: "async", partialAllowed: true },
|
|
63752
|
+
counterparty: { kind: "pool", solvency: "undercollateralized" },
|
|
63753
|
+
principal: { protected: false, risks: ["bad-debt"] }
|
|
63754
|
+
},
|
|
63755
|
+
borrow: {
|
|
63756
|
+
debtShape: "accruing",
|
|
63757
|
+
exit: {
|
|
63758
|
+
// Free, pro-rata early repayment — but only a FULL close is
|
|
63759
|
+
// supported; there is no partial collateral withdrawal.
|
|
63760
|
+
earlyRepay: "free",
|
|
63761
|
+
atMaturityCost: "accrued",
|
|
63762
|
+
lateBehaviour: "default-seizure",
|
|
63763
|
+
partialAllowed: false
|
|
63764
|
+
},
|
|
63765
|
+
maturity: { atMaturity: "default-seizure", graceSecs },
|
|
63766
|
+
liquidation: {
|
|
63767
|
+
trigger: "time",
|
|
63768
|
+
seizure: "full-collateral",
|
|
63769
|
+
gracePeriodSecs: graceSecs
|
|
63770
|
+
},
|
|
63771
|
+
counterparty: { kind: "pool", solvency: "undercollateralized" },
|
|
63772
|
+
availability: attested ? { gating: "attestation", requires: ["token-approval"] } : void 0
|
|
63773
|
+
},
|
|
63774
|
+
constraints: {
|
|
63775
|
+
positionModel: "loan-id",
|
|
63776
|
+
positionIdMeaning: "The Teller bidId of this loan."
|
|
63777
|
+
}
|
|
63778
|
+
};
|
|
63779
|
+
}
|
|
63780
|
+
};
|
|
63781
|
+
var listaBrokerAdapter = {
|
|
63782
|
+
id: "lista-broker",
|
|
63783
|
+
matches: isListaDao,
|
|
63784
|
+
profileId: (input) => input.variableBorrowDisabled && input.terms?.length ? "lista.broker@v1" : "pool.variable@v1",
|
|
63785
|
+
build: (input) => {
|
|
63786
|
+
const brokered = input.variableBorrowDisabled === true && !!input.terms?.length;
|
|
63787
|
+
if (!brokered) return {};
|
|
63788
|
+
return {
|
|
63789
|
+
borrow: {
|
|
63790
|
+
debtShape: "accruing",
|
|
63791
|
+
exit: {
|
|
63792
|
+
earlyRepay: "penalty",
|
|
63793
|
+
atMaturityCost: "accrued",
|
|
63794
|
+
// A keeper refinances an unpaid loan into the variable position
|
|
63795
|
+
// rather than liquidating it.
|
|
63796
|
+
lateBehaviour: "refinanced",
|
|
63797
|
+
partialAllowed: true
|
|
63798
|
+
},
|
|
63799
|
+
maturity: { atMaturity: "refinanced" },
|
|
63800
|
+
liquidation: { trigger: "price", seizure: "proportional" },
|
|
63801
|
+
counterparty: {
|
|
63802
|
+
kind: "broker",
|
|
63803
|
+
address: input.market?.broker ?? void 0,
|
|
63804
|
+
solvency: "overcollateralized"
|
|
63805
|
+
}
|
|
63806
|
+
},
|
|
63807
|
+
constraints: {
|
|
63808
|
+
positionModel: "loan-id",
|
|
63809
|
+
positionIdMeaning: "The broker posId; the dynamic (flexible) loan uses the uint128 max sentinel."
|
|
63810
|
+
}
|
|
63811
|
+
};
|
|
63812
|
+
}
|
|
63813
|
+
};
|
|
63814
|
+
var liquityAdapter = {
|
|
63815
|
+
id: "liquity",
|
|
63816
|
+
matches: isLiquityFamily,
|
|
63817
|
+
profileId: () => "liquity.cdp@v1",
|
|
63818
|
+
build: (input) => {
|
|
63819
|
+
const liquity = input.market?.liquity ?? {};
|
|
63820
|
+
const minDebt = liquity.minDebt != null ? String(liquity.minDebt) : void 0;
|
|
63821
|
+
const minApr = typeof liquity.minAnnualInterestRate === "string" || typeof liquity.minAnnualInterestRate === "number" ? Number(liquity.minAnnualInterestRate) / 1e18 * 100 : void 0;
|
|
63822
|
+
const maxApr = typeof liquity.maxAnnualInterestRate === "string" || typeof liquity.maxAnnualInterestRate === "number" ? Number(liquity.maxAnnualInterestRate) / 1e18 * 100 : void 0;
|
|
63823
|
+
return {
|
|
63824
|
+
supply: {
|
|
63825
|
+
// The Stability Pool is an earn position, never collateral.
|
|
63826
|
+
role: "yield",
|
|
63827
|
+
exit: { mode: "instant", settlement: "sync", partialAllowed: true },
|
|
63828
|
+
counterparty: { kind: "cdp", solvency: "overcollateralized" },
|
|
63829
|
+
principal: { protected: false, risks: ["bad-debt"] }
|
|
63830
|
+
},
|
|
63831
|
+
borrow: {
|
|
63832
|
+
rate: {
|
|
63833
|
+
kind: "user-set",
|
|
63834
|
+
isLocked: false,
|
|
63835
|
+
source: "borrower",
|
|
63836
|
+
minApr,
|
|
63837
|
+
maxApr
|
|
63838
|
+
},
|
|
63839
|
+
debtShape: "accruing",
|
|
63840
|
+
exit: {
|
|
63841
|
+
earlyRepay: "free",
|
|
63842
|
+
atMaturityCost: "accrued",
|
|
63843
|
+
lateBehaviour: "none",
|
|
63844
|
+
partialAllowed: true,
|
|
63845
|
+
minDebt
|
|
63846
|
+
},
|
|
63847
|
+
liquidation: {
|
|
63848
|
+
trigger: "price",
|
|
63849
|
+
seizure: "proportional",
|
|
63850
|
+
// The field that makes redemption visible.
|
|
63851
|
+
redeemable: true
|
|
63852
|
+
},
|
|
63853
|
+
counterparty: { kind: "cdp", solvency: "overcollateralized" }
|
|
63854
|
+
},
|
|
63855
|
+
constraints: {
|
|
63856
|
+
positionModel: "cdp-id",
|
|
63857
|
+
positionIdMeaning: "The trove id (a keccak of owner + branch + index)."
|
|
63858
|
+
}
|
|
63859
|
+
};
|
|
63860
|
+
}
|
|
63861
|
+
};
|
|
63862
|
+
var riverAdapter = {
|
|
63863
|
+
id: "river",
|
|
63864
|
+
matches: isRiver,
|
|
63865
|
+
profileId: () => "river.cdp@v1",
|
|
63866
|
+
build: () => ({
|
|
63867
|
+
supply: {
|
|
63868
|
+
role: "yield",
|
|
63869
|
+
exit: { mode: "instant", settlement: "sync", partialAllowed: true },
|
|
63870
|
+
counterparty: { kind: "cdp", solvency: "overcollateralized" },
|
|
63871
|
+
principal: { protected: false, risks: ["bad-debt"] }
|
|
63872
|
+
},
|
|
63873
|
+
borrow: {
|
|
63874
|
+
rate: { kind: "zero-interest", isLocked: true, compounding: "none" },
|
|
63875
|
+
debtShape: "accruing",
|
|
63876
|
+
exit: {
|
|
63877
|
+
earlyRepay: "free",
|
|
63878
|
+
atMaturityCost: "accrued",
|
|
63879
|
+
lateBehaviour: "none",
|
|
63880
|
+
partialAllowed: true
|
|
63881
|
+
},
|
|
63882
|
+
liquidation: {
|
|
63883
|
+
trigger: "price",
|
|
63884
|
+
seizure: "proportional",
|
|
63885
|
+
redeemable: true
|
|
63886
|
+
},
|
|
63887
|
+
counterparty: { kind: "cdp", solvency: "overcollateralized" }
|
|
63888
|
+
},
|
|
63889
|
+
constraints: {
|
|
63890
|
+
// River troves are ADDRESS-keyed — there is no id to discover.
|
|
63891
|
+
positionModel: "account",
|
|
63892
|
+
positionIdMeaning: "Troves are keyed by owner address; there is no id."
|
|
63893
|
+
}
|
|
63894
|
+
})
|
|
63895
|
+
};
|
|
63896
|
+
var inverseAdapter = {
|
|
63897
|
+
id: "inverse",
|
|
63898
|
+
matches: isInverse,
|
|
63899
|
+
profileId: () => "inverse.firm@v1",
|
|
63900
|
+
build: () => ({
|
|
63901
|
+
borrow: {
|
|
63902
|
+
rate: { kind: "prepaid", isLocked: false, source: "oracle" },
|
|
63903
|
+
debtShape: "prepaid",
|
|
63904
|
+
exit: {
|
|
63905
|
+
earlyRepay: "free",
|
|
63906
|
+
atMaturityCost: "accrued",
|
|
63907
|
+
lateBehaviour: "none",
|
|
63908
|
+
partialAllowed: true
|
|
63909
|
+
},
|
|
63910
|
+
liquidation: { trigger: "price", seizure: "proportional" },
|
|
63911
|
+
counterparty: { kind: "cdp", solvency: "overcollateralized" },
|
|
63912
|
+
availability: {
|
|
63913
|
+
// Every CONTRACT caller must be allowlisted by a 48h governance vote,
|
|
63914
|
+
// which is what blocks leverage and other routed flows.
|
|
63915
|
+
requires: ["token-approval", "caller-allowlist"]
|
|
63916
|
+
}
|
|
63917
|
+
},
|
|
63918
|
+
constraints: {
|
|
63919
|
+
positionModel: "escrow",
|
|
63920
|
+
positionIdMeaning: "A per-user CREATE2 escrow address, computed rather than discovered."
|
|
63921
|
+
}
|
|
63922
|
+
})
|
|
63923
|
+
};
|
|
63924
|
+
var usddAdapter = {
|
|
63925
|
+
id: "usdd",
|
|
63926
|
+
matches: isUsdd,
|
|
63927
|
+
profileId: () => "usdd.cdp@v1",
|
|
63928
|
+
build: () => ({
|
|
63929
|
+
borrow: {
|
|
63930
|
+
rate: { kind: "variable-managed", source: "governance", isLocked: false },
|
|
63931
|
+
debtShape: "accruing",
|
|
63932
|
+
exit: {
|
|
63933
|
+
earlyRepay: "free",
|
|
63934
|
+
atMaturityCost: "accrued",
|
|
63935
|
+
lateBehaviour: "none",
|
|
63936
|
+
partialAllowed: true
|
|
63937
|
+
},
|
|
63938
|
+
liquidation: { trigger: "price", seizure: "proportional" },
|
|
63939
|
+
counterparty: { kind: "cdp", solvency: "overcollateralized" }
|
|
63940
|
+
},
|
|
63941
|
+
constraints: {
|
|
63942
|
+
positionModel: "cdp-id",
|
|
63943
|
+
positionIdMeaning: "The CDP id issued by the manager for this ilk."
|
|
63944
|
+
}
|
|
63945
|
+
})
|
|
63946
|
+
};
|
|
63947
|
+
var compoundV3Adapter = {
|
|
63948
|
+
id: "compound-v3",
|
|
63949
|
+
matches: isCompoundV3Type,
|
|
63950
|
+
profileId: () => "pool.variable@v1",
|
|
63951
|
+
build: (input) => {
|
|
63952
|
+
const isBase = (input.totalDebt ?? 0) > 0 || input.borrowingEnabled === true;
|
|
63953
|
+
if (isBase) return {};
|
|
63954
|
+
return {
|
|
63955
|
+
supply: {
|
|
63956
|
+
role: "collateral",
|
|
63957
|
+
rate: { kind: "none", apr: 0, aprTotal: 0, isLocked: false },
|
|
63958
|
+
// Collateral earns nothing in Comet and cannot be lent out.
|
|
63959
|
+
exit: { mode: "instant", settlement: "sync", partialAllowed: true }
|
|
63960
|
+
},
|
|
63961
|
+
// Explicitly no borrow side on a collateral row.
|
|
63962
|
+
borrow: void 0
|
|
63963
|
+
};
|
|
63964
|
+
}
|
|
63965
|
+
};
|
|
63966
|
+
var fluidAdapter = {
|
|
63967
|
+
id: "fluid",
|
|
63968
|
+
matches: isFluid,
|
|
63969
|
+
profileId: () => "pool.variable@v1",
|
|
63970
|
+
build: (input) => ({
|
|
63971
|
+
// Only claim the liquidity-layer basis when the layer totals are actually
|
|
63972
|
+
// present. The origin does not select `irmTotal*` today, and asserting a
|
|
63973
|
+
// basis we cannot verify is worse than reporting the plain market ratio —
|
|
63974
|
+
// the validator rejects it for exactly that reason.
|
|
63975
|
+
utilization: input.irmTotalDeposits != null && input.irmTotalDebt != null ? { basis: "liquidity-layer", lockupRatio: input.lockupRatio } : { lockupRatio: input.lockupRatio },
|
|
63976
|
+
constraints: {
|
|
63977
|
+
positionModel: "nft",
|
|
63978
|
+
positionIdMeaning: "The Fluid vault position NFT id."
|
|
63979
|
+
}
|
|
63980
|
+
})
|
|
63981
|
+
};
|
|
63982
|
+
var gearboxAdapter = {
|
|
63983
|
+
id: "gearbox",
|
|
63984
|
+
matches: isGearboxV3,
|
|
63985
|
+
profileId: () => "pool.variable@v1",
|
|
63986
|
+
build: () => ({
|
|
63987
|
+
utilization: { basis: "pool" },
|
|
63988
|
+
constraints: {
|
|
63989
|
+
positionModel: "sub-account",
|
|
63990
|
+
positionIdMeaning: "The credit-account address opened for this user."
|
|
63991
|
+
}
|
|
63992
|
+
})
|
|
63993
|
+
};
|
|
63994
|
+
|
|
63995
|
+
// src/terms/adapters/index.ts
|
|
63996
|
+
var TERM_ADAPTERS = [
|
|
63997
|
+
exactlyAdapter,
|
|
63998
|
+
midnightAdapter,
|
|
63999
|
+
termAdapter,
|
|
64000
|
+
termMaxAdapter,
|
|
64001
|
+
tellerAdapter,
|
|
64002
|
+
listaBrokerAdapter,
|
|
64003
|
+
liquityAdapter,
|
|
64004
|
+
riverAdapter,
|
|
64005
|
+
inverseAdapter,
|
|
64006
|
+
usddAdapter,
|
|
64007
|
+
compoundV3Adapter,
|
|
64008
|
+
fluidAdapter,
|
|
64009
|
+
gearboxAdapter
|
|
64010
|
+
];
|
|
64011
|
+
function resolveAdapter(lender) {
|
|
64012
|
+
return TERM_ADAPTERS.find((a) => a.matches(lender));
|
|
64013
|
+
}
|
|
64014
|
+
|
|
64015
|
+
// src/terms/stamp.ts
|
|
64016
|
+
function buildTermSheetsForGroup(rows, ctx = {}, opts = {}) {
|
|
64017
|
+
const inputs = rows.map((r) => toTermSheetInput(r, ctx));
|
|
64018
|
+
const out = /* @__PURE__ */ new Map();
|
|
64019
|
+
const index = opts.enrich === false ? void 0 : enrichmentIndexFromRows(rows);
|
|
64020
|
+
for (const input of inputs) {
|
|
64021
|
+
if (!input.marketUid) continue;
|
|
64022
|
+
const adapter = resolveAdapter(input.lender);
|
|
64023
|
+
const sheet = buildTermSheet(input, {
|
|
64024
|
+
now: opts.now,
|
|
64025
|
+
siblings: inputs,
|
|
64026
|
+
patch: adapter?.build(input),
|
|
64027
|
+
profileId: adapter?.profileId(input) ?? DEFAULT_PROFILE_ID
|
|
64028
|
+
});
|
|
64029
|
+
if (index) enrichTermSheet(sheet, index);
|
|
64030
|
+
if (opts.withImplications !== false) attachImplications(sheet);
|
|
64031
|
+
out.set(input.marketUid, sheet);
|
|
64032
|
+
}
|
|
64033
|
+
return out;
|
|
64034
|
+
}
|
|
64035
|
+
function attachImplications(sheet) {
|
|
64036
|
+
if (sheet.supply) {
|
|
64037
|
+
const found = findingsFor(sheet, "supply").filter((f) => f.severity !== "info").map((f) => f.message);
|
|
64038
|
+
if (found.length) sheet.supply.info.implications = found;
|
|
64039
|
+
}
|
|
64040
|
+
if (sheet.borrow) {
|
|
64041
|
+
const found = findingsFor(sheet, "borrow").filter((f) => f.severity !== "info").map((f) => f.message);
|
|
64042
|
+
if (found.length) sheet.borrow.info.implications = found;
|
|
64043
|
+
}
|
|
64044
|
+
return sheet;
|
|
64045
|
+
}
|
|
64046
|
+
var BAND_ORDER = ["LOW", "MEDIUM", "HIGH", "CRITICAL"];
|
|
64047
|
+
function enrichmentIndexFromRows(rows) {
|
|
64048
|
+
const oracleByMarketUid = /* @__PURE__ */ new Map();
|
|
64049
|
+
const governanceByMarketUid = /* @__PURE__ */ new Map();
|
|
64050
|
+
const assetRisk = {};
|
|
64051
|
+
for (const row of rows) {
|
|
64052
|
+
const uid = String(row?.marketUid ?? "");
|
|
64053
|
+
if (!uid) continue;
|
|
64054
|
+
const feeds = Array.isArray(row?.oracleInfo?.feeds) ? row.oracleInfo.feeds : [];
|
|
64055
|
+
if (feeds.length > 0) {
|
|
64056
|
+
const primary = feeds.reduce(
|
|
64057
|
+
(worst, f) => BAND_ORDER.indexOf(String(f?.band)) > BAND_ORDER.indexOf(String(worst?.band)) ? f : worst
|
|
64058
|
+
);
|
|
64059
|
+
oracleByMarketUid.set(uid, {
|
|
64060
|
+
marketUid: uid,
|
|
64061
|
+
oracle: primary?.oracle ?? void 0,
|
|
64062
|
+
provider: primary?.provider ?? void 0,
|
|
64063
|
+
priceDescription: primary?.priceDescription ?? void 0,
|
|
64064
|
+
intendedPair: primary?.intendedPair ?? void 0,
|
|
64065
|
+
correctOracle: primary?.correctOracle ?? void 0,
|
|
64066
|
+
denominatorMatch: primary?.denominatorMatch ?? void 0,
|
|
64067
|
+
fixedRate: primary?.fixedRate ?? void 0,
|
|
64068
|
+
score: primary?.score ?? row?.oracleInfo?.worstScore,
|
|
64069
|
+
band: primary?.band ?? row?.oracleInfo?.worstBand,
|
|
64070
|
+
flags: Array.isArray(primary?.flags) ? primary.flags : void 0,
|
|
64071
|
+
components: feeds.length > 1 ? feeds.map((f) => String(f?.oracle ?? "")).filter(Boolean) : void 0
|
|
64072
|
+
});
|
|
64073
|
+
}
|
|
64074
|
+
const breakdown = Array.isArray(row?.risk?.breakdown) ? row.risk.breakdown : [];
|
|
64075
|
+
const gov = breakdown.find((b) => b?.category === "governance");
|
|
64076
|
+
if (gov) {
|
|
64077
|
+
governanceByMarketUid.set(uid, {
|
|
64078
|
+
marketUid: uid,
|
|
64079
|
+
tier: gov.tier ?? void 0,
|
|
64080
|
+
score: gov.score ?? void 0,
|
|
64081
|
+
ownerKind: gov.ownerKind ?? void 0,
|
|
64082
|
+
signerThreshold: gov.signerThreshold ?? null,
|
|
64083
|
+
signerCount: gov.signerCount ?? null,
|
|
64084
|
+
mode: gov.mode ?? void 0,
|
|
64085
|
+
// The screen resolves the governance ROOT but does not carry the
|
|
64086
|
+
// timelock delay through to this shape, so the notice period stays
|
|
64087
|
+
// genuinely unknown — see `coverage.pending`.
|
|
64088
|
+
delaySeconds: gov.delaySeconds ?? null
|
|
64089
|
+
});
|
|
64090
|
+
}
|
|
64091
|
+
const token = breakdown.find((b) => b?.category === "token");
|
|
64092
|
+
const chainId = String(row?.chainId ?? uid.split(":")[1] ?? "");
|
|
64093
|
+
const address = String(
|
|
64094
|
+
row?.asset?.address ?? row?.underlying ?? uid.split(":")[2] ?? ""
|
|
64095
|
+
).toLowerCase();
|
|
64096
|
+
if (token?.score != null && chainId && address) {
|
|
64097
|
+
assetRisk[chainId] ??= {};
|
|
64098
|
+
assetRisk[chainId][address] = {
|
|
64099
|
+
riskScore: Number(token.score),
|
|
64100
|
+
source: token.label ?? void 0
|
|
64101
|
+
};
|
|
64102
|
+
}
|
|
64103
|
+
}
|
|
64104
|
+
return { oracleByMarketUid, governanceByMarketUid, assetRisk };
|
|
64105
|
+
}
|
|
64106
|
+
function toDigest(sheet) {
|
|
64107
|
+
const strip = (e) => e ? (({ items, ...rest }) => rest)(e) : void 0;
|
|
64108
|
+
return {
|
|
64109
|
+
schemaVersion: sheet.schemaVersion,
|
|
64110
|
+
profileId: sheet.profileId,
|
|
64111
|
+
marketUid: sheet.marketUid,
|
|
64112
|
+
supply: sheet.supply ? {
|
|
64113
|
+
rateKind: sheet.supply.rate.kind,
|
|
64114
|
+
aprTotal: sheet.supply.rate.aprTotal,
|
|
64115
|
+
maturityKind: sheet.supply.maturity.kind,
|
|
64116
|
+
maturity: sheet.supply.maturity.maturity,
|
|
64117
|
+
exitMode: sheet.supply.exit.mode,
|
|
64118
|
+
settlement: sheet.supply.exit.settlement,
|
|
64119
|
+
canOpen: sheet.supply.availability.canOpen,
|
|
64120
|
+
headline: sheet.supply.info.headline,
|
|
64121
|
+
tags: sheet.supply.info.tags,
|
|
64122
|
+
backedBy: strip(sheet.supply.backedBy)
|
|
64123
|
+
} : void 0,
|
|
64124
|
+
borrow: sheet.borrow ? {
|
|
64125
|
+
rateKind: sheet.borrow.rate.kind,
|
|
64126
|
+
apr: sheet.borrow.rate.apr,
|
|
64127
|
+
maturityKind: sheet.borrow.maturity.kind,
|
|
64128
|
+
maturity: sheet.borrow.maturity.maturity,
|
|
64129
|
+
debtShape: sheet.borrow.debtShape,
|
|
64130
|
+
earlyRepay: sheet.borrow.exit.earlyRepay,
|
|
64131
|
+
liquidationTrigger: sheet.borrow.liquidation.trigger,
|
|
64132
|
+
canOpen: sheet.borrow.availability.canOpen,
|
|
64133
|
+
headline: sheet.borrow.info.headline,
|
|
64134
|
+
tags: sheet.borrow.info.tags,
|
|
64135
|
+
acceptedCollateral: strip(sheet.borrow.acceptedCollateral)
|
|
64136
|
+
} : void 0,
|
|
64137
|
+
oracle: sheet.oracle ? {
|
|
64138
|
+
kind: sheet.oracle.kind,
|
|
64139
|
+
address: sheet.oracle.address,
|
|
64140
|
+
provider: sheet.oracle.provider,
|
|
64141
|
+
band: sheet.oracle.band
|
|
64142
|
+
} : void 0,
|
|
64143
|
+
governance: sheet.governance ? {
|
|
64144
|
+
mutability: sheet.governance.mutability,
|
|
64145
|
+
controllerKind: sheet.governance.controllerKind,
|
|
64146
|
+
timelockSecs: sheet.governance.timelockSecs,
|
|
64147
|
+
tier: sheet.governance.tier
|
|
64148
|
+
} : void 0,
|
|
64149
|
+
utilization: sheet.utilization?.utilization
|
|
64150
|
+
};
|
|
64151
|
+
}
|
|
64152
|
+
function enrichTermSheet(sheet, index) {
|
|
64153
|
+
const uid = sheet.marketUid;
|
|
64154
|
+
if (!uid) return sheet;
|
|
64155
|
+
const oracleRow = index.oracleByMarketUid?.get(uid);
|
|
64156
|
+
if (oracleRow?.oracle && sheet.oracle?.kind !== "none") {
|
|
64157
|
+
sheet.oracle = {
|
|
64158
|
+
...sheet.oracle,
|
|
64159
|
+
kind: "price-feed",
|
|
64160
|
+
address: oracleRow.oracle.toLowerCase(),
|
|
64161
|
+
provider: oracleRow.provider,
|
|
64162
|
+
priceDescription: oracleRow.priceDescription,
|
|
64163
|
+
intendedPair: oracleRow.intendedPair,
|
|
64164
|
+
correctAsset: oracleRow.correctOracle ?? void 0,
|
|
64165
|
+
correctNumeraire: oracleRow.denominatorMatch ?? void 0,
|
|
64166
|
+
fixedRate: oracleRow.fixedRate,
|
|
64167
|
+
score: oracleRow.score,
|
|
64168
|
+
band: oracleRow.band,
|
|
64169
|
+
flags: oracleRow.flags,
|
|
64170
|
+
components: oracleRow.components
|
|
64171
|
+
};
|
|
64172
|
+
}
|
|
64173
|
+
const govRow = index.governanceByMarketUid?.get(uid);
|
|
64174
|
+
if (govRow) {
|
|
64175
|
+
const immutable = govRow.mode === "immutable";
|
|
64176
|
+
sheet.governance = {
|
|
64177
|
+
...sheet.governance,
|
|
64178
|
+
mutability: immutable ? "immutable" : "governed",
|
|
64179
|
+
controllerKind: govRow.ownerKind,
|
|
64180
|
+
safe: govRow.signerThreshold != null && govRow.signerCount != null ? { threshold: govRow.signerThreshold, owners: govRow.signerCount } : void 0,
|
|
64181
|
+
// Only meaningful on a TIMELOCK root — see the validator rule.
|
|
64182
|
+
timelockSecs: govRow.ownerKind === "TIMELOCK" && govRow.delaySeconds != null ? govRow.delaySeconds : void 0,
|
|
64183
|
+
timelockSource: govRow.delaySeconds != null ? "screened" : void 0,
|
|
64184
|
+
// Three states, not two. A TIMELOCK root whose delay we could not read
|
|
64185
|
+
// is NOT the same as "no notice period" — claiming the latter would be
|
|
64186
|
+
// a false alarm on the safest governance shape we screen for.
|
|
64187
|
+
timelockUnknown: govRow.ownerKind === "TIMELOCK" && govRow.delaySeconds == null ? true : void 0,
|
|
64188
|
+
tier: govRow.tier,
|
|
64189
|
+
score: govRow.score
|
|
64190
|
+
};
|
|
64191
|
+
if (immutable) {
|
|
64192
|
+
sheet.governance.timelockSecs = void 0;
|
|
64193
|
+
sheet.governance.powers = void 0;
|
|
64194
|
+
}
|
|
64195
|
+
}
|
|
64196
|
+
const quality = index.assetRisk;
|
|
64197
|
+
for (const exp of [
|
|
64198
|
+
sheet.supply?.backedBy,
|
|
64199
|
+
sheet.borrow?.acceptedCollateral
|
|
64200
|
+
]) {
|
|
64201
|
+
if (!exp) continue;
|
|
64202
|
+
for (const item of exp.items) {
|
|
64203
|
+
const chain = item.asset.chainId;
|
|
64204
|
+
const addr2 = item.asset.address;
|
|
64205
|
+
const q = quality?.[chain]?.[addr2];
|
|
64206
|
+
if (q) {
|
|
64207
|
+
item.quality = {
|
|
64208
|
+
riskScore: q.riskScore,
|
|
64209
|
+
source: q.source,
|
|
64210
|
+
liquidityUsd: q.liquidityUsd,
|
|
64211
|
+
governanceScore: q.governanceScore,
|
|
64212
|
+
governanceLevel: q.governanceLevel,
|
|
64213
|
+
upgradeable: q.upgradeable,
|
|
64214
|
+
canPause: q.canPause,
|
|
64215
|
+
adminKind: q.adminKind
|
|
64216
|
+
};
|
|
64217
|
+
}
|
|
64218
|
+
const itemOracle = item.marketUid ? index.oracleByMarketUid?.get(item.marketUid) : void 0;
|
|
64219
|
+
if (itemOracle?.oracle) {
|
|
64220
|
+
item.oracle = {
|
|
64221
|
+
kind: "price-feed",
|
|
64222
|
+
address: itemOracle.oracle.toLowerCase(),
|
|
64223
|
+
provider: itemOracle.provider,
|
|
64224
|
+
band: itemOracle.band,
|
|
64225
|
+
score: itemOracle.score,
|
|
64226
|
+
flags: itemOracle.flags
|
|
64227
|
+
};
|
|
64228
|
+
}
|
|
64229
|
+
}
|
|
64230
|
+
const scores = exp.items.map((i) => i.quality?.riskScore).filter((n) => n != null);
|
|
64231
|
+
if (scores.length) exp.worstRiskScore = Math.max(...scores);
|
|
64232
|
+
const BAND_ORDER2 = ["LOW", "MEDIUM", "HIGH", "CRITICAL"];
|
|
64233
|
+
const bands = exp.items.map((i) => i.oracle?.band).filter((b) => !!b);
|
|
64234
|
+
if (bands.length) {
|
|
64235
|
+
exp.worstOracleBand = bands.reduce(
|
|
64236
|
+
(worst, b) => BAND_ORDER2.indexOf(b) > BAND_ORDER2.indexOf(worst) ? b : worst
|
|
64237
|
+
);
|
|
64238
|
+
}
|
|
64239
|
+
}
|
|
64240
|
+
if (sheet.coverage) {
|
|
64241
|
+
const present = new Set(sheet.coverage.present);
|
|
64242
|
+
const pending = { ...sheet.coverage.pending ?? {} };
|
|
64243
|
+
if (sheet.oracle) {
|
|
64244
|
+
present.add("oracle");
|
|
64245
|
+
delete pending.oracle;
|
|
64246
|
+
}
|
|
64247
|
+
if (sheet.governance) {
|
|
64248
|
+
present.add("governance");
|
|
64249
|
+
delete pending.governance;
|
|
64250
|
+
if (sheet.governance.timelockUnknown)
|
|
64251
|
+
pending["governance.timelockSecs"] = "delay-not-carried-by-screen";
|
|
64252
|
+
}
|
|
64253
|
+
sheet.coverage = {
|
|
64254
|
+
...sheet.coverage,
|
|
64255
|
+
present: [...present],
|
|
64256
|
+
pending: Object.keys(pending).length ? pending : void 0
|
|
64257
|
+
};
|
|
64258
|
+
}
|
|
64259
|
+
finalizeInfo(sheet);
|
|
64260
|
+
attachImplications(sheet);
|
|
64261
|
+
return sheet;
|
|
64262
|
+
}
|
|
64263
|
+
|
|
64264
|
+
// src/terms/validate.ts
|
|
64265
|
+
var approx = (a, b, tol = 1e-6) => Math.abs(a - b) <= tol;
|
|
64266
|
+
function validateTermSheet(sheet) {
|
|
64267
|
+
const v = [];
|
|
64268
|
+
const uid = sheet.marketUid;
|
|
64269
|
+
const fail = (rule, message) => v.push({ rule, message, marketUid: uid });
|
|
64270
|
+
if (sheet.schemaVersion !== TERM_SHEET_SCHEMA_VERSION)
|
|
64271
|
+
fail("schema-version", `unexpected schemaVersion ${sheet.schemaVersion}`);
|
|
64272
|
+
if (!sheet.profileId) fail("profile-id", "missing profileId");
|
|
64273
|
+
for (const side of ["supply", "borrow"]) {
|
|
64274
|
+
const s = sheet[side];
|
|
64275
|
+
if (!s) continue;
|
|
64276
|
+
const c = s.rate.components;
|
|
64277
|
+
const expected = (c.base ?? 0) + (c.rewards ?? 0) + (c.intrinsic ?? 0);
|
|
64278
|
+
if (!approx(s.rate.aprTotal, expected, 1e-6))
|
|
64279
|
+
fail(
|
|
64280
|
+
"apr-total",
|
|
64281
|
+
`${side}.rate.aprTotal ${s.rate.aprTotal} != base+rewards+intrinsic ${expected}`
|
|
64282
|
+
);
|
|
64283
|
+
const priceable = (s.rate.rewards ?? []).filter((r) => !r.indicative).reduce((sum, r) => sum + r.apr, 0);
|
|
64284
|
+
if (s.rate.rewards && !approx(c.rewards ?? 0, priceable, 1e-6))
|
|
64285
|
+
fail(
|
|
64286
|
+
"rewards-priceable",
|
|
64287
|
+
`${side}.rate.components.rewards ${c.rewards} != sum of non-indicative rewards ${priceable}`
|
|
64288
|
+
);
|
|
64289
|
+
if (s.rate.basis !== "apr-nominal")
|
|
64290
|
+
fail("rate-basis", `${side}.rate.basis must be apr-nominal`);
|
|
64291
|
+
if (s.maturity.kind === "fixed-date" && s.maturity.maturity == null)
|
|
64292
|
+
fail(
|
|
64293
|
+
"maturity-missing",
|
|
64294
|
+
`${side}.maturity.kind is fixed-date with no maturity`
|
|
64295
|
+
);
|
|
64296
|
+
if ((s.rate.kind === "fixed-term" || s.rate.kind === "fixed-open") && s.rate.kind === "fixed-term" && s.maturity.kind === "perpetual")
|
|
64297
|
+
fail(
|
|
64298
|
+
"fixed-term-perpetual",
|
|
64299
|
+
`${side} is fixed-term but maturity.kind is perpetual`
|
|
64300
|
+
);
|
|
64301
|
+
if (s.availability.canOpen === false && !s.availability.blockedBy)
|
|
64302
|
+
fail(
|
|
64303
|
+
"blocked-reason",
|
|
64304
|
+
`${side}.availability.canOpen is false with no blockedBy`
|
|
64305
|
+
);
|
|
64306
|
+
if (!s.info.headline) fail("headline", `${side}.info.headline is empty`);
|
|
64307
|
+
if (!Array.isArray(s.info.tags))
|
|
64308
|
+
fail("tags", `${side}.info.tags is not an array`);
|
|
64309
|
+
}
|
|
64310
|
+
if (sheet.supply) {
|
|
64311
|
+
const e = sheet.supply.exit;
|
|
64312
|
+
if (e.mode === "instant") {
|
|
64313
|
+
if (e.settlement !== "sync")
|
|
64314
|
+
fail("instant-sync", "exit.mode instant must be settlement sync");
|
|
64315
|
+
if (e.cooldownSecs)
|
|
64316
|
+
fail("instant-cooldown", "exit.mode instant must not carry a cooldown");
|
|
64317
|
+
}
|
|
64318
|
+
}
|
|
64319
|
+
if (sheet.borrow) {
|
|
64320
|
+
const l = sheet.borrow.liquidation;
|
|
64321
|
+
if (l.seizure === "full-collateral") {
|
|
64322
|
+
const impl = sheet.borrow.info.implications ?? [];
|
|
64323
|
+
const tags = sheet.borrow.info.tags ?? [];
|
|
64324
|
+
if (!tags.includes("full-collateral-seizure") && impl.length === 0)
|
|
64325
|
+
fail(
|
|
64326
|
+
"full-seizure-warning",
|
|
64327
|
+
"full-collateral seizure must surface a tag or an implication"
|
|
64328
|
+
);
|
|
64329
|
+
}
|
|
64330
|
+
if (l.penalty < 0 || l.penalty > 1)
|
|
64331
|
+
fail("penalty-range", `liquidation.penalty ${l.penalty} outside 0..1`);
|
|
64332
|
+
if (l.closeFactor <= 0 || l.closeFactor > 1)
|
|
64333
|
+
fail(
|
|
64334
|
+
"close-factor-range",
|
|
64335
|
+
`liquidation.closeFactor ${l.closeFactor} outside 0..1`
|
|
64336
|
+
);
|
|
64337
|
+
}
|
|
64338
|
+
for (const [label, exp] of [
|
|
64339
|
+
["supply.backedBy", sheet.supply?.backedBy],
|
|
64340
|
+
["borrow.acceptedCollateral", sheet.borrow?.acceptedCollateral]
|
|
64341
|
+
]) {
|
|
64342
|
+
if (!exp) continue;
|
|
64343
|
+
if (exp.count !== exp.items.length && exp.items.length > 0)
|
|
64344
|
+
fail(
|
|
64345
|
+
"exposure-count",
|
|
64346
|
+
`${label}.count ${exp.count} != items ${exp.items.length}`
|
|
64347
|
+
);
|
|
64348
|
+
if (exp.weightBasis === "unweighted") {
|
|
64349
|
+
if (exp.items.some((i) => i.weightPct != null))
|
|
64350
|
+
fail(
|
|
64351
|
+
"unweighted-has-weight",
|
|
64352
|
+
`${label} is unweighted but an item carries weightPct`
|
|
64353
|
+
);
|
|
64354
|
+
if (exp.topWeightPct != null)
|
|
64355
|
+
fail(
|
|
64356
|
+
"unweighted-top-weight",
|
|
64357
|
+
`${label} is unweighted but has topWeightPct`
|
|
64358
|
+
);
|
|
64359
|
+
} else if (exp.items.length > 0) {
|
|
64360
|
+
const sum = exp.items.reduce((s, i) => s + (i.weightPct ?? 0), 0);
|
|
64361
|
+
if (Math.abs(sum - 100) > 0.5)
|
|
64362
|
+
fail(
|
|
64363
|
+
"weight-sum",
|
|
64364
|
+
`${label} weights sum to ${sum.toFixed(2)}, expected ~100`
|
|
64365
|
+
);
|
|
64366
|
+
}
|
|
64367
|
+
if (exp.items.length > 0) {
|
|
64368
|
+
const scores = exp.items.map((i) => i.quality?.riskScore).filter((n) => n != null);
|
|
64369
|
+
if (scores.length > 0 && exp.worstRiskScore != null) {
|
|
64370
|
+
const worst = Math.max(...scores);
|
|
64371
|
+
if (exp.worstRiskScore !== worst)
|
|
64372
|
+
fail(
|
|
64373
|
+
"exposure-worst-score",
|
|
64374
|
+
`${label}.worstRiskScore ${exp.worstRiskScore} != actual worst ${worst}`
|
|
64375
|
+
);
|
|
64376
|
+
}
|
|
64377
|
+
}
|
|
64378
|
+
}
|
|
64379
|
+
const u = sheet.utilization;
|
|
64380
|
+
if (u) {
|
|
64381
|
+
if (u.utilization < 0 || u.utilization > 1)
|
|
64382
|
+
fail("utilization-range", `utilization ${u.utilization} outside 0..1`);
|
|
64383
|
+
if (u.basis !== "market") {
|
|
64384
|
+
if (u.irmTotalDeposits == null || u.irmTotalDebt == null)
|
|
64385
|
+
fail(
|
|
64386
|
+
"irm-basis-totals",
|
|
64387
|
+
`utilization.basis is ${u.basis} but irmTotal* are missing`
|
|
64388
|
+
);
|
|
64389
|
+
else if (u.irmTotalDeposits > 0) {
|
|
64390
|
+
const derived = u.irmTotalDebt / u.irmTotalDeposits;
|
|
64391
|
+
if (Math.abs(derived - u.utilization) > 0.02)
|
|
64392
|
+
fail(
|
|
64393
|
+
"irm-basis-mismatch",
|
|
64394
|
+
`irmTotalDebt/irmTotalDeposits ${derived.toFixed(4)} != utilization ${u.utilization.toFixed(4)}`
|
|
64395
|
+
);
|
|
64396
|
+
}
|
|
64397
|
+
}
|
|
64398
|
+
}
|
|
64399
|
+
const o = sheet.oracle;
|
|
64400
|
+
if (o) {
|
|
64401
|
+
if (o.kind === "none" && o.address)
|
|
64402
|
+
fail("oracle-none-address", "oracle.kind none must not carry an address");
|
|
64403
|
+
if (o.kind === "none" && sheet.borrow && sheet.borrow.liquidation.trigger !== "time")
|
|
64404
|
+
fail(
|
|
64405
|
+
"no-oracle-non-time",
|
|
64406
|
+
`oracle.kind none but liquidation.trigger is ${sheet.borrow.liquidation.trigger}`
|
|
64407
|
+
);
|
|
64408
|
+
if (o.address && o.address !== o.address.toLowerCase())
|
|
64409
|
+
fail("oracle-lowercase", "oracle.address must be lowercased");
|
|
64410
|
+
}
|
|
64411
|
+
const g = sheet.governance;
|
|
64412
|
+
if (g) {
|
|
64413
|
+
if (g.mutability === "immutable") {
|
|
64414
|
+
if (g.timelockSecs)
|
|
64415
|
+
fail(
|
|
64416
|
+
"immutable-timelock",
|
|
64417
|
+
"immutable governance must not carry a timelock"
|
|
64418
|
+
);
|
|
64419
|
+
if (g.powers?.length)
|
|
64420
|
+
fail("immutable-powers", "immutable governance must not carry powers");
|
|
64421
|
+
}
|
|
64422
|
+
if (g.timelockSecs && g.controllerKind && g.controllerKind !== "TIMELOCK")
|
|
64423
|
+
fail(
|
|
64424
|
+
"timelock-without-timelock-root",
|
|
64425
|
+
`timelockSecs set but controllerKind is ${g.controllerKind}`
|
|
64426
|
+
);
|
|
64427
|
+
}
|
|
64428
|
+
for (const side of ["supply", "borrow"]) {
|
|
64429
|
+
const modes = sheet[side]?.modes;
|
|
64430
|
+
if (!modes) continue;
|
|
64431
|
+
if (modes.filter((m) => m.isDefault).length > 1)
|
|
64432
|
+
fail("multiple-defaults", `${side}.modes has more than one isDefault`);
|
|
64433
|
+
}
|
|
64434
|
+
return v;
|
|
64435
|
+
}
|
|
64436
|
+
function validateTermSheets(sheets) {
|
|
64437
|
+
return sheets.flatMap((s) => validateTermSheet(s));
|
|
64438
|
+
}
|
|
64439
|
+
|
|
64440
|
+
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
64441
|
//# sourceMappingURL=index.js.map
|
|
61562
64442
|
//# sourceMappingURL=index.js.map
|