@0dotxyz/p0-ts-sdk 2.8.0-alpha.1 → 2.8.0-alpha.2

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.cjs CHANGED
@@ -26013,7 +26013,7 @@ var mapPythBanksToOraclePrices = (pythPushBanks, multipliedBanks, oraclePrices,
26013
26013
  multipliedBanks.forEach((bank) => {
26014
26014
  const priceCoeff = priceCoeffByBank[bank.address.toBase58()];
26015
26015
  const oracleKey = bank.config.oracleKeys[0]?.toBase58();
26016
- if (oracleKey && priceCoeff !== void 0) {
26016
+ if (oracleKey && priceCoeff !== void 0 && Number.isFinite(priceCoeff)) {
26017
26017
  const oraclePrice = oraclePrices[oracleKey];
26018
26018
  if (oraclePrice) {
26019
26019
  bankOraclePriceMap.set(bank.address.toBase58(), {
@@ -75259,6 +75259,9 @@ function decodeScopePriceAtIndex(data, entryIndex) {
75259
75259
  const exp = data.readBigUInt64LE(offset + 8);
75260
75260
  const lastUpdatedSlot = data.readBigUInt64LE(offset + 16);
75261
75261
  const unixTimestamp = data.readBigUInt64LE(offset + 24);
75262
+ if (exp >= 24n) {
75263
+ throw new Error(`Scope entry exponent out of bounds: ${exp}`);
75264
+ }
75262
75265
  const price = new BigNumber3__default.default(value.toString()).shiftedBy(-Number(exp));
75263
75266
  return {
75264
75267
  price,
@@ -75267,17 +75270,24 @@ function decodeScopePriceAtIndex(data, entryIndex) {
75267
75270
  };
75268
75271
  }
75269
75272
  new web3_js.PublicKey("MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD");
75270
- var MARINADE_STATE_SIZE = 520;
75273
+ var MARINADE_STATE_DISCRIMINATOR = Buffer.from([216, 146, 107, 94, 104, 75, 182, 177]);
75274
+ var MARINADE_STATE_MIN_SIZE = 520;
75271
75275
  var MSOL_PRICE_OFFSET = 512;
75272
75276
  var MSOL_PRICE_PRECISION = new BigNumber3__default.default(2).pow(32);
75277
+ var MAX_MSOL_SOL_RATE = 200;
75273
75278
  function decodeMarinadeState(data) {
75274
- if (data.length !== MARINADE_STATE_SIZE) {
75279
+ if (data.length < MARINADE_STATE_MIN_SIZE) {
75275
75280
  throw new Error(`Invalid Marinade State account size: ${data.length}`);
75276
75281
  }
75282
+ if (!data.subarray(0, 8).equals(MARINADE_STATE_DISCRIMINATOR)) {
75283
+ throw new Error("Invalid Marinade State discriminator");
75284
+ }
75277
75285
  const msolPriceRaw = data.readBigUInt64LE(MSOL_PRICE_OFFSET);
75278
- return {
75279
- msolPrice: new BigNumber3__default.default(msolPriceRaw.toString()).div(MSOL_PRICE_PRECISION)
75280
- };
75286
+ const msolPrice = new BigNumber3__default.default(msolPriceRaw.toString()).div(MSOL_PRICE_PRECISION);
75287
+ if (!msolPrice.gt(0) || msolPrice.gte(MAX_MSOL_SOL_RATE)) {
75288
+ throw new Error(`Marinade mSOL/SOL rate out of bounds: ${msolPrice.toString()}`);
75289
+ }
75290
+ return { msolPrice };
75281
75291
  }
75282
75292
  new web3_js.PublicKey(
75283
75293
  "SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy"
@@ -75292,6 +75302,7 @@ var ACCOUNT_TYPE_STAKE_POOL = 1;
75292
75302
  var TOTAL_LAMPORTS_OFFSET = 258;
75293
75303
  var POOL_TOKEN_SUPPLY_OFFSET = 266;
75294
75304
  var LAST_UPDATE_EPOCH_OFFSET = 274;
75305
+ var MAX_LST_SOL_RATE = 200;
75295
75306
  function decodeStakePool(data) {
75296
75307
  if (data.length < LAST_UPDATE_EPOCH_OFFSET + 8) {
75297
75308
  throw new Error(`Invalid StakePool account size: ${data.length}`);
@@ -75305,13 +75316,17 @@ function decodeStakePool(data) {
75305
75316
  if (poolTokenSupply === 0n) {
75306
75317
  throw new Error("StakePool has zero token supply");
75307
75318
  }
75319
+ const exchangeRate = new BigNumber3__default.default(totalLamports.toString()).div(
75320
+ new BigNumber3__default.default(poolTokenSupply.toString())
75321
+ );
75322
+ if (!exchangeRate.gt(0) || exchangeRate.gte(MAX_LST_SOL_RATE)) {
75323
+ throw new Error(`StakePool LST/SOL rate out of bounds: ${exchangeRate.toString()}`);
75324
+ }
75308
75325
  return {
75309
75326
  totalLamports,
75310
75327
  poolTokenSupply,
75311
75328
  lastUpdateEpoch,
75312
- exchangeRate: new BigNumber3__default.default(totalLamports.toString()).div(
75313
- new BigNumber3__default.default(poolTokenSupply.toString())
75314
- )
75329
+ exchangeRate
75315
75330
  };
75316
75331
  }
75317
75332
  var GAMMA_VAULT_PROGRAM_ID = new web3_js.PublicKey(
@@ -80964,7 +80979,7 @@ var fetchPythOracleData = async (banks, opts, priceCoeffByBank = {}) => {
80964
80979
  priceCoeffByBank
80965
80980
  );
80966
80981
  pythMultipliedBanks.forEach((bank) => {
80967
- if (priceCoeffByBank[bank.address.toBase58()] === void 0) {
80982
+ if (!Number.isFinite(priceCoeffByBank[bank.address.toBase58()])) {
80968
80983
  bankOraclePriceMap.delete(bank.address.toBase58());
80969
80984
  }
80970
80985
  });
@@ -81002,7 +81017,7 @@ var fetchPythOraclePricesFromAPI = async (pythOracleKeys, apiEndpoint, opts) =>
81002
81017
  };
81003
81018
  var fetchPythOraclePricesFromChain = async (requestedPythOracleKeys, connection) => {
81004
81019
  const updatedOraclePriceByKey = {};
81005
- const oracleAis = await chunkedGetRawMultipleAccountInfoOrdered(
81020
+ const oracleAis = await chunkedGetRawMultipleAccountInfoOrderedWithNulls(
81006
81021
  connection,
81007
81022
  requestedPythOracleKeys
81008
81023
  );
@@ -81239,7 +81254,10 @@ async function fetchSingleCrossbarChunk(endpoint, swbFeedIdsChunk, isPrimary) {
81239
81254
  throw error;
81240
81255
  }
81241
81256
  }
81242
- var scopeRequestKey = (bank) => `${bank.config.oracleKeys[0].toBase58()}:${bank.config.scopeEntryIndex}`;
81257
+ var scopeRequestKey = (bank) => {
81258
+ const oracleKey = bank.config.oracleKeys[0]?.toBase58();
81259
+ return oracleKey ? `${oracleKey}:${bank.config.scopeEntryIndex ?? 0}` : void 0;
81260
+ };
81243
81261
  var fetchScopeOracleData = async (banks, opts) => {
81244
81262
  const scopeBanks = banks.filter((bank) => getOracleSourceFromBank(bank).key === "scope");
81245
81263
  if (!scopeBanks.length) {
@@ -81247,7 +81265,17 @@ var fetchScopeOracleData = async (banks, opts) => {
81247
81265
  bankOraclePriceMap: /* @__PURE__ */ new Map()
81248
81266
  };
81249
81267
  }
81250
- const uniqueRequestKeys = Array.from(new Set(scopeBanks.map(scopeRequestKey)));
81268
+ if (!opts) {
81269
+ console.warn(
81270
+ `fetchScopeOracleData: no scopeOpts provided; ${scopeBanks.length} scope bank(s) will have zero prices`
81271
+ );
81272
+ return {
81273
+ bankOraclePriceMap: /* @__PURE__ */ new Map()
81274
+ };
81275
+ }
81276
+ const uniqueRequestKeys = Array.from(
81277
+ new Set(scopeBanks.map(scopeRequestKey).filter((key) => key !== void 0))
81278
+ );
81251
81279
  let oraclePrices;
81252
81280
  if (opts.mode === "api") {
81253
81281
  oraclePrices = await fetchScopeOraclePricesFromAPI(
@@ -81261,9 +81289,9 @@ var fetchScopeOracleData = async (banks, opts) => {
81261
81289
  const bankOraclePriceMap = /* @__PURE__ */ new Map();
81262
81290
  const nowSeconds = Math.floor(Date.now() / 1e3);
81263
81291
  scopeBanks.forEach((bank) => {
81264
- let oraclePrice = oraclePrices[scopeRequestKey(bank)];
81265
- const isStale = !oraclePrice || nowSeconds - oraclePrice.timestamp.toNumber() > bank.config.oracleMaxAge;
81266
- if (isStale) {
81292
+ const requestKey = scopeRequestKey(bank);
81293
+ let oraclePrice = requestKey ? oraclePrices[requestKey] : void 0;
81294
+ if (!oraclePrice || nowSeconds - oraclePrice.timestamp.toNumber() > bank.config.oracleMaxAge) {
81267
81295
  oraclePrice = {
81268
81296
  priceRealtime: {
81269
81297
  price: new BigNumber3__default.default(0),
@@ -81316,7 +81344,10 @@ var fetchScopeOraclePricesFromAPI = async (requestKeys, apiEndpoint, opts) => {
81316
81344
  };
81317
81345
  var fetchScopeOraclePricesFromChain = async (requestKeys, connection) => {
81318
81346
  const uniqueOracleKeys = Array.from(new Set(requestKeys.map((key) => key.split(":")[0])));
81319
- const oracleAis = await chunkedGetRawMultipleAccountInfoOrdered(connection, uniqueOracleKeys);
81347
+ const oracleAis = await chunkedGetRawMultipleAccountInfoOrderedWithNulls(
81348
+ connection,
81349
+ uniqueOracleKeys
81350
+ );
81320
81351
  const accountDataByKey = {};
81321
81352
  uniqueOracleKeys.forEach((oracleKey, index) => {
81322
81353
  accountDataByKey[oracleKey] = oracleAis[index]?.data;
@@ -81354,6 +81385,9 @@ var fetchScopeOraclePricesFromChain = async (requestKeys, connection) => {
81354
81385
  }
81355
81386
  return oraclePriceByRequestKey;
81356
81387
  };
81388
+ var PT_MAX_MATURITY_HORIZON_SECONDS = 5 * 365 * 24 * 60 * 60;
81389
+ var MAX_SY_EXCHANGE_RATE = new BigNumber3__default.default("18446744073709551615").div(1e12);
81390
+ var MAX_STAKE_POOL_EPOCH_LAG = 1;
81357
81391
  function multiplierAccountKey(bank) {
81358
81392
  switch (bank.config.oracleSetup) {
81359
81393
  case "PythMSOL" /* PythMSOL */:
@@ -81373,8 +81407,17 @@ function multiplierAccountKey(bank) {
81373
81407
  }
81374
81408
  function computePtMultiplier(vault, startPrice, nowSeconds) {
81375
81409
  const maturity = vault.startTs + vault.duration;
81410
+ if (vault.duration <= 0 || maturity > nowSeconds + PT_MAX_MATURITY_HORIZON_SECONDS) {
81411
+ throw new Error("Exponent vault has an invalid maturity schedule");
81412
+ }
81413
+ if (!vault.lastSeenSyExchangeRate.gt(0) || vault.lastSeenSyExchangeRate.gt(MAX_SY_EXCHANGE_RATE)) {
81414
+ throw new Error("Exponent vault SY exchange rate out of bounds");
81415
+ }
81416
+ if (vault.ptSupply === 0n) {
81417
+ throw new Error("Exponent vault has zero PT supply");
81418
+ }
81376
81419
  let expectedRate;
81377
- if (vault.duration <= 0 || nowSeconds <= vault.startTs) {
81420
+ if (nowSeconds <= vault.startTs) {
81378
81421
  expectedRate = startPrice;
81379
81422
  } else if (nowSeconds >= maturity) {
81380
81423
  expectedRate = new BigNumber3__default.default(1);
@@ -81382,9 +81425,6 @@ function computePtMultiplier(vault, startPrice, nowSeconds) {
81382
81425
  const progress = new BigNumber3__default.default(nowSeconds - vault.startTs).div(vault.duration);
81383
81426
  expectedRate = startPrice.plus(new BigNumber3__default.default(1).minus(startPrice).times(progress));
81384
81427
  }
81385
- if (vault.ptSupply === 0n) {
81386
- throw new Error("Exponent vault has zero PT supply");
81387
- }
81388
81428
  const syPerPt = new BigNumber3__default.default(vault.syForPt.toString()).div(
81389
81429
  new BigNumber3__default.default(vault.ptSupply.toString())
81390
81430
  );
@@ -81396,6 +81436,12 @@ var fetchOracleMultipliers = async (banks, opts) => {
81396
81436
  if (!multipliedBanks.length) {
81397
81437
  return {};
81398
81438
  }
81439
+ if (!opts) {
81440
+ console.warn(
81441
+ `fetchOracleMultipliers: no oracleMultiplierOpts provided; ${multipliedBanks.length} multiplier-priced bank(s) will have zero prices`
81442
+ );
81443
+ return {};
81444
+ }
81399
81445
  if (opts.mode === "api") {
81400
81446
  return fetchOracleMultipliersFromAPI(
81401
81447
  multipliedBanks.map((bank) => bank.address.toBase58()),
@@ -81413,7 +81459,7 @@ var fetchOracleMultipliersFromAPI = async (bankAddresses, apiEndpoint, opts) =>
81413
81459
  }
81414
81460
  const { data } = await response.json();
81415
81461
  return Object.fromEntries(
81416
- Object.entries(data).map(([bankAddress, multiplier]) => [bankAddress, Number(multiplier)])
81462
+ Object.entries(data).map(([bankAddress, multiplier]) => [bankAddress, Number(multiplier)]).filter(([, multiplier]) => Number.isFinite(multiplier))
81417
81463
  );
81418
81464
  };
81419
81465
  var fetchOracleMultipliersFromChain = async (multipliedBanks, connection) => {
@@ -81421,7 +81467,12 @@ var fetchOracleMultipliersFromChain = async (multipliedBanks, connection) => {
81421
81467
  multipliedBanks.map((bank) => [bank.address.toBase58(), multiplierAccountKey(bank).toBase58()])
81422
81468
  );
81423
81469
  const uniqueAccountKeys = Array.from(new Set(accountKeyByBank.values()));
81424
- const accountAis = await chunkedGetRawMultipleAccountInfoOrdered(connection, uniqueAccountKeys);
81470
+ const accountAis = await chunkedGetRawMultipleAccountInfoOrderedWithNulls(
81471
+ connection,
81472
+ uniqueAccountKeys
81473
+ );
81474
+ const isLstSetup = (setup) => setup === "PythLST" /* PythLST */ || setup === "KaminoLST" /* KaminoLST */ || setup === "JuplendLST" /* JuplendLST */;
81475
+ const currentEpoch = multipliedBanks.some((bank) => isLstSetup(bank.config.oracleSetup)) ? (await connection.getEpochInfo()).epoch : 0;
81425
81476
  const accountDataByKey = {};
81426
81477
  uniqueAccountKeys.forEach((accountKey, index) => {
81427
81478
  accountDataByKey[accountKey] = accountAis[index]?.data;
@@ -81444,9 +81495,17 @@ var fetchOracleMultipliersFromChain = async (multipliedBanks, connection) => {
81444
81495
  break;
81445
81496
  case "PythLST" /* PythLST */:
81446
81497
  case "KaminoLST" /* KaminoLST */:
81447
- case "JuplendLST" /* JuplendLST */:
81448
- multiplierByBank[bankAddress] = decodeStakePool(data).exchangeRate.toNumber();
81498
+ case "JuplendLST" /* JuplendLST */: {
81499
+ const stakePool = decodeStakePool(data);
81500
+ if (currentEpoch - stakePool.lastUpdateEpoch > MAX_STAKE_POOL_EPOCH_LAG) {
81501
+ console.error(
81502
+ `Stale stake pool for bank ${bankAddress} (last updated epoch ${stakePool.lastUpdateEpoch}, current ${currentEpoch})`
81503
+ );
81504
+ continue;
81505
+ }
81506
+ multiplierByBank[bankAddress] = stakePool.exchangeRate.toNumber();
81449
81507
  break;
81508
+ }
81450
81509
  case "PTPyth" /* PTPyth */:
81451
81510
  case "PTFixed" /* PTFixed */:
81452
81511
  multiplierByBank[bankAddress] = computePtMultiplier(
@@ -81511,8 +81570,9 @@ function classifyBanksForOracleStrategy(banks) {
81511
81570
  function handleFixedOracleBanks(banks, multiplierByBank) {
81512
81571
  const oracleMap = /* @__PURE__ */ new Map();
81513
81572
  banks.forEach((bank) => {
81573
+ const isPtFixed = bank.config.oracleSetup === "PTFixed" /* PTFixed */;
81514
81574
  const multiplier = multiplierByBank[bank.address.toBase58()];
81515
- const fixedPrice = bank.config.oracleSetup === "PTFixed" /* PTFixed */ && multiplier !== void 0 ? BigNumber3__default.default(multiplier) : bank.config.fixedPrice;
81575
+ const fixedPrice = isPtFixed ? Number.isFinite(multiplier) ? BigNumber3__default.default(multiplier) : BigNumber3__default.default(0) : bank.config.fixedPrice;
81516
81576
  const fixedOraclePrice = {
81517
81577
  priceRealtime: {
81518
81578
  price: fixedPrice,
@@ -84148,7 +84208,7 @@ var Bank = class _Bank {
84148
84208
  }
84149
84209
  };
84150
84210
  var BankConfig = class _BankConfig {
84151
- constructor(assetWeightInit, assetWeightMaint, liabilityWeightInit, liabilityWeightMaint, depositLimit, borrowLimit, riskTier, totalAssetValueInitLimit, assetTag, oracleSetup, oracleKeys, oracleMaxAge, interestRateConfig, operationalState, oracleMaxConfidence, fixedPrice, configFlags, scopeEntryIndex = 0) {
84211
+ constructor(assetWeightInit, assetWeightMaint, liabilityWeightInit, liabilityWeightMaint, depositLimit, borrowLimit, riskTier, totalAssetValueInitLimit, assetTag, oracleSetup, oracleKeys, oracleMaxAge, interestRateConfig, operationalState, oracleMaxConfidence, fixedPrice, configFlags, scopeEntryIndex) {
84152
84212
  this.assetWeightInit = assetWeightInit;
84153
84213
  this.assetWeightMaint = assetWeightMaint;
84154
84214
  this.liabilityWeightInit = liabilityWeightInit;
@@ -85646,7 +85706,6 @@ exports.computeBankMetrics = computeBankMetrics;
85646
85706
  exports.computeBankPoolSize = computeBankPoolSize;
85647
85707
  exports.computeBankProjectedAvailableLiquidity = computeBankProjectedAvailableLiquidity;
85648
85708
  exports.computeBankRateLimitRemaining = computeBankRateLimitRemaining;
85649
- exports.computeBankRiskAccountKeys = computeBankRiskAccountKeys;
85650
85709
  exports.computeBankSupplyApy = computeBankSupplyApy;
85651
85710
  exports.computeBankTotalBorrows = computeBankTotalBorrows;
85652
85711
  exports.computeBankTotalBorrowsUsd = computeBankTotalBorrowsUsd;