@1delta/margin-fetcher 5.0.12 → 5.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -23006,6 +23006,12 @@ var bandGrid = (m) => {
23006
23006
  const set = /* @__PURE__ */ new Set([...BANDS_ENDPOINTS, bandsFor(m)]);
23007
23007
  return [...set].sort((a, b) => a - b);
23008
23008
  };
23009
+ var MIN_BANDS = 4;
23010
+ var MAX_BANDS = 50;
23011
+ var LTV_CURVE_BANDS = Array.from(
23012
+ { length: MAX_BANDS - MIN_BANDS + 1 },
23013
+ (_3, i) => MIN_BANDS + i
23014
+ );
23009
23015
  var maxBorrowableCall = (m, oneUnit, n) => m.version === 1 ? {
23010
23016
  address: m.controller,
23011
23017
  name: "max_borrowable",
@@ -23025,7 +23031,8 @@ var buildBandLtv = (market) => {
23025
23031
  // negligible at any realistic position size and converges as it grows.
23026
23032
  collateral: 10n ** BigInt(market.collateralDecimals + 3),
23027
23033
  collateralDecimals: market.collateralDecimals,
23028
- bandCounts: bandGrid(market)
23034
+ // The FULL domain, not the multicall grid — see `LTV_CURVE_BANDS`.
23035
+ bandCounts: LTV_CURVE_BANDS
23029
23036
  });
23030
23037
  return Object.keys(curve).length > 0 ? curve : null;
23031
23038
  } catch {
@@ -24345,6 +24352,23 @@ function convertLlamaLendMarketsToResponse(raw, chainId, prices = {}, additional
24345
24352
  // --- LlamaLend descriptor (consumed by the calldata builders,
24346
24353
  // the leverage sizer and worker-api resolvers) ---
24347
24354
  llamalend: {
24355
+ /**
24356
+ * The market's LLAMMA, and the ONLY readable oracle for it:
24357
+ * `price_oracle()` lives here, quoted in the BORROWED token.
24358
+ *
24359
+ * Carried on the descriptor — not only under `addresses` — because
24360
+ * this object is what consumers persist verbatim (yield-tracer
24361
+ * serialises it into the `llamalend` column of the shared isolated-
24362
+ * market table) and the oracle-price path resolves the AMM from it.
24363
+ * Omitting it does not fail loudly: the reader finds `undefined`,
24364
+ * skips the market, and the lender ends up with NO prices at all
24365
+ * while every row still looks present and correct.
24366
+ *
24367
+ * Do not "clean this up" as a duplicate of `addresses.amm` — the two
24368
+ * are read by different consumers, and only this one survives into
24369
+ * the descriptor column.
24370
+ */
24371
+ amm: market.amm,
24348
24372
  /** 1 = `oneway` (v1), 2 = `oneway-v2`. Branch every encoder on this. */
24349
24373
  version: market.version,
24350
24374
  /** FAIL CLOSED — an unknown market is not delegatable. */
@@ -42540,6 +42564,32 @@ var sreusdSavingsFetcher = {
42540
42564
  return { [SREUSD_KEY]: apr, [SREUSD_GROUP_KEY]: apr };
42541
42565
  }
42542
42566
  };
42567
+
42568
+ // src/yields/intrinsic/fetchers/fxProtocol.ts
42569
+ var ALADDIN_APY_URL = "https://api.aladdin.club/api1/concentrator_aToken_tvl_apy";
42570
+ var FXSAVE_DEFILLAMA_POOL = "ee0b7069-f8f3-4aa2-a415-728f13e6cc3d";
42571
+ var FXSAVE_KEY = "FXSAVE";
42572
+ var FXSAVE_GROUP_KEY = "f(x) USD Saving::fxSAVE";
42573
+ var fetchAladdinApyPercent = async () => {
42574
+ const res = await fetch(ALADDIN_APY_URL, {
42575
+ headers: { accept: "application/json" },
42576
+ signal: AbortSignal.timeout(8e3)
42577
+ });
42578
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
42579
+ const apy = Number((await res.json())?.data?.fxSave?.apy);
42580
+ if (Number.isFinite(apy) && apy > 0) return apy;
42581
+ throw new Error("apy missing");
42582
+ };
42583
+ var fxSaveFetcher = {
42584
+ label: "FXSAVE",
42585
+ fetch: async () => {
42586
+ const apy = await fetchAladdinApyPercent().catch(
42587
+ () => fetchDefiLlamaApy(FXSAVE_DEFILLAMA_POOL)
42588
+ );
42589
+ const apr = apyToAprPercent(apy);
42590
+ return { [FXSAVE_KEY]: apr, [FXSAVE_GROUP_KEY]: apr };
42591
+ }
42592
+ };
42543
42593
  var RATE_ABI = [
42544
42594
  {
42545
42595
  name: "rate",
@@ -45016,6 +45066,31 @@ var SINGLE_CHAIN_ENTRIES = {
45016
45066
  yieldFetcher: sreusdSavingsFetcher,
45017
45067
  yieldKey: "SREUSD"
45018
45068
  },
45069
+ {
45070
+ // f(x) Protocol fxSAVE — 4626 auto-compounder over `fxSP`, the f(x)
45071
+ // Stability Pool share (it holds 57.4M of fxSP's 59.4M supply, i.e.
45072
+ // ~97% of the pool). Plain instant exit: `maxDeposit = uint.max`,
45073
+ // `maxRedeem == balanceOf` on a live holder, `previewRedeem ==
45074
+ // convertToAssets` (no fee, no cooldown) — verified on-chain 2026-08-09.
45075
+ //
45076
+ // The underlying is fxSP and NOT fxUSD: fxSP appreciates on its own
45077
+ // ($1.0101 vs fxUSD's $1.0000 at integration), so pricing `totalAssets`
45078
+ // off fxUSD would understate TVL by ~1%. The token-list row's
45079
+ // `props.savings.underlying: 'fxUSD'` is the classifier's economic
45080
+ // underlying, a different question from `asset()`.
45081
+ address: "0x7743e50f534a7f9f1791dde7dcd89f7783eefc39",
45082
+ underlying: "0x65c9a641afceb9c0e6034e558a319488fa0fa3be",
45083
+ // fxSP
45084
+ symbol: "fxSAVE",
45085
+ brand: "f(x) Protocol",
45086
+ description: "fxUSD is f(x) Protocol's stablecoin; its Stability Pool (fxSP) backstops leveraged positions and earns the protocol's rebalance and liquidation revenue. fxSAVE auto-compounds that revenue back into fxSP, so the share price appreciates. Instant permissionless exit, no fee or cooldown.",
45087
+ decimals: 18,
45088
+ isRebasing: false,
45089
+ isMintable: true,
45090
+ withdrawalMode: "instant",
45091
+ yieldFetcher: fxSaveFetcher,
45092
+ yieldKey: FXSAVE_KEY
45093
+ },
45019
45094
  {
45020
45095
  // Frax sFRAX — the pre-rename sibling of sfrxUSD, over legacy
45021
45096
  // FRAX. Still live and roughly 2x sfrxUSD's size, so it stays a
@@ -46177,32 +46252,85 @@ var upshiftFetcher = {
46177
46252
  }
46178
46253
  };
46179
46254
 
46180
- // src/yields/yieldTypes.ts
46181
- var addressToAssetYearn = {
46182
- "0xe007ca01894c863d7898045ed5a3b4abf0b18f37": "vbETH yVault::yvvbETH",
46183
- "0x80c34bd3a3569e126e7055831036aa7b212cb159": "vbUSDC yVault::yvvbUSDC",
46184
- "0x9a6bd7b6fd5c4f87eb66356441502fc7dcdd185b": "vbUSDT yVault::yvvbUSDT",
46185
- "0xaa0362ecc584b985056e47812931270b99c91f9d": "vbWBTC yVault::yvvbWBTC",
46186
- "0x93fec6639717b6215a48e5a72a162c50dcc40d68": "vbAUSD yVault::yvvbAUSD"
46187
- };
46255
+ // src/vaults/yearn/api.ts
46256
+ var YEARN_YDAEMON_BASE = "https://ydaemon.yearn.fi";
46257
+ var YEARN_CHAIN_IDS = /* @__PURE__ */ new Set([
46258
+ "1",
46259
+ // Ethereum
46260
+ "137",
46261
+ // Polygon
46262
+ "8453",
46263
+ // Base
46264
+ "42161",
46265
+ // Arbitrum
46266
+ "100",
46267
+ // Gnosis
46268
+ "146",
46269
+ // Sonic
46270
+ "747474"
46271
+ // Katana
46272
+ ]);
46273
+ var hasYearnVaults = (chainId) => YEARN_CHAIN_IDS.has(String(chainId));
46274
+ var isYearnV3 = (v) => typeof v?.version === "string" && v.version.startsWith("3.");
46275
+ var PAGE_SIZE = 2e3;
46276
+ async function fetchYearnApiVaults(chainId) {
46277
+ const out = [];
46278
+ const seen = /* @__PURE__ */ new Set();
46279
+ for (let page = 1; page <= 20; page++) {
46280
+ const url = `${YEARN_YDAEMON_BASE}/${chainId}/vaults/all?limit=${PAGE_SIZE}&page=${page}`;
46281
+ const response = await fetch(url);
46282
+ if (!response.ok) {
46283
+ throw new Error(
46284
+ `Yearn yDaemon API failed: ${response.status} - ${response.statusText}`
46285
+ );
46286
+ }
46287
+ const body = await response.text();
46288
+ if (!body.trim()) break;
46289
+ const items = JSON.parse(body);
46290
+ if (!Array.isArray(items)) break;
46291
+ for (const v of items) {
46292
+ const address = (v?.address ?? "").toLowerCase();
46293
+ if (!address || seen.has(address)) continue;
46294
+ if (isYearnV3(v) && v.endorsed) {
46295
+ seen.add(address);
46296
+ out.push(v);
46297
+ }
46298
+ }
46299
+ if (items.length < PAGE_SIZE) break;
46300
+ }
46301
+ return out;
46302
+ }
46188
46303
 
46189
- // src/yields/intrinsic/fetchers/yearnKatana.ts
46190
- var YEARN_KATANA_URL = "https://katana-apr-service.vercel.app/api/vaults";
46191
- var yearnKatanaFetcher = {
46192
- label: "YEARN_KATANA",
46304
+ // src/yields/intrinsic/fetchers/yearn.ts
46305
+ var toPct = (v) => typeof v === "number" && Number.isFinite(v) ? v * 100 : null;
46306
+ var yearnVaultApr = (v) => toPct(v.apr?.netAPR) ?? toPct(v.apr?.forwardAPR?.netAPR);
46307
+ var yearnFetcher = {
46308
+ label: "YEARN",
46193
46309
  fetch: async () => {
46194
- const res = await fetch(
46195
- YEARN_KATANA_URL
46196
- ).then((r) => r.json());
46197
- let map = {};
46198
- Object.entries(res).forEach(([addr2, data]) => {
46199
- const key2 = addressToAssetYearn[addr2.toLowerCase()];
46200
- if (key2) {
46201
- const apr = ((data.apr.netAPR ?? 0) + (data.apr.extra?.extrinsicYield ?? 0) + (data.apr.extra?.katanaNativeYield ?? 0)) * 100;
46202
- map[key2] = apr;
46203
- }
46204
- });
46205
- return map;
46310
+ const chainIds = [...YEARN_CHAIN_IDS];
46311
+ const lists = await getListsMulti(chainIds);
46312
+ const perChain = await Promise.all(
46313
+ chainIds.map(async (chainId) => {
46314
+ const out = {};
46315
+ let vaults = [];
46316
+ try {
46317
+ vaults = await fetchYearnApiVaults(chainId);
46318
+ } catch (e) {
46319
+ console.error(`Error fetching YEARN vaults on chain ${chainId}:`, e);
46320
+ return out;
46321
+ }
46322
+ for (const v of vaults) {
46323
+ const address = (v.address ?? "").toLowerCase();
46324
+ const assetKey = lists[chainId]?.list?.[address]?.assetGroup;
46325
+ if (!assetKey) continue;
46326
+ const apr = yearnVaultApr(v);
46327
+ if (apr == null) continue;
46328
+ out[assetKey] = apr;
46329
+ }
46330
+ return out;
46331
+ })
46332
+ );
46333
+ return Object.assign({}, ...perChain);
46206
46334
  }
46207
46335
  };
46208
46336
 
@@ -46266,7 +46394,9 @@ var LENDING_ONLY_FETCHERS = [
46266
46394
  nestFetcher,
46267
46395
  accountableFetcher,
46268
46396
  upshiftFetcher,
46269
- yearnKatanaFetcher,
46397
+ // Every endorsed Yearn V3 vault on every Yearn chain (Katana included —
46398
+ // it superseded the Katana-only fetcher, which double-counted).
46399
+ yearnFetcher,
46270
46400
  // DefiLlama-mapped intrinsic yields for classifier assets lacking a bespoke source
46271
46401
  lsethFetcher,
46272
46402
  tethFetcher,
@@ -46367,6 +46497,76 @@ function extractRewardBreakdowns(opp) {
46367
46497
  distributionType: b.distributionType
46368
46498
  }));
46369
46499
  }
46500
+ function unixSeconds(v) {
46501
+ if (v === void 0 || v === null) return void 0;
46502
+ const n = typeof v === "number" ? v : Number(v);
46503
+ return Number.isFinite(n) && n > 0 ? n : void 0;
46504
+ }
46505
+ function buildMerklStreams(opp, side, totalApr, source) {
46506
+ const rewardBreakdowns = opp.rewardsRecord?.breakdowns ?? [];
46507
+ const endsAt = unixSeconds(opp.earliestCampaignEnd ?? opp.latestCampaignEnd);
46508
+ const startsAt = unixSeconds(
46509
+ opp.earliestCampaignStart ?? opp.latestCampaignStart
46510
+ );
46511
+ const base = {
46512
+ side,
46513
+ kind: "token",
46514
+ claim: "merkl",
46515
+ endsAt,
46516
+ startsAt
46517
+ };
46518
+ if (!rewardBreakdowns.length) {
46519
+ return [{ ...base, apr: totalApr, token: void 0, source }];
46520
+ }
46521
+ const aprByCampaign = /* @__PURE__ */ new Map();
46522
+ for (const b of opp.aprRecord?.breakdowns ?? []) {
46523
+ if (b?.identifier && Number.isFinite(b.value))
46524
+ aprByCampaign.set(b.identifier.toLowerCase(), b.value);
46525
+ }
46526
+ const totalDaily = rewardBreakdowns.reduce((s, b) => s + (b.value || 0), 0);
46527
+ return rewardBreakdowns.map((b) => {
46528
+ const campaignApr = b.onChainCampaignId ? aprByCampaign.get(b.onChainCampaignId.toLowerCase()) : void 0;
46529
+ const apr = campaignApr ?? (totalDaily > 0 ? totalApr * (b.value || 0) / totalDaily : totalApr / rewardBreakdowns.length);
46530
+ return {
46531
+ ...base,
46532
+ apr,
46533
+ token: {
46534
+ address: b.token.address.toLowerCase(),
46535
+ chainId: String(opp.chainId),
46536
+ symbol: b.token.symbol,
46537
+ decimals: b.token.decimals,
46538
+ logoURI: b.token.icon,
46539
+ priceUsd: b.token.price ?? void 0
46540
+ },
46541
+ dailyRewardsUsd: b.value,
46542
+ source: {
46543
+ ...source,
46544
+ refs: {
46545
+ ...source.refs ?? {},
46546
+ ...b.onChainCampaignId ? { onChainCampaignId: b.onChainCampaignId } : {},
46547
+ ...b.campaignId ? { campaignId: b.campaignId } : {},
46548
+ ...b.distributionType ? { distributionType: b.distributionType } : {}
46549
+ }
46550
+ }
46551
+ };
46552
+ });
46553
+ }
46554
+ function merklSourceRef(opp, merklProtocolId) {
46555
+ const protocolName = opp.protocol?.name ?? merklProtocolId;
46556
+ return {
46557
+ id: `merkl:${merklProtocolId}`,
46558
+ label: `Merkl \xB7 ${protocolName}`,
46559
+ link: getMerkleUrl(opp),
46560
+ platform: "merkl",
46561
+ refs: {
46562
+ opportunityId: opp.identifier,
46563
+ opportunityType: opp.type,
46564
+ chain: opp.chain?.name ?? String(opp.chainId),
46565
+ ...opp.name ? { opportunityName: opp.name } : {},
46566
+ ...opp.liveCampaigns ? { liveCampaigns: opp.liveCampaigns } : {}
46567
+ }
46568
+ };
46569
+ }
46370
46570
  function resolveUnderlyingFromOpportunity(opp) {
46371
46571
  if (!opp.tokens?.length) return void 0;
46372
46572
  if (opp.depositUrl) {
@@ -46432,6 +46632,11 @@ function createMerklRewardFetcher(config) {
46432
46632
  );
46433
46633
  const breakdowns = extractRewardBreakdowns(opp);
46434
46634
  reward.link = getMerkleUrl(opp);
46635
+ const source = merklSourceRef(opp, config.merklProtocolId);
46636
+ if (!reward.streams) reward.streams = [];
46637
+ reward.streams.push(
46638
+ ...buildMerklStreams(opp, bucket, opp.apr, source)
46639
+ );
46435
46640
  if (bucket === "deposit") {
46436
46641
  reward.deposit += opp.apr;
46437
46642
  reward.additionalDepositData.push(...breakdowns);
@@ -46636,8 +46841,21 @@ var ABI = [
46636
46841
  stateMutability: "view",
46637
46842
  inputs: [],
46638
46843
  outputs: [{ type: "uint8" }]
46844
+ },
46845
+ {
46846
+ name: "symbol",
46847
+ type: "function",
46848
+ stateMutability: "view",
46849
+ inputs: [],
46850
+ outputs: [{ type: "string" }]
46639
46851
  }
46640
46852
  ];
46853
+ var DTRINITY_SOURCE = {
46854
+ id: "dtrinity:rebate",
46855
+ label: "dTRINITY borrow rebate",
46856
+ link: "https://docs.dtrinity.org/protocol-components/dlend",
46857
+ platform: "protocol"
46858
+ };
46641
46859
  var isZero = (a) => !a || a.toLowerCase() === ZERO_ADDRESS5;
46642
46860
  var scale = (raw, decimals) => Number(raw) / 10 ** decimals;
46643
46861
  function emptyReward2() {
@@ -46751,19 +46969,25 @@ async function fetchForPool(chainId, lender, pool, nowSeconds) {
46751
46969
  calls: pairs.flatMap((p) => [
46752
46970
  call(p.leg.controller, "getRewardsData", [p.leg.balanceToken, p.reward]),
46753
46971
  call(p.reward, "decimals"),
46754
- call(oracle, "getAssetPrice", [p.reward])
46972
+ call(oracle, "getAssetPrice", [p.reward]),
46973
+ call(p.reward, "symbol")
46755
46974
  ]),
46756
46975
  allowFailure: false
46757
46976
  });
46977
+ const STRIDE = 4;
46758
46978
  const result = {};
46759
46979
  pairs.forEach((p, i) => {
46760
- const data = round5[i * 3];
46980
+ const data = round5[i * STRIDE];
46761
46981
  if (!data) return;
46982
+ const rewardDecimals = Number(round5[i * STRIDE + 1] ?? 18);
46983
+ const rewardPrice = Number(round5[i * STRIDE + 2] ?? 0n);
46984
+ const rewardSymbol = round5[i * STRIDE + 3];
46985
+ const distributionEnd = Number(data[3] ?? 0);
46762
46986
  const apr = rebateApr({
46763
46987
  emissionPerSecond: BigInt(data[1] ?? 0),
46764
46988
  distributionEnd: BigInt(data[3] ?? 0),
46765
- rewardDecimals: Number(round5[i * 3 + 1] ?? 18),
46766
- rewardPrice: Number(round5[i * 3 + 2] ?? 0n),
46989
+ rewardDecimals,
46990
+ rewardPrice,
46767
46991
  balanceTotalSupply: BigInt(p.leg.totalSupply ?? 0),
46768
46992
  balanceDecimals: p.leg.decimals,
46769
46993
  assetPrice: priceByAsset[p.leg.asset] ?? 0,
@@ -46778,9 +47002,34 @@ async function fetchForPool(chainId, lender, pool, nowSeconds) {
46778
47002
  const breakdown = {
46779
47003
  tokenAddress: p.reward.toLowerCase(),
46780
47004
  emissionPerSecond: String(data[1]),
46781
- distributionEnd: Number(data[3]),
47005
+ distributionEnd,
46782
47006
  distributionType: "aave-incentives"
46783
47007
  };
47008
+ if (!entry.streams) entry.streams = [];
47009
+ entry.streams.push({
47010
+ side: p.leg.bucket,
47011
+ apr,
47012
+ kind: "token",
47013
+ token: {
47014
+ address: p.reward.toLowerCase(),
47015
+ chainId,
47016
+ symbol: rewardSymbol,
47017
+ decimals: rewardDecimals,
47018
+ // The pool's AaveOracle is 1e8-based on every dLEND deployment.
47019
+ priceUsd: rewardPrice > 0 ? rewardPrice / 1e8 : void 0
47020
+ },
47021
+ source: {
47022
+ ...DTRINITY_SOURCE,
47023
+ refs: {
47024
+ incentivesController: p.leg.controller.toLowerCase(),
47025
+ balanceToken: p.leg.balanceToken.toLowerCase(),
47026
+ emissionPerSecond: String(data[1])
47027
+ }
47028
+ },
47029
+ claim: "accrual",
47030
+ endsAt: distributionEnd > 0 ? distributionEnd : void 0,
47031
+ dailyRewardsUsd: rewardPrice > 0 ? Number(data[1] ?? 0) / 10 ** rewardDecimals * 86400 * (rewardPrice / 1e8) : void 0
47032
+ });
46784
47033
  if (p.leg.bucket === "deposit") {
46785
47034
  entry.deposit += apr;
46786
47035
  entry.additionalDepositData.push(breakdown);
@@ -51721,7 +51970,7 @@ var fetchGearboxV3Pools = async (chainId, multicallRetry, prices = {}, tokenList
51721
51970
  // src/vaults/morpho/fetchFromApi.ts
51722
51971
  var MORPHO_SHARE_DECIMALS = 18;
51723
51972
  var MORPHO_API_URL = "https://blue-api.morpho.org/graphql";
51724
- var PAGE_SIZE = 200;
51973
+ var PAGE_SIZE2 = 200;
51725
51974
  var vaultsQuery = (first, skip, chainId) => `
51726
51975
  query GetVaults {
51727
51976
  vaults(
@@ -51886,14 +52135,14 @@ async function fetchMorphoVaultsFromApi(chainId, prices = {}, tokenList = {}, ma
51886
52135
  const out = {};
51887
52136
  let skip = 0;
51888
52137
  while (skip < maxItems) {
51889
- const page = await fetchPage(chainId, PAGE_SIZE, skip);
52138
+ const page = await fetchPage(chainId, PAGE_SIZE2, skip);
51890
52139
  if (page.length === 0) break;
51891
52140
  for (const v of page) {
51892
52141
  const parsed = parseVault2(v, chainId, prices, tokenList);
51893
52142
  if (parsed) out[parsed.address] = parsed;
51894
52143
  }
51895
- if (page.length < PAGE_SIZE) break;
51896
- skip += PAGE_SIZE;
52144
+ if (page.length < PAGE_SIZE2) break;
52145
+ skip += PAGE_SIZE2;
51897
52146
  }
51898
52147
  return out;
51899
52148
  }
@@ -59690,7 +59939,7 @@ query LagoonVaults($where: VaultFilterInput!, $first: Int!, $skip: Int!) {
59690
59939
  }
59691
59940
  }
59692
59941
  }`;
59693
- var PAGE_SIZE2 = 200;
59942
+ var PAGE_SIZE3 = 200;
59694
59943
  async function fetchLagoonApiVaults(chainId) {
59695
59944
  const out = [];
59696
59945
  let skip = 0;
@@ -59702,7 +59951,7 @@ async function fetchLagoonApiVaults(chainId) {
59702
59951
  query: VAULTS_QUERY,
59703
59952
  variables: {
59704
59953
  where: { chainId_eq: Number(chainId), isVisible_eq: true },
59705
- first: PAGE_SIZE2,
59954
+ first: PAGE_SIZE3,
59706
59955
  skip
59707
59956
  }
59708
59957
  })
@@ -59720,8 +59969,8 @@ async function fetchLagoonApiVaults(chainId) {
59720
59969
  }
59721
59970
  const items = json?.data?.vaults?.items ?? [];
59722
59971
  out.push(...items);
59723
- if (items.length < PAGE_SIZE2) break;
59724
- skip += PAGE_SIZE2;
59972
+ if (items.length < PAGE_SIZE3) break;
59973
+ skip += PAGE_SIZE3;
59725
59974
  }
59726
59975
  return out;
59727
59976
  }
@@ -59841,7 +60090,7 @@ var Erc4626TotalAssetsAbi = [
59841
60090
 
59842
60091
  // src/vaults/aave-earn/api.ts
59843
60092
  var AAVE_EARN_API_URL = "https://api.v3.aave.com/graphql";
59844
- var PAGE_SIZE3 = "FIFTY";
60093
+ var PAGE_SIZE4 = "FIFTY";
59845
60094
  var VAULT_FIELDS = `
59846
60095
  address
59847
60096
  shareName
@@ -59899,7 +60148,7 @@ async function fetchAaveEarnApiVaults(chainId, targets) {
59899
60148
  for (let page = 0; page < 20; page++) {
59900
60149
  const request = {
59901
60150
  criteria: { ownedBy: [owner] },
59902
- pageSize: PAGE_SIZE3
60151
+ pageSize: PAGE_SIZE4
59903
60152
  };
59904
60153
  if (cursor) request.cursor = cursor;
59905
60154
  const data = await postGraphql(OWNED_VAULTS_QUERY, { request });
@@ -60158,47 +60407,6 @@ var fetchUpshiftVaults = async (chainId, prices = {}, tokenList = {}) => {
60158
60407
  return out;
60159
60408
  };
60160
60409
 
60161
- // src/vaults/yearn/api.ts
60162
- var YEARN_YDAEMON_BASE = "https://ydaemon.yearn.fi";
60163
- var YEARN_CHAIN_IDS = /* @__PURE__ */ new Set([
60164
- "1",
60165
- // Ethereum
60166
- "137",
60167
- // Polygon
60168
- "8453",
60169
- // Base
60170
- "42161",
60171
- // Arbitrum
60172
- "100",
60173
- // Gnosis
60174
- "146",
60175
- // Sonic
60176
- "747474"
60177
- // Katana
60178
- ]);
60179
- var hasYearnVaults = (chainId) => YEARN_CHAIN_IDS.has(String(chainId));
60180
- var isYearnV3 = (v) => typeof v?.version === "string" && v.version.startsWith("3.");
60181
- var PAGE_SIZE4 = 500;
60182
- async function fetchYearnApiVaults(chainId) {
60183
- const out = [];
60184
- for (let page = 1; page <= 20; page++) {
60185
- const url = `${YEARN_YDAEMON_BASE}/${chainId}/vaults/all?limit=${PAGE_SIZE4}&page=${page}`;
60186
- const response = await fetch(url);
60187
- if (!response.ok) {
60188
- throw new Error(
60189
- `Yearn yDaemon API failed: ${response.status} - ${response.statusText}`
60190
- );
60191
- }
60192
- const items = await response.json();
60193
- if (!Array.isArray(items)) break;
60194
- for (const v of items) {
60195
- if (isYearnV3(v) && v.endorsed) out.push(v);
60196
- }
60197
- if (items.length < PAGE_SIZE4) break;
60198
- }
60199
- return out;
60200
- }
60201
-
60202
60410
  // src/vaults/yearn/fetchPublic.ts
60203
60411
  var BRAND3 = "Yearn";
60204
60412
  var ZERO_ADDR = "0x0000000000000000000000000000000000000000";
@@ -60244,7 +60452,7 @@ var asBig = (v) => {
60244
60452
  }
60245
60453
  };
60246
60454
  var num9 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
60247
- var toPct = (v) => {
60455
+ var toPct2 = (v) => {
60248
60456
  const n = num9(v);
60249
60457
  return n == null ? 0 : n * 100;
60250
60458
  };
@@ -60280,9 +60488,9 @@ function parseVault9(v, chainId, prices, tokenList) {
60280
60488
  const realized = num9(v.apr?.netAPR);
60281
60489
  const forward = num9(v.apr?.forwardAPR?.netAPR);
60282
60490
  const isForwardApr = realized == null && forward != null;
60283
- const supplyRate = toPct(realized ?? forward);
60284
- const rewardsRate = toPct(v.apr?.extra?.stakingRewardsAPR);
60285
- const fee = toPct(v.apr?.fees?.performance);
60491
+ const supplyRate = toPct2(realized ?? forward);
60492
+ const rewardsRate = toPct2(v.apr?.extra?.stakingRewardsAPR);
60493
+ const fee = toPct2(v.apr?.fees?.performance);
60286
60494
  const oracleKey = toOracleKey(assetMeta?.assetGroup) ?? toGenericPriceKey(assetAddr, chainId);
60287
60495
  const apiPrice = num9(v.tvl?.price) ?? 0;
60288
60496
  const priceUsd = prices[oracleKey] ?? prices[assetAddr] ?? apiPrice;
@@ -65066,11 +65274,12 @@ var llamaLendAdapter = {
65066
65274
  /**
65067
65275
  * The band count as an editable TERM, not just a curve to read.
65068
65276
  *
65069
- * `bandLtv` alone cannot drive a control: it is four sampled points,
65070
- * and it is missing on any market whose curve could not be computed.
65071
- * The domain is always known — `MIN_TICKS`/`MAX_TICKS` are 4..50 on
65072
- * both generations — so the control works even where the curve does
65073
- * not.
65277
+ * `bandLtv` alone cannot drive a control: it is missing on any
65278
+ * market whose geometry could not be read. The domain is always
65279
+ * known — `MIN_TICKS`/`MAX_TICKS` are 4..50 on both generations — so
65280
+ * the control works even where the curve does not. Where the curve
65281
+ * IS present it spans the full domain, so a control can show the LTV
65282
+ * for every value it offers.
65074
65283
  *
65075
65284
  * `immutableAfterOpen` is what tells the UI to render this read-only
65076
65285
  * on an existing loan: `_add_collateral_borrow` reuses the tick
@@ -65135,7 +65344,16 @@ var frankencoinAdapter = {
65135
65344
  return {
65136
65345
  // Not a coverage hole: the protocol has no price feed by design.
65137
65346
  oracle: { kind: "none" },
65347
+ supply: {
65348
+ // Collateral inside a position. There is no ZCHF lending deposit at
65349
+ // all — the earn surface is the savings module, a separate provider —
65350
+ // so this side never pays interest.
65351
+ role: "collateral",
65352
+ availability: { opensWith: "both-legs" },
65353
+ counterparty: { kind: "cdp", solvency: "overcollateralized" }
65354
+ },
65138
65355
  borrow: {
65356
+ availability: { opensWith: "both-legs" },
65139
65357
  debtShape: "accruing",
65140
65358
  maturity: expiration > 0 ? {
65141
65359
  kind: "fixed-date",