@suilend/sdk 13.7.0-next.g7a9fc12 → 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.
@@ -651,7 +651,7 @@ export type ReserveAssetDataEventJSON = {
651
651
  /**
652
652
  * Deprecated: this struct's layout is frozen by Sui's upgrade-compatibility check, since
653
653
  * it is already published on mainnet. Use `ReserveAssetDataEventV2` instead, which carries
654
- * every field here plus `conf`.
654
+ * every field here plus `conf` and the unclamped prices.
655
655
  */
656
656
  export declare class ReserveAssetDataEvent implements StructClass {
657
657
  __StructClass: true;
@@ -717,6 +717,8 @@ export interface ReserveAssetDataEventV2Fields {
717
717
  smoothedPrice: ToField<Decimal>;
718
718
  priceLastUpdateTimestampS: ToField<'u64'>;
719
719
  conf: ToField<Decimal>;
720
+ unclampedPrice: ToField<Decimal>;
721
+ unclampedSmoothedPrice: ToField<Decimal>;
720
722
  }
721
723
  export type ReserveAssetDataEventV2Reified = Reified<ReserveAssetDataEventV2, ReserveAssetDataEventV2Fields>;
722
724
  export type ReserveAssetDataEventV2JSONField = {
@@ -737,6 +739,8 @@ export type ReserveAssetDataEventV2JSONField = {
737
739
  smoothedPrice: ToJSON<Decimal>;
738
740
  priceLastUpdateTimestampS: string;
739
741
  conf: ToJSON<Decimal>;
742
+ unclampedPrice: ToJSON<Decimal>;
743
+ unclampedSmoothedPrice: ToJSON<Decimal>;
740
744
  };
741
745
  export type ReserveAssetDataEventV2JSON = {
742
746
  $typeName: typeof ReserveAssetDataEventV2.$typeName;
@@ -768,6 +772,8 @@ export declare class ReserveAssetDataEventV2 implements StructClass {
768
772
  readonly smoothedPrice: ToField<Decimal>;
769
773
  readonly priceLastUpdateTimestampS: ToField<'u64'>;
770
774
  readonly conf: ToField<Decimal>;
775
+ readonly unclampedPrice: ToField<Decimal>;
776
+ readonly unclampedSmoothedPrice: ToField<Decimal>;
771
777
  private constructor();
772
778
  static reified(): ReserveAssetDataEventV2Reified;
773
779
  static get r(): ReserveAssetDataEventV2Reified;
@@ -1757,7 +1757,7 @@ export function isReserveAssetDataEvent(type) {
1757
1757
  /**
1758
1758
  * Deprecated: this struct's layout is frozen by Sui's upgrade-compatibility check, since
1759
1759
  * it is already published on mainnet. Use `ReserveAssetDataEventV2` instead, which carries
1760
- * every field here plus `conf`.
1760
+ * every field here plus `conf` and the unclamped prices.
1761
1761
  */
1762
1762
  export class ReserveAssetDataEvent {
1763
1763
  __StructClass = true;
@@ -2020,6 +2020,8 @@ export class ReserveAssetDataEventV2 {
2020
2020
  smoothedPrice;
2021
2021
  priceLastUpdateTimestampS;
2022
2022
  conf;
2023
+ unclampedPrice;
2024
+ unclampedSmoothedPrice;
2023
2025
  constructor(typeArgs, fields) {
2024
2026
  this.$fullTypeName = composeSuiType(ReserveAssetDataEventV2.$typeName, ...typeArgs);
2025
2027
  this.$typeArgs = typeArgs;
@@ -2040,6 +2042,8 @@ export class ReserveAssetDataEventV2 {
2040
2042
  this.smoothedPrice = fields.smoothedPrice;
2041
2043
  this.priceLastUpdateTimestampS = fields.priceLastUpdateTimestampS;
2042
2044
  this.conf = fields.conf;
2045
+ this.unclampedPrice = fields.unclampedPrice;
2046
+ this.unclampedSmoothedPrice = fields.unclampedSmoothedPrice;
2043
2047
  }
2044
2048
  static reified() {
2045
2049
  const reifiedBcs = ReserveAssetDataEventV2.bcs;
@@ -2101,6 +2105,8 @@ export class ReserveAssetDataEventV2 {
2101
2105
  smoothed_price: Decimal.bcs,
2102
2106
  price_last_update_timestamp_s: bcs.u64(),
2103
2107
  conf: Decimal.bcs,
2108
+ unclamped_price: Decimal.bcs,
2109
+ unclamped_smoothed_price: Decimal.bcs,
2104
2110
  });
2105
2111
  }
2106
2112
  static cachedBcs = null;
@@ -2129,6 +2135,8 @@ export class ReserveAssetDataEventV2 {
2129
2135
  smoothedPrice: decodeFromFields(Decimal.reified(), fields.smoothed_price),
2130
2136
  priceLastUpdateTimestampS: decodeFromFields('u64', fields.price_last_update_timestamp_s),
2131
2137
  conf: decodeFromFields(Decimal.reified(), fields.conf),
2138
+ unclampedPrice: decodeFromFields(Decimal.reified(), fields.unclamped_price),
2139
+ unclampedSmoothedPrice: decodeFromFields(Decimal.reified(), fields.unclamped_smoothed_price),
2132
2140
  });
2133
2141
  }
2134
2142
  static fromFieldsWithTypes(item) {
@@ -2153,6 +2161,8 @@ export class ReserveAssetDataEventV2 {
2153
2161
  smoothedPrice: decodeFromFieldsWithTypes(Decimal.reified(), item.fields.smoothed_price),
2154
2162
  priceLastUpdateTimestampS: decodeFromFieldsWithTypes('u64', item.fields.price_last_update_timestamp_s),
2155
2163
  conf: decodeFromFieldsWithTypes(Decimal.reified(), item.fields.conf),
2164
+ unclampedPrice: decodeFromFieldsWithTypes(Decimal.reified(), item.fields.unclamped_price),
2165
+ unclampedSmoothedPrice: decodeFromFieldsWithTypes(Decimal.reified(), item.fields.unclamped_smoothed_price),
2156
2166
  });
2157
2167
  }
2158
2168
  static fromBcs(data) {
@@ -2177,6 +2187,8 @@ export class ReserveAssetDataEventV2 {
2177
2187
  smoothedPrice: this.smoothedPrice.toJSONField(),
2178
2188
  priceLastUpdateTimestampS: this.priceLastUpdateTimestampS.toString(),
2179
2189
  conf: this.conf.toJSONField(),
2190
+ unclampedPrice: this.unclampedPrice.toJSONField(),
2191
+ unclampedSmoothedPrice: this.unclampedSmoothedPrice.toJSONField(),
2180
2192
  };
2181
2193
  }
2182
2194
  toJSON() {
@@ -2201,6 +2213,8 @@ export class ReserveAssetDataEventV2 {
2201
2213
  smoothedPrice: decodeFromJSONField(Decimal.reified(), field.smoothedPrice),
2202
2214
  priceLastUpdateTimestampS: decodeFromJSONField('u64', field.priceLastUpdateTimestampS),
2203
2215
  conf: decodeFromJSONField(Decimal.reified(), field.conf),
2216
+ unclampedPrice: decodeFromJSONField(Decimal.reified(), field.unclampedPrice),
2217
+ unclampedSmoothedPrice: decodeFromJSONField(Decimal.reified(), field.unclampedSmoothedPrice),
2204
2218
  });
2205
2219
  }
2206
2220
  static fromJSON(json) {
@@ -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
@@ -3,6 +3,8 @@ import { SuiGrpcClient } from "@mysten/sui/grpc";
3
3
  import { Transaction, TransactionArgument, TransactionObjectArgument, TransactionObjectInput } from "@mysten/sui/transactions";
4
4
  import { SuiPythClient } from "@pythnetwork/pyth-sui-js";
5
5
  import BigNumber from "bignumber.js";
6
+ import { Decimal } from "./_generated/suilend/decimal/structs";
7
+ import { type ConfidenceShare } from "./utils/simulate";
6
8
  import { LendingMarket, ObligationOwnerCap } from "./_generated/suilend/lending-market/structs";
7
9
  import { Obligation } from "./_generated/suilend/obligation/structs";
8
10
  import { NewConfigArgs as CreateRateLimiterConfigArgs } from "./_generated/suilend/rate-limiter/functions";
@@ -54,6 +56,8 @@ export type ClaimRewardsReward = {
54
56
  };
55
57
  export type PatchReserveConfigArgs = Partial<CreateReserveConfigArgs> & {
56
58
  confidenceMultiplierBps?: bigint | TransactionArgument;
59
+ minConfidenceRatio?: bigint | TransactionArgument;
60
+ maxStalenessSeconds?: bigint | TransactionArgument;
57
61
  };
58
62
  export type LendingMarketObject = {
59
63
  objectId: string;
@@ -102,8 +106,13 @@ export declare class SuilendClient {
102
106
  }>;
103
107
  /** A reserve's confidence multiplier `k`, in bps; pass `parsedReserve.config.additionalFields.id`. 0 when unset. */
104
108
  static getReserveConfidenceMultiplierBps(suiGrpcClient: SuiGrpcClient, additionalFieldsBagId: string): Promise<BigNumber>;
105
- /** 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. */
106
- 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>;
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. */
112
+ static getReserveRawPrices(suiGrpcClient: SuiGrpcClient, reserveId: string): Promise<{
113
+ price: Decimal;
114
+ smoothedPrice: Decimal;
115
+ } | undefined>;
107
116
  static createNewLendingMarket(registryId: string, lendingMarketType: string, transaction: Transaction): {
108
117
  NestedResult: [number, number];
109
118
  $kind: "NestedResult";
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,17 +467,32 @@ 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,
478
478
  });
479
479
  return decimalToBigNumber(Decimal.fromBcs(dynamicField.value.bcs));
480
480
  }
481
+ /** 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. */
482
+ static async getReserveRawPrices(suiGrpcClient, reserveId) {
483
+ const match = await findBagEntryByTypeSuffix(suiGrpcClient, reserveId, "::reserve::RawPriceKey");
484
+ if (!match)
485
+ return undefined;
486
+ const { dynamicField } = await suiGrpcClient.getDynamicField({
487
+ parentId: reserveId,
488
+ name: match.name,
489
+ });
490
+ const bytes = dynamicField.value.bcs;
491
+ return {
492
+ price: Decimal.fromBcs(bytes.slice(0, 32)),
493
+ smoothedPrice: Decimal.fromBcs(bytes.slice(32, 64)),
494
+ };
495
+ }
481
496
  static createNewLendingMarket(registryId, lendingMarketType, transaction) {
482
497
  const [ownerCap, lendingMarket] = createLendingMarket(transaction, lendingMarketType, transaction.object(registryId));
483
498
  transaction.moveCall({
@@ -679,6 +694,16 @@ export class SuilendClient {
679
694
  builder,
680
695
  confidenceMultiplierBps: overrides.confidenceMultiplierBps,
681
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
+ });
682
707
  const [config] = buildReserveConfig(transaction, builder);
683
708
  return this.submitReserveConfigUpdate(lendingMarketOwnerCapId, transaction, coinType, config);
684
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/lib/types.d.ts CHANGED
@@ -51,6 +51,8 @@ export type ApiReserveAssetDataEvent = {
51
51
  smoothedPrice: string;
52
52
  priceLastUpdateTimestampS: number;
53
53
  conf: string | null;
54
+ unclampedPrice: string | null;
55
+ unclampedSmoothedPrice: string | null;
54
56
  timestamp: number;
55
57
  digest: string;
56
58
  eventIndex: number;
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@suilend/sdk","version":"13.7.0-next.g7a9fc12","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);
@@ -30,7 +30,14 @@ kBps = ZERO_K_BPS) => {
30
30
  // Null conf (a pre-upgrade event) has no margin to apply -- 0 reproduces
31
31
  // plain min/max exactly, same as kBps=0 does.
32
32
  const conf = event.conf ? new BigNumber(event.conf).div(WAD) : new BigNumber(0);
33
- const { lowerBound: minPrice, upperBound: maxPrice } = applyConfidenceMargin(price, smoothedPrice, conf, kBps);
33
+ const unclampedPrice = event.unclampedPrice
34
+ ? new BigNumber(event.unclampedPrice).div(WAD)
35
+ : price;
36
+ const unclampedSmoothedPrice = event.unclampedSmoothedPrice
37
+ ? new BigNumber(event.unclampedSmoothedPrice).div(WAD)
38
+ : smoothedPrice;
39
+ const rangePrices = [price, smoothedPrice, unclampedPrice, unclampedSmoothedPrice];
40
+ const { lowerBound: minPrice, upperBound: maxPrice } = applyConfidenceMargin(BigNumber.min(...rangePrices), BigNumber.max(...rangePrices), confidenceShare(conf, unclampedPrice), kBps);
34
41
  // Custom
35
42
  const utilizationPercent = depositedAmount.eq(0)
36
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,13 +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
- // plain min/max(price, smoothedPrice) bounds.
14
- conf, kBps) => {
13
+ // plain bounds. The raw prices of a guarded reserve come attached with
14
+ // simulate.withUnclampedPrices.
15
+ share, kBps) => {
15
16
  const config = parseReserveConfig(reserve);
16
17
  const $typeName = reserve.$typeName;
17
18
  const id = reserve.id;
@@ -22,11 +23,10 @@ conf, kBps) => {
22
23
  const priceIdentifier = `0x${toHexString(reserve.priceIdentifier.bytes)}`;
23
24
  const price = new BigNumber(reserve.price.value.toString()).div(WAD);
24
25
  const smoothedPrice = new BigNumber(reserve.smoothedPrice.value.toString()).div(WAD);
25
- const bounds = conf !== undefined && kBps !== undefined
26
- ? simulate.applyConfidenceMargin(price, smoothedPrice, conf, kBps)
27
- : { lowerBound: BigNumber.min(price, smoothedPrice), upperBound: BigNumber.max(price, smoothedPrice) };
28
- const minPrice = bounds.lowerBound;
29
- const maxPrice = bounds.upperBound;
26
+ const { low, high } = simulate.priceRange(reserve);
27
+ const { lowerBound: minPrice, upperBound: maxPrice } = share !== undefined && kBps !== undefined
28
+ ? simulate.applyConfidenceMargin(low, high, share, kBps)
29
+ : { lowerBound: low, upperBound: high };
30
30
  const priceLastUpdateTimestampS = reserve.priceLastUpdateTimestampS;
31
31
  const availableAmount = new BigNumber(reserve.availableAmount.toString()).div(10 ** mintDecimals);
32
32
  const ctokenSupply = new BigNumber(reserve.ctokenSupply.toString()).div(10 ** mintDecimals);
package/utils/events.d.ts CHANGED
@@ -88,6 +88,8 @@ export declare class ReserveAssetDataEventV2 extends TypedParamsSuiEvent<{
88
88
  smoothed_price: u256;
89
89
  price_last_update_timestamp_s: u64;
90
90
  conf: u256;
91
+ unclamped_price: u256;
92
+ unclamped_smoothed_price: u256;
91
93
  }> {
92
94
  }
93
95
  export declare class MintEvent extends TypedParamsSuiEvent<{
@@ -40,6 +40,13 @@ export interface FeedPrice {
40
40
  }
41
41
  export declare const feedConfidenceOf: (reserve: Reserve<string>) => BigNumber | undefined;
42
42
  export declare const withUnclampedPrices: (reserve: Reserve<string>, price: Decimal, smoothedPrice: Decimal) => Reserve<string>;
43
+ /** Mirrors reserve.move's `price_range`. */
44
+ export declare const priceRange: (reserve: Reserve<string>) => {
45
+ low: BigNumber;
46
+ high: BigNumber;
47
+ };
48
+ /** Mirrors reserve.move's `unclamped_price`: the spot a reserve's conf was quoted with. */
49
+ export declare const unclampedSpot: (reserve: Reserve<string>) => BigNumber;
43
50
  /**
44
51
  * A source of latest prices. Structural on purpose: any object with this method
45
52
  * qualifies, so a caller can inject its own client (authenticated, failover,
@@ -139,6 +146,29 @@ export type ObligationRefresh = {
139
146
  weightedBorrowedValueUsd: BigNumber;
140
147
  weightedBorrowedValueUpperBoundUsd: BigNumber;
141
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;
142
172
  /**
143
173
  * Pure computation half of `refreshObligation`: same math, but returns fresh
144
174
  * deposits/borrows/reward-manager arrays and the five USD aggregates instead
@@ -148,28 +178,28 @@ export type ObligationRefresh = {
148
178
  * shallow-copied), silently corrupting `unrefreshedObligation` for anyone still
149
179
  * holding a reference to it.
150
180
  */
151
- export declare function computeObligationRefresh(obligation: Obligation<string>, refreshedReserves: Reserve<string>[], nowMs: number, confidenceByCoinType?: Map<string, {
152
- conf?: BigNumber;
153
- kBps: BigNumber;
154
- }>): ObligationRefresh;
155
- 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>;
156
183
  export declare const numberToDecimal: (value: number) => Decimal;
157
184
  export declare const stringToDecimal: (value: string) => Decimal;
158
185
  export declare const decimalToBigNumber: (value: Decimal) => BigNumber;
159
186
  /**
160
187
  * Mirrors reserve.move's `price_lower_bound`/`price_upper_bound`: the one
161
- * off-chain place this formula lives. `conf` and `kBps` are the reserve's
162
- * stored Pyth confidence and its `ReserveConfig` confidence multiplier (in
163
- * bps, e.g. 30000 for a logical k=3) -- both plain human-scale BigNumbers,
164
- * not WAD-scaled Decimals. `kBps=0` disables the margin entirely: both
165
- * bounds collapse to plain `min`/`max(price, smoothedPrice)`.
188
+ * off-chain place this formula lives. `low` and `high` are the ends of the
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`.
166
196
  */
167
- export declare const applyConfidenceMargin: (price: BigNumber, smoothedPrice: BigNumber, conf: BigNumber, kBps: BigNumber) => {
197
+ export declare const applyConfidenceMargin: (low: BigNumber, high: BigNumber, share: ConfidenceShare, kBps: BigNumber) => {
168
198
  lowerBound: BigNumber;
169
199
  upperBound: BigNumber;
170
200
  };
171
201
  export declare const getCTokenMarketValue: (reserve: Reserve<string>, depositedCTokenAmount: BigNumber) => BigNumber;
172
- 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;
173
203
  export declare const cTokenRatio: (reserve: Reserve<string>) => BigNumber;
174
204
  export declare const totalSupply: (reserve: Reserve<string>) => BigNumber;
175
205
  export declare const compoundBorrowedAmount: (snapshotAmount: BigNumber, currentCumulativeBorrowRate: BigNumber, snapshotCumulativeBorrowRate: BigNumber) => BigNumber;
package/utils/simulate.js CHANGED
@@ -124,7 +124,7 @@ export const withUnclampedPrices = (reserve, price, smoothedPrice) => Object.ass
124
124
  unclampedPrices: { price, smoothedPrice },
125
125
  });
126
126
  /** Mirrors reserve.move's `price_range`. */
127
- const priceRange = (reserve) => {
127
+ export const priceRange = (reserve) => {
128
128
  const unclamped = reserve.unclampedPrices;
129
129
  const prices = [
130
130
  reserve.price,
@@ -133,6 +133,9 @@ const priceRange = (reserve) => {
133
133
  ].map(decimalToBigNumber);
134
134
  return { low: BigNumber.min(...prices), high: BigNumber.max(...prices) };
135
135
  };
136
+ /** Mirrors reserve.move's `unclamped_price`: the spot a reserve's conf was quoted with. */
137
+ export const unclampedSpot = (reserve) => decimalToBigNumber(reserve.unclampedPrices?.price ??
138
+ reserve.price);
136
139
  // Re-exported, not redefined: it was part of this module's public surface before
137
140
  // it moved to ./feedId, and every consumer that normalizes a feed id to compare
138
141
  // it against `unpricedCoinTypes` imports it from here.
@@ -349,18 +352,29 @@ export const findReserveOrThrow = (reserves, coinType) => {
349
352
  // (`v * WAD / 10_000`); dividing in BigNumber here matches that.
350
353
  export const computeBorrowWeight = (config) => new BigNumber(config.borrowWeightBps.toString()).div(10000);
351
354
  export const computeCloseLtv = (config) => new BigNumber(config.closeLtvPct / 100);
352
- 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) => {
353
364
  if (entry === undefined)
354
365
  return undefined;
355
- const refreshedFromFeed = "feedConfidence" in reserve;
356
- const conf = feedConfidenceOf(reserve) ??
357
- (refreshedFromFeed ? undefined : entry.conf) ??
358
- (entry.kBps.isZero() ? new BigNumber(0) : undefined);
359
- 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)
360
374
  throw new Error(refreshedFromFeed
361
- ? `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`
362
- : `No conf for reserve ${reserve.coinType}: refresh it from a feed that carries conf, or supply conf in the entry`);
363
- 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 };
364
378
  };
365
379
  /**
366
380
  * Pure computation half of `refreshObligation`: same math, but returns fresh
@@ -373,18 +387,15 @@ const resolveConfidence = (entry, reserve) => {
373
387
  */
374
388
  export function computeObligationRefresh(obligation, refreshedReserves, nowMs,
375
389
  // Per-reserve CURRENT confidence multiplier, keyed by coinType in either
376
- // raw or normalized form. `conf` is the fallback for a reserve that was never
377
- // price-refreshed; a reserve refreshed from a feed without `conf` throws
378
- // instead (unless kBps is 0), because its price and the stored conf come from
379
- // different updates. A reserve's own `feedConfidence` takes precedence because
380
- // it comes from the same update as its price. A reserve with no entry here
381
- // 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.
382
397
  confidenceByCoinType) {
383
- const normalizedConfidence = confidenceByCoinType &&
384
- new Map([...confidenceByCoinType].map(([coinType, entry]) => [
385
- normalizeStructTag(coinType),
386
- entry,
387
- ]));
398
+ const normalizedConfidence = confidenceByCoinType && normalizeConfidenceByCoinType(confidenceByCoinType);
388
399
  const userRewardManagers = [...obligation.userRewardManagers];
389
400
  let depositValueUsd = new BigNumber(0);
390
401
  let allowedBorrowValueUsd = new BigNumber(0);
@@ -397,7 +408,7 @@ confidenceByCoinType) {
397
408
  updateUserRewardManager(reserve.depositsPoolRewardManager, userRewardManagers[Number(deposit.userRewardManagerIndex)], nowMs);
398
409
  const confidence = resolveConfidence(normalizedConfidence?.get(normalizeStructTag(deposit.coinType)), reserve);
399
410
  const marketValue = getCTokenMarketValue(reserve, new BigNumber(deposit.depositedCtokenAmount.toString()));
400
- 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);
401
412
  deposit.marketValue = stringToDecimal(marketValue.toString());
402
413
  depositValueUsd = depositValueUsd.plus(new BigNumber(marketValue.toString()));
403
414
  allowedBorrowValueUsd = allowedBorrowValueUsd.plus(marketValueLowerBound.multipliedBy(new BigNumber(config.openLtvPct / 100)));
@@ -432,7 +443,7 @@ confidenceByCoinType) {
432
443
  const confidence = resolveConfidence(normalizedConfidence?.get(normalizeStructTag(unrefreshedBorrow.coinType)), reserve);
433
444
  const { low, high } = priceRange(reserve);
434
445
  const upperBoundPrice = confidence !== undefined
435
- ? applyConfidenceMargin(low, high, confidence.conf, confidence.kBps)
446
+ ? applyConfidenceMargin(low, high, confidence.share, confidence.kBps)
436
447
  .upperBound
437
448
  : high;
438
449
  // `exactBorrowedAmount` (this borrow's own amount), not
@@ -465,8 +476,8 @@ confidenceByCoinType) {
465
476
  weightedBorrowedValueUpperBoundUsd,
466
477
  };
467
478
  }
468
- export const refreshObligation = (unrefreshedObligation, refreshedReserves) => {
469
- const refresh = computeObligationRefresh(unrefreshedObligation, refreshedReserves, Date.now());
479
+ export const refreshObligation = (unrefreshedObligation, refreshedReserves, confidenceByCoinType) => {
480
+ const refresh = computeObligationRefresh(unrefreshedObligation, refreshedReserves, Date.now(), confidenceByCoinType);
470
481
  return {
471
482
  ...unrefreshedObligation,
472
483
  deposits: refresh.deposits,
@@ -492,16 +503,21 @@ export const decimalToBigNumber = (value) => {
492
503
  };
493
504
  /**
494
505
  * Mirrors reserve.move's `price_lower_bound`/`price_upper_bound`: the one
495
- * off-chain place this formula lives. `conf` and `kBps` are the reserve's
496
- * stored Pyth confidence and its `ReserveConfig` confidence multiplier (in
497
- * bps, e.g. 30000 for a logical k=3) -- both plain human-scale BigNumbers,
498
- * not WAD-scaled Decimals. `kBps=0` disables the margin entirely: both
499
- * bounds collapse to plain `min`/`max(price, smoothedPrice)`.
506
+ * off-chain place this formula lives. `low` and `high` are the ends of the
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`.
500
514
  */
501
- export const applyConfidenceMargin = (price, smoothedPrice, conf, kBps) => {
502
- const margin = kBps.dividedBy(10_000).multipliedBy(conf);
503
- const lowerBound = BigNumber.max(BigNumber.min(price, smoothedPrice).minus(margin), new BigNumber(0));
504
- const upperBound = BigNumber.max(price, smoothedPrice).plus(margin);
515
+ export const applyConfidenceMargin = (low, high, share, kBps) => {
516
+ if (kBps.isZero() || share.isZero())
517
+ return { lowerBound: low, upperBound: high };
518
+ const fraction = kBps.dividedBy(10_000).multipliedBy(share);
519
+ const lowerBound = BigNumber.max(low.minus(low.multipliedBy(fraction)), new BigNumber(0));
520
+ const upperBound = high.plus(high.multipliedBy(fraction));
505
521
  return { lowerBound, upperBound };
506
522
  };
507
523
  export const getCTokenMarketValue = (reserve, depositedCTokenAmount) => {
@@ -511,14 +527,14 @@ export const getCTokenMarketValue = (reserve, depositedCTokenAmount) => {
511
527
  .dividedBy(new BigNumber(10 ** reserve.mintDecimals));
512
528
  };
513
529
  export const getCTokenMarketValueLowerBound = (reserve, depositedCTokenAmount,
514
- // The reserve's stored Pyth confidence and its CURRENT confidence
530
+ // The reserve's stored conf / spot share and its CURRENT confidence
515
531
  // multiplier (in bps). Omit either (or both) for the plain
516
532
  // min(price, smoothedPrice) lower bound.
517
- conf, kBps) => {
533
+ share, kBps) => {
518
534
  const liquidityAmount = depositedCTokenAmount.multipliedBy(cTokenRatio(reserve));
519
535
  const { low, high } = priceRange(reserve);
520
- const lowerBoundPrice = conf !== undefined && kBps !== undefined
521
- ? applyConfidenceMargin(low, high, conf, kBps).lowerBound
536
+ const lowerBoundPrice = share !== undefined && kBps !== undefined
537
+ ? applyConfidenceMargin(low, high, share, kBps).lowerBound
522
538
  : low;
523
539
  return lowerBoundPrice
524
540
  .multipliedBy(liquidityAmount)