@1delta/margin-fetcher 0.0.402 → 0.0.404

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
@@ -11288,8 +11288,10 @@ var MORPHO_LENS = {
11288
11288
  [Chain.ETHEREUM_MAINNET]: "0xFc107f469A92c0de7B3105B802584CD6c7D710C2",
11289
11289
  [Chain.PLASMA_MAINNET]: "0xcB6Eb8df68153cebF60E1872273Ef52075a5C297",
11290
11290
  [Chain.BERACHAIN]: "0x7a59ddbB76521E8982Fa3A08598C9a83b14A6C07",
11291
- [Chain.BNB_SMART_CHAIN_MAINNET]: "0x79f4061BF049c5c6CAC6bfe2415c2460815F4ac7",
11292
- // lista lens
11291
+ // Serves BOTH Morpho Blue and Lista on 56 — one lens exposes
11292
+ // `get{,Lista,Moolah}{Market,User}DataCompact`. Byte-identical to the
11293
+ // previous entry 0x79f4061B… (same keccak), so Lista is unaffected.
11294
+ [Chain.BNB_SMART_CHAIN_MAINNET]: "0x7812227c90D55Cc1620C425980141e60b7635824",
11293
11295
  [Chain.UNICHAIN]: "0xA453ba397c61B0c292EA3959A858821145B2707F",
11294
11296
  [Chain.SEI_NETWORK]: "0xcB6Eb8df68153cebF60E1872273Ef52075a5C297",
11295
11297
  [Chain.MONAD_MAINNET]: "0x0bd7473CbBf81d9dD936c61117eD230d95006CA2",
@@ -11304,13 +11306,24 @@ var MORPHO_LENS = {
11304
11306
  [Chain.ABSTRACT]: "0x8e24cfc19c6c00c524353cb8816f5f1c2f33c201",
11305
11307
  [Chain.INK]: "0x0bd7473CbBf81d9dD936c61117eD230d95006CA2",
11306
11308
  [Chain.KAIA_MAINNET]: "0x925716D57c842B50806884EDb295bA3E3A8EBdFE",
11307
- // blue-api does not index these three, so the lens is the ONLY way to read
11309
+ // blue-api does not index these four, so the lens is the ONLY way to read
11308
11310
  // their Morpho markets — see `lenderApiOnly` in fetchLenderAll.ts.
11309
11311
  [Chain.GNOSIS]: "0x98542B95B44a4732EffB1F4F01A696F1546236d3",
11312
+ // Same address as Gnosis — same deployer + nonce.
11313
+ [Chain.XDC_NETWORK]: "0x98542B95B44a4732EffB1F4F01A696F1546236d3",
11310
11314
  [Chain.AVALANCHE_C_CHAIN]: "0x2eA75bBBc5615651efc9401FBc9Db4d03B055CA6",
11311
11315
  // Same address as HyperEVM — same deployer + nonce, so CREATE lands
11312
11316
  // identically on both chains.
11313
- [Chain.MEGAETH_MAINNET]: "0x6Bc6aCB905c1216B0119C87Bf9E178ce298310FA"
11317
+ [Chain.MEGAETH_MAINNET]: "0x6Bc6aCB905c1216B0119C87Bf9E178ce298310FA",
11318
+ // Deployed 2026-08-03. Same reason as the block above — blue-api indexes
11319
+ // none of these, so the lens is the only read path. Sonic and Morph already
11320
+ // had markets in the registry (9 and 1) that were silently unreadable.
11321
+ [Chain.SONIC_MAINNET]: "0xd184c5315B728c1C990f59dDD275c8155f8e255c",
11322
+ [Chain.CRONOS_MAINNET]: "0x79f4061BF049c5c6CAC6bfe2415c2460815F4ac7",
11323
+ [Chain.MORPH]: "0x79f4061BF049c5c6CAC6bfe2415c2460815F4ac7",
11324
+ [Chain.MODE]: "0x2eA75bBBc5615651efc9401FBc9Db4d03B055CA6",
11325
+ [Chain.LINEA]: "0x2eA75bBBc5615651efc9401FBc9Db4d03B055CA6",
11326
+ [Chain.SCROLL]: "0x4aEA1CE479BF7E036bBB6826A2bF084bce6560a0"
11314
11327
  };
11315
11328
  var buildMorphoUserCallWithLens = (chainId, account, lender, marketsToQuery) => {
11316
11329
  const marketSlices = chunk(marketsToQuery, 100);
@@ -11326,7 +11339,13 @@ var buildMorphoUserCallWithLens = (chainId, account, lender, marketsToQuery) =>
11326
11339
  }));
11327
11340
  };
11328
11341
  var buildMorphoTypeUserCallWithLens = (chainId, account, lender, marketsToQuery, getClient) => {
11329
- return lender === Lender.MORPHO_BLUE ? buildMorphoUserCallWithLens(chainId, account, lender, marketsToQuery) : buildListaUserCallWithLens(chainId, account, lender, marketsToQuery, getClient);
11342
+ return lender === Lender.MORPHO_BLUE ? buildMorphoUserCallWithLens(chainId, account, lender, marketsToQuery) : buildListaUserCallWithLens(
11343
+ chainId,
11344
+ account,
11345
+ lender,
11346
+ marketsToQuery,
11347
+ getClient
11348
+ );
11330
11349
  };
11331
11350
  var buildListaUserCallWithLens = async (chainId, account, lender, marketsToQuery, getClient) => {
11332
11351
  await fetchListaBrokerUserData(
@@ -21451,6 +21470,20 @@ var toBig3 = (v) => {
21451
21470
  return 0n;
21452
21471
  }
21453
21472
  };
21473
+ function pickAuction(rows, nowSec7 = Math.floor(Date.now() / 1e3)) {
21474
+ if (!Array.isArray(rows) || rows.length === 0) return null;
21475
+ const live = rows.filter((a) => a && !a.delisted && !a.nonViableAuction).map((a) => ({
21476
+ id: String(a.id ?? ""),
21477
+ startTime: toNum(a.auctionStartTime),
21478
+ revealTime: toNum(a.revealTime),
21479
+ endTime: toNum(a.auctionEndTime),
21480
+ minBidAmount: String(a.auctionMinBidAmount ?? "0"),
21481
+ minOfferAmount: String(a.auctionMinOfferAmount ?? "0"),
21482
+ maxBidPriceWad: String(a.auctionMaxBidPrice ?? "0"),
21483
+ maxOfferPriceWad: String(a.auctionMaxOfferPrice ?? "0")
21484
+ })).filter((a) => a.endTime > nowSec7).sort((a, b) => a.endTime - b.endTime);
21485
+ return live[0] ?? null;
21486
+ }
21454
21487
  var TermSubgraphSource = class {
21455
21488
  url;
21456
21489
  fetchImpl;
@@ -21485,29 +21518,54 @@ var TermSubgraphSource = class {
21485
21518
  }
21486
21519
  /**
21487
21520
  * ONE query → the aggregate top (best APR + FULL depth) PLUS a bounded book
21488
- * slice (top `maxLevels` open orders per side). `asks` = orders selling repo
21489
- * tokens (the secondary LEND book); `bids` = the rest (borrow side, usually
21490
- * empty Term borrow is sealed-bid auction, not a continuous book). Term
21491
- * secondary orders carry no per-order rate, so every level shares the market's
21492
- * clearing APR; the levels expose per-order SIZE for filtering.
21521
+ * slice (top `maxLevels` open orders per side) PLUS the repo's current
21522
+ * auction round. `asks` = orders selling repo tokens (the secondary LEND
21523
+ * book); `bids` = the rest (borrow side, usually empty Term borrow is
21524
+ * sealed-bid auction, not a continuous book). Term secondary orders carry no
21525
+ * per-order rate, so every level shares the market's clearing APR; the levels
21526
+ * expose per-order SIZE for filtering.
21527
+ *
21528
+ * Two auction reads, deliberately distinct:
21529
+ * - `cleared` — the latest COMPLETE round, whose clearing price IS the
21530
+ * market's fixed APR (and stays the reference rate between auctions).
21531
+ * - `pending` — rounds not yet complete/cancelled. Only one of these is a
21532
+ * real, actionable round; the rest are abandoned listings the subgraph
21533
+ * never marked complete, filtered out below.
21493
21534
  */
21494
21535
  async getTopAndBook(config, maxLevels = 20) {
21495
21536
  const id = config.termRepoId.toLowerCase();
21496
21537
  const repoToken = config.repoToken.toLowerCase();
21497
21538
  const data = await this.gql(
21498
- `query Top($id: ID!, $repoId: Bytes!) {
21539
+ `query Top($id: ID!, $termId: String!, $repoId: Bytes!) {
21499
21540
  termRepo(id: $id) { termRepoTokenRedemptionRatio }
21500
21541
  termAuctions(where: { term: $id, auctionComplete: true }, orderBy: auctionEndTime, orderDirection: desc, first: 1) {
21501
21542
  auctionClearingPrice
21502
21543
  dayCountFractionMantissa
21503
21544
  }
21545
+ pending: termAuctions(
21546
+ where: { term: $termId, auctionComplete: false, auctionCancelled: false }
21547
+ orderBy: auctionEndTime
21548
+ orderDirection: desc
21549
+ first: 5
21550
+ ) {
21551
+ id
21552
+ auctionStartTime
21553
+ revealTime
21554
+ auctionEndTime
21555
+ delisted
21556
+ nonViableAuction
21557
+ auctionMinBidAmount
21558
+ auctionMinOfferAmount
21559
+ auctionMaxBidPrice
21560
+ auctionMaxOfferPrice
21561
+ }
21504
21562
  termOrders(where: { termRepoId: $repoId, orderCancelled: false }, first: 500) {
21505
21563
  makerToken
21506
21564
  originalOrderAmount
21507
21565
  filledAmount
21508
21566
  }
21509
21567
  }`,
21510
- { id, repoId: id }
21568
+ { id, termId: id, repoId: id }
21511
21569
  );
21512
21570
  if (!data) return null;
21513
21571
  const redemptionRatio = toBig3(data.termRepo?.termRepoTokenRedemptionRatio) || BigInt(WAD3);
@@ -21544,7 +21602,7 @@ var TermSubgraphSource = class {
21544
21602
  supplyLiquidity: toLoan(supplyUnits),
21545
21603
  borrowLiquidity: toLoan(borrowUnits)
21546
21604
  };
21547
- return { top, book: { bids, asks } };
21605
+ return { top, book: { bids, asks }, auction: pickAuction(data.pending) };
21548
21606
  }
21549
21607
  async getListings(config) {
21550
21608
  const id = config.termRepoId.toLowerCase();
@@ -21653,17 +21711,23 @@ async function fetchTopAndBookWithFallback2(source, chainId, config, nowSec7) {
21653
21711
  fresh = await source.getTopAndBook(config, TERM_BOOK_LEVELS).catch(() => null);
21654
21712
  } else {
21655
21713
  const top = await source.getBookTop(config).catch(() => null);
21656
- if (top) fresh = { top, book: { bids: [], asks: [] } };
21714
+ if (top) fresh = { top, book: { bids: [], asks: [] }, auction: null };
21657
21715
  }
21658
21716
  if (fresh) {
21659
- lastGood2.set(key, { top: fresh.top, book: fresh.book, at: nowSec7 });
21660
- return { top: fresh.top, book: fresh.book };
21717
+ lastGood2.set(key, {
21718
+ top: fresh.top,
21719
+ book: fresh.book,
21720
+ auction: fresh.auction ?? null,
21721
+ at: nowSec7
21722
+ });
21723
+ return { top: fresh.top, book: fresh.book, auction: fresh.auction ?? null };
21661
21724
  }
21662
21725
  const cached = lastGood2.get(key);
21663
21726
  if (cached && nowSec7 - cached.at <= LKG_TTL_SEC2) {
21664
- return { top: cached.top, book: cached.book };
21727
+ const stillOpen = cached.auction && cached.auction.endTime > nowSec7 ? cached.auction : null;
21728
+ return { top: cached.top, book: cached.book, auction: stillOpen };
21665
21729
  }
21666
- return { top: null, book: null };
21730
+ return { top: null, book: null, auction: null };
21667
21731
  }
21668
21732
  async function fetchTermMarkets(chainId, source = createTermBookSource(chainId)) {
21669
21733
  const markets = termMarketsByChain(chainId);
@@ -21672,15 +21736,15 @@ async function fetchTermMarkets(chainId, source = createTermBookSource(chainId))
21672
21736
  return Promise.all(
21673
21737
  markets.map(async (config) => {
21674
21738
  if (Number(config.maturity) <= nowSec7) {
21675
- return { config, top: null, book: null };
21739
+ return { config, top: null, book: null, auction: null };
21676
21740
  }
21677
- const { top, book } = await fetchTopAndBookWithFallback2(
21741
+ const { top, book, auction } = await fetchTopAndBookWithFallback2(
21678
21742
  source,
21679
21743
  chainId,
21680
21744
  config,
21681
21745
  nowSec7
21682
21746
  );
21683
- return { config, top, book };
21747
+ return { config, top, book, auction };
21684
21748
  })
21685
21749
  );
21686
21750
  }
@@ -21714,6 +21778,76 @@ function termLenderKey(termRepoId) {
21714
21778
  const body = termRepoId.startsWith("0x") ? termRepoId.slice(2) : termRepoId;
21715
21779
  return "TERM_FINANCE_" + body.toUpperCase();
21716
21780
  }
21781
+ function fmtDuration(secs) {
21782
+ if (secs >= 2 * 86400) return `${Math.round(secs / 86400)} days`;
21783
+ if (secs >= 2 * 3600) return `${Math.round(secs / 3600)} hours`;
21784
+ return `${Math.max(1, Math.round(secs / 60))} minutes`;
21785
+ }
21786
+ function auctionImplications(status, matured, secondsUntilClose) {
21787
+ const out = [];
21788
+ if (matured) {
21789
+ out.push(
21790
+ "This term has MATURED \u2014 it can no longer be borrowed or lent. Existing positions settle through the repurchase/redemption window."
21791
+ );
21792
+ return out;
21793
+ }
21794
+ if (status === "open") {
21795
+ out.push(
21796
+ `\u26A0 Borrowing is a SEALED BID into an auction, not an instant loan. Submissions close in ${secondsUntilClose != null ? fmtDuration(secondsUntilClose) : "a limited window"}; after that nothing can be borrowed until the next round is listed.`
21797
+ );
21798
+ out.push(
21799
+ "Your rate is NOT the rate shown \u2014 the shown figure is the last round\u2019s clearing rate. Your actual rate is set when this round clears, and a bid above the clearing rate may not be filled at all."
21800
+ );
21801
+ } else if (status === "revealing") {
21802
+ out.push(
21803
+ "\u26A0 Bidding has CLOSED for this round \u2014 sealed prices are being revealed and the auction is clearing. No new borrows until the next round is listed."
21804
+ );
21805
+ } else if (status === "upcoming") {
21806
+ out.push(
21807
+ "An auction round is scheduled but not yet accepting bids. Borrowing becomes possible when it opens."
21808
+ );
21809
+ } else {
21810
+ out.push(
21811
+ "\u26A0 NO auction is open, so this market CANNOT be borrowed right now. Term originates loans only during scheduled sealed-bid auctions."
21812
+ );
21813
+ out.push(
21814
+ "Any borrow rate shown is the last round\u2019s clearing rate \u2014 historical, not obtainable."
21815
+ );
21816
+ }
21817
+ out.push(
21818
+ "Lending is still possible between rounds by buying repo tokens on the secondary market; only borrowing depends on the auction window."
21819
+ );
21820
+ out.push(
21821
+ "Fixed maturity: the debt is a static face value with no interest accrual, repayable up to the end of the repurchase window."
21822
+ );
21823
+ return out;
21824
+ }
21825
+ function toFixedTermAuction(auction, now, matured = false) {
21826
+ const base = { canBorrow: false, canLend: !matured };
21827
+ if (!auction || auction.endTime <= now) {
21828
+ return {
21829
+ status: "closed",
21830
+ ...base,
21831
+ implications: auctionImplications("closed", matured)
21832
+ };
21833
+ }
21834
+ const status = now < auction.startTime ? "upcoming" : now < auction.revealTime ? "open" : "revealing";
21835
+ const secondsUntilClose = status === "open" ? Math.max(0, auction.revealTime - now) : void 0;
21836
+ return {
21837
+ status,
21838
+ ...base,
21839
+ // A matured repo is never borrowable, whatever the round says.
21840
+ canBorrow: status === "open" && !matured,
21841
+ secondsUntilClose,
21842
+ implications: auctionImplications(status, matured, secondsUntilClose),
21843
+ id: auction.id,
21844
+ startTime: auction.startTime,
21845
+ revealTime: auction.revealTime,
21846
+ endTime: auction.endTime,
21847
+ minBorrowAmount: auction.minBidAmount,
21848
+ minLendAmount: auction.minOfferAmount
21849
+ };
21850
+ }
21717
21851
  function currencyFor2(address, decimals, tokens) {
21718
21852
  const lower = address.toLowerCase();
21719
21853
  return tokens[lower] ?? { address: lower, symbol: "", name: "", decimals };
@@ -21725,7 +21859,7 @@ function convertTermMarketsToResponse(raw, chainId, prices = {}, _additionalYiel
21725
21859
  }, tokens = {}) {
21726
21860
  const out = {};
21727
21861
  const now = nowSec2();
21728
- for (const { config, top, book } of raw) {
21862
+ for (const { config, top, book, auction } of raw) {
21729
21863
  if (!config?.termRepoId || !config.purchaseToken) continue;
21730
21864
  const m = termLenderKey(config.termRepoId);
21731
21865
  const maturity = Number(config.maturity);
@@ -21733,7 +21867,9 @@ function convertTermMarketsToResponse(raw, chainId, prices = {}, _additionalYiel
21733
21867
  const matured = ttm <= 0;
21734
21868
  const supplyAprPct = !matured ? top?.supplyAprPct ?? 0 : 0;
21735
21869
  const borrowAprPct = !matured ? top?.borrowAprPct ?? 0 : 0;
21736
- const terms = borrowAprPct > 0 && !matured ? [
21870
+ const auctionInfo = toFixedTermAuction(auction, now, matured);
21871
+ const auctionOpen = auctionInfo.canBorrow;
21872
+ const terms = borrowAprPct > 0 && auctionOpen ? [
21737
21873
  {
21738
21874
  termId: 0,
21739
21875
  durationSecs: Math.max(0, ttm),
@@ -21788,10 +21924,15 @@ function convertTermMarketsToResponse(raw, chainId, prices = {}, _additionalYiel
21788
21924
  },
21789
21925
  closeFactor: 1,
21790
21926
  collateralActive: false,
21791
- borrowingEnabled: !matured,
21792
- depositsEnabled: !matured,
21927
+ // Borrow needs a LIVE auction round, not just an unmatured repo.
21928
+ // `depositsEnabled` stays on the maturity alone: lending is also possible
21929
+ // between rounds by buying repo tokens on the secondary book.
21930
+ borrowingEnabled: auctionOpen,
21931
+ depositsEnabled: auctionInfo.canLend,
21793
21932
  hasStable: false,
21794
- variableBorrowDisabled: false,
21933
+ // Term has no variable rate at all; between auctions there is no
21934
+ // borrowable rate either, so flag it rather than let a 0 read as free.
21935
+ variableBorrowDisabled: true,
21795
21936
  isActive: true,
21796
21937
  isFrozen: false
21797
21938
  };
@@ -21874,7 +22015,11 @@ function convertTermMarketsToResponse(raw, chainId, prices = {}, _additionalYiel
21874
22015
  earlyRepay: { kind: "none" },
21875
22016
  // Fixed rate is discovered by the periodic sealed-bid auction; the
21876
22017
  // per-repo servicer is the market-level venue reference.
21877
- provider: { kind: "auction", address: config.servicer }
22018
+ provider: { kind: "auction", address: config.servicer },
22019
+ // The origination window. `closed` (the common state between rounds)
22020
+ // is what tells a consumer that this market is display-only right
22021
+ // now, rather than an ordinary market with an empty rate card.
22022
+ auction: auctionInfo
21878
22023
  },
21879
22024
  collateralParams: config.collateralParams,
21880
22025
  // Bounded book slice (top-N open orders/side) for later filtering. Term
@@ -22864,7 +23009,7 @@ function currencyFor6(address, decimals, symbol, tokens) {
22864
23009
  const lower = address.toLowerCase();
22865
23010
  return tokens[lower] ?? { address: lower, symbol, name: symbol, decimals };
22866
23011
  }
22867
- function fmtDuration(sec) {
23012
+ function fmtDuration2(sec) {
22868
23013
  if (sec == null || sec <= 0) return "a short window";
22869
23014
  if (sec < 3600) return `${Math.round(sec / 60)} minute${sec < 120 ? "" : "s"}`;
22870
23015
  if (sec < 86400) return `${Math.round(sec / 3600)} hour${sec < 7200 ? "" : "s"}`;
@@ -23030,7 +23175,7 @@ function convertTellerMarketsToResponse(raw, chainId, prices = {}, _additionalYi
23030
23175
  const originationFeeBps = marketFeeBps + protocolFeeBps;
23031
23176
  const originationFeePercent = originationFeeBps / 100;
23032
23177
  const graceSec = p.paymentDefaultDuration;
23033
- const graceHuman = fmtDuration(graceSec);
23178
+ const graceHuman = fmtDuration2(graceSec);
23034
23179
  const ltvPct = ltv > 0 ? Math.round(ltv * 100) : void 0;
23035
23180
  const lossX = ltv > 0 ? (1 / ltv).toFixed(1) : void 0;
23036
23181
  const implications = [];
@@ -23966,6 +24111,13 @@ function lenderApiOnly(lender, chainId) {
23966
24111
  if (chainId === Chain.GNOSIS) return false;
23967
24112
  if (chainId === Chain.AVALANCHE_C_CHAIN) return false;
23968
24113
  if (chainId === Chain.MEGAETH_MAINNET) return false;
24114
+ if (chainId === Chain.XDC_NETWORK) return false;
24115
+ if (chainId === Chain.SONIC_MAINNET) return false;
24116
+ if (chainId === Chain.CRONOS_MAINNET) return false;
24117
+ if (chainId === Chain.MORPH) return false;
24118
+ if (chainId === Chain.MODE) return false;
24119
+ if (chainId === Chain.LINEA) return false;
24120
+ if (chainId === Chain.SCROLL) return false;
23969
24121
  return true;
23970
24122
  }
23971
24123
  if (isMidnight(lender)) return true;
@@ -38064,6 +38216,127 @@ var sparkSavingsFetcher = {
38064
38216
  return out;
38065
38217
  }
38066
38218
  };
38219
+ var RATE_ABI = [
38220
+ {
38221
+ name: "rate",
38222
+ type: "function",
38223
+ stateMutability: "view",
38224
+ inputs: [],
38225
+ outputs: [{ type: "uint256" }]
38226
+ }
38227
+ ];
38228
+ var SUSDP_VAULTS = [
38229
+ {
38230
+ chain: Chain.AVALANCHE_C_CHAIN,
38231
+ address: "0x9d92c21205383651610f90722131655a5b8ed3e0"
38232
+ },
38233
+ {
38234
+ chain: Chain.HYPEREVM,
38235
+ address: "0x9b3a8f7cec208e247d97dee13313690977e24459"
38236
+ },
38237
+ {
38238
+ chain: Chain.ETHEREUM_MAINNET,
38239
+ address: "0x0d45b129dc868963025db79a9074ea9c9e32cae4"
38240
+ },
38241
+ {
38242
+ chain: Chain.BASE,
38243
+ address: "0x472ed57b376fe400259fb28e5c46eb53f0e3e7e7"
38244
+ }
38245
+ ];
38246
+ var YEAR_SECONDS5 = 31536e3;
38247
+ var BASE_27 = 1e27;
38248
+ var aprFromRate = (rate) => Number(rate) / BASE_27 * YEAR_SECONDS5 * 100;
38249
+ var SUSDP_KEY = "SUSDP";
38250
+ var SUSDP_GROUP_KEY = "Staked USDp::sUSDp";
38251
+ var parallelFetcher = {
38252
+ label: "PARALLEL_SUSDP",
38253
+ fetch: async () => {
38254
+ let apr = 0;
38255
+ for (const { chain, address } of SUSDP_VAULTS) {
38256
+ try {
38257
+ const [rate] = await multicallRetryUniversal({
38258
+ chain,
38259
+ abi: RATE_ABI,
38260
+ calls: [{ address, name: "rate", params: [] }],
38261
+ allowFailure: false
38262
+ });
38263
+ apr = aprFromRate(rate);
38264
+ break;
38265
+ } catch {
38266
+ }
38267
+ }
38268
+ return { [SUSDP_KEY]: apr, [SUSDP_GROUP_KEY]: apr };
38269
+ }
38270
+ };
38271
+
38272
+ // src/yields/intrinsic/fetchers/rwaFunds.ts
38273
+ var RWA_FUNDS = {
38274
+ // Circle (ex-Hashnote) USYC — $3.0B, the largest tokenized MMF.
38275
+ // Base's `US Yield Coin::USYC` (0xeee7af83…) is unverified → skipped.
38276
+ USYC: {
38277
+ pool: "7c0a89c7-70cf-460c-b62e-cb278bf97e8f",
38278
+ keys: ["Hashnote USYC::USYC", "Circle USYC::USYC"]
38279
+ },
38280
+ // BlackRock BUIDL — $2.0B. Rebasing (NAV $1, monthly dividend
38281
+ // distributions); the percent is still the holder's carry. Chain-1
38282
+ // group is the truncated variant.
38283
+ BUIDL: {
38284
+ pool: "b663ca59-c7e6-4435-ae4a-28d339ce6a15",
38285
+ keys: [
38286
+ "BlackRock USD Institutional Digital Liquidity Fund::BUIDL",
38287
+ "BlackRock USD Institutional Digital Liq::BUIDL"
38288
+ ]
38289
+ },
38290
+ // Superstate USTB — $740M short-duration govt securities.
38291
+ USTB: {
38292
+ pool: "1910847a-f8b5-40ce-a1ab-1dafdded5fbb",
38293
+ keys: ["Superstate Short Duration US Government Securities Fund::USTB"]
38294
+ },
38295
+ // Superstate USCC — crypto-carry fund (basis, not T-bills).
38296
+ USCC: {
38297
+ pool: "aff74ce8-4fe3-462b-af11-542cc16d24b2",
38298
+ keys: ["Superstate USCC::USCC", "Superstate Crypto Carry Fund::USCC"]
38299
+ },
38300
+ // VanEck VBILL (via Securitize) — $179M.
38301
+ VBILL: {
38302
+ pool: "5663e2ef-7e9c-4755-9701-9b59b90dbba3",
38303
+ keys: ["VanEck Treasury Fund::VBILL"]
38304
+ },
38305
+ // OpenEden TBILL — bare group on 1/42161. (THBILL is Theo's fund —
38306
+ // a different asset with its own fetcher.)
38307
+ TBILL: {
38308
+ pool: "e140f3b2-0327-46ea-93f5-88b17b0a0a16",
38309
+ keys: ["TBILL"]
38310
+ },
38311
+ // OpenEden cUSDO — compounding ERC-4626 wrapper over USDO; accrues
38312
+ // USDO's T-bill yield. The sweep's one full-vault graduate: it also
38313
+ // has savings-registry rows on 1/56/8453 (permissionless wrap —
38314
+ // `maxDeposit = uint256.max` verified) binding these same keys.
38315
+ CUSDO: {
38316
+ pool: "f083596e-032d-4d6b-a7a8-1836d3f99bcd",
38317
+ keys: [
38318
+ "Compounding Open Dollar::CUSDO",
38319
+ "Compounding Open Dollar::cUSDO",
38320
+ "Compounding OpenDollar::CUSDO"
38321
+ ]
38322
+ }
38323
+ };
38324
+ var rwaFundsFetcher = {
38325
+ label: "RWA_FUNDS",
38326
+ fetch: async () => {
38327
+ const out = {};
38328
+ await Promise.all(
38329
+ Object.entries(RWA_FUNDS).map(async ([label, fund]) => {
38330
+ try {
38331
+ const apr = apyToAprPercent(await fetchDefiLlamaApy(fund.pool));
38332
+ for (const key of fund.keys) out[key] = apr;
38333
+ } catch {
38334
+ }
38335
+ })
38336
+ );
38337
+ return out;
38338
+ }
38339
+ };
38067
38340
 
38068
38341
  // src/yields/intrinsic/fetchers/reProtocol.ts
38069
38342
  var REUSD_KEY = "RE::reUSD";
@@ -39402,12 +39675,74 @@ var reUsdGroup = {
39402
39675
  // is no price source and they are deliberately NOT registered.
39403
39676
  }
39404
39677
  };
39678
+ var sUsdpGroup = {
39679
+ base: {
39680
+ symbol: "sUSDp",
39681
+ brand: "Parallel",
39682
+ decimals: 18,
39683
+ isRebasing: false,
39684
+ isMintable: true,
39685
+ withdrawalMode: "instant",
39686
+ yieldFetcher: parallelFetcher,
39687
+ yieldKey: SUSDP_KEY
39688
+ },
39689
+ chains: {
39690
+ "43114": {
39691
+ address: "0x9d92c21205383651610f90722131655a5b8ed3e0",
39692
+ underlying: "0x9ee1963f05553ef838604dd39403be21cef26aa4"
39693
+ // USDp
39694
+ },
39695
+ "999": {
39696
+ address: "0x9b3a8f7cec208e247d97dee13313690977e24459",
39697
+ underlying: "0xbe65f0f410a72bec163dc65d46c83699e957d588"
39698
+ },
39699
+ "1": {
39700
+ address: "0x0d45b129dc868963025db79a9074ea9c9e32cae4",
39701
+ // Yes, the HyperEVM sUSDp address — see the address-reuse trap
39702
+ // in the group comment.
39703
+ underlying: "0x9b3a8f7cec208e247d97dee13313690977e24459"
39704
+ },
39705
+ "8453": {
39706
+ address: "0x472ed57b376fe400259fb28e5c46eb53f0e3e7e7",
39707
+ underlying: "0x76a9a0062ec6712b99b4f63bd2b4270185759dd5"
39708
+ }
39709
+ }
39710
+ };
39711
+ var cUsdoGroup = {
39712
+ base: {
39713
+ symbol: "cUSDO",
39714
+ brand: "OpenEden",
39715
+ decimals: 18,
39716
+ isRebasing: false,
39717
+ isMintable: true,
39718
+ withdrawalMode: "instant",
39719
+ yieldFetcher: rwaFundsFetcher,
39720
+ yieldKey: "Compounding Open Dollar::CUSDO"
39721
+ },
39722
+ chains: {
39723
+ "1": {
39724
+ address: "0xad55aebc9b8c03fc43cd9f62260391c13c23e7c0",
39725
+ underlying: "0x8238884ec9668ef77b90c6dff4d1a9f4f4823bfe"
39726
+ // USDO
39727
+ },
39728
+ "56": {
39729
+ address: "0x64748ea3e31d0b7916f0ff91b017b9f404ded8ef",
39730
+ underlying: "0x302e52aff9815b9d1682473dbfb9c74f9b750aa8"
39731
+ },
39732
+ "8453": {
39733
+ address: "0x83db73ef5192de4b6a4c92bd0141ba1a0dc87c65",
39734
+ underlying: "0xad55aebc9b8c03fc43cd9f62260391c13c23e7c0"
39735
+ }
39736
+ }
39737
+ };
39405
39738
  var MULTI_CHAIN_GROUPS = [
39406
39739
  stUsdGroup,
39407
39740
  stEurGroup,
39408
39741
  yUsdGroup,
39409
39742
  sUsdcGroup,
39410
- reUsdGroup
39743
+ reUsdGroup,
39744
+ sUsdpGroup,
39745
+ cUsdoGroup
39411
39746
  ];
39412
39747
  var sparkV2Base = (symbol, decimals) => ({
39413
39748
  reader: "erc4626-idle",
@@ -40209,6 +40544,10 @@ var usdaiFetcher = {
40209
40544
 
40210
40545
  // src/yields/intrinsic/fetchers/ondo.ts
40211
40546
  var ONDO_BASE_URL = "https://app.ondo.finance/api/v2/assets";
40547
+ var OUSG_KEY = "OUSG";
40548
+ var OUSG_GROUP_KEY = "Ondo Short-Term US Government Treasuries::OUSG";
40549
+ var USDY_KEY = "USDY";
40550
+ var USDY_GROUP_KEY = "Ondo US Dollar Yield::USDY";
40212
40551
  var ondoFetcher = {
40213
40552
  label: "ONDO",
40214
40553
  fetch: async () => {
@@ -40216,9 +40555,13 @@ var ondoFetcher = {
40216
40555
  fetch(`${ONDO_BASE_URL}/ousg/info`).then((r) => r.json()),
40217
40556
  fetch(`${ONDO_BASE_URL}/usdy/info`).then((r) => r.json())
40218
40557
  ]);
40558
+ const ousg = apyToAprPercent(Number(ousgRes?.primaryMarket?.apy ?? 0));
40559
+ const usdy = apyToAprPercent(Number(usdyRes?.primaryMarket?.apy ?? 0));
40219
40560
  return {
40220
- OUSG: apyToAprPercent(Number(ousgRes?.primaryMarket?.apy ?? 0)),
40221
- USDY: apyToAprPercent(Number(usdyRes?.primaryMarket?.apy ?? 0))
40561
+ [OUSG_KEY]: ousg,
40562
+ [OUSG_GROUP_KEY]: ousg,
40563
+ [USDY_KEY]: usdy,
40564
+ [USDY_GROUP_KEY]: usdy
40222
40565
  };
40223
40566
  }
40224
40567
  };
@@ -40237,38 +40580,78 @@ var syzusdFetcher = {
40237
40580
  // src/yields/intrinsic/fetchers/midas.ts
40238
40581
  var MIDAS_APY_URL = "https://api-prod.midas.app/api/data/apys";
40239
40582
  var MIDAS_TOKENS = [
40240
- { apyKey: "mtbill", assetGroup: "Midas mTBILL::MTBILL" },
40241
- { apyKey: "mbasis", assetGroup: "Midas mBASIS::MBASIS" },
40242
- { apyKey: "mbtc", assetGroup: "Midas mBTC::MBTC" },
40243
- { apyKey: "medge", assetGroup: "Midas mEDGE::MEDGE" },
40244
- { apyKey: "mre7", assetGroup: "Midas mRe7YIELD::MRE7YIELD" },
40245
- { apyKey: "mre7sol", assetGroup: "Midas mRe7SOL::MRE7SOL" },
40246
- { apyKey: "mmev", assetGroup: "Midas mMEV::MMEV" },
40247
- { apyKey: "mfone", assetGroup: "Midas Fasanara ONE::mF-ONE" },
40248
- { apyKey: "mhyper", assetGroup: "Midas Hyperithm::mHYPER" },
40249
- { apyKey: "mapollo", assetGroup: "Midas mAPOLLO::MAPOLLO" },
40250
- { apyKey: "mevbtc", assetGroup: "Midas mevBTC::MEVBTC" },
40251
- { apyKey: "mfarm", assetGroup: "Midas mFARM::MFARM" },
40252
- { apyKey: "msyrupusd", assetGroup: "Midas msyrupUSD::MSYRUPUSD" },
40253
- { apyKey: "msyrupusdp", assetGroup: "Midas msyrupUSDp::MSYRUPUSDP" },
40254
- { apyKey: "mre7btc", assetGroup: "Midas mRe7BTC::MRE7BTC" },
40255
- { apyKey: "mxrp", assetGroup: "Midas XRP::mXRP" },
40256
- { apyKey: "mevusd", assetGroup: "Midas Everstake USD::MEVUSD" },
40257
- { apyKey: "mhypereth", assetGroup: "Midas Hyperithm ETH::mHyperETH" },
40258
- { apyKey: "mhyperbtc", assetGroup: "Midas Hyperithm BTC::mHyperBTC" },
40259
- { apyKey: "mm1usd", assetGroup: "Midas M1 USD Market Neutral::mM1-USD" }
40583
+ { apyKey: "mtbill", keys: ["Midas mTBILL::MTBILL"] },
40584
+ {
40585
+ apyKey: "mbasis",
40586
+ keys: ["Midas mBASIS::MBASIS", "Midas mBASIS::mBASIS"]
40587
+ },
40588
+ {
40589
+ apyKey: "mbtc",
40590
+ keys: ["Midas mBTC::MBTC", "Midas BTC Yield Token::mBTC"]
40591
+ },
40592
+ { apyKey: "medge", keys: ["Midas mEDGE::MEDGE", "Midas mEDGE::mEDGE"] },
40593
+ {
40594
+ apyKey: "mre7",
40595
+ keys: ["Midas mRe7YIELD::MRE7YIELD", "Midas Re7 Yield::mRe7YIELD"]
40596
+ },
40597
+ { apyKey: "mre7sol", keys: ["Midas mRe7SOL::MRE7SOL"] },
40598
+ { apyKey: "mre7eth", keys: ["Midas Re7 Ethereum::mRe7ETH"] },
40599
+ { apyKey: "mmev", keys: ["Midas mMEV::MMEV"] },
40600
+ { apyKey: "mfone", keys: ["Midas Fasanara ONE::mF-ONE"] },
40601
+ {
40602
+ apyKey: "mhyper",
40603
+ keys: ["Midas Hyperithm::mHYPER", "mHYPER::Midas Hyperithm"]
40604
+ },
40605
+ { apyKey: "mapollo", keys: ["Midas mAPOLLO::MAPOLLO"] },
40606
+ // NB Avalanche carries a `MEV BTC::mevBTC` row (0x1f8e769b…) that MAY be
40607
+ // this asset, but the issuer is unverifiable (Midas's docs/API are
40608
+ // Cloudflare-walled) — deliberately not keyed, same rule as bondusd.
40609
+ { apyKey: "mevbtc", keys: ["Midas mevBTC::MEVBTC"] },
40610
+ { apyKey: "mfarm", keys: ["Midas mFARM::MFARM"] },
40611
+ { apyKey: "msyrupusd", keys: ["Midas msyrupUSD::MSYRUPUSD"] },
40612
+ { apyKey: "msyrupusdp", keys: ["Midas msyrupUSDp::MSYRUPUSDP"] },
40613
+ { apyKey: "mre7btc", keys: ["Midas mRe7BTC::MRE7BTC"] },
40614
+ { apyKey: "mxrp", keys: ["Midas XRP::mXRP"] },
40615
+ { apyKey: "mevusd", keys: ["Midas Everstake USD::MEVUSD"] },
40616
+ { apyKey: "mhypereth", keys: ["Midas Hyperithm ETH::mHyperETH"] },
40617
+ {
40618
+ apyKey: "mhyperbtc",
40619
+ keys: ["Midas Hyperithm BTC::mHyperBTC", "Midas Hyperithm BTC::MHYPERBTC"]
40620
+ },
40621
+ { apyKey: "mm1usd", keys: ["Midas M1 USD Market Neutral::mM1-USD"] },
40622
+ // 2026-08 sweep additions
40623
+ { apyKey: "mrox", keys: ["Midas Rockaway Market Neutral::MROX"] },
40624
+ { apyKey: "mglobal", keys: ["Midas Fasanara Global::MGLOBAL"] },
40625
+ { apyKey: "mglo", keys: ["Midas Fasanara Global Open::MGLO"] },
40626
+ { apyKey: "weeur", keys: ["Liquid Euro::weEUR"] },
40627
+ { apyKey: "plusd", keys: ["Plasma USD::plUSD"] },
40628
+ { apyKey: "splusd", keys: ["Staked Plasma USD::splUSD"] }
40260
40629
  ];
40630
+ var MAX_DECIMAL_APY = 5;
40261
40631
  var midasFetcher = {
40262
40632
  label: "MIDAS",
40263
40633
  fetch: async () => {
40264
- const apys = await fetch(MIDAS_APY_URL).then(
40265
- (r) => r.json()
40266
- );
40634
+ const res = await fetch(MIDAS_APY_URL, {
40635
+ headers: {
40636
+ accept: "application/json",
40637
+ // WAF: bare fetches get 403 + `{}` — see header note.
40638
+ "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36",
40639
+ origin: "https://midas.app",
40640
+ referer: "https://midas.app/"
40641
+ },
40642
+ signal: AbortSignal.timeout(8e3)
40643
+ });
40644
+ if (!res.ok) throw new Error(`Midas APY HTTP ${res.status}`);
40645
+ const apys = await res.json();
40646
+ if (!apys || Object.keys(apys).length === 0) {
40647
+ throw new Error("Midas APY payload empty (WAF block?)");
40648
+ }
40267
40649
  const result = {};
40268
- for (const { apyKey, assetGroup } of MIDAS_TOKENS) {
40650
+ for (const { apyKey, keys } of MIDAS_TOKENS) {
40269
40651
  const apy = apys[apyKey];
40270
- if (apy && apy > 0) {
40271
- result[assetGroup] = apyToAprPercent(apy * 100);
40652
+ if (typeof apy === "number" && apy > 0 && apy <= MAX_DECIMAL_APY) {
40653
+ const apr = apyToAprPercent(apy * 100);
40654
+ for (const key of keys) result[key] = apr;
40272
40655
  }
40273
40656
  }
40274
40657
  return result;
@@ -40563,6 +40946,8 @@ var LENDING_ONLY_FETCHERS = [
40563
40946
  pendleFetcher,
40564
40947
  usdaiFetcher,
40565
40948
  ondoFetcher,
40949
+ // rwaFundsFetcher is NOT here: cUSDO's savings rows vault-wire it, so
40950
+ // it reaches the global map via `collectVaultFetchers` (disjointness rule).
40566
40951
  syzusdFetcher,
40567
40952
  midasFetcher,
40568
40953
  capFetcher,
@@ -54653,7 +55038,7 @@ var readVaultSharePrices = async (chainId, addresses, multicallRetry) => {
54653
55038
  };
54654
55039
 
54655
55040
  // src/vaults/yield/annualize.ts
54656
- var YEAR_SECONDS5 = 365 * 24 * 60 * 60;
55041
+ var YEAR_SECONDS6 = 365 * 24 * 60 * 60;
54657
55042
  var SCALE = 10n ** 18n;
54658
55043
  var appendSnapshot = (points, snap, options) => {
54659
55044
  const maxPoints = options?.maxPoints ?? 90;
@@ -54682,7 +55067,7 @@ var computeVaultApr = (points, options) => {
54682
55067
  if (pThen === 0n) return void 0;
54683
55068
  const ratioScaled = BigInt(now.p) * SCALE / pThen;
54684
55069
  const ratio = Number(ratioScaled) / 1e18;
54685
- const apr = (ratio - 1) * (YEAR_SECONDS5 / windowSeconds);
55070
+ const apr = (ratio - 1) * (YEAR_SECONDS6 / windowSeconds);
54686
55071
  return {
54687
55072
  apr,
54688
55073
  sharePriceNow: now.p,