@suilend/sdk 13.7.0-next.ge2ee95d → 13.8.0-next.gbe76414
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_generated/suilend/reserve-config/functions.d.ts +19 -0
- package/_generated/suilend/reserve-config/functions.js +23 -0
- package/client.d.ts +5 -2
- package/client.js +15 -5
- package/lib/initialize.d.ts +15 -2
- package/lib/initialize.js +42 -11
- package/package.json +1 -1
- package/parsers/apiReserveAssetDataEvent.js +2 -2
- package/parsers/lendingMarket.d.ts +2 -1
- package/parsers/lendingMarket.js +9 -2
- package/parsers/reserve.d.ts +2 -1
- package/parsers/reserve.js +5 -3
- package/utils/priceGuard.d.ts +6 -1
- package/utils/priceGuard.js +23 -5
- package/utils/simulate.d.ts +34 -14
- package/utils/simulate.js +47 -39
|
@@ -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
|
|
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
|
|
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
|
}
|
package/lib/initialize.d.ts
CHANGED
|
@@ -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
|
|
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.
|
|
1
|
+
{"name":"@suilend/sdk","version":"13.8.0-next.gbe76414","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,
|
|
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: {
|
package/parsers/lendingMarket.js
CHANGED
|
@@ -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
|
|
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;
|
package/parsers/reserve.d.ts
CHANGED
|
@@ -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>,
|
|
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;
|
package/parsers/reserve.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 } =
|
|
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);
|
package/utils/priceGuard.d.ts
CHANGED
|
@@ -6,7 +6,12 @@ export type PriceGuardState = {
|
|
|
6
6
|
anchorTimestampS: bigint;
|
|
7
7
|
};
|
|
8
8
|
export declare const clampToBand: (anchor: bigint, price: bigint, bandBps: bigint) => bigint;
|
|
9
|
-
export
|
|
9
|
+
export type StoredPrices = {
|
|
10
|
+
price: bigint;
|
|
11
|
+
smoothedPrice: bigint;
|
|
12
|
+
};
|
|
13
|
+
export declare const nextAnchor: (anchor: bigint, stored: bigint, incoming: bigint, bandBps: bigint) => bigint;
|
|
14
|
+
export declare const applyPriceGuard: (price: bigint, smoothedPrice: bigint, guard: PriceGuardState, nowS: bigint, stored: StoredPrices | null) => {
|
|
10
15
|
price: bigint;
|
|
11
16
|
smoothedPrice: bigint;
|
|
12
17
|
wasClamped: boolean;
|
package/utils/priceGuard.js
CHANGED
|
@@ -14,11 +14,29 @@ export const clampToBand = (anchor, price, bandBps) => {
|
|
|
14
14
|
const atLeastLower = price > lower ? price : lower;
|
|
15
15
|
return atLeastLower < upper ? atLeastLower : upper;
|
|
16
16
|
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
17
|
+
export const nextAnchor = (anchor, stored, incoming, bandBps) => {
|
|
18
|
+
if (anchor === 0n)
|
|
19
|
+
return incoming;
|
|
20
|
+
const carried = clampToBand(anchor, stored, bandBps);
|
|
21
|
+
return carried === 0n ? clampToBand(anchor, incoming, bandBps) : carried;
|
|
22
|
+
};
|
|
23
|
+
// Models one refresh at nowS. Once the guard's interval has elapsed, the chain opens the
|
|
24
|
+
// next one on the prices the reserve holds, so without `stored` this clamps against the
|
|
25
|
+
// read anchors instead and can land up to a band off. The chain also writes the anchors,
|
|
26
|
+
// which this does not, so callers must re-read the guard instead of chaining results.
|
|
27
|
+
export const applyPriceGuard = (price, smoothedPrice, guard, nowS, stored) => {
|
|
28
|
+
const band = guard.maxPriceChangeBps;
|
|
29
|
+
const opens = stored != null &&
|
|
30
|
+
band > 0n &&
|
|
31
|
+
nowS - guard.anchorTimestampS >= guard.minUpdateIntervalS;
|
|
32
|
+
const priceAnchor = opens
|
|
33
|
+
? nextAnchor(guard.priceAnchor, stored.price, price, band)
|
|
34
|
+
: guard.priceAnchor;
|
|
35
|
+
const smoothedPriceAnchor = opens
|
|
36
|
+
? nextAnchor(guard.smoothedPriceAnchor, stored.smoothedPrice, smoothedPrice, band)
|
|
37
|
+
: guard.smoothedPriceAnchor;
|
|
38
|
+
const clampedPrice = clampToBand(priceAnchor, price, band);
|
|
39
|
+
const clampedSmoothedPrice = clampToBand(smoothedPriceAnchor, smoothedPrice, band);
|
|
22
40
|
return {
|
|
23
41
|
price: clampedPrice,
|
|
24
42
|
smoothedPrice: clampedSmoothedPrice,
|
package/utils/simulate.d.ts
CHANGED
|
@@ -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?:
|
|
159
|
-
|
|
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
|
|
170
|
-
* `k * conf / spot
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
|
365
|
-
: `No conf for reserve ${reserve.coinType}: refresh it from a feed that carries conf, or supply
|
|
366
|
-
return {
|
|
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. `
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
//
|
|
384
|
-
//
|
|
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?.
|
|
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.
|
|
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
|
|
499
|
-
* `k * conf / spot
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
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,
|
|
507
|
-
if (kBps.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(
|
|
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
|
|
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
|
-
|
|
533
|
+
share, kBps) => {
|
|
525
534
|
const liquidityAmount = depositedCTokenAmount.multipliedBy(cTokenRatio(reserve));
|
|
526
535
|
const { low, high } = priceRange(reserve);
|
|
527
|
-
const lowerBoundPrice =
|
|
528
|
-
? applyConfidenceMargin(low, high,
|
|
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)
|