@defisaver/positions-sdk 2.1.146 → 2.1.147-spark-ltv0-fallback-dev

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.
@@ -66,6 +66,49 @@ export const sparkGetEmodeMutableProps = ({
66
66
  return ({ liquidationRatio, collateralFactor });
67
67
  };
68
68
 
69
+ /**
70
+ * @description Offset subtracted from the liquidation threshold (LLTV) when crediting LTV-0 collateral
71
+ * in the safety-ratio fallback. Matches SparkView.getSafetyRatioWithLtvZeroFallback ('LLTV - 5%').
72
+ * Values are fractions (e.g. 0.8), so 5% === 0.05.
73
+ */
74
+ const LTV_ZERO_FALLBACK_LLTV_OFFSET = '0.05';
75
+
76
+ /**
77
+ * @description Per-asset effective LTV and liquidation threshold (LLTV) for the user, eMode-aware.
78
+ * Mirrors SparkRatioHelper._getUserAccountDataWithLtvZeroFallback: unlike Aave v3 (eMode ltv-zero
79
+ * bitmap), Spark's LTV-0 state lives on the RESERVE config (asset offboarding zeroes the reserve ltv),
80
+ * so a zeroed reserve keeps `ltv` 0 even inside the active eMode category, while its `lltv` stays the
81
+ * eMode liquidation threshold there (liquidations only consider LLTV). The returned `ltv` is identical
82
+ * to sparkGetEmodeMutableProps().collateralFactor whenever the reserve ltv is non-zero, so a ratio
83
+ * built on this matches the regular safety ratio whenever no collateral is LTV-0.
84
+ */
85
+ export const sparkGetUserReserveLtvAndLltv = (
86
+ {
87
+ eModeCategory,
88
+ eModeCategoriesData,
89
+ assetsData,
90
+ }: SparkHelperCommon,
91
+ _asset: string,
92
+ ): { ltv: string, lltv: string } => {
93
+ const asset = getNativeAssetFromWrapped(_asset);
94
+ const assetData = assetsData[asset];
95
+ const eModeCategoryData = eModeCategoriesData?.[eModeCategory];
96
+
97
+ if (
98
+ eModeCategory === 0
99
+ || !eModeCategoryData
100
+ || !eModeCategoryData.collateralAssets.includes(asset)
101
+ || new Dec(eModeCategoryData.collateralFactor || 0).eq(0)
102
+ ) {
103
+ return { ltv: assetData.collateralFactor, lltv: assetData.liquidationRatio };
104
+ }
105
+
106
+ // In the active eMode category the fallback trigger is still the reserve-level ltv (the contract
107
+ // checks the reserve config's ltv), so an offboarded asset must not pick up the category-wide eMode ltv.
108
+ const ltv = new Dec(assetData.collateralFactor || 0).eq(0) ? '0' : eModeCategoryData.collateralFactor;
109
+ return { ltv, lltv: eModeCategoryData.liquidationRatio };
110
+ };
111
+
69
112
  export const sparkGetAggregatedPositionData = ({
70
113
  usedAssets,
71
114
  eModeCategory,
@@ -96,6 +139,21 @@ export const sparkGetAggregatedPositionData = ({
96
139
  payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
97
140
  payload.ratio = +payload.suppliedUsd ? new Dec(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
98
141
  payload.collRatio = +payload.suppliedUsd ? new Dec(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
142
+ // Safety ratio as evaluated by the automation bots: LTV-0 collateral is credited at (LLTV - 5%)
143
+ // instead of 0 (SparkView.getSafetyRatioWithLtvZeroFallback). Equals `ratio` when no collateral
144
+ // is LTV-0. Computed off-chain here so it is available for after-value simulations too.
145
+ payload.borrowLimitWithLtvZeroFallbackUsd = getAssetsTotal(
146
+ usedAssets,
147
+ ({ isSupplied, collateral }: { isSupplied: boolean, collateral: boolean }) => isSupplied && collateral,
148
+ ({ symbol, suppliedUsd }: { symbol: string, suppliedUsd: string }) => {
149
+ const { ltv, lltv } = sparkGetUserReserveLtvAndLltv(data, symbol);
150
+ const effectiveLtv = new Dec(ltv).eq(0)
151
+ ? Dec.max(0, new Dec(lltv).sub(LTV_ZERO_FALLBACK_LLTV_OFFSET))
152
+ : new Dec(ltv);
153
+ return new Dec(suppliedUsd).mul(effectiveLtv);
154
+ },
155
+ );
156
+ payload.safetyRatioWithLtvZeroFallback = +payload.suppliedUsd ? new Dec(payload.borrowLimitWithLtvZeroFallbackUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
99
157
  const { netApy, incentiveUsd, totalInterestUsd } = calculateNetApy({ usedAssets, assetsData });
100
158
  payload.netApy = netApy;
101
159
  payload.incentiveUsd = incentiveUsd;
@@ -46,8 +46,9 @@ export const _getLiquityV2MarketData = async (provider: Client, network: Network
46
46
  const {
47
47
  marketAddress, debtToken, collateralToken, isLegacy,
48
48
  } = selectedMarket;
49
- const viewContract = getLiquityV2ViewContract(provider, network, isLegacy);
50
- const data = await viewContract.read.getMarketData([marketAddress]);
49
+ const data = isLegacy
50
+ ? await LiquityV2LegacyViewContractViem(provider, network).read.getMarketData([marketAddress])
51
+ : (await LiquityV2ViewContractViem(provider, network).simulate.getMarketData([marketAddress])).result;
51
52
  const hintHelperAddress = data.hintHelpers;
52
53
  const troveNFTAddress = data.troveNFT;
53
54
  const borrowerOperationsAddress = data.borrowerOperations;
@@ -381,11 +382,15 @@ export const _getLiquityV2TroveData = async (
381
382
  },
382
383
  fetchDebtInFront: boolean = true,
383
384
  ): Promise<LiquityV2TroveData> => {
384
- const viewContract = getLiquityV2ViewContract(provider, network, selectedMarket.isLegacy);
385
385
  const { minCollRatio, batchCollRatio } = allMarketsData[selectedMarket.value].marketData;
386
386
  const { collateralToken, marketAddress, debtToken } = selectedMarket;
387
+ const troveInfoPromise = selectedMarket.isLegacy
388
+ ? LiquityV2LegacyViewContractViem(provider, network).read.getTroveInfo([marketAddress, BigInt(troveId)])
389
+ : LiquityV2ViewContractViem(provider, network).simulate
390
+ .getTroveInfo([marketAddress, BigInt(troveId)])
391
+ .then(({ result }) => result);
387
392
  const [_data, debtInFront] = await Promise.all([
388
- viewContract.read.getTroveInfo([marketAddress, BigInt(troveId)]),
393
+ troveInfoPromise,
389
394
  fetchDebtInFront ? getDebtInFrontLiquityV2(allMarketsData, selectedMarket.value, provider, network, selectedMarket.isLegacy, troveId) : Promise.resolve('0'),
390
395
  ]);
391
396
  const data = {
@@ -99,6 +99,8 @@ export interface SparkAggregatedPositionData {
99
99
  leftToBorrowUsd: string,
100
100
  ratio: string,
101
101
  collRatio: string,
102
+ borrowLimitWithLtvZeroFallbackUsd: string,
103
+ safetyRatioWithLtvZeroFallback: string,
102
104
  netApy: string,
103
105
  incentiveUsd: string,
104
106
  totalInterestUsd: string,
@@ -119,6 +121,8 @@ export interface SparkPositionData extends MMPositionData {
119
121
  ratio: string,
120
122
  minRatio: string,
121
123
  collRatio: string,
124
+ // Safety ratio as evaluated by automation bots (LTV-0 collateral credited at LLTV - 5%).
125
+ safetyRatioWithLtvZeroFallback?: string,
122
126
  suppliedUsd: string,
123
127
  borrowedUsd: string,
124
128
  borrowLimitUsd: string,