@suilend/sdk 13.7.0-next.ge2ee95d → 13.8.0-next.g33ed981

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.
@@ -457,6 +457,25 @@ export interface SetConfidenceMultiplierBuilderArgs {
457
457
  export declare function setConfidenceMultiplierBuilder(tx: Transaction, args: SetConfidenceMultiplierBuilderArgs, options?: {
458
458
  env?: EnvConfig;
459
459
  }): TransactionResult;
460
+ export interface SetMinConfidenceRatioBuilderArgs {
461
+ builder: TransactionObjectInput;
462
+ minConfidenceRatio: bigint | TransactionArgument;
463
+ }
464
+ /**
465
+ * Seeded by `from` like the confidence multiplier. Must be non-zero, and must stay
466
+ * above the confidence multiplier `k` (see `build`).
467
+ */
468
+ export declare function setMinConfidenceRatioBuilder(tx: Transaction, args: SetMinConfidenceRatioBuilderArgs, options?: {
469
+ env?: EnvConfig;
470
+ }): TransactionResult;
471
+ export interface SetMaxStalenessSecondsBuilderArgs {
472
+ builder: TransactionObjectInput;
473
+ maxStalenessSeconds: bigint | TransactionArgument;
474
+ }
475
+ /** Seeded by `from` like the confidence multiplier. Must be non-zero. */
476
+ export declare function setMaxStalenessSecondsBuilder(tx: Transaction, args: SetMaxStalenessSecondsBuilderArgs, options?: {
477
+ env?: EnvConfig;
478
+ }): TransactionResult;
460
479
  export interface SetArgs {
461
480
  builder: TransactionObjectInput;
462
481
  field: GenericArg;
@@ -531,6 +531,29 @@ export function setConfidenceMultiplierBuilder(tx, args, options) {
531
531
  ],
532
532
  });
533
533
  }
534
+ /**
535
+ * Seeded by `from` like the confidence multiplier. Must be non-zero, and must stay
536
+ * above the confidence multiplier `k` (see `build`).
537
+ */
538
+ export function setMinConfidenceRatioBuilder(tx, args, options) {
539
+ return tx.moveCall({
540
+ target: `${getPublishedAt('suilend', options?.env)}::reserve_config::set_min_confidence_ratio_builder`,
541
+ arguments: [
542
+ obj(tx, args.builder),
543
+ pure(tx, args.minConfidenceRatio, `u64`),
544
+ ],
545
+ });
546
+ }
547
+ /** Seeded by `from` like the confidence multiplier. Must be non-zero. */
548
+ export function setMaxStalenessSecondsBuilder(tx, args, options) {
549
+ return tx.moveCall({
550
+ target: `${getPublishedAt('suilend', options?.env)}::reserve_config::set_max_staleness_seconds_builder`,
551
+ arguments: [
552
+ obj(tx, args.builder),
553
+ pure(tx, args.maxStalenessSeconds, `u64`),
554
+ ],
555
+ });
556
+ }
534
557
  /**
535
558
  * Sets a field in the reserve configuration builder.
536
559
  *
package/client.d.ts CHANGED
@@ -4,6 +4,7 @@ import { Transaction, TransactionArgument, TransactionObjectArgument, Transactio
4
4
  import { SuiPythClient } from "@pythnetwork/pyth-sui-js";
5
5
  import BigNumber from "bignumber.js";
6
6
  import { Decimal } from "./_generated/suilend/decimal/structs";
7
+ import { type ConfidenceShare } from "./utils/simulate";
7
8
  import { LendingMarket, ObligationOwnerCap } from "./_generated/suilend/lending-market/structs";
8
9
  import { Obligation } from "./_generated/suilend/obligation/structs";
9
10
  import { NewConfigArgs as CreateRateLimiterConfigArgs } from "./_generated/suilend/rate-limiter/functions";
@@ -55,6 +56,8 @@ export type ClaimRewardsReward = {
55
56
  };
56
57
  export type PatchReserveConfigArgs = Partial<CreateReserveConfigArgs> & {
57
58
  confidenceMultiplierBps?: bigint | TransactionArgument;
59
+ minConfidenceRatio?: bigint | TransactionArgument;
60
+ maxStalenessSeconds?: bigint | TransactionArgument;
58
61
  };
59
62
  export type LendingMarketObject = {
60
63
  objectId: string;
@@ -103,8 +106,8 @@ export declare class SuilendClient {
103
106
  }>;
104
107
  /** A reserve's confidence multiplier `k`, in bps; pass `parsedReserve.config.additionalFields.id`. 0 when unset. */
105
108
  static getReserveConfidenceMultiplierBps(suiGrpcClient: SuiGrpcClient, additionalFieldsBagId: string): Promise<BigNumber>;
106
- /** A reserve's stored Pyth confidence (the `ConfidenceKey` dynamic field on `reserve.id` directly, not inside `config.additionalFields`). Reads as 0 until the reserve's first price update writes it. */
107
- static getReserveStoredConfidence(suiGrpcClient: SuiGrpcClient, reserveId: string): Promise<BigNumber>;
109
+ /** A reserve's stored Pyth confidence as a share of the spot it was read with, `conf / spot` (the `ConfidenceKey` dynamic field on `reserve.id` directly, not inside `config.additionalFields`). Reads as 0 until the reserve's first price update writes it. */
110
+ static getReserveConfidenceShare(suiGrpcClient: SuiGrpcClient, reserveId: string): Promise<ConfidenceShare>;
108
111
  /** The raw spot and EMA of the reserve's last refresh under a price guard (the `RawPriceKey` dynamic field on `reserve.id`). Undefined until its first refresh under a guard. Attach them with `withUnclampedPrices` only to a reserve as stored on chain, never to one a feed refresh has repriced. */
109
112
  static getReserveRawPrices(suiGrpcClient: SuiGrpcClient, reserveId: string): Promise<{
110
113
  price: Decimal;
package/client.js CHANGED
@@ -5,7 +5,7 @@ import BigNumber from "bignumber.js";
5
5
  import { extractCTokenCoinType, getAllCoins, getSpendableCoin, } from "@suilend/sui-core";
6
6
  import { phantom } from "./_generated/_framework/reified.js";
7
7
  import { Decimal } from "./_generated/suilend/decimal/structs.js";
8
- import { decimalToBigNumber } from "./utils/simulate.js";
8
+ import { confidenceShare, decimalToBigNumber, } from "./utils/simulate.js";
9
9
  import { PKG_V10, setPublishedAt } from "./_generated/suilend/index.js";
10
10
  import { getPublishedAt } from "./_generated/_envs/index.js";
11
11
  import { PACKAGE_ID } from "./_generated/suilend/index.js";
@@ -14,7 +14,7 @@ import { LendingMarket, ObligationOwnerCap, } from "./_generated/suilend/lending
14
14
  import { createLendingMarket } from "./_generated/suilend/lending-market-registry/functions.js";
15
15
  import { Obligation } from "./_generated/suilend/obligation/structs.js";
16
16
  import { newConfig as createRateLimiterConfig, } from "./_generated/suilend/rate-limiter/functions.js";
17
- import { build as buildReserveConfig, createReserveConfig, setBorrowFeeBps, setBorrowLimit, setBorrowLimitUsd, setBorrowWeightBps, setCloseAttributedBorrowLimitUsd, setCloseLtvPct, setConfidenceMultiplierBuilder, setDepositLimit, setDepositLimitUsd, setInterestRateAprs, setInterestRateUtils, setIsolated, setLiquidationBonusBps, setMaxCloseLtvPct, setMaxLiquidationBonusBps, setOpenAttributedBorrowLimitUsd, setOpenLtvPct, setProtocolLiquidationFeeBps, setSpreadFeeBps, } from "./_generated/suilend/reserve-config/functions.js";
17
+ import { build as buildReserveConfig, createReserveConfig, setBorrowFeeBps, setBorrowLimit, setBorrowLimitUsd, setBorrowWeightBps, setCloseAttributedBorrowLimitUsd, setCloseLtvPct, setConfidenceMultiplierBuilder, setDepositLimit, setDepositLimitUsd, setInterestRateAprs, setInterestRateUtils, setIsolated, setLiquidationBonusBps, setMaxCloseLtvPct, setMaxLiquidationBonusBps, setMaxStalenessSecondsBuilder, setMinConfidenceRatioBuilder, setOpenAttributedBorrowLimitUsd, setOpenLtvPct, setProtocolLiquidationFeeBps, setSpreadFeeBps, } from "./_generated/suilend/reserve-config/functions.js";
18
18
  import { addReserveProCompatible, addReserveV2ProCompatible, changeReservePriceFeedProCompatible, priceInfoPublishTime, refreshReservePriceProCompatible, } from "./lib/proCompatible.js";
19
19
  import { resolvePriceUpdateDataSource, } from "./lib/pyth.js";
20
20
  import { Side } from "./lib/types.js";
@@ -467,11 +467,11 @@ export class SuilendClient {
467
467
  });
468
468
  return new BigNumber(bcs.u64().parse(dynamicField.value.bcs));
469
469
  }
470
- /** A reserve's stored Pyth confidence (the `ConfidenceKey` dynamic field on `reserve.id` directly, not inside `config.additionalFields`). Reads as 0 until the reserve's first price update writes it. */
471
- static async getReserveStoredConfidence(suiGrpcClient, reserveId) {
470
+ /** A reserve's stored Pyth confidence as a share of the spot it was read with, `conf / spot` (the `ConfidenceKey` dynamic field on `reserve.id` directly, not inside `config.additionalFields`). Reads as 0 until the reserve's first price update writes it. */
471
+ static async getReserveConfidenceShare(suiGrpcClient, reserveId) {
472
472
  const match = await findBagEntryByTypeSuffix(suiGrpcClient, reserveId, "::reserve::ConfidenceKey");
473
473
  if (!match)
474
- return new BigNumber(0);
474
+ return confidenceShare(new BigNumber(0), new BigNumber(0));
475
475
  const { dynamicField } = await suiGrpcClient.getDynamicField({
476
476
  parentId: reserveId,
477
477
  name: match.name,
@@ -694,6 +694,16 @@ export class SuilendClient {
694
694
  builder,
695
695
  confidenceMultiplierBps: overrides.confidenceMultiplierBps,
696
696
  });
697
+ if (overrides.minConfidenceRatio !== undefined)
698
+ setMinConfidenceRatioBuilder(transaction, {
699
+ builder,
700
+ minConfidenceRatio: overrides.minConfidenceRatio,
701
+ });
702
+ if (overrides.maxStalenessSeconds !== undefined)
703
+ setMaxStalenessSecondsBuilder(transaction, {
704
+ builder,
705
+ maxStalenessSeconds: overrides.maxStalenessSeconds,
706
+ });
697
707
  const [config] = buildReserveConfig(transaction, builder);
698
708
  return this.submitReserveConfigUpdate(lendingMarketOwnerCapId, transaction, coinType, config);
699
709
  }
@@ -158,6 +158,7 @@ export type InitializePriceInputs = {
158
158
  export declare const getInitializePriceInputs: (lendingMarkets: {
159
159
  reserves: Reserve<string>[];
160
160
  }[], nowMs: number) => InitializePriceInputs;
161
+ type ConfidenceByCoinType = simulate.ConfidenceByCoinType;
161
162
  export declare const initializeSuilendAll: (suiGrpcClient: SuiGrpcClient, markets: InitializeSuilendMarket[], fallbackPythEndpoint?: string, options?: InitializeSuilendOptions) => Promise<{
162
163
  lendingMarket: {
163
164
  version: bigint;
@@ -410,6 +411,10 @@ export declare const initializeSuilendAll: (suiGrpcClient: SuiGrpcClient, market
410
411
  description: string;
411
412
  totalDeposits: BigNumber;
412
413
  }>;
414
+ confidenceByCoinType: Map<string, {
415
+ readonly share: simulate.ConfidenceShare | undefined;
416
+ readonly kBps: BigNumber;
417
+ }>;
413
418
  reserveCoinTypes: string[];
414
419
  reserveCoinMetadataMap: Record<string, import("@suilend/sui-core").Token>;
415
420
  rewardCoinTypes: string[];
@@ -669,6 +674,10 @@ export declare const initializeSuilendWithRewardsAll: (suiGrpcClient: SuiGrpcCli
669
674
  description: string;
670
675
  totalDeposits: BigNumber;
671
676
  }>;
677
+ confidenceByCoinType: Map<string, {
678
+ readonly share: simulate.ConfidenceShare | undefined;
679
+ readonly kBps: BigNumber;
680
+ }>;
672
681
  reserveCoinTypes: string[];
673
682
  reserveCoinMetadataMap: Record<string, import("@suilend/sui-core").Token>;
674
683
  rewardCoinTypes: string[];
@@ -927,6 +936,10 @@ export declare const initializeSuilend: (suiGrpcClient: SuiGrpcClient, suilendCl
927
936
  description: string;
928
937
  totalDeposits: BigNumber;
929
938
  }>;
939
+ confidenceByCoinType: Map<string, {
940
+ readonly share: simulate.ConfidenceShare | undefined;
941
+ readonly kBps: BigNumber;
942
+ }>;
930
943
  reserveCoinTypes: string[];
931
944
  reserveCoinMetadataMap: Record<string, import("@suilend/sui-core").Token>;
932
945
  rewardCoinTypes: string[];
@@ -952,7 +965,7 @@ export type OwnedObligations = {
952
965
  obligationErrors: Record<string, unknown>;
953
966
  };
954
967
  export declare const fetchOwnedObligations: (suiGrpcClient: SuiGrpcClient, address: string) => Promise<OwnedObligations>;
955
- export declare const parseOwnedObligations: (owned: OwnedObligations, suilendClient: SuilendClient, refreshedRawReserves: Reserve<string>[], reserveMap: Record<string, ParsedReserve>) => {
968
+ export declare const parseOwnedObligations: (owned: OwnedObligations, suilendClient: SuilendClient, refreshedRawReserves: Reserve<string>[], reserveMap: Record<string, ParsedReserve>, confidenceByCoinType?: ConfidenceByCoinType) => {
956
969
  strategyOwnerCaps: StrategyOwnerCap[];
957
970
  strategyObligations: {
958
971
  id: string;
@@ -1474,7 +1487,7 @@ export declare const parseOwnedObligations: (owned: OwnedObligations, suilendCli
1474
1487
  minPriceBorrowLimit: BigNumber;
1475
1488
  }[];
1476
1489
  };
1477
- export declare const initializeObligations: (suiGrpcClient: SuiGrpcClient, suilendClient: SuilendClient, refreshedRawReserves: Reserve<string>[], reserveMap: Record<string, ParsedReserve>, address?: string) => Promise<{
1490
+ export declare const initializeObligations: (suiGrpcClient: SuiGrpcClient, suilendClient: SuilendClient, refreshedRawReserves: Reserve<string>[], reserveMap: Record<string, ParsedReserve>, address?: string, confidenceByCoinType?: ConfidenceByCoinType) => Promise<{
1478
1491
  strategyOwnerCaps: StrategyOwnerCap[];
1479
1492
  strategyObligations: {
1480
1493
  id: string;
package/lib/initialize.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { normalizeStructTag, parseStructTag } from "@mysten/sui/utils";
2
2
  import BigNumber from "bignumber.js";
3
+ import pLimit from "p-limit";
3
4
  import { NORMALIZED_ALKIMI_COINTYPE, NORMALIZED_AUSD_COINTYPE, NORMALIZED_BLUE_COINTYPE, NORMALIZED_BUCK_COINTYPE, NORMALIZED_DEEP_COINTYPE, NORMALIZED_DMC_COINTYPE, NORMALIZED_FUD_COINTYPE, NORMALIZED_HAEDAL_COINTYPE, NORMALIZED_HIPPO_COINTYPE, NORMALIZED_IKA_COINTYPE, NORMALIZED_KOBAN_COINTYPE, NORMALIZED_LBTC_COINTYPE, NORMALIZED_NS_COINTYPE, NORMALIZED_SEND_COINTYPE, NORMALIZED_SEND_POINTS_S1_COINTYPE, NORMALIZED_SEND_POINTS_S2_COINTYPE, NORMALIZED_SOL_COINTYPE, NORMALIZED_SUI_COINTYPE, NORMALIZED_UP_COINTYPE, NORMALIZED_USDB_COINTYPE, NORMALIZED_USDC_COINTYPE, NORMALIZED_USDsui_COINTYPE, NORMALIZED_WAL_COINTYPE, NORMALIZED_WBTC_COINTYPE, NORMALIZED_WETH_COINTYPE, NORMALIZED_XAUm_COINTYPE, NORMALIZED_eEARN_COINTYPE, NORMALIZED_eTHIRD_COINTYPE, NORMALIZED_flSUI_COINTYPE, NORMALIZED_fpSUI_COINTYPE, NORMALIZED_fudSUI_COINTYPE, NORMALIZED_iSUI_COINTYPE, NORMALIZED_jugSUI_COINTYPE, NORMALIZED_kSUI_COINTYPE, NORMALIZED_mSUI_COINTYPE, NORMALIZED_mUSD_COINTYPE, NORMALIZED_oshiSUI_COINTYPE, NORMALIZED_sSUI_COINTYPE, NORMALIZED_sdeUSD_COINTYPE, NORMALIZED_stratSUI_COINTYPE, NORMALIZED_suiETH_COINTYPE, NORMALIZED_suiUSDT_COINTYPE, NORMALIZED_suiUSDe_COINTYPE, NORMALIZED_suiWBTC_COINTYPE, NORMALIZED_trevinSUI_COINTYPE, NORMALIZED_upSUI_COINTYPE, NORMALIZED_wUSDC_COINTYPE, NORMALIZED_wUSDT_COINTYPE, NORMALIZED_xBTC_COINTYPE, NORMALIZED_yapSUI_COINTYPE, TEMPORARY_PYTH_PRICE_FEED_COINTYPES, getCoinMetadataMapPartial, getPrices, isSendPoints, isSteammPoints, } from "@suilend/sui-core";
4
5
  import { phantom } from "../_generated/_framework/reified.js";
5
6
  import { PACKAGE_ID } from "../_generated/suilend/index.js";
@@ -310,7 +311,34 @@ const fillRewardPriceMap = (rewardPriceMap, reservelessActiveRewardCoinTypes, ca
310
311
  }
311
312
  return rewardPriceMap;
312
313
  };
313
- const initializeSuilendMarkets = async (markets, fallbackPythEndpoint, options, includeRewards) => {
314
+ const kBpsByConfigBagId = new Map();
315
+ const confidenceReadLimit = pLimit(8);
316
+ const configConfidenceMultiplierBps = (suiGrpcClient, bagId) => {
317
+ const cached = kBpsByConfigBagId.get(bagId);
318
+ if (cached)
319
+ return cached;
320
+ const read = confidenceReadLimit(() => SuilendClient.getReserveConfidenceMultiplierBps(suiGrpcClient, bagId));
321
+ kBpsByConfigBagId.set(bagId, read);
322
+ read.catch(() => kBpsByConfigBagId.delete(bagId));
323
+ return read;
324
+ };
325
+ const reserveConfidenceMultipliers = (suiGrpcClient, reserves) => Promise.all(reserves.map(async (reserve) => {
326
+ const { id: bagId, size } = reserve.config.element
327
+ .additionalFields;
328
+ const kBps = BigInt(size) === BigInt(0)
329
+ ? new BigNumber(0)
330
+ : await configConfidenceMultiplierBps(suiGrpcClient, bagId);
331
+ return [normalizeStructTag(reserve.coinType), kBps];
332
+ })).then((entries) => new Map(entries));
333
+ const reserveConfidenceByCoinType = async (suiGrpcClient, reserves, kBpsByCoinType) => new Map(await Promise.all(reserves.map(async (reserve) => {
334
+ const coinType = normalizeStructTag(reserve.coinType);
335
+ const kBps = kBpsByCoinType.get(coinType) ?? new BigNumber(0);
336
+ const share = kBps.isZero() || simulate.isRefreshedFromFeed(reserve)
337
+ ? undefined
338
+ : await confidenceReadLimit(() => SuilendClient.getReserveConfidenceShare(suiGrpcClient, reserve.id));
339
+ return [coinType, { share, kBps }];
340
+ })));
341
+ const initializeSuilendMarkets = async (suiGrpcClient, markets, fallbackPythEndpoint, options, includeRewards) => {
314
342
  const nowMs = Date.now();
315
343
  const nowS = Math.floor(nowMs / 1000);
316
344
  const fetchPrices = options?.fetchPrices ?? getPrices;
@@ -330,7 +358,7 @@ const initializeSuilendMarkets = async (markets, fallbackPythEndpoint, options,
330
358
  });
331
359
  const coinTypesByMarket = reservesByMarket.map(({ withoutTemporaryPythPriceFeeds, withTemporaryPythPriceFeeds }) => marketCoinTypes([...withoutTemporaryPythPriceFeeds, ...withTemporaryPythPriceFeeds], nowMs));
332
360
  const reservelessActiveRewardCoinTypesByMarket = coinTypesByMarket.map(reservelessActiveRewardCoinTypes);
333
- const [[refreshedWithoutTemporaryByMarket, fabricatedPriceCoinTypesByMarket], coinMetadataByMarket, rewardUsdPrices,] = await Promise.all([
361
+ const [[refreshedWithoutTemporaryByMarket, fabricatedPriceCoinTypesByMarket], coinMetadataByMarket, rewardUsdPrices, kBpsByMarket,] = await Promise.all([
334
362
  Promise.all([
335
363
  resolvePythConnection(options?.pythConnection, fallbackPythEndpoint, options?.pythConnectionConfig).then((pythConnection) => refreshReservesForInitializeAll(reservesByMarket.map((r) => r.withoutTemporaryPythPriceFeeds), pythConnection, options?.tolerateMissingPriceFeeds)),
336
364
  priceTemporaryPythFeedReservesAll(reservesByMarket.map((r) => r.withTemporaryPythPriceFeeds), fetchPrices),
@@ -346,6 +374,7 @@ const initializeSuilendMarkets = async (markets, fallbackPythEndpoint, options,
346
374
  return {};
347
375
  })
348
376
  : Promise.resolve({}),
377
+ Promise.all(markets.map(({ suilendClient }) => reserveConfidenceMultipliers(suiGrpcClient, suilendClient.lendingMarket.reserves))),
349
378
  ]);
350
379
  const prepared = markets.map(({ suilendClient, lendingMarketMetadata }, index) => {
351
380
  const refreshedRawReserves = [
@@ -379,6 +408,7 @@ const initializeSuilendMarkets = async (markets, fallbackPythEndpoint, options,
379
408
  ...coinTypesByMarket[index],
380
409
  };
381
410
  });
411
+ const confidenceByMarket = await Promise.all(prepared.map(({ refreshedRawReserves }, index) => reserveConfidenceByCoinType(suiGrpcClient, refreshedRawReserves, kBpsByMarket[index])));
382
412
  const initialized = prepared.map(({ suilendClient, lendingMarketMetadata, refreshedRawReserves, unpricedCoinTypes, uniqueReserveCoinTypes, uniqueRewardCoinTypes, uniqueActiveRewardsCoinTypes, }, index) => {
383
413
  const { coinMetadataMap, reserveCoinMetadataMap, rewardCoinMetadataMap } = coinMetadataByMarket[index];
384
414
  // const walReserve = refreshedRawReserves.find(
@@ -391,7 +421,7 @@ const initializeSuilendMarkets = async (markets, fallbackPythEndpoint, options,
391
421
  // (walReserve.price.value as bigint) = walPrice;
392
422
  // (walReserve.smoothedPrice.value as bigint) = walPrice;
393
423
  // }
394
- const lendingMarket = parseLendingMarket(suilendClient.lendingMarket, refreshedRawReserves, coinMetadataMap, nowS, lendingMarketMetadata);
424
+ const lendingMarket = parseLendingMarket(suilendClient.lendingMarket, refreshedRawReserves, coinMetadataMap, nowS, lendingMarketMetadata, confidenceByMarket[index]);
395
425
  const reserveOrder = resolveReserveOrder(lendingMarket.id, lendingMarketMetadata);
396
426
  if (reserveOrder) {
397
427
  lendingMarket.reserves = sortReservesByOrder(lendingMarket.reserves, reserveOrder);
@@ -403,6 +433,7 @@ const initializeSuilendMarkets = async (markets, fallbackPythEndpoint, options,
403
433
  refreshedRawReserves,
404
434
  unpricedCoinTypes,
405
435
  reserveMap,
436
+ confidenceByCoinType: confidenceByMarket[index],
406
437
  reserveCoinTypes: uniqueReserveCoinTypes,
407
438
  reserveCoinMetadataMap,
408
439
  rewardCoinTypes: uniqueRewardCoinTypes,
@@ -416,11 +447,11 @@ const initializeSuilendMarkets = async (markets, fallbackPythEndpoint, options,
416
447
  return { initialized, rewardPriceMaps };
417
448
  };
418
449
  export const initializeSuilendAll = async (suiGrpcClient, markets, fallbackPythEndpoint, options) => {
419
- const { initialized } = await initializeSuilendMarkets(markets, fallbackPythEndpoint, options, false);
450
+ const { initialized } = await initializeSuilendMarkets(suiGrpcClient, markets, fallbackPythEndpoint, options, false);
420
451
  return initialized;
421
452
  };
422
453
  export const initializeSuilendWithRewardsAll = async (suiGrpcClient, markets, fallbackPythEndpoint, options) => {
423
- const { initialized, rewardPriceMaps } = await initializeSuilendMarkets(markets, fallbackPythEndpoint, options, true);
454
+ const { initialized, rewardPriceMaps } = await initializeSuilendMarkets(suiGrpcClient, markets, fallbackPythEndpoint, options, true);
424
455
  return initialized.map((market, index) => ({
425
456
  ...market,
426
457
  rewardPriceMap: rewardPriceMaps[index],
@@ -518,9 +549,9 @@ const obligationIdsOf = ({ strategyOwnerCaps, obligationOwnerCaps, }) => [
518
549
  ...strategyOwnerCaps.map((soc) => soc.obligationId),
519
550
  ...obligationOwnerCaps.map((ownerCap) => ownerCap.obligationId),
520
551
  ];
521
- const parseMarketObligations = (rawObligations, { strategyOwnerCaps, obligationOwnerCaps }, refreshedRawReserves, reserveMap) => {
552
+ const parseMarketObligations = (rawObligations, { strategyOwnerCaps, obligationOwnerCaps }, refreshedRawReserves, reserveMap, confidenceByCoinType) => {
522
553
  const obligations = rawObligations
523
- .map((rawObligation) => simulate.refreshObligation(rawObligation, refreshedRawReserves))
554
+ .map((rawObligation) => simulate.refreshObligation(rawObligation, refreshedRawReserves, confidenceByCoinType))
524
555
  .map((refreshedObligation) => parseObligation(refreshedObligation, reserveMap, strategyOwnerCaps.some((soc) => soc.obligationId === refreshedObligation.id)))
525
556
  .sort((a, b) => +b.netValueUsd.minus(a.netValueUsd));
526
557
  const strategyObligations = obligations.filter((o) => strategyOwnerCaps.some((soc) => soc.obligationId === o.id));
@@ -568,7 +599,7 @@ export const fetchOwnedObligations = async (suiGrpcClient, address) => {
568
599
  obligationErrors,
569
600
  };
570
601
  };
571
- export const parseOwnedObligations = (owned, suilendClient, refreshedRawReserves, reserveMap) => {
602
+ export const parseOwnedObligations = (owned, suilendClient, refreshedRawReserves, reserveMap, confidenceByCoinType) => {
572
603
  const marketCaps = selectMarketCaps(owned, suilendClient);
573
604
  const obligationsById = new Map(owned.obligations.map((obligation) => [obligation.id, obligation]));
574
605
  const rawObligations = obligationIdsOf(marketCaps).map((obligationId) => {
@@ -579,9 +610,9 @@ export const parseOwnedObligations = (owned, suilendClient, refreshedRawReserves
579
610
  throw owned.obligationErrors[obligationId];
580
611
  throw new Error(`Obligation ${obligationId} is not in the owned obligations`);
581
612
  });
582
- return parseMarketObligations(rawObligations, marketCaps, refreshedRawReserves, reserveMap);
613
+ return parseMarketObligations(rawObligations, marketCaps, refreshedRawReserves, reserveMap, confidenceByCoinType);
583
614
  };
584
- export const initializeObligations = async (suiGrpcClient, suilendClient, refreshedRawReserves, reserveMap, address) => {
615
+ export const initializeObligations = async (suiGrpcClient, suilendClient, refreshedRawReserves, reserveMap, address, confidenceByCoinType) => {
585
616
  if (!address)
586
617
  return {
587
618
  strategyOwnerCaps: [],
@@ -591,5 +622,5 @@ export const initializeObligations = async (suiGrpcClient, suilendClient, refres
591
622
  };
592
623
  const marketCaps = selectMarketCaps(await listOwnedCaps(suiGrpcClient, address, hasStrategies(suilendClient)), suilendClient);
593
624
  const rawObligations = await SuilendClient.getObligations(obligationIdsOf(marketCaps), suilendClient.lendingMarket.$typeArgs, suiGrpcClient);
594
- return parseMarketObligations(rawObligations, marketCaps, refreshedRawReserves, reserveMap);
625
+ return parseMarketObligations(rawObligations, marketCaps, refreshedRawReserves, reserveMap, confidenceByCoinType);
595
626
  };
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@suilend/sdk","version":"13.7.0-next.ge2ee95d","private":false,"description":"A TypeScript SDK for interacting with the Suilend program","author":"Suilend","license":"MIT","main":"./index.js","exports":{".":"./index.js","./client":"./client.js","./mmt":"./mmt.js","./strategies":"./strategies.js","./api/events":"./api/events.js","./api":"./api/index.js","./lib/constants":"./lib/constants.js","./lib":"./lib/index.js","./lib/initialize":"./lib/initialize.js","./lib/liquidityMining":"./lib/liquidityMining.js","./lib/proCompatible":"./lib/proCompatible.js","./lib/pyth":"./lib/pyth.js","./lib/strategyOwnerCap":"./lib/strategyOwnerCap.js","./lib/transactions":"./lib/transactions.js","./lib/types":"./lib/types.js","./margin":"./margin/index.js","./parsers/apiReserveAssetDataEvent":"./parsers/apiReserveAssetDataEvent.js","./parsers":"./parsers/index.js","./parsers/lendingMarket":"./parsers/lendingMarket.js","./parsers/obligation":"./parsers/obligation.js","./parsers/rateLimiter":"./parsers/rateLimiter.js","./parsers/reserve":"./parsers/reserve.js","./swap":"./swap/index.js","./swap/quote":"./swap/quote.js","./swap/transaction":"./swap/transaction.js","./utils/events":"./utils/events.js","./utils/feedId":"./utils/feedId.js","./utils":"./utils/index.js","./utils/interpolate":"./utils/interpolate.js","./utils/obligation":"./utils/obligation.js","./utils/priceGuard":"./utils/priceGuard.js","./utils/simulate":"./utils/simulate.js","./_generated/_envs":"./_generated/_envs/index.js","./_generated/_envs/mainnet":"./_generated/_envs/mainnet.js","./_generated/_framework/env":"./_generated/_framework/env.js","./_generated/_framework/reified":"./_generated/_framework/reified.js","./_generated/_framework/util":"./_generated/_framework/util.js","./_generated/_framework/vector":"./_generated/_framework/vector.js","./_generated/suilend":"./_generated/suilend/index.js","./margin/margin/admin_cap":"./margin/margin/admin_cap.js","./margin/margin/market":"./margin/margin/market.js","./margin/margin/permissions":"./margin/margin/permissions.js","./margin/margin/position":"./margin/margin/position.js","./margin/margin/router":"./margin/margin/router.js","./margin/margin/version":"./margin/margin/version.js","./margin/utils":"./margin/utils/index.js","./_generated/suilend/cell/structs":"./_generated/suilend/cell/structs.js","./_generated/suilend/decimal/structs":"./_generated/suilend/decimal/structs.js","./_generated/suilend/lending-market/functions":"./_generated/suilend/lending-market/functions.js","./_generated/suilend/lending-market/structs":"./_generated/suilend/lending-market/structs.js","./_generated/suilend/lending-market-registry/functions":"./_generated/suilend/lending-market-registry/functions.js","./_generated/suilend/liquidity-mining/structs":"./_generated/suilend/liquidity-mining/structs.js","./_generated/suilend/obligation/structs":"./_generated/suilend/obligation/structs.js","./_generated/suilend/rate-limiter/functions":"./_generated/suilend/rate-limiter/functions.js","./_generated/suilend/rate-limiter/structs":"./_generated/suilend/rate-limiter/structs.js","./_generated/suilend/reserve/structs":"./_generated/suilend/reserve/structs.js","./_generated/suilend/reserve-config/functions":"./_generated/suilend/reserve-config/functions.js","./_generated/suilend/reserve-config/structs":"./_generated/suilend/reserve-config/structs.js","./_generated/_dependencies/pyth-2/price-identifier/structs":"./_generated/_dependencies/pyth-2/price-identifier/structs.js","./_generated/_dependencies/std/ascii/structs":"./_generated/_dependencies/std/ascii/structs.js","./_generated/_dependencies/std/option/structs":"./_generated/_dependencies/std/option/structs.js","./_generated/_dependencies/std/type-name/structs":"./_generated/_dependencies/std/type-name/structs.js","./_generated/_dependencies/sui/bag/structs":"./_generated/_dependencies/sui/bag/structs.js","./_generated/_dependencies/sui/balance/structs":"./_generated/_dependencies/sui/balance/structs.js","./_generated/_dependencies/sui/object/structs":"./_generated/_dependencies/sui/object/structs.js","./_generated/_dependencies/sui/object-table/structs":"./_generated/_dependencies/sui/object-table/structs.js","./margin/margin/deps/std/type_name":"./margin/margin/deps/std/type_name.js","./margin/margin/deps/sui/vec_set":"./margin/margin/deps/sui/vec_set.js","./margin/margin/deps/suilend/lending_market":"./margin/margin/deps/suilend/lending_market.js"},"types":"./index.d.ts","scripts":{"codegen":"node ./codegen.js","codegen:install":"node ./install-codegen.js","build":"rm -rf ./dist && tsc && node ./fix-esm-imports.js","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","lint:ci":"yarn run typecheck","prettier":"prettier --write src/ tests/","release":"yarn run build && node ./release.js && cd ./dist && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/fireflyprotocol/lending-mono.git","directory":"ts/sdks/sdk"},"dependencies":{"@bluefin-exchange/bluefin7k-aggregator-sdk":"7.7.0","@cetusprotocol/aggregator-sdk":"1.7.3","@flowx-finance/sdk":"2.1.0","@pythnetwork/hermes-client":"3.1.0","@pythnetwork/pyth-sui-js":"4.0.0","@suilend/springsui-sdk":"4.1.0","bignumber.js":"11.1.5","bn.js":"5.2.5","crypto-js":"4.2.0","lodash":"4.18.1","p-limit":"7.3.3","uuid":"14.0.2"},"devDependencies":{"@mysten/bcs":"2.1.2","@mysten/sui":"2.35.0","@suilend/sui-core":"2.2.0","@tsconfig/recommended":"1.0.13","@types/bn.js":"5.2.0","@types/lodash":"4.17.26","@types/node":"26.6.5","fast-check":"4.10.2","prettier":"3.9.10","ts-node":"10.9.2","typescript":"6.0.3","vite":"8.3.4","vitest":"5.0.4"},"peerDependencies":{"@mysten/bcs":"^2.1.1","@mysten/sui":">=2.31.3 <3","@suilend/sui-core":"^2.0.1"},"type":"module"}
1
+ {"name":"@suilend/sdk","version":"13.8.0-next.g33ed981","private":false,"description":"A TypeScript SDK for interacting with the Suilend program","author":"Suilend","license":"MIT","main":"./index.js","exports":{".":"./index.js","./client":"./client.js","./mmt":"./mmt.js","./strategies":"./strategies.js","./api/events":"./api/events.js","./api":"./api/index.js","./lib/constants":"./lib/constants.js","./lib":"./lib/index.js","./lib/initialize":"./lib/initialize.js","./lib/liquidityMining":"./lib/liquidityMining.js","./lib/proCompatible":"./lib/proCompatible.js","./lib/pyth":"./lib/pyth.js","./lib/strategyOwnerCap":"./lib/strategyOwnerCap.js","./lib/transactions":"./lib/transactions.js","./lib/types":"./lib/types.js","./margin":"./margin/index.js","./parsers/apiReserveAssetDataEvent":"./parsers/apiReserveAssetDataEvent.js","./parsers":"./parsers/index.js","./parsers/lendingMarket":"./parsers/lendingMarket.js","./parsers/obligation":"./parsers/obligation.js","./parsers/rateLimiter":"./parsers/rateLimiter.js","./parsers/reserve":"./parsers/reserve.js","./swap":"./swap/index.js","./swap/quote":"./swap/quote.js","./swap/transaction":"./swap/transaction.js","./utils/events":"./utils/events.js","./utils/feedId":"./utils/feedId.js","./utils":"./utils/index.js","./utils/interpolate":"./utils/interpolate.js","./utils/obligation":"./utils/obligation.js","./utils/priceGuard":"./utils/priceGuard.js","./utils/simulate":"./utils/simulate.js","./_generated/_envs":"./_generated/_envs/index.js","./_generated/_envs/mainnet":"./_generated/_envs/mainnet.js","./_generated/_framework/env":"./_generated/_framework/env.js","./_generated/_framework/reified":"./_generated/_framework/reified.js","./_generated/_framework/util":"./_generated/_framework/util.js","./_generated/_framework/vector":"./_generated/_framework/vector.js","./_generated/suilend":"./_generated/suilend/index.js","./margin/margin/admin_cap":"./margin/margin/admin_cap.js","./margin/margin/market":"./margin/margin/market.js","./margin/margin/permissions":"./margin/margin/permissions.js","./margin/margin/position":"./margin/margin/position.js","./margin/margin/router":"./margin/margin/router.js","./margin/margin/version":"./margin/margin/version.js","./margin/utils":"./margin/utils/index.js","./_generated/suilend/cell/structs":"./_generated/suilend/cell/structs.js","./_generated/suilend/decimal/structs":"./_generated/suilend/decimal/structs.js","./_generated/suilend/lending-market/functions":"./_generated/suilend/lending-market/functions.js","./_generated/suilend/lending-market/structs":"./_generated/suilend/lending-market/structs.js","./_generated/suilend/lending-market-registry/functions":"./_generated/suilend/lending-market-registry/functions.js","./_generated/suilend/liquidity-mining/structs":"./_generated/suilend/liquidity-mining/structs.js","./_generated/suilend/obligation/structs":"./_generated/suilend/obligation/structs.js","./_generated/suilend/rate-limiter/functions":"./_generated/suilend/rate-limiter/functions.js","./_generated/suilend/rate-limiter/structs":"./_generated/suilend/rate-limiter/structs.js","./_generated/suilend/reserve/structs":"./_generated/suilend/reserve/structs.js","./_generated/suilend/reserve-config/functions":"./_generated/suilend/reserve-config/functions.js","./_generated/suilend/reserve-config/structs":"./_generated/suilend/reserve-config/structs.js","./_generated/_dependencies/pyth-2/price-identifier/structs":"./_generated/_dependencies/pyth-2/price-identifier/structs.js","./_generated/_dependencies/std/ascii/structs":"./_generated/_dependencies/std/ascii/structs.js","./_generated/_dependencies/std/option/structs":"./_generated/_dependencies/std/option/structs.js","./_generated/_dependencies/std/type-name/structs":"./_generated/_dependencies/std/type-name/structs.js","./_generated/_dependencies/sui/bag/structs":"./_generated/_dependencies/sui/bag/structs.js","./_generated/_dependencies/sui/balance/structs":"./_generated/_dependencies/sui/balance/structs.js","./_generated/_dependencies/sui/object/structs":"./_generated/_dependencies/sui/object/structs.js","./_generated/_dependencies/sui/object-table/structs":"./_generated/_dependencies/sui/object-table/structs.js","./margin/margin/deps/std/type_name":"./margin/margin/deps/std/type_name.js","./margin/margin/deps/sui/vec_set":"./margin/margin/deps/sui/vec_set.js","./margin/margin/deps/suilend/lending_market":"./margin/margin/deps/suilend/lending_market.js"},"types":"./index.d.ts","scripts":{"codegen":"node ./codegen.js","codegen:install":"node ./install-codegen.js","build":"rm -rf ./dist && tsc && node ./fix-esm-imports.js","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","lint:ci":"yarn run typecheck","prettier":"prettier --write src/ tests/","release":"yarn run build && node ./release.js && cd ./dist && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/fireflyprotocol/lending-mono.git","directory":"ts/sdks/sdk"},"dependencies":{"@bluefin-exchange/bluefin7k-aggregator-sdk":"7.7.0","@cetusprotocol/aggregator-sdk":"1.7.3","@flowx-finance/sdk":"2.1.0","@pythnetwork/hermes-client":"3.1.0","@pythnetwork/pyth-sui-js":"4.0.0","@suilend/springsui-sdk":"4.1.0","bignumber.js":"11.1.5","bn.js":"5.2.5","crypto-js":"4.2.0","lodash":"4.18.1","p-limit":"7.3.3","uuid":"14.0.2"},"devDependencies":{"@mysten/bcs":"2.1.2","@mysten/sui":"2.35.0","@suilend/sui-core":"2.2.0","@tsconfig/recommended":"1.0.13","@types/bn.js":"5.2.0","@types/lodash":"4.17.26","@types/node":"26.6.5","fast-check":"4.10.2","prettier":"3.9.10","ts-node":"10.9.2","typescript":"6.0.3","vite":"8.3.4","vitest":"5.0.4"},"peerDependencies":{"@mysten/bcs":"^2.1.1","@mysten/sui":">=2.31.3 <3","@suilend/sui-core":"^2.0.1"},"type":"module"}
@@ -1,7 +1,7 @@
1
1
  import { normalizeStructTag } from "@mysten/sui/utils";
2
2
  import BigNumber from "bignumber.js";
3
3
  import { WAD } from "../lib/constants.js";
4
- import { applyConfidenceMargin } from "../utils/simulate.js";
4
+ import { applyConfidenceMargin, confidenceShare } from "../utils/simulate.js";
5
5
  // Reproduces the exact pre-confidence-margin bounds when a caller doesn't
6
6
  // supply the reserve's current multiplier.
7
7
  const ZERO_K_BPS = new BigNumber(0);
@@ -37,7 +37,7 @@ kBps = ZERO_K_BPS) => {
37
37
  ? new BigNumber(event.unclampedSmoothedPrice).div(WAD)
38
38
  : smoothedPrice;
39
39
  const rangePrices = [price, smoothedPrice, unclampedPrice, unclampedSmoothedPrice];
40
- const { lowerBound: minPrice, upperBound: maxPrice } = applyConfidenceMargin(BigNumber.min(...rangePrices), BigNumber.max(...rangePrices), conf, kBps, unclampedPrice);
40
+ const { lowerBound: minPrice, upperBound: maxPrice } = applyConfidenceMargin(BigNumber.min(...rangePrices), BigNumber.max(...rangePrices), confidenceShare(conf, unclampedPrice), kBps);
41
41
  // Custom
42
42
  const utilizationPercent = depositedAmount.eq(0)
43
43
  ? new BigNumber(0)
@@ -3,8 +3,9 @@ import BigNumber from "bignumber.js";
3
3
  import { LendingMarket } from "../_generated/suilend/lending-market/structs";
4
4
  import { Reserve } from "../_generated/suilend/reserve/structs";
5
5
  import { LendingMarketMetadata } from "../lib";
6
+ import * as simulate from "../utils/simulate";
6
7
  export type ParsedLendingMarket = ReturnType<typeof parseLendingMarket>;
7
- export declare const parseLendingMarket: (lendingMarket: LendingMarket<string>, reserves: Reserve<string>[], coinMetadataMap: Record<string, SuiClientTypes.CoinMetadata>, nowS: number, lendingMarketMetadata?: LendingMarketMetadata) => {
8
+ export declare const parseLendingMarket: (lendingMarket: LendingMarket<string>, reserves: Reserve<string>[], coinMetadataMap: Record<string, SuiClientTypes.CoinMetadata>, nowS: number, lendingMarketMetadata?: LendingMarketMetadata, confidenceByCoinType?: simulate.ConfidenceByCoinType) => {
8
9
  version: bigint;
9
10
  reserves: {
10
11
  config: {
@@ -1,11 +1,18 @@
1
+ import { normalizeStructTag } from "@mysten/sui/utils";
1
2
  import BigNumber from "bignumber.js";
3
+ import * as simulate from "../utils/simulate.js";
2
4
  import { parseRateLimiter } from "./rateLimiter.js";
3
5
  import { parseReserve } from "./reserve.js";
4
- export const parseLendingMarket = (lendingMarket, reserves, coinMetadataMap, nowS, lendingMarketMetadata) => {
6
+ export const parseLendingMarket = (lendingMarket, reserves, coinMetadataMap, nowS, lendingMarketMetadata, confidenceByCoinType) => {
5
7
  const id = lendingMarket.id;
6
8
  const type = lendingMarket.$typeArgs[0];
7
9
  const version = lendingMarket.version;
8
- const parsedReserves = reserves.map((reserve) => parseReserve(reserve, coinMetadataMap));
10
+ const normalizedConfidence = confidenceByCoinType &&
11
+ simulate.normalizeConfidenceByCoinType(confidenceByCoinType);
12
+ const parsedReserves = reserves.map((reserve) => {
13
+ const confidence = simulate.resolveConfidence(normalizedConfidence?.get(normalizeStructTag(reserve.coinType)), reserve);
14
+ return parseReserve(reserve, coinMetadataMap, confidence?.share, confidence?.kBps);
15
+ });
9
16
  const obligations = lendingMarket.obligations;
10
17
  const parsedRateLimiter = parseRateLimiter(lendingMarket.rateLimiter, nowS);
11
18
  const feeReceiver = lendingMarket.feeReceiver;
@@ -2,11 +2,12 @@ import { SuiClientTypes } from "@mysten/sui/client";
2
2
  import BigNumber from "bignumber.js";
3
3
  import { PoolReward, PoolRewardManager } from "../_generated/suilend/liquidity-mining/structs";
4
4
  import { Reserve } from "../_generated/suilend/reserve/structs";
5
+ import * as simulate from "../utils/simulate";
5
6
  export type ParsedReserve = ReturnType<typeof parseReserve>;
6
7
  export type ParsedReserveConfig = ReturnType<typeof parseReserveConfig>;
7
8
  export type ParsedPoolRewardManager = ReturnType<typeof parsePoolRewardManager>;
8
9
  export type ParsedPoolReward = NonNullable<ReturnType<typeof parsePoolReward>>;
9
- export declare const parseReserve: (reserve: Reserve<string>, coinMetadataMap: Record<string, SuiClientTypes.CoinMetadata>, conf?: BigNumber, kBps?: BigNumber) => {
10
+ export declare const parseReserve: (reserve: Reserve<string>, coinMetadataMap: Record<string, SuiClientTypes.CoinMetadata>, share?: simulate.ConfidenceShare, kBps?: BigNumber) => {
10
11
  config: {
11
12
  $typeName: `${string}::reserve_config::ReserveConfig`;
12
13
  openLtvPct: number;
@@ -5,14 +5,14 @@ import { WAD } from "../lib/constants.js";
5
5
  import { toHexString } from "../utils/index.js";
6
6
  import * as simulate from "../utils/simulate.js";
7
7
  export const parseReserve = (reserve, coinMetadataMap,
8
- // The reserve's stored Pyth confidence and its CURRENT confidence
8
+ // The reserve's stored conf / spot share and its CURRENT confidence
9
9
  // multiplier (in bps) -- a ConfidenceKey dynamic field and a
10
10
  // ReserveConfig.additionalFields Bag entry respectively, neither of which
11
11
  // lives on the Reserve object's own BCS content, so this parser can't
12
12
  // fetch them itself without becoming async. Omit either (or both) for
13
13
  // plain bounds. The raw prices of a guarded reserve come attached with
14
14
  // simulate.withUnclampedPrices.
15
- conf, kBps) => {
15
+ share, kBps) => {
16
16
  const config = parseReserveConfig(reserve);
17
17
  const $typeName = reserve.$typeName;
18
18
  const id = reserve.id;
@@ -24,7 +24,9 @@ conf, kBps) => {
24
24
  const price = new BigNumber(reserve.price.value.toString()).div(WAD);
25
25
  const smoothedPrice = new BigNumber(reserve.smoothedPrice.value.toString()).div(WAD);
26
26
  const { low, high } = simulate.priceRange(reserve);
27
- const { lowerBound: minPrice, upperBound: maxPrice } = simulate.applyConfidenceMargin(low, high, conf !== undefined && kBps !== undefined ? conf : new BigNumber(0), kBps ?? new BigNumber(0), simulate.unclampedSpot(reserve));
27
+ const { lowerBound: minPrice, upperBound: maxPrice } = share !== undefined && kBps !== undefined
28
+ ? simulate.applyConfidenceMargin(low, high, share, kBps)
29
+ : { lowerBound: low, upperBound: high };
28
30
  const priceLastUpdateTimestampS = reserve.priceLastUpdateTimestampS;
29
31
  const availableAmount = new BigNumber(reserve.availableAmount.toString()).div(10 ** mintDecimals);
30
32
  const ctokenSupply = new BigNumber(reserve.ctokenSupply.toString()).div(10 ** mintDecimals);
@@ -146,6 +146,29 @@ export type ObligationRefresh = {
146
146
  weightedBorrowedValueUsd: BigNumber;
147
147
  weightedBorrowedValueUpperBoundUsd: BigNumber;
148
148
  };
149
+ /**
150
+ * `conf / spot` from a single price read: what a reserve stores in
151
+ * `ConfidenceKey`. Branded so a USD conf cannot be passed where a share is
152
+ * expected; build one with `confidenceShare`.
153
+ */
154
+ export type ConfidenceShare = BigNumber & {
155
+ readonly __brand: "ConfidenceShare";
156
+ };
157
+ /** Mirrors reserve.move's `record_confidence`: a zero spot records a zero share. */
158
+ export declare const confidenceShare: (conf: BigNumber, spot: BigNumber) => ConfidenceShare;
159
+ export type ConfidenceByCoinType = Map<string, {
160
+ share: ConfidenceShare | undefined;
161
+ kBps: BigNumber;
162
+ }>;
163
+ export declare const normalizeConfidenceByCoinType: (confidenceByCoinType: ConfidenceByCoinType) => ConfidenceByCoinType;
164
+ export declare const isRefreshedFromFeed: (reserve: Reserve<string>) => boolean;
165
+ export declare const resolveConfidence: (entry: {
166
+ share: ConfidenceShare | undefined;
167
+ kBps: BigNumber;
168
+ } | undefined, reserve: Reserve<string>) => {
169
+ share: ConfidenceShare;
170
+ kBps: BigNumber;
171
+ } | undefined;
149
172
  /**
150
173
  * Pure computation half of `refreshObligation`: same math, but returns fresh
151
174
  * deposits/borrows/reward-manager arrays and the five USD aggregates instead
@@ -155,31 +178,28 @@ export type ObligationRefresh = {
155
178
  * shallow-copied), silently corrupting `unrefreshedObligation` for anyone still
156
179
  * holding a reference to it.
157
180
  */
158
- export declare function computeObligationRefresh(obligation: Obligation<string>, refreshedReserves: Reserve<string>[], nowMs: number, confidenceByCoinType?: Map<string, {
159
- conf?: BigNumber;
160
- kBps: BigNumber;
161
- }>): ObligationRefresh;
162
- export declare const refreshObligation: (unrefreshedObligation: Obligation<string>, refreshedReserves: Reserve<string>[]) => Obligation<string>;
181
+ export declare function computeObligationRefresh(obligation: Obligation<string>, refreshedReserves: Reserve<string>[], nowMs: number, confidenceByCoinType?: ConfidenceByCoinType): ObligationRefresh;
182
+ export declare const refreshObligation: (unrefreshedObligation: Obligation<string>, refreshedReserves: Reserve<string>[], confidenceByCoinType?: ConfidenceByCoinType) => Obligation<string>;
163
183
  export declare const numberToDecimal: (value: number) => Decimal;
164
184
  export declare const stringToDecimal: (value: string) => Decimal;
165
185
  export declare const decimalToBigNumber: (value: Decimal) => BigNumber;
166
186
  /**
167
187
  * Mirrors reserve.move's `price_lower_bound`/`price_upper_bound`: the one
168
188
  * off-chain place this formula lives. `low` and `high` are the ends of the
169
- * reserve's price range (`priceRange`), which the bounds widen by the share
170
- * `k * conf / spot`, where `spot` is the unclamped spot of the same update
171
- * `conf` came from. `conf` and `kBps` are the reserve's stored
172
- * Pyth confidence and its `ReserveConfig` confidence multiplier (in bps, e.g.
173
- * 30000 for a logical k=3) -- both plain human-scale BigNumbers, not
174
- * WAD-scaled Decimals. `kBps=0` or `conf=0` disables the margin entirely:
175
- * both bounds collapse to `low` and `high`.
189
+ * reserve's price range (`priceRange`), which the bounds widen by
190
+ * `k * share`. `share` is `conf / spot` from a single price read -- what the
191
+ * reserve stores in `ConfidenceKey` -- and `kBps` is its `ReserveConfig`
192
+ * confidence multiplier (in bps, e.g. 30000 for a logical k=3); both are
193
+ * plain human-scale BigNumbers, not WAD-scaled Decimals. `kBps=0` or
194
+ * `share=0` disables the margin entirely: both bounds collapse to `low` and
195
+ * `high`.
176
196
  */
177
- export declare const applyConfidenceMargin: (low: BigNumber, high: BigNumber, conf: BigNumber, kBps: BigNumber, spot: BigNumber) => {
197
+ export declare const applyConfidenceMargin: (low: BigNumber, high: BigNumber, share: ConfidenceShare, kBps: BigNumber) => {
178
198
  lowerBound: BigNumber;
179
199
  upperBound: BigNumber;
180
200
  };
181
201
  export declare const getCTokenMarketValue: (reserve: Reserve<string>, depositedCTokenAmount: BigNumber) => BigNumber;
182
- export declare const getCTokenMarketValueLowerBound: (reserve: Reserve<string>, depositedCTokenAmount: BigNumber, conf?: BigNumber, kBps?: BigNumber) => BigNumber;
202
+ export declare const getCTokenMarketValueLowerBound: (reserve: Reserve<string>, depositedCTokenAmount: BigNumber, share?: ConfidenceShare, kBps?: BigNumber) => BigNumber;
183
203
  export declare const cTokenRatio: (reserve: Reserve<string>) => BigNumber;
184
204
  export declare const totalSupply: (reserve: Reserve<string>) => BigNumber;
185
205
  export declare const compoundBorrowedAmount: (snapshotAmount: BigNumber, currentCumulativeBorrowRate: BigNumber, snapshotCumulativeBorrowRate: BigNumber) => BigNumber;
package/utils/simulate.js CHANGED
@@ -352,18 +352,29 @@ export const findReserveOrThrow = (reserves, coinType) => {
352
352
  // (`v * WAD / 10_000`); dividing in BigNumber here matches that.
353
353
  export const computeBorrowWeight = (config) => new BigNumber(config.borrowWeightBps.toString()).div(10000);
354
354
  export const computeCloseLtv = (config) => new BigNumber(config.closeLtvPct / 100);
355
- const resolveConfidence = (entry, reserve) => {
355
+ /** Mirrors reserve.move's `record_confidence`: a zero spot records a zero share. */
356
+ export const confidenceShare = (conf, spot) => (spot.isZero() ? new BigNumber(0) : conf.dividedBy(spot));
357
+ const ZERO_SHARE = confidenceShare(new BigNumber(0), new BigNumber(0));
358
+ export const normalizeConfidenceByCoinType = (confidenceByCoinType) => new Map([...confidenceByCoinType].map(([coinType, entry]) => [
359
+ normalizeStructTag(coinType),
360
+ entry,
361
+ ]));
362
+ export const isRefreshedFromFeed = (reserve) => "feedConfidence" in reserve;
363
+ export const resolveConfidence = (entry, reserve) => {
356
364
  if (entry === undefined)
357
365
  return undefined;
358
- const refreshedFromFeed = "feedConfidence" in reserve;
359
- const conf = feedConfidenceOf(reserve) ??
360
- (refreshedFromFeed ? undefined : entry.conf) ??
361
- (entry.kBps.isZero() ? new BigNumber(0) : undefined);
362
- if (conf === undefined)
366
+ const refreshedFromFeed = isRefreshedFromFeed(reserve);
367
+ const feedConf = feedConfidenceOf(reserve);
368
+ const share = (feedConf !== undefined
369
+ ? confidenceShare(feedConf, unclampedSpot(reserve))
370
+ : undefined) ??
371
+ (refreshedFromFeed ? undefined : entry.share) ??
372
+ (entry.kBps.isZero() ? ZERO_SHARE : undefined);
373
+ if (share === undefined)
363
374
  throw new Error(refreshedFromFeed
364
- ? `Reserve ${reserve.coinType} was refreshed from a feed without conf, so the stored conf is from a different update: use a feed that carries conf`
365
- : `No conf for reserve ${reserve.coinType}: refresh it from a feed that carries conf, or supply conf in the entry`);
366
- return { conf, kBps: entry.kBps };
375
+ ? `Reserve ${reserve.coinType} was refreshed from a feed without conf, so the stored share is from a different update: use a feed that carries conf`
376
+ : `No conf for reserve ${reserve.coinType}: refresh it from a feed that carries conf, or supply the stored share in the entry`);
377
+ return { share, kBps: entry.kBps };
367
378
  };
368
379
  /**
369
380
  * Pure computation half of `refreshObligation`: same math, but returns fresh
@@ -376,18 +387,15 @@ const resolveConfidence = (entry, reserve) => {
376
387
  */
377
388
  export function computeObligationRefresh(obligation, refreshedReserves, nowMs,
378
389
  // Per-reserve CURRENT confidence multiplier, keyed by coinType in either
379
- // raw or normalized form. `conf` is the fallback for a reserve that was never
380
- // price-refreshed; a reserve refreshed from a feed without `conf` throws
381
- // instead (unless kBps is 0), because its price and the stored conf come from
382
- // different updates. A reserve's own `feedConfidence` takes precedence because
383
- // it comes from the same update as its price. A reserve with no entry here
384
- // gets plain min/max(price, smoothedPrice) bounds.
390
+ // raw or normalized form. `share` is the reserve's stored conf / spot
391
+ // (`SuilendClient.getReserveConfidenceShare`), the fallback for a reserve that
392
+ // was never price-refreshed; a reserve refreshed from a feed without `conf`
393
+ // throws instead (unless kBps is 0), because its price and the stored share
394
+ // come from different updates. A reserve's own `feedConfidence` takes
395
+ // precedence because it comes from the same update as its price. A reserve
396
+ // with no entry here gets plain min/max(price, smoothedPrice) bounds.
385
397
  confidenceByCoinType) {
386
- const normalizedConfidence = confidenceByCoinType &&
387
- new Map([...confidenceByCoinType].map(([coinType, entry]) => [
388
- normalizeStructTag(coinType),
389
- entry,
390
- ]));
398
+ const normalizedConfidence = confidenceByCoinType && normalizeConfidenceByCoinType(confidenceByCoinType);
391
399
  const userRewardManagers = [...obligation.userRewardManagers];
392
400
  let depositValueUsd = new BigNumber(0);
393
401
  let allowedBorrowValueUsd = new BigNumber(0);
@@ -400,7 +408,7 @@ confidenceByCoinType) {
400
408
  updateUserRewardManager(reserve.depositsPoolRewardManager, userRewardManagers[Number(deposit.userRewardManagerIndex)], nowMs);
401
409
  const confidence = resolveConfidence(normalizedConfidence?.get(normalizeStructTag(deposit.coinType)), reserve);
402
410
  const marketValue = getCTokenMarketValue(reserve, new BigNumber(deposit.depositedCtokenAmount.toString()));
403
- const marketValueLowerBound = getCTokenMarketValueLowerBound(reserve, new BigNumber(deposit.depositedCtokenAmount.toString()), confidence?.conf, confidence?.kBps);
411
+ const marketValueLowerBound = getCTokenMarketValueLowerBound(reserve, new BigNumber(deposit.depositedCtokenAmount.toString()), confidence?.share, confidence?.kBps);
404
412
  deposit.marketValue = stringToDecimal(marketValue.toString());
405
413
  depositValueUsd = depositValueUsd.plus(new BigNumber(marketValue.toString()));
406
414
  allowedBorrowValueUsd = allowedBorrowValueUsd.plus(marketValueLowerBound.multipliedBy(new BigNumber(config.openLtvPct / 100)));
@@ -435,7 +443,8 @@ confidenceByCoinType) {
435
443
  const confidence = resolveConfidence(normalizedConfidence?.get(normalizeStructTag(unrefreshedBorrow.coinType)), reserve);
436
444
  const { low, high } = priceRange(reserve);
437
445
  const upperBoundPrice = confidence !== undefined
438
- ? applyConfidenceMargin(low, high, confidence.conf, confidence.kBps, unclampedSpot(reserve)).upperBound
446
+ ? applyConfidenceMargin(low, high, confidence.share, confidence.kBps)
447
+ .upperBound
439
448
  : high;
440
449
  // `exactBorrowedAmount` (this borrow's own amount), not
441
450
  // `reserve.borrowedAmount`: the reserve field is the whole reserve's
@@ -467,8 +476,8 @@ confidenceByCoinType) {
467
476
  weightedBorrowedValueUpperBoundUsd,
468
477
  };
469
478
  }
470
- export const refreshObligation = (unrefreshedObligation, refreshedReserves) => {
471
- const refresh = computeObligationRefresh(unrefreshedObligation, refreshedReserves, Date.now());
479
+ export const refreshObligation = (unrefreshedObligation, refreshedReserves, confidenceByCoinType) => {
480
+ const refresh = computeObligationRefresh(unrefreshedObligation, refreshedReserves, Date.now(), confidenceByCoinType);
472
481
  return {
473
482
  ...unrefreshedObligation,
474
483
  deposits: refresh.deposits,
@@ -495,18 +504,18 @@ export const decimalToBigNumber = (value) => {
495
504
  /**
496
505
  * Mirrors reserve.move's `price_lower_bound`/`price_upper_bound`: the one
497
506
  * off-chain place this formula lives. `low` and `high` are the ends of the
498
- * reserve's price range (`priceRange`), which the bounds widen by the share
499
- * `k * conf / spot`, where `spot` is the unclamped spot of the same update
500
- * `conf` came from. `conf` and `kBps` are the reserve's stored
501
- * Pyth confidence and its `ReserveConfig` confidence multiplier (in bps, e.g.
502
- * 30000 for a logical k=3) -- both plain human-scale BigNumbers, not
503
- * WAD-scaled Decimals. `kBps=0` or `conf=0` disables the margin entirely:
504
- * both bounds collapse to `low` and `high`.
507
+ * reserve's price range (`priceRange`), which the bounds widen by
508
+ * `k * share`. `share` is `conf / spot` from a single price read -- what the
509
+ * reserve stores in `ConfidenceKey` -- and `kBps` is its `ReserveConfig`
510
+ * confidence multiplier (in bps, e.g. 30000 for a logical k=3); both are
511
+ * plain human-scale BigNumbers, not WAD-scaled Decimals. `kBps=0` or
512
+ * `share=0` disables the margin entirely: both bounds collapse to `low` and
513
+ * `high`.
505
514
  */
506
- export const applyConfidenceMargin = (low, high, conf, kBps, spot) => {
507
- if (kBps.isZero() || conf.isZero())
515
+ export const applyConfidenceMargin = (low, high, share, kBps) => {
516
+ if (kBps.isZero() || share.isZero())
508
517
  return { lowerBound: low, upperBound: high };
509
- const fraction = kBps.dividedBy(10_000).multipliedBy(conf).dividedBy(spot);
518
+ const fraction = kBps.dividedBy(10_000).multipliedBy(share);
510
519
  const lowerBound = BigNumber.max(low.minus(low.multipliedBy(fraction)), new BigNumber(0));
511
520
  const upperBound = high.plus(high.multipliedBy(fraction));
512
521
  return { lowerBound, upperBound };
@@ -518,15 +527,14 @@ export const getCTokenMarketValue = (reserve, depositedCTokenAmount) => {
518
527
  .dividedBy(new BigNumber(10 ** reserve.mintDecimals));
519
528
  };
520
529
  export const getCTokenMarketValueLowerBound = (reserve, depositedCTokenAmount,
521
- // The reserve's stored Pyth confidence and its CURRENT confidence
530
+ // The reserve's stored conf / spot share and its CURRENT confidence
522
531
  // multiplier (in bps). Omit either (or both) for the plain
523
532
  // min(price, smoothedPrice) lower bound.
524
- conf, kBps) => {
533
+ share, kBps) => {
525
534
  const liquidityAmount = depositedCTokenAmount.multipliedBy(cTokenRatio(reserve));
526
535
  const { low, high } = priceRange(reserve);
527
- const lowerBoundPrice = conf !== undefined && kBps !== undefined
528
- ? applyConfidenceMargin(low, high, conf, kBps, unclampedSpot(reserve))
529
- .lowerBound
536
+ const lowerBoundPrice = share !== undefined && kBps !== undefined
537
+ ? applyConfidenceMargin(low, high, share, kBps).lowerBound
530
538
  : low;
531
539
  return lowerBoundPrice
532
540
  .multipliedBy(liquidityAmount)