@evaafi/sdk 0.9.5 → 0.9.7

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.
Files changed (33) hide show
  1. package/dist/api/math.d.ts +16 -3
  2. package/dist/api/math.js +356 -33
  3. package/dist/api/parser.js +34 -12
  4. package/dist/constants/assets/assetId.d.ts +2 -0
  5. package/dist/constants/assets/assetId.js +3 -0
  6. package/dist/constants/assets/mainnet.d.ts +2 -0
  7. package/dist/constants/assets/mainnet.js +13 -1
  8. package/dist/constants/general/mainnet.d.ts +2 -0
  9. package/dist/constants/general/mainnet.js +4 -1
  10. package/dist/constants/pools/mainnet.d.ts +2 -0
  11. package/dist/constants/pools/mainnet.js +45 -1
  12. package/dist/constants/pools/testnet.d.ts +3 -2
  13. package/dist/constants/pools/testnet.js +16 -1
  14. package/dist/oracles/collectors/ClassicCollector.js +13 -18
  15. package/dist/oracles/collectors/FakeCollector.d.ts +30 -0
  16. package/dist/oracles/collectors/FakeCollector.js +128 -0
  17. package/dist/oracles/collectors/index.d.ts +1 -0
  18. package/dist/oracles/collectors/index.js +1 -0
  19. package/dist/types/Master.d.ts +8 -2
  20. package/dist/types/User.d.ts +7 -3
  21. package/package.json +1 -1
  22. package/src/api/math.ts +504 -40
  23. package/src/api/parser.ts +57 -12
  24. package/src/constants/assets/assetId.ts +4 -0
  25. package/src/constants/assets/mainnet.ts +24 -0
  26. package/src/constants/general/mainnet.ts +4 -0
  27. package/src/constants/pools/mainnet.ts +53 -1
  28. package/src/constants/pools/testnet.ts +30 -5
  29. package/src/oracles/collectors/ClassicCollector.ts +10 -14
  30. package/src/oracles/collectors/FakeCollector.ts +153 -0
  31. package/src/oracles/collectors/index.ts +1 -0
  32. package/src/types/Master.ts +9 -2
  33. package/src/types/User.ts +7 -3
@@ -1,6 +1,6 @@
1
1
  import { Dictionary } from '@ton/core';
2
- import { AgregatedBalances, AssetApy, AssetConfig, AssetData, AssetInterest, ExtendedAssetData, ExtendedAssetsConfig, ExtendedAssetsData, MasterConstants, PoolConfig } from '../types/Master';
3
- import { HealthParamsArgs, LiquidationData, PredictAPYArgs, PredictHealthFactorArgs, UserBalance } from '../types/User';
2
+ import type { AgregatedBalances, AssetApy, AssetConfig, AssetData, AssetInterest, ExtendedAssetData, ExtendedAssetsConfig, ExtendedAssetsData, MasterConstants, PoolConfig } from '../types/Master';
3
+ import { type HealthParamsArgs, type LiquidationData, type PredictAPYArgs, type PredictHealthFactorArgs, type UserBalance } from '../types/User';
4
4
  export declare function mulFactor(decimal: bigint, a: bigint, b: bigint): bigint;
5
5
  export declare function mulDiv(x: bigint, y: bigint, z: bigint): bigint;
6
6
  export declare function mulDivC(x: bigint, y: bigint, z: bigint): bigint;
@@ -28,8 +28,20 @@ export declare function calculateAssetData(assetsConfigDict: ExtendedAssetsConfi
28
28
  export declare function calculateAssetInterest(assetConfig: AssetConfig, assetData: AssetData, masterConstants: MasterConstants): AssetInterest;
29
29
  export declare function calculateInterestWithSupplyBorrow(totalSupply: bigint, totalBorrow: bigint, assetConfig: AssetConfig, masterConstants: MasterConstants): AssetInterest;
30
30
  export declare function checkNotInDebtAtAll(principals: Dictionary<bigint, bigint>): boolean;
31
+ export declare function exceedsStandardBorrowLimit(principals: Dictionary<bigint, bigint>, assetsConfig: ExtendedAssetsConfig, assetsData: ExtendedAssetsData, prices: Dictionary<bigint, bigint>, masterConstants: MasterConstants): boolean;
32
+ export declare function determineHeCategory(assetsConfig: ExtendedAssetsConfig, principals: Dictionary<bigint, bigint>, poolConfig?: PoolConfig): number;
33
+ export declare function calculateRepayToExitEMode(assetsConfig: ExtendedAssetsConfig, assetsData: ExtendedAssetsData, principals: Dictionary<bigint, bigint>, prices: Dictionary<bigint, bigint>, poolConfig: PoolConfig): {
34
+ activeHeCategory: number;
35
+ requiredRepayInUsd: bigint;
36
+ repayAmounts: Dictionary<bigint, bigint>;
37
+ enoughPriceData: boolean;
38
+ };
39
+ export declare function getAvailableToBorrowWithEMode(assetsConfig: ExtendedAssetsConfig, assetsData: ExtendedAssetsData, principals: Dictionary<bigint, bigint>, prices: Dictionary<bigint, bigint>, masterConstants: MasterConstants, poolConfig?: PoolConfig): {
40
+ availableToBorrow: bigint;
41
+ heCategory: number;
42
+ };
31
43
  export declare function getAgregatedBalances(assetsData: ExtendedAssetsData, assetsConfig: ExtendedAssetsConfig, principals: Dictionary<bigint, bigint>, prices: Dictionary<bigint, bigint>, masterConstants: MasterConstants): AgregatedBalances;
32
- export declare function calculateMaximumWithdrawAmount(assetsConfig: ExtendedAssetsConfig, assetsData: ExtendedAssetsData, principals: Dictionary<bigint, bigint>, prices: Dictionary<bigint, bigint>, masterConstants: MasterConstants, assetId: bigint): bigint;
44
+ export declare function calculateMaximumWithdrawAmount(assetsConfig: ExtendedAssetsConfig, assetsData: ExtendedAssetsData, principals: Dictionary<bigint, bigint>, prices: Dictionary<bigint, bigint>, poolConfig: PoolConfig, assetId: bigint): bigint;
33
45
  export declare function getAvailableToBorrow(assetsConfig: ExtendedAssetsConfig, assetsData: ExtendedAssetsData, principals: Dictionary<bigint, bigint>, prices: Dictionary<bigint, bigint>, masterConstants: MasterConstants): bigint;
34
46
  /**
35
47
  * Calculates balance value for asset principal.
@@ -47,6 +59,7 @@ export declare function calculateHealthParams(parameters: HealthParamsArgs): {
47
59
  totalDebt: bigint;
48
60
  totalLimit: bigint;
49
61
  totalSupply: bigint;
62
+ activeHeCategory: number;
50
63
  isLiquidatable: boolean;
51
64
  isBadDebt: (liquidationBonus: bigint) => boolean;
52
65
  };
package/dist/api/math.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.predictAPY = exports.predictHealthFactor = exports.calculateLiquidationData = exports.calculateHealthParams = exports.presentValue = exports.getAvailableToBorrow = exports.calculateMaximumWithdrawAmount = exports.getAgregatedBalances = exports.checkNotInDebtAtAll = exports.calculateInterestWithSupplyBorrow = exports.calculateAssetInterest = exports.calculateAssetData = exports.calculateCurrentRates = exports.getAssetLiquidityMinusReserves = exports.calculatePresentValue = exports.BigMath = exports.bigIntMin = exports.bigIntMax = exports.bigAbs = exports.mulDivC = exports.mulDiv = exports.mulFactor = void 0;
3
+ exports.predictAPY = exports.predictHealthFactor = exports.calculateLiquidationData = exports.calculateHealthParams = exports.presentValue = exports.getAvailableToBorrow = exports.calculateMaximumWithdrawAmount = exports.getAgregatedBalances = exports.getAvailableToBorrowWithEMode = exports.calculateRepayToExitEMode = exports.determineHeCategory = exports.exceedsStandardBorrowLimit = exports.checkNotInDebtAtAll = exports.calculateInterestWithSupplyBorrow = exports.calculateAssetInterest = exports.calculateAssetData = exports.calculateCurrentRates = exports.getAssetLiquidityMinusReserves = exports.calculatePresentValue = exports.BigMath = exports.bigIntMin = exports.bigIntMax = exports.bigAbs = exports.mulDivC = exports.mulDiv = exports.mulFactor = void 0;
4
+ const core_1 = require("@ton/core");
4
5
  const assets_1 = require("../constants/assets");
5
6
  const User_1 = require("../types/User");
6
7
  const liquidation_1 = require("./liquidation");
@@ -119,6 +120,222 @@ function checkNotInDebtAtAll(principals) {
119
120
  return principals.values().every((x) => x >= 0n);
120
121
  }
121
122
  exports.checkNotInDebtAtAll = checkNotInDebtAtAll;
123
+ function exceedsStandardBorrowLimit(principals, assetsConfig, assetsData, prices, masterConstants) {
124
+ let standardBorrowLimit = 0n;
125
+ let totalBorrow = 0n;
126
+ for (const [assetId, principal] of principals) {
127
+ if (principal === 0n)
128
+ continue;
129
+ const assetConfig = assetsConfig.get(assetId);
130
+ const assetData = assetsData.get(assetId);
131
+ if (!assetConfig || !assetData || !prices.has(assetId))
132
+ continue;
133
+ const price = prices.get(assetId);
134
+ const assetBalance = presentValue(assetData.sRate, assetData.bRate, principal, masterConstants);
135
+ const assetWorth = (assetBalance.amount * price) / 10n ** assetConfig.decimals;
136
+ if (assetBalance.type === User_1.BalanceType.supply) {
137
+ standardBorrowLimit += (assetWorth * assetConfig.collateralFactor) / masterConstants.ASSET_COEFFICIENT_SCALE;
138
+ }
139
+ else if (assetBalance.type === User_1.BalanceType.borrow && assetConfig.dust < assetBalance.amount) {
140
+ totalBorrow += assetWorth;
141
+ }
142
+ }
143
+ return totalBorrow > standardBorrowLimit;
144
+ }
145
+ exports.exceedsStandardBorrowLimit = exceedsStandardBorrowLimit;
146
+ function determineHeCategory(assetsConfig, principals, poolConfig) {
147
+ const heCategoryByAssetId = new Map();
148
+ if (poolConfig) {
149
+ for (const heConfig of poolConfig.poolAssetsHEConfig) {
150
+ for (const asset of heConfig.assets) {
151
+ heCategoryByAssetId.set(asset.assetId, heConfig.heCategory);
152
+ }
153
+ }
154
+ }
155
+ let heCategory = -1;
156
+ for (const [assetID, principal] of principals) {
157
+ if (principal >= 0n) {
158
+ continue;
159
+ }
160
+ const heCategoryForAsset = heCategoryByAssetId.get(assetID) ?? assetsConfig.get(assetID)?.heCategory ?? 0;
161
+ if (heCategoryForAsset <= 0) {
162
+ return -1;
163
+ }
164
+ if (heCategory === -1) {
165
+ heCategory = heCategoryForAsset;
166
+ continue;
167
+ }
168
+ if (heCategory !== heCategoryForAsset) {
169
+ return -1;
170
+ }
171
+ }
172
+ return heCategory > 0 ? heCategory : -1;
173
+ }
174
+ exports.determineHeCategory = determineHeCategory;
175
+ function calculateRepayToExitEMode(assetsConfig, assetsData, principals, prices, poolConfig) {
176
+ const repayAmounts = core_1.Dictionary.empty();
177
+ const heCategoryRaw = determineHeCategory(assetsConfig, principals, poolConfig);
178
+ const activeHeCategory = heCategoryRaw > 0 && exceedsStandardBorrowLimit(principals, assetsConfig, assetsData, prices, poolConfig.masterConstants)
179
+ ? heCategoryRaw
180
+ : -1;
181
+ if (activeHeCategory <= 0) {
182
+ return { activeHeCategory: -1, requiredRepayInUsd: 0n, repayAmounts, enoughPriceData: true };
183
+ }
184
+ const availableToBorrowStandard = getAvailableToBorrow(assetsConfig, assetsData, principals, prices, poolConfig.masterConstants);
185
+ const requiredRepayInUsd = bigIntMax(0n, -availableToBorrowStandard);
186
+ if (requiredRepayInUsd === 0n) {
187
+ return {
188
+ activeHeCategory,
189
+ requiredRepayInUsd,
190
+ repayAmounts,
191
+ enoughPriceData: true,
192
+ };
193
+ }
194
+ let totalDebtWorth = 0n;
195
+ const debtEntries = [];
196
+ for (const [assetID, principal] of principals) {
197
+ if (principal >= 0n) {
198
+ continue;
199
+ }
200
+ if (!prices.has(assetID)) {
201
+ return {
202
+ activeHeCategory,
203
+ requiredRepayInUsd,
204
+ repayAmounts: core_1.Dictionary.empty(),
205
+ enoughPriceData: false,
206
+ };
207
+ }
208
+ const assetConfig = assetsConfig.get(assetID);
209
+ const assetData = assetsData.get(assetID);
210
+ if (!assetData || !assetConfig) {
211
+ continue;
212
+ }
213
+ const price = prices.get(assetID);
214
+ const debtAmount = calculatePresentValue(assetData.bRate, -principal, poolConfig.masterConstants);
215
+ const debtWorth = mulDiv(debtAmount, price, 10n ** assetConfig.decimals);
216
+ totalDebtWorth += debtWorth;
217
+ debtEntries.push({ assetID, debtAmount, debtWorth, decimals: assetConfig.decimals, price });
218
+ }
219
+ if (totalDebtWorth === 0n) {
220
+ return {
221
+ activeHeCategory,
222
+ requiredRepayInUsd,
223
+ repayAmounts,
224
+ enoughPriceData: true,
225
+ };
226
+ }
227
+ let distributedRepayWorth = 0n;
228
+ debtEntries.forEach((entry, index) => {
229
+ const repayWorth = index === debtEntries.length - 1
230
+ ? requiredRepayInUsd - distributedRepayWorth
231
+ : mulDiv(requiredRepayInUsd, entry.debtWorth, totalDebtWorth);
232
+ distributedRepayWorth += repayWorth;
233
+ const repayAmount = bigIntMin(entry.debtAmount, mulDivC(repayWorth, 10n ** entry.decimals, entry.price));
234
+ if (repayAmount > 0n) {
235
+ repayAmounts.set(entry.assetID, repayAmount);
236
+ }
237
+ });
238
+ if (distributedRepayWorth < requiredRepayInUsd && debtEntries.length > 0) {
239
+ const lastEntry = debtEntries[debtEntries.length - 1];
240
+ const currentRepayAmount = repayAmounts.get(lastEntry.assetID) ?? 0n;
241
+ if (currentRepayAmount < lastEntry.debtAmount) {
242
+ repayAmounts.set(lastEntry.assetID, bigIntMin(lastEntry.debtAmount, currentRepayAmount + 1n));
243
+ }
244
+ }
245
+ return {
246
+ activeHeCategory,
247
+ requiredRepayInUsd,
248
+ repayAmounts,
249
+ enoughPriceData: true,
250
+ };
251
+ }
252
+ exports.calculateRepayToExitEMode = calculateRepayToExitEMode;
253
+ function getAvailableToBorrowWithEMode(assetsConfig, assetsData, principals, prices, masterConstants, poolConfig) {
254
+ const heCategoryByAssetId = new Map();
255
+ if (poolConfig) {
256
+ for (const heConfig of poolConfig.poolAssetsHEConfig) {
257
+ for (const asset of heConfig.assets) {
258
+ heCategoryByAssetId.set(asset.assetId, heConfig.heCategory);
259
+ }
260
+ }
261
+ }
262
+ const calculateForHeCategory = (heCategory) => {
263
+ let borrowLimit = 0n;
264
+ let borrowAmount = 0n;
265
+ for (const assetID of principals.keys()) {
266
+ const principal = principals.get(assetID);
267
+ if (principal == 0n) {
268
+ continue;
269
+ }
270
+ if (!prices.has(assetID)) {
271
+ return 0n;
272
+ }
273
+ const assetConfig = assetsConfig.get(assetID);
274
+ const assetData = assetsData.get(assetID);
275
+ const price = prices.get(assetID);
276
+ if (principal < 0n) {
277
+ borrowAmount += mulDivC(calculatePresentValue(assetData.bRate, -principal, masterConstants), price, 10n ** assetConfig.decimals);
278
+ }
279
+ else {
280
+ const suppliedAssetInDollars = mulDiv(calculatePresentValue(assetData.sRate, principal, masterConstants), price, 10n ** assetConfig.decimals);
281
+ const heCategoryForAsset = heCategoryByAssetId.get(assetID) ?? assetConfig.heCategory;
282
+ const collateralFactor = heCategory > 0 && heCategoryForAsset === heCategory
283
+ ? assetConfig.heCollateralFactor
284
+ : assetConfig.collateralFactor;
285
+ borrowLimit += mulDiv(suppliedAssetInDollars, collateralFactor, masterConstants.ASSET_COEFFICIENT_SCALE);
286
+ }
287
+ }
288
+ return borrowLimit - borrowAmount;
289
+ };
290
+ const activeHeCategory = determineHeCategory(assetsConfig, principals, poolConfig);
291
+ if (activeHeCategory > 0) {
292
+ return {
293
+ availableToBorrow: calculateForHeCategory(activeHeCategory),
294
+ heCategory: activeHeCategory,
295
+ };
296
+ }
297
+ const availableToBorrowWithoutEmode = calculateForHeCategory(0);
298
+ if (!checkNotInDebtAtAll(principals)) {
299
+ return {
300
+ availableToBorrow: availableToBorrowWithoutEmode,
301
+ heCategory: 0,
302
+ };
303
+ }
304
+ const availableHeCategories = new Set();
305
+ if (poolConfig && poolConfig.poolAssetsHEConfig.length > 0) {
306
+ for (const heConfig of poolConfig.poolAssetsHEConfig) {
307
+ const hasSupplyInCategory = heConfig.assets.some((asset) => (principals.get(asset.assetId) ?? 0n) > 0n);
308
+ if (hasSupplyInCategory && heConfig.heCategory > 0) {
309
+ availableHeCategories.add(heConfig.heCategory);
310
+ }
311
+ }
312
+ }
313
+ else {
314
+ for (const [assetID, principal] of principals) {
315
+ if (principal <= 0n) {
316
+ continue;
317
+ }
318
+ const heCategory = assetsConfig.get(assetID).heCategory;
319
+ if (heCategory > 0) {
320
+ availableHeCategories.add(heCategory);
321
+ }
322
+ }
323
+ }
324
+ let bestHeCategory = 0;
325
+ let bestAvailableToBorrow = availableToBorrowWithoutEmode;
326
+ for (const heCategory of availableHeCategories) {
327
+ const availableToBorrow = calculateForHeCategory(heCategory);
328
+ if (availableToBorrow > bestAvailableToBorrow) {
329
+ bestAvailableToBorrow = availableToBorrow;
330
+ bestHeCategory = heCategory;
331
+ }
332
+ }
333
+ return {
334
+ availableToBorrow: bestAvailableToBorrow,
335
+ heCategory: bestHeCategory,
336
+ };
337
+ }
338
+ exports.getAvailableToBorrowWithEMode = getAvailableToBorrowWithEMode;
122
339
  function getAgregatedBalances(assetsData, assetsConfig, principals, prices, masterConstants) {
123
340
  let user_total_supply = 0n;
124
341
  let user_total_borrow = 0n;
@@ -145,13 +362,13 @@ function getAgregatedBalances(assetsData, assetsConfig, principals, prices, mast
145
362
  return { totalSupply: user_total_supply, totalBorrow: user_total_borrow };
146
363
  }
147
364
  exports.getAgregatedBalances = getAgregatedBalances;
148
- function calculateMaximumWithdrawAmount(assetsConfig, assetsData, principals, prices, masterConstants, assetId) {
365
+ function calculateMaximumWithdrawAmount(assetsConfig, assetsData, principals, prices, poolConfig, assetId) {
149
366
  let withdrawAmountMax = 0n;
150
367
  const assetConfig = assetsConfig.get(assetId);
151
368
  const assetData = assetsData.get(assetId);
152
369
  const oldPrincipal = principals.get(assetId);
153
370
  if (oldPrincipal > assetConfig.dust) {
154
- const oldPresentValue = presentValue(assetData.sRate, assetData.bRate, oldPrincipal, masterConstants);
371
+ const oldPresentValue = presentValue(assetData.sRate, assetData.bRate, oldPrincipal, poolConfig.masterConstants);
155
372
  if (checkNotInDebtAtAll(principals)) {
156
373
  withdrawAmountMax = oldPresentValue.amount;
157
374
  }
@@ -159,15 +376,19 @@ function calculateMaximumWithdrawAmount(assetsConfig, assetsData, principals, pr
159
376
  if (!prices.has(assetId)) {
160
377
  return 0n;
161
378
  }
162
- const borrowable = getAvailableToBorrow(assetsConfig, assetsData, principals, prices, masterConstants);
379
+ const borrowable = getAvailableToBorrow(assetsConfig, assetsData, principals, prices, poolConfig.masterConstants);
163
380
  const price = prices.get(assetId);
164
381
  let maxAmountToReclaim = 0n;
165
382
  if (assetConfig.collateralFactor == 0n) {
166
383
  maxAmountToReclaim = oldPresentValue.amount;
167
384
  }
168
385
  else if (price > 0) {
169
- maxAmountToReclaim = bigIntMax(0n, mulDiv(mulDiv(borrowable, masterConstants.ASSET_COEFFICIENT_SCALE, assetConfig.collateralFactor), 10n ** assetConfig.decimals, price) -
170
- calculatePresentValue(assetData.sRate, assetConfig.dust, masterConstants) / 2n);
386
+ const { availableToBorrow: borrowable, heCategory } = getAvailableToBorrowWithEMode(assetsConfig, assetsData, principals, prices, poolConfig.masterConstants, poolConfig);
387
+ const collateralFactor = heCategory > 0 && assetConfig.heCategory === heCategory
388
+ ? assetConfig.heCollateralFactor
389
+ : assetConfig.collateralFactor;
390
+ maxAmountToReclaim = bigIntMax(0n, mulDiv(mulDiv(borrowable, poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE, collateralFactor), 10n ** assetConfig.decimals, price) -
391
+ calculatePresentValue(assetData.sRate, assetConfig.dust, poolConfig.masterConstants) / 2n);
171
392
  }
172
393
  withdrawAmountMax = bigIntMin(maxAmountToReclaim, oldPresentValue.amount);
173
394
  }
@@ -177,7 +398,7 @@ function calculateMaximumWithdrawAmount(assetsConfig, assetsData, principals, pr
177
398
  return 0n;
178
399
  }
179
400
  const price = prices.get(assetId);
180
- return ((getAvailableToBorrow(assetsConfig, assetsData, principals, prices, masterConstants) *
401
+ return ((getAvailableToBorrowWithEMode(assetsConfig, assetsData, principals, prices, poolConfig.masterConstants, poolConfig).availableToBorrow *
181
402
  10n ** assetConfig.decimals) /
182
403
  price);
183
404
  }
@@ -243,9 +464,13 @@ exports.presentValue = presentValue;
243
464
  function calculateHealthParams(parameters) {
244
465
  const { principals, prices, assetsData, assetsConfig, poolConfig } = parameters;
245
466
  const { ASSET_LIQUIDATION_THRESHOLD_SCALE } = poolConfig.masterConstants;
467
+ let activeHeCategory = determineHeCategory(assetsConfig, principals, poolConfig);
246
468
  let totalSupply = 0n;
247
469
  let totalDebt = 0n;
248
470
  let totalLimit = 0n;
471
+ if (activeHeCategory > 0 && !exceedsStandardBorrowLimit(principals, assetsConfig, assetsData, prices, poolConfig.masterConstants)) {
472
+ activeHeCategory = -1;
473
+ }
249
474
  for (const asset of poolConfig.poolAssetsConfig) {
250
475
  if (!principals.has(asset.assetId))
251
476
  continue;
@@ -264,8 +489,11 @@ function calculateHealthParams(parameters) {
264
489
  const assetBalance = presentValue(sRate, bRate, assetPrincipal, poolConfig.masterConstants);
265
490
  const assetWorth = (assetBalance.amount * assetPrice) / assetScale;
266
491
  if (assetBalance.type === User_1.BalanceType.supply) {
492
+ const liquidationThreshold = activeHeCategory > 0 && assetConfig.heCategory === activeHeCategory
493
+ ? assetConfig.heLiquidationThreshold
494
+ : assetConfig.liquidationThreshold;
267
495
  totalSupply += assetWorth;
268
- totalLimit += (assetWorth * assetConfig.liquidationThreshold) / ASSET_LIQUIDATION_THRESHOLD_SCALE;
496
+ totalLimit += (assetWorth * liquidationThreshold) / ASSET_LIQUIDATION_THRESHOLD_SCALE;
269
497
  }
270
498
  else if (assetBalance.type === User_1.BalanceType.borrow && assetConfig.dust < assetBalance.amount) {
271
499
  totalDebt += assetWorth;
@@ -281,6 +509,7 @@ function calculateHealthParams(parameters) {
281
509
  totalDebt,
282
510
  totalLimit,
283
511
  totalSupply,
512
+ activeHeCategory,
284
513
  isLiquidatable: _isLiquidable,
285
514
  isBadDebt: _isBadDebt,
286
515
  };
@@ -302,6 +531,10 @@ function calculateLiquidationData(assetsConfig, assetsData, principals, prices,
302
531
  let loanAsset = assets_1.UNDEFINED_ASSET;
303
532
  let totalDebt = 0n;
304
533
  let totalLimit = 0n;
534
+ let activeHeCategory = determineHeCategory(assetsConfig, principals, poolConfig);
535
+ if (activeHeCategory > 0 && !exceedsStandardBorrowLimit(principals, assetsConfig, assetsData, prices, poolConfig.masterConstants)) {
536
+ activeHeCategory = -1;
537
+ }
305
538
  const { ASSET_SRATE_SCALE, ASSET_BRATE_SCALE, COLLATERAL_WORTH_THRESHOLD } = poolConfig.masterConstants;
306
539
  for (const asset of poolConfig.poolAssetsConfig) {
307
540
  const principal = principals.get(asset.assetId);
@@ -314,8 +547,10 @@ function calculateLiquidationData(assetsConfig, assetsData, principals, prices,
314
547
  : (principal * assetData.bRate) / ASSET_BRATE_SCALE;
315
548
  const assetWorth = (bigAbs(balance) * prices.get(asset.assetId)) / 10n ** assetConfig.decimals;
316
549
  if (balance > 0) {
317
- totalLimit +=
318
- (assetWorth * assetConfig.liquidationThreshold) / poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE;
550
+ const liquidationThreshold = activeHeCategory > 0 && assetConfig.heCategory === activeHeCategory
551
+ ? assetConfig.heLiquidationThreshold
552
+ : assetConfig.liquidationThreshold;
553
+ totalLimit += (assetWorth * liquidationThreshold) / poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE;
319
554
  // get the greatest collateral
320
555
  if (assetWorth > collateralValue) {
321
556
  collateralValue = assetWorth;
@@ -380,42 +615,130 @@ function calculateLiquidationData(assetsConfig, assetsData, principals, prices,
380
615
  }
381
616
  exports.calculateLiquidationData = calculateLiquidationData;
382
617
  function predictHealthFactor(args) {
383
- const healthParams = calculateHealthParams(args);
618
+ const { principals, prices, assetsData, assetsConfig, poolConfig } = args;
384
619
  const assetId = args.asset.assetId;
385
- const assetConfig = args.assetsConfig.get(assetId);
386
- const assetPrice = Number(args.prices.get(assetId));
387
- let totalLimit = Number(healthParams.totalLimit);
388
- let totalBorrow = Number(healthParams.totalDebt);
389
- const currentAmount = args.amount;
390
- const decimals = Number(assetConfig.decimals);
391
- const currentBalance = (assetPrice * Number(currentAmount)) / Math.pow(10, decimals);
392
620
  const changeType = args.balanceChangeType;
621
+ const currentAmount = args.amount;
622
+ const heCategoryByAssetId = new Map();
623
+ for (const heConfig of poolConfig.poolAssetsHEConfig) {
624
+ for (const asset of heConfig.assets) {
625
+ heCategoryByAssetId.set(asset.assetId, heConfig.heCategory);
626
+ }
627
+ }
628
+ const projectedBalances = new Map();
629
+ for (const asset of poolConfig.poolAssetsConfig) {
630
+ if (!principals.has(asset.assetId)) {
631
+ continue;
632
+ }
633
+ const assetPrincipal = principals.get(asset.assetId);
634
+ const assetConfig = assetsConfig.get(asset.assetId);
635
+ const assetData = assetsData.get(asset.assetId);
636
+ const balance = presentValue(assetData.sRate, assetData.bRate, assetPrincipal, poolConfig.masterConstants);
637
+ if (balance.type === User_1.BalanceType.supply) {
638
+ projectedBalances.set(asset.assetId, balance.amount);
639
+ }
640
+ else if (balance.type === User_1.BalanceType.borrow) {
641
+ projectedBalances.set(asset.assetId, -balance.amount);
642
+ }
643
+ }
393
644
  if (currentAmount != null && currentAmount != 0n) {
645
+ const currentSignedBalance = projectedBalances.get(assetId) ?? 0n;
646
+ const actionAssetConfig = assetsConfig.get(assetId);
647
+ let newSignedBalance = currentSignedBalance;
394
648
  if (changeType == User_1.BalanceChangeType.Borrow) {
395
- totalBorrow +=
396
- currentBalance *
397
- (1 +
398
- Number(assetConfig.originationFee) /
399
- Number(args.poolConfig.masterConstants.ASSET_ORIGINATION_FEE_SCALE));
649
+ const borrowWithFee = currentAmount +
650
+ mulDivC(currentAmount, actionAssetConfig.originationFee, poolConfig.masterConstants.ASSET_ORIGINATION_FEE_SCALE);
651
+ newSignedBalance -= borrowWithFee;
400
652
  }
401
653
  else if (changeType == User_1.BalanceChangeType.Repay) {
402
- totalBorrow -= currentBalance;
654
+ newSignedBalance += currentAmount;
403
655
  }
404
656
  else if (changeType == User_1.BalanceChangeType.Withdraw) {
405
- totalLimit -=
406
- (currentBalance * Number(assetConfig.liquidationThreshold)) /
407
- Number(args.poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE);
657
+ newSignedBalance -= currentAmount;
408
658
  }
409
659
  else if (changeType == User_1.BalanceChangeType.Supply) {
410
- totalLimit +=
411
- (currentBalance * Number(assetConfig.liquidationThreshold)) /
412
- Number(args.poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE);
660
+ newSignedBalance += currentAmount;
413
661
  }
662
+ projectedBalances.set(assetId, newSignedBalance);
414
663
  }
415
- if (Number(totalLimit) == 0) {
416
- return 1;
664
+ const getHeCategoryForAsset = (assetID, assetConfig) => {
665
+ return heCategoryByAssetId.get(assetID) ?? assetConfig.heCategory;
666
+ };
667
+ let projectedActiveHeCategory = -1;
668
+ for (const asset of poolConfig.poolAssetsConfig) {
669
+ const signedBalance = projectedBalances.get(asset.assetId) ?? 0n;
670
+ if (signedBalance >= 0n) {
671
+ continue;
672
+ }
673
+ const assetConfig = assetsConfig.get(asset.assetId);
674
+ const heCategory = getHeCategoryForAsset(asset.assetId, assetConfig);
675
+ if (heCategory <= 0) {
676
+ projectedActiveHeCategory = -1;
677
+ break;
678
+ }
679
+ if (projectedActiveHeCategory === -1) {
680
+ projectedActiveHeCategory = heCategory;
681
+ continue;
682
+ }
683
+ if (projectedActiveHeCategory !== heCategory) {
684
+ projectedActiveHeCategory = -1;
685
+ break;
686
+ }
687
+ }
688
+ const calculateProjectedHealthFactor = (heCategoryForCalculation) => {
689
+ let totalLimit = 0n;
690
+ let totalBorrow = 0n;
691
+ for (const asset of poolConfig.poolAssetsConfig) {
692
+ const signedBalance = projectedBalances.get(asset.assetId) ?? 0n;
693
+ if (signedBalance === 0n) {
694
+ continue;
695
+ }
696
+ const assetConfig = assetsConfig.get(asset.assetId);
697
+ const price = prices.get(asset.assetId);
698
+ const assetWorth = (bigAbs(signedBalance) * price) / 10n ** assetConfig.decimals;
699
+ if (signedBalance > 0n) {
700
+ const heCategory = getHeCategoryForAsset(asset.assetId, assetConfig);
701
+ const liquidationThreshold = heCategoryForCalculation > 0 && heCategory === heCategoryForCalculation
702
+ ? assetConfig.heLiquidationThreshold
703
+ : assetConfig.liquidationThreshold;
704
+ totalLimit +=
705
+ (assetWorth * liquidationThreshold) / poolConfig.masterConstants.ASSET_LIQUIDATION_THRESHOLD_SCALE;
706
+ continue;
707
+ }
708
+ const borrowAmount = -signedBalance;
709
+ if (borrowAmount > assetConfig.dust) {
710
+ totalBorrow += assetWorth;
711
+ }
712
+ }
713
+ if (totalLimit === 0n) {
714
+ return 1;
715
+ }
716
+ return Math.min(Math.max(1 - Number(totalBorrow) / Number(totalLimit), 0), 1);
717
+ };
718
+ if (projectedActiveHeCategory > 0) {
719
+ let standardBorrowLimit = 0n;
720
+ let projectedTotalBorrow = 0n;
721
+ for (const asset of poolConfig.poolAssetsConfig) {
722
+ const signedBalance = projectedBalances.get(asset.assetId) ?? 0n;
723
+ if (signedBalance === 0n)
724
+ continue;
725
+ const assetConfig = assetsConfig.get(asset.assetId);
726
+ const price = prices.get(asset.assetId);
727
+ const assetWorth = (bigAbs(signedBalance) * price) / 10n ** assetConfig.decimals;
728
+ if (signedBalance > 0n) {
729
+ standardBorrowLimit +=
730
+ (assetWorth * assetConfig.collateralFactor) /
731
+ poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE;
732
+ }
733
+ else if (-signedBalance > assetConfig.dust) {
734
+ projectedTotalBorrow += assetWorth;
735
+ }
736
+ }
737
+ if (projectedTotalBorrow > standardBorrowLimit) {
738
+ return calculateProjectedHealthFactor(projectedActiveHeCategory);
739
+ }
417
740
  }
418
- return Math.min(Math.max(1 - totalBorrow / totalLimit, 0), 1); // let's limit a result to zero below and one above
741
+ return calculateProjectedHealthFactor(-1);
419
742
  }
420
743
  exports.predictHealthFactor = predictHealthFactor;
421
744
  /**
@@ -109,8 +109,8 @@ function createAssetConfig() {
109
109
  const baseTrackingBorrowSpeed = ref.loadUintBig(64);
110
110
  const borrowCap = ref.loadInt(64);
111
111
  const heCategory = ref.loadUint(8);
112
- const heCollateralFactor = ref.loadUint(16);
113
- const heLiquidationThreshold = ref.loadUint(16);
112
+ const heCollateralFactor = ref.loadUintBig(16);
113
+ const heLiquidationThreshold = ref.loadUintBig(16);
114
114
  return {
115
115
  jwAddress,
116
116
  decimals,
@@ -202,8 +202,8 @@ function parseUserLiteData(userDataBOC, assetsData, assetsConfig, poolConfig, ap
202
202
  const masterConstants = poolConfig.masterConstants;
203
203
  const userSlice = core_1.Cell.fromBase64(userDataBOC).beginParse();
204
204
  const codeVersion = userSlice.loadCoins();
205
- const masterAddress = userSlice.loadAddress();
206
- const userAddress = userSlice.loadAddress();
205
+ const masterAddress = userSlice.loadAddressAny();
206
+ const userAddress = userSlice.loadAddressAny();
207
207
  const realPrincipals = userSlice.loadDict(core_1.Dictionary.Keys.BigUint(256), core_1.Dictionary.Values.BigInt(64));
208
208
  const principalsDict = core_1.Dictionary.empty(core_1.Dictionary.Keys.BigUint(256), core_1.Dictionary.Values.BigInt(64));
209
209
  const userState = userSlice.loadInt(64);
@@ -215,18 +215,27 @@ function parseUserLiteData(userDataBOC, assetsData, assetsConfig, poolConfig, ap
215
215
  let backupCell1 = null;
216
216
  let backupCell2 = null;
217
217
  const bitsLeft = userSlice.remainingBits;
218
- if (bitsLeft > 32) {
218
+ const refsLeft = userSlice.remainingRefs;
219
+ if (bitsLeft === 0 && refsLeft === 0) {
220
+ // Init format: no extra data after state
221
+ }
222
+ else if (bitsLeft >= 64 + 64 + 32 && refsLeft >= 1) {
223
+ // Old format with tracking indexes
219
224
  trackingSupplyIndex = userSlice.loadUintBig(64);
220
225
  trackingBorrowIndex = userSlice.loadUintBig(64);
221
226
  dutchAuctionStart = userSlice.loadUint(32);
222
227
  backupCell = (0, helpers_1.loadMyRef)(userSlice);
223
228
  }
224
- else {
229
+ else if (bitsLeft >= 3 && refsLeft >= 1) {
230
+ // New format with rewards dict + maybe_refs
225
231
  rewards = userSlice.loadDict(core_1.Dictionary.Keys.BigUint(256), createUserRewards());
226
- backupCell1 = userSlice.loadMaybeRef();
227
- backupCell2 = userSlice.loadMaybeRef();
232
+ if (userSlice.remainingBits >= 2) {
233
+ backupCell1 = userSlice.loadMaybeRef();
234
+ backupCell2 = userSlice.loadMaybeRef();
235
+ }
228
236
  }
229
- userSlice.endParse();
237
+ // Skip remaining data if any (for forward compatibility)
238
+ // userSlice.endParse();
230
239
  const userBalances = core_1.Dictionary.empty();
231
240
  for (const [_, asset] of Object.entries(poolAssetsConfig)) {
232
241
  const assetData = assetsData.get(asset.assetId);
@@ -273,6 +282,7 @@ function parseUserData(userLiteData, assetsData, assetsConfig, prices, poolConfi
273
282
  const masterConstants = poolConfig.masterConstants;
274
283
  const withdrawalLimits = core_1.Dictionary.empty();
275
284
  const borrowLimits = core_1.Dictionary.empty();
285
+ const borrowLimitsWithEmode = core_1.Dictionary.empty();
276
286
  let supplyBalance = 0n;
277
287
  let borrowBalance = 0n;
278
288
  for (const [assetId, principal] of userLiteData.realPrincipals) {
@@ -308,24 +318,32 @@ function parseUserData(userLiteData, assetsData, assetsConfig, prices, poolConfi
308
318
  }
309
319
  }
310
320
  const availableToBorrow = (0, math_1.getAvailableToBorrow)(assetsConfig, assetsData, userLiteData.realPrincipals, prices, masterConstants);
321
+ const { availableToBorrow: availableToBorrowWithEmode, heCategory: predictedHeCategory } = (0, math_1.getAvailableToBorrowWithEMode)(assetsConfig, assetsData, userLiteData.realPrincipals, prices, masterConstants, poolConfig);
322
+ let activeHeCategory = (0, math_1.determineHeCategory)(assetsConfig, userLiteData.realPrincipals, poolConfig);
323
+ if (activeHeCategory > 0 && !(0, math_1.exceedsStandardBorrowLimit)(userLiteData.realPrincipals, assetsConfig, assetsData, prices, masterConstants)) {
324
+ activeHeCategory = -1;
325
+ }
311
326
  for (const [_, asset] of Object.entries(poolAssetsConfig)) {
312
327
  const balance = userLiteData.balances.get(asset.assetId);
313
328
  const assetConfig = assetsConfig.get(asset.assetId);
314
329
  const assetData = assetsData.get(asset.assetId);
315
330
  const assetLiquidityMinusReserves = (0, math_1.getAssetLiquidityMinusReserves)(assetData, masterConstants);
316
331
  if (balance.type === User_1.BalanceType.supply) {
317
- withdrawalLimits.set(asset.assetId, (0, math_1.bigIntMin)((0, math_1.calculateMaximumWithdrawAmount)(assetsConfig, assetsData, userLiteData.realPrincipals, prices, masterConstants, asset.assetId), assetData.balance));
332
+ withdrawalLimits.set(asset.assetId, (0, math_1.bigIntMin)((0, math_1.calculateMaximumWithdrawAmount)(assetsConfig, assetsData, userLiteData.realPrincipals, prices, poolConfig, asset.assetId), assetData.balance));
318
333
  }
319
334
  if (!prices.has(asset.assetId)) {
320
335
  borrowLimits.set(asset.assetId, 0n);
336
+ borrowLimitsWithEmode.set(asset.assetId, 0n);
321
337
  continue;
322
338
  }
323
339
  borrowLimits.set(asset.assetId, (0, math_1.bigIntMax)(0n, (0, math_1.bigIntMin)((availableToBorrow * 10n ** assetConfig.decimals) / prices.get(asset.assetId), assetLiquidityMinusReserves)));
340
+ borrowLimitsWithEmode.set(asset.assetId, (0, math_1.bigIntMax)(0n, (0, math_1.bigIntMin)((availableToBorrowWithEmode * 10n ** assetConfig.decimals) / prices.get(asset.assetId), assetLiquidityMinusReserves)));
324
341
  }
325
- const limitUsed = borrowBalance + availableToBorrow;
342
+ const limitUsed = borrowBalance + availableToBorrowWithEmode;
326
343
  const limitUsedPercent = limitUsed === 0n
327
344
  ? 0
328
- : Number(BigInt(1e9) - (availableToBorrow * BigInt(1e9)) / (borrowBalance + availableToBorrow)) / 1e7;
345
+ : Number(BigInt(1e9) -
346
+ (availableToBorrowWithEmode * BigInt(1e9)) / (borrowBalance + availableToBorrowWithEmode)) / 1e7;
329
347
  let healthFactor = 1;
330
348
  let liquidationData;
331
349
  if (!havePrincipalWithoutPrice) {
@@ -338,14 +356,18 @@ function parseUserData(userLiteData, assetsData, assetsConfig, prices, poolConfi
338
356
  ...userLiteData,
339
357
  withdrawalLimits: withdrawalLimits,
340
358
  borrowLimits: borrowLimits,
359
+ borrowLimitsWithEmode: borrowLimitsWithEmode,
341
360
  supplyBalance: supplyBalance,
342
361
  borrowBalance: borrowBalance,
343
362
  availableToBorrow: availableToBorrow,
363
+ availableToBorrowWithEmode: availableToBorrowWithEmode,
364
+ predictedHeCategory: predictedHeCategory,
344
365
  limitUsedPercent: limitUsedPercent,
345
366
  limitUsed: limitUsed,
346
367
  liquidationData: liquidationData,
347
368
  healthFactor: healthFactor,
348
369
  havePrincipalWithoutPrice: havePrincipalWithoutPrice,
370
+ activeHeCategory: activeHeCategory,
349
371
  };
350
372
  }
351
373
  exports.parseUserData = parseUserData;
@@ -19,6 +19,8 @@ export declare const ASSET_ID: {
19
19
  STON: bigint;
20
20
  PT_tsUSDe_01Sep2025: bigint;
21
21
  PT_tsUSDe_18Dec2025: bigint;
22
+ TUSDT: bigint;
23
+ TUSDe: bigint;
22
24
  EUSDT: bigint;
23
25
  EUSDC: bigint;
24
26
  };
@@ -27,6 +27,9 @@ exports.ASSET_ID = {
27
27
  // STABLE
28
28
  PT_tsUSDe_01Sep2025: (0, sha256BigInt_1.sha256Hash)('PT_tsUSDe_01Sep2025'),
29
29
  PT_tsUSDe_18Dec2025: (0, sha256BigInt_1.sha256Hash)('PT_tsUSDe_18Dec2025'),
30
+ // Mainnet test assets
31
+ TUSDT: (0, sha256BigInt_1.sha256Hash)('TUSDT'),
32
+ TUSDe: (0, sha256BigInt_1.sha256Hash)('TUSDe'),
30
33
  // Testnet assets, faucet t.me/evaabuidl
31
34
  EUSDT: (0, sha256BigInt_1.sha256Hash)('EUSDT'),
32
35
  EUSDC: (0, sha256BigInt_1.sha256Hash)('EUSDC'),