@1delta/margin-fetcher-sol 0.0.2 → 0.0.4

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.mjs CHANGED
@@ -7855,6 +7855,8 @@ var RESERVE = {
7855
7855
  maxLiquidationBonusBps: 4876,
7856
7856
  originationFeeSf: 4896,
7857
7857
  flashLoanFeeSf: 4904,
7858
+ // config.borrowRateCurve — 11 × CurvePoint { utilizationRateBps u32, borrowRateBps u32 }, after fees' 8 padding bytes
7859
+ borrowRateCurve: 4920,
7858
7860
  borrowFactorPct: 5008,
7859
7861
  depositLimit: 5016,
7860
7862
  borrowLimit: 5024,
@@ -7997,6 +7999,14 @@ function readKlendReserveChain(reserve, data) {
7997
7999
  debtWithdrawalCap: withdrawalCap(data, RESERVE.debtWithdrawalCap),
7998
8000
  disableUsageAsCollOutsideEmode: data[RESERVE.disableUsageAsCollOutsideEmode] !== 0,
7999
8001
  utilizationLimitBlockBorrowingAbovePct: data[RESERVE.utilizationLimitBlockBorrowingAbovePct],
8002
+ borrowRateCurve: Array.from({ length: 11 }, (_, i) => {
8003
+ const v = new DataView(data.buffer, data.byteOffset);
8004
+ const off = RESERVE.borrowRateCurve + 8 * i;
8005
+ return [
8006
+ v.getUint32(off, true) / 1e4,
8007
+ v.getUint32(off + 4, true) / 1e4
8008
+ ];
8009
+ }),
8000
8010
  borrowLimitOutsideElevationGroup: u64(
8001
8011
  data,
8002
8012
  RESERVE.borrowLimitOutsideElevationGroup
@@ -8487,6 +8497,15 @@ function exchangeRateOf(inp) {
8487
8497
  if (!t || !s || t.num <= 0n || s.num <= 0n) return { num: 1n, den: 1n };
8488
8498
  return { num: t.num * s.den, den: t.den * s.num };
8489
8499
  }
8500
+ function dedupeKnots(knots) {
8501
+ const out = [];
8502
+ for (const k of knots) {
8503
+ const last = out[out.length - 1];
8504
+ if (last && last[0] === k[0] && last[1] === k[1]) continue;
8505
+ out.push(k);
8506
+ }
8507
+ return out;
8508
+ }
8490
8509
  function convertKaminoReserve(inp, ctx) {
8491
8510
  const { stats, metrics, detail, chain } = inp;
8492
8511
  const mint = chain?.mint ?? stats.token;
@@ -8584,7 +8603,12 @@ function convertKaminoReserve(inp, ctx) {
8584
8603
  interestRateBasis: stats.interestRateBasis,
8585
8604
  protocolTakeRatePct: stats.protocolTakeRatePct,
8586
8605
  hostFixedInterestRateBps: stats.hostFixedInterestRateBps,
8587
- borrowCurve: detail.borrowCurve,
8606
+ borrowCurve: chain ? dedupeKnots(chain.borrowRateCurve) : detail.borrowCurve,
8607
+ borrowCurveSource: chain ? "chain" : "api",
8608
+ ...stats.interestRateBasis === "legacy" && ctx.slotSeconds ? { slotSeconds: ctx.slotSeconds } : {},
8609
+ ...chain ? {
8610
+ utilizationLimitBlockBorrowingAbovePct: chain.utilizationLimitBlockBorrowingAbovePct
8611
+ } : {},
8588
8612
  autoDeleverage: stats.autoDeleverage,
8589
8613
  source: chain ? "chain" : "api",
8590
8614
  maxLiquidationBonus: chain ? chain.maxLiquidationBonusBps / 1e4 : num(detail.maxLiquidationBonus),
@@ -8630,6 +8654,7 @@ function convertKaminoMarket(inp) {
8630
8654
  closeFactor,
8631
8655
  chainMarket: inp.chainMarket,
8632
8656
  slot: inp.slot,
8657
+ slotSeconds: inp.slotSeconds,
8633
8658
  tokenList: inp.tokenList
8634
8659
  });
8635
8660
  data[row.marketUid] = row;
@@ -8645,6 +8670,7 @@ function convertKaminoMarket(inp) {
8645
8670
  }
8646
8671
 
8647
8672
  // src/lending/public-data/kamino/fetchPublic.ts
8673
+ import { measureSlotSeconds } from "@1delta/svm-kit";
8648
8674
  async function fetchKaminoPublic(opts = {}) {
8649
8675
  const api = createKaminoApi(opts);
8650
8676
  const { markets, metricsByMarket } = await listServedMarkets(api, opts);
@@ -8711,6 +8737,7 @@ async function fetchKaminoPublic(opts = {}) {
8711
8737
  return false;
8712
8738
  });
8713
8739
  const rpc = opts.rpc;
8740
+ const slotSeconds = opts.slotSeconds ?? (rpc ? await measureSlotSeconds(rpc) : void 0);
8714
8741
  const chainReads = await Promise.all(
8715
8742
  served.map(async (mr) => {
8716
8743
  if (!rpc) return void 0;
@@ -8743,7 +8770,8 @@ async function fetchKaminoPublic(opts = {}) {
8743
8770
  reserves,
8744
8771
  ...chain?.market ? { chainMarket: chain.market, slot: chain.slot } : {},
8745
8772
  leveragePairs: mr.leveragePairs,
8746
- tokenList: opts.tokenList
8773
+ tokenList: opts.tokenList,
8774
+ slotSeconds
8747
8775
  });
8748
8776
  out[conv.lenderKey] = {
8749
8777
  chainId: conv.chainId,
@@ -10879,6 +10907,68 @@ async function readJupiterLendVaultsOnchain(rpc, market, vaultIds, opts = {}) {
10879
10907
  }
10880
10908
  return out;
10881
10909
  }
10910
+ function decodeJlRateModel(data) {
10911
+ if (data.length < 53)
10912
+ throw new Error(`RateModel is ${data.length} bytes, expected 53`);
10913
+ const v = new DataView(data.buffer, data.byteOffset, data.byteLength);
10914
+ const u16 = (o) => v.getUint16(o, true);
10915
+ const version = data[40];
10916
+ if (version !== 1 && version !== 2)
10917
+ throw new Error(`RateModel version ${version} not supported`);
10918
+ return {
10919
+ version,
10920
+ rateAtZero: u16(41),
10921
+ kink1: u16(43),
10922
+ rateAtKink1: u16(45),
10923
+ rateAtMax: u16(47),
10924
+ ...version === 2 ? { kink2: u16(49), rateAtKink2: u16(51) } : {}
10925
+ };
10926
+ }
10927
+ async function readJlLayerIrms(rpc, market, mints, now = BigInt(Math.floor(Date.now() / 1e3))) {
10928
+ const uniq = [...new Set(mints)];
10929
+ const keys = await Promise.all(
10930
+ uniq.map(async (m2) => ({
10931
+ mint: m2,
10932
+ model: await liq([utf83("rate_model"), await pk3(m2)], market),
10933
+ reserve: await liq([utf83("reserve"), await pk3(m2)], market)
10934
+ }))
10935
+ );
10936
+ const { m } = await read(
10937
+ rpc,
10938
+ keys.flatMap((k) => [k.model, k.reserve])
10939
+ );
10940
+ const out = /* @__PURE__ */ new Map();
10941
+ for (const k of keys) {
10942
+ const ma = m.get(k.model);
10943
+ const ra = m.get(k.reserve);
10944
+ if (!ma || !ra) continue;
10945
+ try {
10946
+ const model = decodeJlRateModel(fromBase643(ma.data[0]));
10947
+ const r = dec("TokenReserve", ra.data[0]);
10948
+ const o = jlOverallTokenData(r, now);
10949
+ const P = JL_EXCHANGE_PRICES_PRECISION;
10950
+ const bWi = Number(big(r.total_borrow_with_interest) * o.borrowEx / P);
10951
+ const sWi = Number(big(r.total_supply_with_interest) * o.supplyEx / P);
10952
+ const tb = Number(o.totalBorrow);
10953
+ const ts = Number(o.totalSupply);
10954
+ const u = ts > 0 ? tb / ts : 0;
10955
+ const ratio2 = u > 0 && sWi > 0 ? bWi / sWi / u : 1;
10956
+ out.set(k.mint, {
10957
+ ...model,
10958
+ feeOnInterest: Number(r.fee_on_interest),
10959
+ maxUtilization: Number(o.maxUtilization),
10960
+ supplyWithInterestRatio: Number.isFinite(ratio2) ? ratio2 : 1,
10961
+ lastUtilization: Number(r.last_utilization),
10962
+ borrowRate: Number(r.borrow_rate)
10963
+ });
10964
+ } catch (e) {
10965
+ console.warn(
10966
+ `[jupiter-lend] rate model for ${k.mint} not read: ${e.message}`
10967
+ );
10968
+ }
10969
+ }
10970
+ return out;
10971
+ }
10882
10972
 
10883
10973
  // src/lending/public-data/jupiter-lend/convertPublic.ts
10884
10974
  var JL_FACTOR_SCALE = 1e3;
@@ -11197,6 +11287,22 @@ async function fetchJupiterLendPublic(opts = {}) {
11197
11287
  }
11198
11288
  }
11199
11289
  const fromChain = new Map(chain.map((c) => [c.vault.id, c]));
11290
+ let irms = /* @__PURE__ */ new Map();
11291
+ if (opts.rpc) {
11292
+ const mints = /* @__PURE__ */ new Set();
11293
+ for (const v of [...vaults, ...chain.map((c) => c.vault)]) {
11294
+ const cv = fromChain.get(v.id);
11295
+ if (!cv?.smartCol) mints.add(v.supplyToken.address);
11296
+ if (!cv?.smartDebt) mints.add(v.borrowToken.address);
11297
+ }
11298
+ try {
11299
+ irms = await readJlLayerIrms(opts.rpc, market, [...mints]);
11300
+ } catch (e) {
11301
+ console.warn(
11302
+ `[jupiter-lend] layer rate models for ${market} not read: ${e.message}`
11303
+ );
11304
+ }
11305
+ }
11200
11306
  for (const v of [...vaults, ...chain.map((c) => c.vault)]) {
11201
11307
  if (only && !only.includes(v.id)) continue;
11202
11308
  const conv = convertJupiterLendVault(market, v, {
@@ -11205,6 +11311,15 @@ async function fetchJupiterLendPublic(opts = {}) {
11205
11311
  layer
11206
11312
  });
11207
11313
  const cv = fromChain.get(v.id);
11314
+ const mags = {
11315
+ supplyMagnifier: Number(v.supplyRateMagnifier) || 0,
11316
+ borrowMagnifier: Number(v.borrowRateMagnifier) || 0
11317
+ };
11318
+ for (const row of Object.values(conv.data)) {
11319
+ const smart = row.underlying === v.supplyToken.address ? cv?.smartCol : cv?.smartDebt;
11320
+ const irm = smart ? void 0 : irms.get(row.underlying);
11321
+ if (irm) row.jupiterLend = { irm: { ...irm, ...mags } };
11322
+ }
11208
11323
  const meta = conv.params.jupiterLendVault;
11209
11324
  meta.source = cv ? "chain" : "api";
11210
11325
  if (cv) {
@@ -11256,7 +11371,7 @@ var SAVE_API_BASE = "https://api.save.finance";
11256
11371
  var SAVE_WAD = 10n ** 18n;
11257
11372
  var SAVE_U64_MAX = "18446744073709551615";
11258
11373
  var SAVE_SLOTS_PER_YEAR = 63072e3;
11259
- var SAVE_DEFAULT_AVG_SLOT_SECONDS = 0.4;
11374
+ var SAVE_DEFAULT_AVG_SLOT_SECONDS = 0.2665;
11260
11375
  var SAVE_LIQUIDATION_CLOSE_FACTOR = 0.2;
11261
11376
  var SAVE_MAX_LIQUIDATABLE_VALUE_USD = 5e5;
11262
11377
  var SAVE_FULL_LIQUIDATION_VALUE_USD = 1;
@@ -12203,7 +12318,12 @@ function convertLoopscalePair(pair, opts) {
12203
12318
  termId: t.termId,
12204
12319
  durationDays: t.durationDays,
12205
12320
  durationSecs: t.durationDays * 86400,
12206
- apr: t.apr
12321
+ apr: t.apr,
12322
+ ladder: t.ladder.map((l) => ({
12323
+ apr: l.apr,
12324
+ assets: fmt2(l.sum, pDec),
12325
+ fill: "single"
12326
+ }))
12207
12327
  }));
12208
12328
  const common = {
12209
12329
  totalDebtStable: "0",
@@ -36403,6 +36523,9 @@ async function getLenderPublicData(chainId, opts = {}) {
36403
36523
  return Object.assign({}, ...parts);
36404
36524
  }
36405
36525
 
36526
+ // src/lending/public-data/getLenderPublicDataAll.ts
36527
+ import { measureSlotSeconds as measureSlotSeconds2 } from "@1delta/svm-kit";
36528
+
36406
36529
  // src/lending/public-data/basket.ts
36407
36530
  function describeBasket(lenderKey, bundle) {
36408
36531
  const brand = lenderBrandKey(lenderKey) ?? lenderKey;
@@ -36522,12 +36645,15 @@ async function getLenderPublicDataAll(chainId, lenders, opts = {}) {
36522
36645
  if (chainId !== SOLANA_CHAIN_ID)
36523
36646
  throw new Error(`getLenderPublicDataAll: unsupported chain ${chainId}`);
36524
36647
  const out = {};
36648
+ const needsSlot = opts.rpc && (lenders.some(isSave) && opts.save?.avgSlotSeconds === void 0 || lenders.some(isKamino) && opts.kamino?.slotSeconds === void 0);
36649
+ const slotSeconds = needsSlot ? await measureSlotSeconds2(opts.rpc) : void 0;
36525
36650
  const kaminoKeys = lenders.filter(isKamino);
36526
36651
  if (kaminoKeys.length) {
36527
36652
  const explicit = kaminoKeys.map(kaminoMarketFromKey).filter((m) => !!m);
36528
36653
  const wantsAll = kaminoKeys.some((k) => !kaminoMarketFromKey(k));
36529
36654
  const res = await fetchKaminoPublic({
36530
36655
  ...opts.kamino,
36656
+ slotSeconds: opts.kamino?.slotSeconds ?? slotSeconds,
36531
36657
  rpc: opts.kamino?.rpc ?? opts.rpc,
36532
36658
  tokenList: opts.tokenList ?? opts.kamino?.tokenList,
36533
36659
  ...wantsAll ? {} : { markets: explicit }
@@ -36567,6 +36693,7 @@ async function getLenderPublicDataAll(chainId, lenders, opts = {}) {
36567
36693
  const wantsAll = saveKeys.some((k) => !saveMarketFromKey(k));
36568
36694
  const res = await fetchSavePublic({
36569
36695
  ...opts.save,
36696
+ avgSlotSeconds: opts.save?.avgSlotSeconds ?? slotSeconds,
36570
36697
  tokenList: opts.tokenList ?? opts.save?.tokenList,
36571
36698
  ...wantsAll ? {} : { markets: explicit }
36572
36699
  });
@@ -36863,6 +36990,8 @@ async function fetchJupiterPrices(mints, tokenList = {}, opts = {}) {
36863
36990
  for (const [mint, r] of Object.entries(res ?? {})) {
36864
36991
  const p = num(r?.usdPrice);
36865
36992
  if (!(p > 0)) continue;
36993
+ if (opts.minLiquidityUsd !== void 0 && !(num(r?.liquidity) >= opts.minLiquidityUsd))
36994
+ continue;
36866
36995
  byMint[mint] = p;
36867
36996
  const stock = num(r?.stockData?.price);
36868
36997
  if (stock > 0) stockPriceByMint[mint] = stock;
@@ -68890,6 +69019,7 @@ export {
68890
69019
  decodeHumaPoolConfig,
68891
69020
  decodeHumaPoolState,
68892
69021
  decodeHumaRedemptionRequest,
69022
+ decodeJlRateModel,
68893
69023
  decodeJuniorUnlockShare,
68894
69024
  decodeJupFlashloanAdmin,
68895
69025
  decodeKaminoObligation,
@@ -69089,6 +69219,7 @@ export {
69089
69219
  readHumaChainState,
69090
69220
  readHumaLenderState,
69091
69221
  readHumaRedemptionRequests,
69222
+ readJlLayerIrms,
69092
69223
  readJlNextVaultId,
69093
69224
  readJupFlashAssets,
69094
69225
  readJupFlashAvailable,