@1delta/margin-fetcher 5.0.56 → 5.0.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -8559,6 +8559,17 @@ interface UpshiftVault extends VaultClassificationFields {
8559
8559
  vaultAddress: string;
8560
8560
  /** Underlying ERC-20 address (first accepted deposit asset), lowercased. */
8561
8561
  underlying: string;
8562
+ /**
8563
+ * EVERY asset the vault accepts, lowercased — `underlying` is only the
8564
+ * first of them.
8565
+ *
8566
+ * The entry is genuinely multi-asset (`deposit(asset, amount, receiver)`
8567
+ * names the asset it pulls), and several vaults take four or five: Clearstar
8568
+ * Prism accepts USDC, USDT, USDS, deUSD and sdeUSD. Publishing only the
8569
+ * first made the other legs unreachable — a caller paying USDT was told the
8570
+ * vault takes USDC, which is true and not the whole truth.
8571
+ */
8572
+ depositAssets: string[];
8562
8573
  /** Receipt-token symbol, e.g. `earnAUSD`. */
8563
8574
  symbol: string;
8564
8575
  /** Vault display name as returned by the API. */
@@ -8698,7 +8709,8 @@ type UpshiftVaults = {
8698
8709
  };
8699
8710
 
8700
8711
  /**
8701
- * One-shot fetcher for all visible Upshift vaults on a chain.
8712
+ * One-shot fetcher for the Upshift vaults on a chain (see
8713
+ * {@link isServable} for which of them we serve).
8702
8714
  *
8703
8715
  * HTTP-only (Upshift public REST listing) — no multicall executor needed,
8704
8716
  * mirroring the Lagoon fetcher. The endpoint is global; we filter to the
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import './chunk-BYTNVMX7.js';
3
3
  import './chunk-PR4QN5HX.js';
4
4
  import { Lender, isExactly, isMidnight, isTerm, isTermMax, isTeller, isListaDao, isLiquityFamily, isRiver, isInverse, isCooler, isDssType, isCompoundV3Type, isFluid, isGearboxV3, isLlamaLend, isFrankencoin, isResupply, isCurvance, isTwyne, isFraxlend, LIQUITY_FAMILY_LENDERS, isAaveType, isCompoundV3, isMultiMarket, isSiloV2Type, isSiloV3Type, isDolomite, hasCrossMarginRisk, isEulerType, isAaveV4Type, isInit, isMorphoType, isCompoundV2Type, isVenusType, isSumerType, AAVE_V3_LENDERS, AAVE_V2_LENDERS, isMorphoBlue, isAaveV2Type, isAaveV32Type, isAaveV3Type, isUsdd, isSky, isYLDR, isLista, hasSubAccounts, isTectonicType, isKineticType, isBenqiType } from '@1delta/lender-registry';
5
5
  export { hasCrossMarginRisk, isAaveType, isAaveV2Type, isAaveV32Type, isAaveV3Type, isCompoundV3, isCompoundV3Type, isInit, isMorphoType, isMultiMarket, isYLDR } from '@1delta/lender-registry';
6
- import { fetchTokenLists, fetchTokenList, aavePools, compoundV3Pools, initConfig, morphoPools, compoundV2Pools, eulerVaults, siloMarkets, siloV2LenderKey, siloMarketsV3, siloV3LenderKey, aaveV4Spokes, aaveV4SpokeLenderKey, fluidResolvers, gearboxResolvers, dolomiteConfigs, midnightConfig, termConfig, termMarketsByChain, exactlyConfig, liquityLendersByChain, riverLendersByChain, inverseLendersByChain, coolerLendersByChain, llamaLendLendersByChain, usddLendersByChain, skyLendersByChain, frankencoinLendersByChain, resupplyLendersByChain, curvanceLendersByChain, fraxlendLendersByChain, twyneLendersByChain, tellerConfigByChain, tellerPoolsByChain, termMaxConfigByChain, aaveReserves, compoundV3Reserves, liquityConfigFor, liquityBranchesByChain, riverConfigFor, riverChainData, termMaxApiBaseUrl, termMaxLenderKey, inverseConfigFor, inverseChainData, coolerConfigFor, llamaLendConfigFor, llamaLendChainData, llamaLendApiBaseUrl, resupplyConfigFor, fraxlendConfigFor, dssConfigFor, dssChainData, dssDebtToken, dssDebtJoin, frankencoinConfigFor, frankencoinChainData, aaveOracles, morphoTypeMarkets, resolveMidnightApiBase, resolveTermApiBase, llamaLendMarketByVault, getSiloV2MarketEntry, getSiloV3MarketEntry, dolomiteEmode, curvanceConfigFor, aaveTokens, morphoTypeVaults, listaCollateralProvider, eulerConfigs, gearboxMarketCompressor, gearboxMarketConfigurators, gearboxV3LenderKey, midnightMarketsByChain, twyneConfigFor, twyneChainData, parseAaveV4SpokeLenderKey, fraxlendPairsFor, gearboxAccountCompressor, compoundV2Tokens, exactlyMarketsByChain, aaveOraclesConfig, compoundV2Oracles, compoundV2TokenArray, compoundV2OraclesConfig, compoundV3OraclesData, compoundV3BaseData, compoundV2Reserves, aaveV4Oracles } from '@1delta/data-sdk';
6
+ import { fetchTokenLists, fetchTokenList, aavePools, compoundV3Pools, initConfig, morphoPools, compoundV2Pools, eulerVaults, siloMarkets, siloV2LenderKey, siloMarketsV3, siloV3LenderKey, aaveV4Spokes, aaveV4SpokeLenderKey, fluidResolvers, gearboxResolvers, dolomiteConfigs, midnightConfig, termConfig, termMarketsByChain, exactlyConfig, liquityLendersByChain, riverLendersByChain, inverseLendersByChain, coolerLendersByChain, llamaLendLendersByChain, usddLendersByChain, skyLendersByChain, frankencoinLendersByChain, resupplyLendersByChain, curvanceLendersByChain, fraxlendLendersByChain, twyneLendersByChain, tellerConfigByChain, tellerPoolsByChain, termMaxConfigByChain, aaveReserves, compoundV3Reserves, liquityConfigFor, liquityBranchesByChain, riverConfigFor, riverChainData, termMaxApiBaseUrl, termMaxLenderKey, inverseConfigFor, inverseChainData, coolerConfigFor, llamaLendConfigFor, llamaLendChainData, llamaLendApiBaseUrl, resupplyConfigFor, fraxlendConfigFor, dssConfigFor, dssChainData, dssDebtToken, dssDebtJoin, frankencoinConfigFor, frankencoinChainData, aaveOracles, morphoTypeMarkets, resolveMidnightApiBase, resolveTermApiBase, llamaLendMarketByVault, getSiloV2MarketEntry, getSiloV3MarketEntry, dolomiteEmode, curvanceConfigFor, aaveTokens, morphoTypeVaults, listaCollateralProvider, eulerConfigs, gearboxMarketCompressor, gearboxMarketConfigurators, gearboxV3LenderKey, midnightMarketsByChain, twyneConfigFor, twyneChainData, parseAaveV4SpokeLenderKey, fraxlendPairsFor, gearboxAccountCompressor, compoundV2Tokens, exactlyMarketsByChain, aaveOraclesConfig, compoundV2Oracles, compoundV2TokenArray, compoundV2OraclesConfig, compoundV3OraclesData, compoundV3BaseData, compoundV2Reserves, aaveV4Oracles, morphoTypeOracles } from '@1delta/data-sdk';
7
7
  import lodash from 'lodash';
8
8
  import { Chain } from '@1delta/chain-registry';
9
9
  import { multicallRetryUniversal, getEvmClient, getEvmChain, getEvmClientUniversal } from '@1delta/providers';
@@ -28325,6 +28325,99 @@ var getLenderPublicDataViaApi = async (chainId, lenders, prices, additionalYield
28325
28325
  }
28326
28326
  return lenderData;
28327
28327
  };
28328
+ var ERC20_BALANCE_OF_ABI2 = [
28329
+ {
28330
+ inputs: [{ name: "account", type: "address" }],
28331
+ name: "balanceOf",
28332
+ outputs: [{ name: "", type: "uint256" }],
28333
+ stateMutability: "view",
28334
+ type: "function"
28335
+ }
28336
+ ];
28337
+ var MORPHO_BLUE_BUCKET_PREFIX = "MORPHO_BLUE_";
28338
+ function formatBalance(raw, decimals) {
28339
+ if (raw === void 0 || raw === null || raw === "0x") return void 0;
28340
+ let value;
28341
+ try {
28342
+ value = BigInt(raw);
28343
+ } catch {
28344
+ return void 0;
28345
+ }
28346
+ return Number(value) / 10 ** decimals;
28347
+ }
28348
+ async function enrichMorphoCollateralTotals(chainId, result, prices, multicallRetry) {
28349
+ const morpho = morphoPools()?.MORPHO_BLUE?.[chainId];
28350
+ if (!morpho) return result;
28351
+ const markets = [];
28352
+ for (const [bucket, entry] of Object.entries(result)) {
28353
+ if (!bucket.startsWith(MORPHO_BLUE_BUCKET_PREFIX)) continue;
28354
+ const market = entry?.params?.market;
28355
+ const rows = entry?.data;
28356
+ if (!market || !rows) continue;
28357
+ const collateral = String(market.collateralAddress ?? "").toLowerCase();
28358
+ const loan = String(market.loanAddress ?? "").toLowerCase();
28359
+ if (!collateral || !loan) continue;
28360
+ const loanRow = rows[createMarketUid(chainId, bucket, loan)];
28361
+ const supply = Number(loanRow?.totalDeposits ?? 0);
28362
+ const borrow = Number(loanRow?.totalDebt ?? 0);
28363
+ const loanIdle = Number.isFinite(supply) && Number.isFinite(borrow) ? supply - borrow : 0;
28364
+ markets.push({
28365
+ bucket,
28366
+ collateralUid: createMarketUid(chainId, bucket, collateral),
28367
+ collateral,
28368
+ loan,
28369
+ collateralDecimals: Number(market.collateralDecimals ?? 18),
28370
+ loanIdle
28371
+ });
28372
+ }
28373
+ if (markets.length === 0) return result;
28374
+ const marketsByCollateral = /* @__PURE__ */ new Map();
28375
+ for (const m of markets) {
28376
+ const list = marketsByCollateral.get(m.collateral);
28377
+ if (list) list.push(m);
28378
+ else marketsByCollateral.set(m.collateral, [m]);
28379
+ }
28380
+ const soleCollateralTokens = [...marketsByCollateral.entries()].filter(([, list]) => list.length === 1).map(([token]) => token);
28381
+ if (soleCollateralTokens.length === 0) return result;
28382
+ let balances;
28383
+ try {
28384
+ balances = await multicallRetry({
28385
+ chain: chainId,
28386
+ calls: soleCollateralTokens.map((token) => ({
28387
+ address: token,
28388
+ functionName: "balanceOf",
28389
+ args: [morpho]
28390
+ })),
28391
+ abi: soleCollateralTokens.map(() => ERC20_BALANCE_OF_ABI2),
28392
+ allowFailure: true
28393
+ });
28394
+ } catch {
28395
+ return result;
28396
+ }
28397
+ const idleByToken = /* @__PURE__ */ new Map();
28398
+ for (const m of markets) {
28399
+ idleByToken.set(m.loan, (idleByToken.get(m.loan) ?? 0) + m.loanIdle);
28400
+ }
28401
+ soleCollateralTokens.forEach((token, i) => {
28402
+ const m = marketsByCollateral.get(token)[0];
28403
+ const balance = formatBalance(balances?.[i], m.collateralDecimals);
28404
+ if (balance === void 0) return;
28405
+ const total = balance - (idleByToken.get(token) ?? 0);
28406
+ if (!Number.isFinite(total) || total < 0) return;
28407
+ const row = result[m.bucket]?.data?.[m.collateralUid];
28408
+ if (!row) return;
28409
+ const priceKey = toOracleKey(row.asset?.assetGroup ?? null) || toGenericPriceKey(token, chainId);
28410
+ const price2 = prices[priceKey] ?? 0;
28411
+ const usd = total * price2;
28412
+ row.totalDeposits = total;
28413
+ row.totalLiquidity = total;
28414
+ row.borrowLiquidity = total;
28415
+ row.totalDepositsUSD = usd;
28416
+ row.totalLiquidityUSD = usd;
28417
+ row.borrowLiquidityUSD = usd;
28418
+ });
28419
+ return result;
28420
+ }
28328
28421
  var warn5 = (...args) => {
28329
28422
  };
28330
28423
  function lenderApiOnly(lender, chainId) {
@@ -28425,6 +28518,21 @@ var getLenderPublicDataAll = async (chainId, lenders, prices, additionalYields,
28425
28518
  e?.message ?? e
28426
28519
  );
28427
28520
  }
28521
+ if (lendersOnChain.some((l) => l === Lender.MORPHO_BLUE)) {
28522
+ try {
28523
+ onChainRes = await enrichMorphoCollateralTotals(
28524
+ chainId,
28525
+ onChainRes,
28526
+ prices,
28527
+ multicallRetry
28528
+ );
28529
+ } catch (e) {
28530
+ warn5(
28531
+ `[lending] morpho collateral enrichment failed for chain ${chainId}:`,
28532
+ e?.message ?? e
28533
+ );
28534
+ }
28535
+ }
28428
28536
  }
28429
28537
  return { ...onChainRes, ...apiRes };
28430
28538
  };
@@ -49837,8 +49945,13 @@ var UPSHIFT_CHAIN_IDS = /* @__PURE__ */ new Set([
49837
49945
  // Plasma
49838
49946
  "57073",
49839
49947
  // Ink
49840
- "999"
49948
+ "999",
49841
49949
  // HyperEVM
49950
+ // Citrea. Added 2026-08-19: the listing carries a live, visible $2.56M
49951
+ // RockawayX vault there (`Earn ctUSD`) that this gate was short-circuiting
49952
+ // away — the fetch itself is HTTP-only and chain-agnostic, so a chain
49953
+ // missing from this set is the only thing that can hide one.
49954
+ "4114"
49842
49955
  ]);
49843
49956
  var hasUpshiftVaults = (chainId) => UPSHIFT_CHAIN_IDS.has(String(chainId));
49844
49957
  async function fetchUpshiftApiVaults() {
@@ -51466,7 +51579,8 @@ function generateMorphoLenderKey(marketId) {
51466
51579
  }
51467
51580
  function getMorphoMarketsForChain(chainId, marketOverrides) {
51468
51581
  const chainOverrides = marketOverrides?.[chainId] ?? [];
51469
- return chainOverrides.map((override) => ({
51582
+ const markets = chainOverrides.length ? chainOverrides : morphoTypeOracles()?.[chainId]?.[Lender.MORPHO_BLUE] ?? [];
51583
+ return markets.map((override) => ({
51470
51584
  oracle: override.oracle,
51471
51585
  loanAsset: override.loanAsset,
51472
51586
  collateralAsset: override.collateralAsset,
@@ -57267,18 +57381,31 @@ function parseVault3(entry, results, chainId, prices, tokenList, marketIds, rate
57267
57381
  };
57268
57382
  }
57269
57383
 
57384
+ // src/vaults/morpho/rateSanity.ts
57385
+ var IMPLAUSIBLE_APR_PERCENT = 100;
57386
+ var PINNED_LIQUIDITY_FRACTION = 1e-3;
57387
+ var isUnrealizableRate = (v) => {
57388
+ if (!(v.depositRate > IMPLAUSIBLE_APR_PERCENT)) return false;
57389
+ if (!(v.totalAssetsFormatted > 0)) return false;
57390
+ return v.liquidityFormatted / v.totalAssetsFormatted < PINNED_LIQUIDITY_FRACTION;
57391
+ };
57392
+ var dropUnrealizableRates = (vaults) => {
57393
+ const entries = Object.entries(vaults);
57394
+ const kept = entries.filter(([, v]) => !isUnrealizableRate(v));
57395
+ if (kept.length === entries.length) return vaults;
57396
+ return Object.fromEntries(kept);
57397
+ };
57398
+
57270
57399
  // src/vaults/morpho/fetchPublic.ts
57271
57400
  var hasMorphoOnChainVaults = (chainId) => (morphoTypeVaults()?.MORPHO_BLUE?.[chainId]?.length ?? 0) > 0;
57272
57401
  var fetchMorphoVaults = async (chainId, multicallRetry, prices = {}, tokenList = {}) => {
57273
- if (hasMorphoOnChainVaults(chainId)) {
57274
- return fetchMorphoVaultsFromChain(
57275
- chainId,
57276
- multicallRetry,
57277
- prices,
57278
- tokenList
57279
- );
57280
- }
57281
- return fetchMorphoVaultsFromApi(chainId, prices, tokenList);
57402
+ const vaults = hasMorphoOnChainVaults(chainId) ? await fetchMorphoVaultsFromChain(
57403
+ chainId,
57404
+ multicallRetry,
57405
+ prices,
57406
+ tokenList
57407
+ ) : await fetchMorphoVaultsFromApi(chainId, prices, tokenList);
57408
+ return dropUnrealizableRates(vaults);
57282
57409
  };
57283
57410
  var { chunk: chunk6 } = lodash;
57284
57411
  var LISTA_PROTOCOL = "LISTA_DAO";
@@ -57430,7 +57557,7 @@ var fetchListaVaultsFromChain = async (chainId, multicallRetry, prices = {}, tok
57430
57557
  );
57431
57558
  if (parsed) out[parsed.address] = parsed;
57432
57559
  }
57433
- return out;
57560
+ return dropUnrealizableRates(out);
57434
57561
  };
57435
57562
  async function fetchListaMarketRates(chainId, moolah, marketIds, multicallRetry) {
57436
57563
  const map = /* @__PURE__ */ new Map();
@@ -65305,6 +65432,7 @@ function parseVault8(v, chainId, prices, tokenList) {
65305
65432
  const vaultAddr = (v?.address ?? receiptAddr).toLowerCase();
65306
65433
  const asset0 = v.depositAssets?.find((a) => a?.address);
65307
65434
  const assetAddr = (asset0?.address ?? "").toLowerCase();
65435
+ const depositAssets = (v.depositAssets ?? []).map((a) => (a?.address ?? "").toLowerCase()).filter((a) => isEvmAddress(a));
65308
65436
  if (!receiptAddr || !assetAddr) return null;
65309
65437
  if (!isEvmAddress(receiptAddr) || !isEvmAddress(assetAddr)) return null;
65310
65438
  const assetMeta = tokenList[assetAddr];
@@ -65342,6 +65470,7 @@ function parseVault8(v, chainId, prices, tokenList) {
65342
65470
  address: receiptAddr,
65343
65471
  vaultAddress: vaultAddr,
65344
65472
  underlying: assetAddr,
65473
+ depositAssets,
65345
65474
  symbol,
65346
65475
  name: name || symbol,
65347
65476
  decimals,
@@ -65392,6 +65521,7 @@ function parseVault8(v, chainId, prices, tokenList) {
65392
65521
  totalAssetsUsd
65393
65522
  };
65394
65523
  }
65524
+ var isServable = (v) => v?.isVisible !== false || v?.withdrawalOnly === true;
65395
65525
  var fetchUpshiftVaults = async (chainId, prices = {}, tokenList = {}) => {
65396
65526
  if (!hasUpshiftVaults(chainId)) return {};
65397
65527
  const items = await fetchUpshiftApiVaults();
@@ -65399,7 +65529,7 @@ var fetchUpshiftVaults = async (chainId, prices = {}, tokenList = {}) => {
65399
65529
  const out = {};
65400
65530
  for (const item of items) {
65401
65531
  if (Number(item?.chainId) !== want) continue;
65402
- if (item?.isVisible === false) continue;
65532
+ if (!isServable(item)) continue;
65403
65533
  const parsed = parseVault8(item, chainId, prices, tokenList);
65404
65534
  if (parsed) out[parsed.address] = parsed;
65405
65535
  }
@@ -74329,6 +74459,7 @@ function acceptsVaultZap(row, provider) {
74329
74459
  if (ZAP_EXCLUDED_PROVIDERS.has(provider)) return false;
74330
74460
  const share = row.shareToken?.address ?? row.ref;
74331
74461
  if (!share) return false;
74462
+ if (provider === "upshift" && isSplitUpshiftRow(row, share)) return false;
74332
74463
  try {
74333
74464
  const entry = findSavingsWithdrawEntry(row.chainId, share);
74334
74465
  if (entry && NON_4626_DEPOSIT_KINDS.has(entry.kind)) return false;
@@ -74337,6 +74468,10 @@ function acceptsVaultZap(row, provider) {
74337
74468
  }
74338
74469
  return true;
74339
74470
  }
74471
+ function isSplitUpshiftRow(row, share) {
74472
+ const deposit = (row.providerMeta ?? {}).depositContract;
74473
+ return typeof deposit === "string" && deposit.toLowerCase() !== String(share).toLowerCase();
74474
+ }
74340
74475
  function isSecondaryMarketRow(row) {
74341
74476
  const meta = row.providerMeta ?? {};
74342
74477
  if (meta.secondaryMarketOnly === true) return true;
@@ -74413,7 +74548,7 @@ function vaultCapabilities(row) {
74413
74548
  caps.push({
74414
74549
  action: "claim",
74415
74550
  async: false,
74416
- requires: claimRequires()
74551
+ requires: claimRequires(row, provider)
74417
74552
  });
74418
74553
  }
74419
74554
  if (supportsCancel(provider, meta)) {
@@ -74429,6 +74564,11 @@ function swapRoutedProvidersArePriceConsistent() {
74429
74564
  return [...SWAP_ROUTED_PROVIDERS].filter((p) => !isBookPriced(p));
74430
74565
  }
74431
74566
  function depositInputs(row, provider) {
74567
+ if (provider === "upshift") {
74568
+ const assets = (row.providerMeta ?? {}).depositAssets;
74569
+ if (!Array.isArray(assets) || assets.length < 2) return void 0;
74570
+ return assets.filter((a) => typeof a === "string").map((asset) => ({ asset, mode: "direct" }));
74571
+ }
74432
74572
  if (provider !== "lst") return void 0;
74433
74573
  const share = row.shareToken?.address ?? row.ref;
74434
74574
  if (!share) return void 0;
@@ -74475,7 +74615,10 @@ function requestRequires(provider) {
74475
74615
  if (isBookPriced(provider)) requires.push("slippage");
74476
74616
  return requires.length ? requires : void 0;
74477
74617
  }
74478
- function claimRequires(provider) {
74618
+ function claimRequires(row, provider) {
74619
+ const share = row.shareToken?.address ?? row.ref;
74620
+ if (provider === "upshift" && share && isSplitUpshiftRow(row, share))
74621
+ return ["claimableEpoch"];
74479
74622
  return void 0;
74480
74623
  }
74481
74624
  function supportsCancel(provider, meta) {