@0dotxyz/p0-ts-sdk 2.8.0-alpha.0 → 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
@@ -13265,6 +13265,26 @@ var marginfi_0_1_10_default = {
13265
13265
  code: 6604,
13266
13266
  name: "CircuitBreakerPriceJump",
13267
13267
  msg: "Oracle price deviates too far from the circuit breaker reference; action rejected"
13268
+ },
13269
+ {
13270
+ code: 6700,
13271
+ name: "ScopeInvalidAccount",
13272
+ msg: "Scope oracle account is not owned by the Scope program or is malformed"
13273
+ },
13274
+ {
13275
+ code: 6701,
13276
+ name: "ScopeInvalidEntry",
13277
+ msg: "Scope entry index is out of range or the entry has never been refreshed"
13278
+ },
13279
+ {
13280
+ code: 6702,
13281
+ name: "ScopeStalePrice",
13282
+ msg: "Scope price is stale"
13283
+ },
13284
+ {
13285
+ code: 6703,
13286
+ name: "UseConfigureBankOracleScope",
13287
+ msg: "Use lending_pool_configure_bank_oracle_scope; Scope requires an entry index"
13268
13288
  }
13269
13289
  ],
13270
13290
  types: [
@@ -22681,7 +22701,7 @@ function parseBankConfigRaw(bankConfigRaw) {
22681
22701
  const configFlags = bankConfigRaw.configFlags;
22682
22702
  const oracleSetup = parseOracleSetup(bankConfigRaw.oracleSetup);
22683
22703
  const oracleKeys = bankConfigRaw.oracleKeys;
22684
- const oracleMaxAge = bankConfigRaw.oracleMaxAge === 0 ? DEFAULT_ORACLE_MAX_AGE : bankConfigRaw.oracleMaxAge;
22704
+ const oracleMaxAge = bankConfigRaw.oracleMaxAge === 0 && oracleSetup !== "Scope" /* Scope */ ? DEFAULT_ORACLE_MAX_AGE : bankConfigRaw.oracleMaxAge;
22685
22705
  const interestRateConfig = {
22686
22706
  insuranceFeeFixedApr: wrappedI80F48toBigNumber(
22687
22707
  bankConfigRaw.interestRateConfig.insuranceFeeFixedApr
@@ -25993,7 +26013,7 @@ var mapPythBanksToOraclePrices = (pythPushBanks, multipliedBanks, oraclePrices,
25993
26013
  multipliedBanks.forEach((bank) => {
25994
26014
  const priceCoeff = priceCoeffByBank[bank.address.toBase58()];
25995
26015
  const oracleKey = bank.config.oracleKeys[0]?.toBase58();
25996
- if (oracleKey && priceCoeff !== void 0) {
26016
+ if (oracleKey && priceCoeff !== void 0 && Number.isFinite(priceCoeff)) {
25997
26017
  const oraclePrice = oraclePrices[oracleKey];
25998
26018
  if (oraclePrice) {
25999
26019
  bankOraclePriceMap.set(bank.address.toBase58(), {
@@ -75239,6 +75259,9 @@ function decodeScopePriceAtIndex(data, entryIndex) {
75239
75259
  const exp = data.readBigUInt64LE(offset + 8);
75240
75260
  const lastUpdatedSlot = data.readBigUInt64LE(offset + 16);
75241
75261
  const unixTimestamp = data.readBigUInt64LE(offset + 24);
75262
+ if (exp >= 24n) {
75263
+ throw new Error(`Scope entry exponent out of bounds: ${exp}`);
75264
+ }
75242
75265
  const price = new BigNumber3__default.default(value.toString()).shiftedBy(-Number(exp));
75243
75266
  return {
75244
75267
  price,
@@ -75247,17 +75270,24 @@ function decodeScopePriceAtIndex(data, entryIndex) {
75247
75270
  };
75248
75271
  }
75249
75272
  new web3_js.PublicKey("MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD");
75250
- 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;
75251
75275
  var MSOL_PRICE_OFFSET = 512;
75252
75276
  var MSOL_PRICE_PRECISION = new BigNumber3__default.default(2).pow(32);
75277
+ var MAX_MSOL_SOL_RATE = 200;
75253
75278
  function decodeMarinadeState(data) {
75254
- if (data.length !== MARINADE_STATE_SIZE) {
75279
+ if (data.length < MARINADE_STATE_MIN_SIZE) {
75255
75280
  throw new Error(`Invalid Marinade State account size: ${data.length}`);
75256
75281
  }
75282
+ if (!data.subarray(0, 8).equals(MARINADE_STATE_DISCRIMINATOR)) {
75283
+ throw new Error("Invalid Marinade State discriminator");
75284
+ }
75257
75285
  const msolPriceRaw = data.readBigUInt64LE(MSOL_PRICE_OFFSET);
75258
- return {
75259
- msolPrice: new BigNumber3__default.default(msolPriceRaw.toString()).div(MSOL_PRICE_PRECISION)
75260
- };
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 };
75261
75291
  }
75262
75292
  new web3_js.PublicKey(
75263
75293
  "SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy"
@@ -75272,6 +75302,7 @@ var ACCOUNT_TYPE_STAKE_POOL = 1;
75272
75302
  var TOTAL_LAMPORTS_OFFSET = 258;
75273
75303
  var POOL_TOKEN_SUPPLY_OFFSET = 266;
75274
75304
  var LAST_UPDATE_EPOCH_OFFSET = 274;
75305
+ var MAX_LST_SOL_RATE = 200;
75275
75306
  function decodeStakePool(data) {
75276
75307
  if (data.length < LAST_UPDATE_EPOCH_OFFSET + 8) {
75277
75308
  throw new Error(`Invalid StakePool account size: ${data.length}`);
@@ -75285,13 +75316,17 @@ function decodeStakePool(data) {
75285
75316
  if (poolTokenSupply === 0n) {
75286
75317
  throw new Error("StakePool has zero token supply");
75287
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
+ }
75288
75325
  return {
75289
75326
  totalLamports,
75290
75327
  poolTokenSupply,
75291
75328
  lastUpdateEpoch,
75292
- exchangeRate: new BigNumber3__default.default(totalLamports.toString()).div(
75293
- new BigNumber3__default.default(poolTokenSupply.toString())
75294
- )
75329
+ exchangeRate
75295
75330
  };
75296
75331
  }
75297
75332
  var GAMMA_VAULT_PROGRAM_ID = new web3_js.PublicKey(
@@ -80944,7 +80979,7 @@ var fetchPythOracleData = async (banks, opts, priceCoeffByBank = {}) => {
80944
80979
  priceCoeffByBank
80945
80980
  );
80946
80981
  pythMultipliedBanks.forEach((bank) => {
80947
- if (priceCoeffByBank[bank.address.toBase58()] === void 0) {
80982
+ if (!Number.isFinite(priceCoeffByBank[bank.address.toBase58()])) {
80948
80983
  bankOraclePriceMap.delete(bank.address.toBase58());
80949
80984
  }
80950
80985
  });
@@ -80982,7 +81017,7 @@ var fetchPythOraclePricesFromAPI = async (pythOracleKeys, apiEndpoint, opts) =>
80982
81017
  };
80983
81018
  var fetchPythOraclePricesFromChain = async (requestedPythOracleKeys, connection) => {
80984
81019
  const updatedOraclePriceByKey = {};
80985
- const oracleAis = await chunkedGetRawMultipleAccountInfoOrdered(
81020
+ const oracleAis = await chunkedGetRawMultipleAccountInfoOrderedWithNulls(
80986
81021
  connection,
80987
81022
  requestedPythOracleKeys
80988
81023
  );
@@ -81219,7 +81254,10 @@ async function fetchSingleCrossbarChunk(endpoint, swbFeedIdsChunk, isPrimary) {
81219
81254
  throw error;
81220
81255
  }
81221
81256
  }
81222
- 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
+ };
81223
81261
  var fetchScopeOracleData = async (banks, opts) => {
81224
81262
  const scopeBanks = banks.filter((bank) => getOracleSourceFromBank(bank).key === "scope");
81225
81263
  if (!scopeBanks.length) {
@@ -81227,7 +81265,17 @@ var fetchScopeOracleData = async (banks, opts) => {
81227
81265
  bankOraclePriceMap: /* @__PURE__ */ new Map()
81228
81266
  };
81229
81267
  }
81230
- 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
+ );
81231
81279
  let oraclePrices;
81232
81280
  if (opts.mode === "api") {
81233
81281
  oraclePrices = await fetchScopeOraclePricesFromAPI(
@@ -81241,9 +81289,9 @@ var fetchScopeOracleData = async (banks, opts) => {
81241
81289
  const bankOraclePriceMap = /* @__PURE__ */ new Map();
81242
81290
  const nowSeconds = Math.floor(Date.now() / 1e3);
81243
81291
  scopeBanks.forEach((bank) => {
81244
- let oraclePrice = oraclePrices[scopeRequestKey(bank)];
81245
- const isStale = !oraclePrice || nowSeconds - oraclePrice.timestamp.toNumber() > bank.config.oracleMaxAge;
81246
- 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) {
81247
81295
  oraclePrice = {
81248
81296
  priceRealtime: {
81249
81297
  price: new BigNumber3__default.default(0),
@@ -81296,7 +81344,10 @@ var fetchScopeOraclePricesFromAPI = async (requestKeys, apiEndpoint, opts) => {
81296
81344
  };
81297
81345
  var fetchScopeOraclePricesFromChain = async (requestKeys, connection) => {
81298
81346
  const uniqueOracleKeys = Array.from(new Set(requestKeys.map((key) => key.split(":")[0])));
81299
- const oracleAis = await chunkedGetRawMultipleAccountInfoOrdered(connection, uniqueOracleKeys);
81347
+ const oracleAis = await chunkedGetRawMultipleAccountInfoOrderedWithNulls(
81348
+ connection,
81349
+ uniqueOracleKeys
81350
+ );
81300
81351
  const accountDataByKey = {};
81301
81352
  uniqueOracleKeys.forEach((oracleKey, index) => {
81302
81353
  accountDataByKey[oracleKey] = oracleAis[index]?.data;
@@ -81334,6 +81385,9 @@ var fetchScopeOraclePricesFromChain = async (requestKeys, connection) => {
81334
81385
  }
81335
81386
  return oraclePriceByRequestKey;
81336
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;
81337
81391
  function multiplierAccountKey(bank) {
81338
81392
  switch (bank.config.oracleSetup) {
81339
81393
  case "PythMSOL" /* PythMSOL */:
@@ -81353,8 +81407,17 @@ function multiplierAccountKey(bank) {
81353
81407
  }
81354
81408
  function computePtMultiplier(vault, startPrice, nowSeconds) {
81355
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
+ }
81356
81419
  let expectedRate;
81357
- if (vault.duration <= 0 || nowSeconds <= vault.startTs) {
81420
+ if (nowSeconds <= vault.startTs) {
81358
81421
  expectedRate = startPrice;
81359
81422
  } else if (nowSeconds >= maturity) {
81360
81423
  expectedRate = new BigNumber3__default.default(1);
@@ -81362,9 +81425,6 @@ function computePtMultiplier(vault, startPrice, nowSeconds) {
81362
81425
  const progress = new BigNumber3__default.default(nowSeconds - vault.startTs).div(vault.duration);
81363
81426
  expectedRate = startPrice.plus(new BigNumber3__default.default(1).minus(startPrice).times(progress));
81364
81427
  }
81365
- if (vault.ptSupply === 0n) {
81366
- throw new Error("Exponent vault has zero PT supply");
81367
- }
81368
81428
  const syPerPt = new BigNumber3__default.default(vault.syForPt.toString()).div(
81369
81429
  new BigNumber3__default.default(vault.ptSupply.toString())
81370
81430
  );
@@ -81376,6 +81436,12 @@ var fetchOracleMultipliers = async (banks, opts) => {
81376
81436
  if (!multipliedBanks.length) {
81377
81437
  return {};
81378
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
+ }
81379
81445
  if (opts.mode === "api") {
81380
81446
  return fetchOracleMultipliersFromAPI(
81381
81447
  multipliedBanks.map((bank) => bank.address.toBase58()),
@@ -81393,7 +81459,7 @@ var fetchOracleMultipliersFromAPI = async (bankAddresses, apiEndpoint, opts) =>
81393
81459
  }
81394
81460
  const { data } = await response.json();
81395
81461
  return Object.fromEntries(
81396
- Object.entries(data).map(([bankAddress, multiplier]) => [bankAddress, Number(multiplier)])
81462
+ Object.entries(data).map(([bankAddress, multiplier]) => [bankAddress, Number(multiplier)]).filter(([, multiplier]) => Number.isFinite(multiplier))
81397
81463
  );
81398
81464
  };
81399
81465
  var fetchOracleMultipliersFromChain = async (multipliedBanks, connection) => {
@@ -81401,7 +81467,12 @@ var fetchOracleMultipliersFromChain = async (multipliedBanks, connection) => {
81401
81467
  multipliedBanks.map((bank) => [bank.address.toBase58(), multiplierAccountKey(bank).toBase58()])
81402
81468
  );
81403
81469
  const uniqueAccountKeys = Array.from(new Set(accountKeyByBank.values()));
81404
- 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;
81405
81476
  const accountDataByKey = {};
81406
81477
  uniqueAccountKeys.forEach((accountKey, index) => {
81407
81478
  accountDataByKey[accountKey] = accountAis[index]?.data;
@@ -81424,9 +81495,17 @@ var fetchOracleMultipliersFromChain = async (multipliedBanks, connection) => {
81424
81495
  break;
81425
81496
  case "PythLST" /* PythLST */:
81426
81497
  case "KaminoLST" /* KaminoLST */:
81427
- case "JuplendLST" /* JuplendLST */:
81428
- 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();
81429
81507
  break;
81508
+ }
81430
81509
  case "PTPyth" /* PTPyth */:
81431
81510
  case "PTFixed" /* PTFixed */:
81432
81511
  multiplierByBank[bankAddress] = computePtMultiplier(
@@ -81491,8 +81570,9 @@ function classifyBanksForOracleStrategy(banks) {
81491
81570
  function handleFixedOracleBanks(banks, multiplierByBank) {
81492
81571
  const oracleMap = /* @__PURE__ */ new Map();
81493
81572
  banks.forEach((bank) => {
81573
+ const isPtFixed = bank.config.oracleSetup === "PTFixed" /* PTFixed */;
81494
81574
  const multiplier = multiplierByBank[bank.address.toBase58()];
81495
- 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;
81496
81576
  const fixedOraclePrice = {
81497
81577
  priceRealtime: {
81498
81578
  price: fixedPrice,
@@ -84128,7 +84208,7 @@ var Bank = class _Bank {
84128
84208
  }
84129
84209
  };
84130
84210
  var BankConfig = class _BankConfig {
84131
- 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) {
84132
84212
  this.assetWeightInit = assetWeightInit;
84133
84213
  this.assetWeightMaint = assetWeightMaint;
84134
84214
  this.liabilityWeightInit = liabilityWeightInit;
@@ -85626,7 +85706,6 @@ exports.computeBankMetrics = computeBankMetrics;
85626
85706
  exports.computeBankPoolSize = computeBankPoolSize;
85627
85707
  exports.computeBankProjectedAvailableLiquidity = computeBankProjectedAvailableLiquidity;
85628
85708
  exports.computeBankRateLimitRemaining = computeBankRateLimitRemaining;
85629
- exports.computeBankRiskAccountKeys = computeBankRiskAccountKeys;
85630
85709
  exports.computeBankSupplyApy = computeBankSupplyApy;
85631
85710
  exports.computeBankTotalBorrows = computeBankTotalBorrows;
85632
85711
  exports.computeBankTotalBorrowsUsd = computeBankTotalBorrowsUsd;