@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.
- package/dist/api/math.d.ts +16 -3
- package/dist/api/math.js +356 -33
- package/dist/api/parser.js +34 -12
- package/dist/constants/assets/assetId.d.ts +2 -0
- package/dist/constants/assets/assetId.js +3 -0
- package/dist/constants/assets/mainnet.d.ts +2 -0
- package/dist/constants/assets/mainnet.js +13 -1
- package/dist/constants/general/mainnet.d.ts +2 -0
- package/dist/constants/general/mainnet.js +4 -1
- package/dist/constants/pools/mainnet.d.ts +2 -0
- package/dist/constants/pools/mainnet.js +45 -1
- package/dist/constants/pools/testnet.d.ts +3 -2
- package/dist/constants/pools/testnet.js +16 -1
- package/dist/oracles/collectors/ClassicCollector.js +13 -18
- package/dist/oracles/collectors/FakeCollector.d.ts +30 -0
- package/dist/oracles/collectors/FakeCollector.js +128 -0
- package/dist/oracles/collectors/index.d.ts +1 -0
- package/dist/oracles/collectors/index.js +1 -0
- package/dist/types/Master.d.ts +8 -2
- package/dist/types/User.d.ts +7 -3
- package/package.json +1 -1
- package/src/api/math.ts +504 -40
- package/src/api/parser.ts +57 -12
- package/src/constants/assets/assetId.ts +4 -0
- package/src/constants/assets/mainnet.ts +24 -0
- package/src/constants/general/mainnet.ts +4 -0
- package/src/constants/pools/mainnet.ts +53 -1
- package/src/constants/pools/testnet.ts +30 -5
- package/src/oracles/collectors/ClassicCollector.ts +10 -14
- package/src/oracles/collectors/FakeCollector.ts +153 -0
- package/src/oracles/collectors/index.ts +1 -0
- package/src/types/Master.ts +9 -2
- package/src/types/User.ts +7 -3
package/src/api/math.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Dictionary } from '@ton/core';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import type {
|
|
4
3
|
AgregatedBalances,
|
|
5
4
|
AssetApy,
|
|
6
5
|
AssetConfig,
|
|
@@ -12,14 +11,18 @@ import {
|
|
|
12
11
|
MasterConstants,
|
|
13
12
|
PoolConfig,
|
|
14
13
|
} from '../types/Master';
|
|
14
|
+
import { UNDEFINED_ASSET } from '../constants/assets';
|
|
15
15
|
import {
|
|
16
16
|
BalanceChangeType,
|
|
17
17
|
BalanceType,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
UserData,
|
|
19
|
+
UserDataActive,
|
|
20
|
+
UserLiteData,
|
|
21
|
+
type HealthParamsArgs,
|
|
22
|
+
type LiquidationData,
|
|
23
|
+
type PredictAPYArgs,
|
|
24
|
+
type PredictHealthFactorArgs,
|
|
25
|
+
type UserBalance,
|
|
23
26
|
} from '../types/User';
|
|
24
27
|
import { addReserve, isBadDebt } from './liquidation';
|
|
25
28
|
|
|
@@ -177,6 +180,309 @@ export function checkNotInDebtAtAll(principals: Dictionary<bigint, bigint>): boo
|
|
|
177
180
|
return principals.values().every((x) => x >= 0n);
|
|
178
181
|
}
|
|
179
182
|
|
|
183
|
+
export function exceedsStandardBorrowLimit(
|
|
184
|
+
principals: Dictionary<bigint, bigint>,
|
|
185
|
+
assetsConfig: ExtendedAssetsConfig,
|
|
186
|
+
assetsData: ExtendedAssetsData,
|
|
187
|
+
prices: Dictionary<bigint, bigint>,
|
|
188
|
+
masterConstants: MasterConstants,
|
|
189
|
+
): boolean {
|
|
190
|
+
let standardBorrowLimit = 0n;
|
|
191
|
+
let totalBorrow = 0n;
|
|
192
|
+
for (const [assetId, principal] of principals) {
|
|
193
|
+
if (principal === 0n) continue;
|
|
194
|
+
const assetConfig = assetsConfig.get(assetId);
|
|
195
|
+
const assetData = assetsData.get(assetId);
|
|
196
|
+
if (!assetConfig || !assetData || !prices.has(assetId)) continue;
|
|
197
|
+
const price = prices.get(assetId)!;
|
|
198
|
+
const assetBalance = presentValue(assetData.sRate, assetData.bRate, principal, masterConstants);
|
|
199
|
+
const assetWorth = (assetBalance.amount * price) / 10n ** assetConfig.decimals;
|
|
200
|
+
if (assetBalance.type === BalanceType.supply) {
|
|
201
|
+
standardBorrowLimit += (assetWorth * assetConfig.collateralFactor) / masterConstants.ASSET_COEFFICIENT_SCALE;
|
|
202
|
+
} else if (assetBalance.type === BalanceType.borrow && assetConfig.dust < assetBalance.amount) {
|
|
203
|
+
totalBorrow += assetWorth;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return totalBorrow > standardBorrowLimit;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function determineHeCategory(
|
|
210
|
+
assetsConfig: ExtendedAssetsConfig,
|
|
211
|
+
principals: Dictionary<bigint, bigint>,
|
|
212
|
+
poolConfig?: PoolConfig,
|
|
213
|
+
): number {
|
|
214
|
+
const heCategoryByAssetId = new Map<bigint, number>();
|
|
215
|
+
if (poolConfig) {
|
|
216
|
+
for (const heConfig of poolConfig.poolAssetsHEConfig) {
|
|
217
|
+
for (const asset of heConfig.assets) {
|
|
218
|
+
heCategoryByAssetId.set(asset.assetId, heConfig.heCategory);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let heCategory = -1;
|
|
224
|
+
|
|
225
|
+
for (const [assetID, principal] of principals) {
|
|
226
|
+
if (principal >= 0n) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const heCategoryForAsset = heCategoryByAssetId.get(assetID) ?? assetsConfig.get(assetID)?.heCategory ?? 0;
|
|
231
|
+
if (heCategoryForAsset <= 0) {
|
|
232
|
+
return -1;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (heCategory === -1) {
|
|
236
|
+
heCategory = heCategoryForAsset;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (heCategory !== heCategoryForAsset) {
|
|
241
|
+
return -1;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return heCategory > 0 ? heCategory : -1;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function calculateRepayToExitEMode(
|
|
249
|
+
assetsConfig: ExtendedAssetsConfig,
|
|
250
|
+
assetsData: ExtendedAssetsData,
|
|
251
|
+
principals: Dictionary<bigint, bigint>,
|
|
252
|
+
prices: Dictionary<bigint, bigint>,
|
|
253
|
+
poolConfig: PoolConfig,
|
|
254
|
+
): {
|
|
255
|
+
activeHeCategory: number;
|
|
256
|
+
requiredRepayInUsd: bigint;
|
|
257
|
+
repayAmounts: Dictionary<bigint, bigint>;
|
|
258
|
+
enoughPriceData: boolean;
|
|
259
|
+
} {
|
|
260
|
+
const repayAmounts = Dictionary.empty<bigint, bigint>();
|
|
261
|
+
|
|
262
|
+
const heCategoryRaw = determineHeCategory(assetsConfig, principals, poolConfig);
|
|
263
|
+
const activeHeCategory =
|
|
264
|
+
heCategoryRaw > 0 && exceedsStandardBorrowLimit(principals, assetsConfig, assetsData, prices, poolConfig.masterConstants)
|
|
265
|
+
? heCategoryRaw
|
|
266
|
+
: -1;
|
|
267
|
+
|
|
268
|
+
if (activeHeCategory <= 0) {
|
|
269
|
+
return { activeHeCategory: -1, requiredRepayInUsd: 0n, repayAmounts, enoughPriceData: true };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const availableToBorrowStandard = getAvailableToBorrow(
|
|
273
|
+
assetsConfig,
|
|
274
|
+
assetsData,
|
|
275
|
+
principals,
|
|
276
|
+
prices,
|
|
277
|
+
poolConfig.masterConstants,
|
|
278
|
+
);
|
|
279
|
+
const requiredRepayInUsd = bigIntMax(0n, -availableToBorrowStandard);
|
|
280
|
+
|
|
281
|
+
if (requiredRepayInUsd === 0n) {
|
|
282
|
+
return {
|
|
283
|
+
activeHeCategory,
|
|
284
|
+
requiredRepayInUsd,
|
|
285
|
+
repayAmounts,
|
|
286
|
+
enoughPriceData: true,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
let totalDebtWorth = 0n;
|
|
291
|
+
const debtEntries: Array<{
|
|
292
|
+
assetID: bigint;
|
|
293
|
+
debtAmount: bigint;
|
|
294
|
+
debtWorth: bigint;
|
|
295
|
+
decimals: bigint;
|
|
296
|
+
price: bigint;
|
|
297
|
+
}> = [];
|
|
298
|
+
|
|
299
|
+
for (const [assetID, principal] of principals) {
|
|
300
|
+
if (principal >= 0n) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (!prices.has(assetID)) {
|
|
305
|
+
return {
|
|
306
|
+
activeHeCategory,
|
|
307
|
+
requiredRepayInUsd,
|
|
308
|
+
repayAmounts: Dictionary.empty<bigint, bigint>(),
|
|
309
|
+
enoughPriceData: false,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const assetConfig = assetsConfig.get(assetID);
|
|
314
|
+
const assetData = assetsData.get(assetID);
|
|
315
|
+
if (!assetData || !assetConfig) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const price = prices.get(assetID)!;
|
|
320
|
+
const debtAmount = calculatePresentValue(assetData.bRate, -principal, poolConfig.masterConstants);
|
|
321
|
+
const debtWorth = mulDiv(debtAmount, price, 10n ** assetConfig.decimals);
|
|
322
|
+
|
|
323
|
+
totalDebtWorth += debtWorth;
|
|
324
|
+
debtEntries.push({ assetID, debtAmount, debtWorth, decimals: assetConfig.decimals, price });
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (totalDebtWorth === 0n) {
|
|
328
|
+
return {
|
|
329
|
+
activeHeCategory,
|
|
330
|
+
requiredRepayInUsd,
|
|
331
|
+
repayAmounts,
|
|
332
|
+
enoughPriceData: true,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
let distributedRepayWorth = 0n;
|
|
337
|
+
debtEntries.forEach((entry, index) => {
|
|
338
|
+
const repayWorth =
|
|
339
|
+
index === debtEntries.length - 1
|
|
340
|
+
? requiredRepayInUsd - distributedRepayWorth
|
|
341
|
+
: mulDiv(requiredRepayInUsd, entry.debtWorth, totalDebtWorth);
|
|
342
|
+
|
|
343
|
+
distributedRepayWorth += repayWorth;
|
|
344
|
+
|
|
345
|
+
const repayAmount = bigIntMin(entry.debtAmount, mulDivC(repayWorth, 10n ** entry.decimals, entry.price));
|
|
346
|
+
if (repayAmount > 0n) {
|
|
347
|
+
repayAmounts.set(entry.assetID, repayAmount);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
if (distributedRepayWorth < requiredRepayInUsd && debtEntries.length > 0) {
|
|
352
|
+
const lastEntry = debtEntries[debtEntries.length - 1];
|
|
353
|
+
const currentRepayAmount = repayAmounts.get(lastEntry.assetID) ?? 0n;
|
|
354
|
+
if (currentRepayAmount < lastEntry.debtAmount) {
|
|
355
|
+
repayAmounts.set(lastEntry.assetID, bigIntMin(lastEntry.debtAmount, currentRepayAmount + 1n));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
activeHeCategory,
|
|
361
|
+
requiredRepayInUsd,
|
|
362
|
+
repayAmounts,
|
|
363
|
+
enoughPriceData: true,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function getAvailableToBorrowWithEMode(
|
|
368
|
+
assetsConfig: ExtendedAssetsConfig,
|
|
369
|
+
assetsData: ExtendedAssetsData,
|
|
370
|
+
principals: Dictionary<bigint, bigint>,
|
|
371
|
+
prices: Dictionary<bigint, bigint>,
|
|
372
|
+
masterConstants: MasterConstants,
|
|
373
|
+
poolConfig?: PoolConfig,
|
|
374
|
+
): { availableToBorrow: bigint; heCategory: number } {
|
|
375
|
+
const heCategoryByAssetId = new Map<bigint, number>();
|
|
376
|
+
if (poolConfig) {
|
|
377
|
+
for (const heConfig of poolConfig.poolAssetsHEConfig) {
|
|
378
|
+
for (const asset of heConfig.assets) {
|
|
379
|
+
heCategoryByAssetId.set(asset.assetId, heConfig.heCategory);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const calculateForHeCategory = (heCategory: number): bigint => {
|
|
385
|
+
let borrowLimit = 0n;
|
|
386
|
+
let borrowAmount = 0n;
|
|
387
|
+
|
|
388
|
+
for (const assetID of principals.keys()) {
|
|
389
|
+
const principal = principals.get(assetID) as bigint;
|
|
390
|
+
|
|
391
|
+
if (principal == 0n) {
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (!prices.has(assetID)) {
|
|
396
|
+
return 0n;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const assetConfig = assetsConfig.get(assetID) as AssetConfig;
|
|
400
|
+
const assetData = assetsData.get(assetID) as ExtendedAssetData;
|
|
401
|
+
const price = prices.get(assetID) as bigint;
|
|
402
|
+
|
|
403
|
+
if (principal < 0n) {
|
|
404
|
+
borrowAmount += mulDivC(
|
|
405
|
+
calculatePresentValue(assetData.bRate, -principal, masterConstants),
|
|
406
|
+
price,
|
|
407
|
+
10n ** assetConfig.decimals,
|
|
408
|
+
);
|
|
409
|
+
} else {
|
|
410
|
+
const suppliedAssetInDollars = mulDiv(
|
|
411
|
+
calculatePresentValue(assetData.sRate, principal, masterConstants),
|
|
412
|
+
price,
|
|
413
|
+
10n ** assetConfig.decimals,
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
const heCategoryForAsset = heCategoryByAssetId.get(assetID) ?? assetConfig.heCategory;
|
|
417
|
+
|
|
418
|
+
const collateralFactor =
|
|
419
|
+
heCategory > 0 && heCategoryForAsset === heCategory
|
|
420
|
+
? assetConfig.heCollateralFactor
|
|
421
|
+
: assetConfig.collateralFactor;
|
|
422
|
+
|
|
423
|
+
borrowLimit += mulDiv(
|
|
424
|
+
suppliedAssetInDollars,
|
|
425
|
+
collateralFactor,
|
|
426
|
+
masterConstants.ASSET_COEFFICIENT_SCALE,
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return borrowLimit - borrowAmount;
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const activeHeCategory = determineHeCategory(assetsConfig, principals, poolConfig);
|
|
435
|
+
if (activeHeCategory > 0) {
|
|
436
|
+
return {
|
|
437
|
+
availableToBorrow: calculateForHeCategory(activeHeCategory),
|
|
438
|
+
heCategory: activeHeCategory,
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const availableToBorrowWithoutEmode = calculateForHeCategory(0);
|
|
443
|
+
if (!checkNotInDebtAtAll(principals)) {
|
|
444
|
+
return {
|
|
445
|
+
availableToBorrow: availableToBorrowWithoutEmode,
|
|
446
|
+
heCategory: 0,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const availableHeCategories = new Set<number>();
|
|
451
|
+
if (poolConfig && poolConfig.poolAssetsHEConfig.length > 0) {
|
|
452
|
+
for (const heConfig of poolConfig.poolAssetsHEConfig) {
|
|
453
|
+
const hasSupplyInCategory = heConfig.assets.some((asset) => (principals.get(asset.assetId) ?? 0n) > 0n);
|
|
454
|
+
if (hasSupplyInCategory && heConfig.heCategory > 0) {
|
|
455
|
+
availableHeCategories.add(heConfig.heCategory);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
for (const [assetID, principal] of principals) {
|
|
460
|
+
if (principal <= 0n) {
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
const heCategory = (assetsConfig.get(assetID) as AssetConfig).heCategory;
|
|
464
|
+
if (heCategory > 0) {
|
|
465
|
+
availableHeCategories.add(heCategory);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
let bestHeCategory = 0;
|
|
471
|
+
let bestAvailableToBorrow = availableToBorrowWithoutEmode;
|
|
472
|
+
for (const heCategory of availableHeCategories) {
|
|
473
|
+
const availableToBorrow = calculateForHeCategory(heCategory);
|
|
474
|
+
if (availableToBorrow > bestAvailableToBorrow) {
|
|
475
|
+
bestAvailableToBorrow = availableToBorrow;
|
|
476
|
+
bestHeCategory = heCategory;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return {
|
|
481
|
+
availableToBorrow: bestAvailableToBorrow,
|
|
482
|
+
heCategory: bestHeCategory,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
|
|
180
486
|
export function getAgregatedBalances(
|
|
181
487
|
assetsData: ExtendedAssetsData,
|
|
182
488
|
assetsConfig: ExtendedAssetsConfig,
|
|
@@ -215,7 +521,7 @@ export function calculateMaximumWithdrawAmount(
|
|
|
215
521
|
assetsData: ExtendedAssetsData,
|
|
216
522
|
principals: Dictionary<bigint, bigint>,
|
|
217
523
|
prices: Dictionary<bigint, bigint>,
|
|
218
|
-
|
|
524
|
+
poolConfig: PoolConfig,
|
|
219
525
|
assetId: bigint,
|
|
220
526
|
): bigint {
|
|
221
527
|
let withdrawAmountMax = 0n;
|
|
@@ -225,7 +531,12 @@ export function calculateMaximumWithdrawAmount(
|
|
|
225
531
|
const oldPrincipal = principals.get(assetId) as bigint;
|
|
226
532
|
|
|
227
533
|
if (oldPrincipal > assetConfig.dust) {
|
|
228
|
-
const oldPresentValue = presentValue(
|
|
534
|
+
const oldPresentValue = presentValue(
|
|
535
|
+
assetData.sRate,
|
|
536
|
+
assetData.bRate,
|
|
537
|
+
oldPrincipal,
|
|
538
|
+
poolConfig.masterConstants,
|
|
539
|
+
);
|
|
229
540
|
if (checkNotInDebtAtAll(principals)) {
|
|
230
541
|
withdrawAmountMax = oldPresentValue.amount;
|
|
231
542
|
} else {
|
|
@@ -233,7 +544,13 @@ export function calculateMaximumWithdrawAmount(
|
|
|
233
544
|
return 0n;
|
|
234
545
|
}
|
|
235
546
|
|
|
236
|
-
const borrowable = getAvailableToBorrow(
|
|
547
|
+
const borrowable = getAvailableToBorrow(
|
|
548
|
+
assetsConfig,
|
|
549
|
+
assetsData,
|
|
550
|
+
principals,
|
|
551
|
+
prices,
|
|
552
|
+
poolConfig.masterConstants,
|
|
553
|
+
);
|
|
237
554
|
const price = prices.get(assetId) as bigint;
|
|
238
555
|
|
|
239
556
|
let maxAmountToReclaim = 0n;
|
|
@@ -241,14 +558,28 @@ export function calculateMaximumWithdrawAmount(
|
|
|
241
558
|
if (assetConfig.collateralFactor == 0n) {
|
|
242
559
|
maxAmountToReclaim = oldPresentValue.amount;
|
|
243
560
|
} else if (price > 0) {
|
|
561
|
+
const { availableToBorrow: borrowable, heCategory } = getAvailableToBorrowWithEMode(
|
|
562
|
+
assetsConfig,
|
|
563
|
+
assetsData,
|
|
564
|
+
principals,
|
|
565
|
+
prices,
|
|
566
|
+
poolConfig.masterConstants,
|
|
567
|
+
poolConfig,
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
const collateralFactor =
|
|
571
|
+
heCategory > 0 && assetConfig.heCategory === heCategory
|
|
572
|
+
? assetConfig.heCollateralFactor
|
|
573
|
+
: assetConfig.collateralFactor;
|
|
574
|
+
|
|
244
575
|
maxAmountToReclaim = bigIntMax(
|
|
245
576
|
0n,
|
|
246
577
|
mulDiv(
|
|
247
|
-
mulDiv(borrowable, masterConstants.ASSET_COEFFICIENT_SCALE,
|
|
578
|
+
mulDiv(borrowable, poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE, collateralFactor),
|
|
248
579
|
10n ** assetConfig.decimals,
|
|
249
580
|
price,
|
|
250
581
|
) -
|
|
251
|
-
calculatePresentValue(assetData.sRate, assetConfig.dust, masterConstants) / 2n,
|
|
582
|
+
calculatePresentValue(assetData.sRate, assetConfig.dust, poolConfig.masterConstants) / 2n,
|
|
252
583
|
);
|
|
253
584
|
}
|
|
254
585
|
|
|
@@ -262,7 +593,14 @@ export function calculateMaximumWithdrawAmount(
|
|
|
262
593
|
const price = prices.get(assetId) as bigint;
|
|
263
594
|
|
|
264
595
|
return (
|
|
265
|
-
(
|
|
596
|
+
(getAvailableToBorrowWithEMode(
|
|
597
|
+
assetsConfig,
|
|
598
|
+
assetsData,
|
|
599
|
+
principals,
|
|
600
|
+
prices,
|
|
601
|
+
poolConfig.masterConstants,
|
|
602
|
+
poolConfig,
|
|
603
|
+
).availableToBorrow *
|
|
266
604
|
10n ** assetConfig.decimals) /
|
|
267
605
|
price
|
|
268
606
|
);
|
|
@@ -357,11 +695,16 @@ export function calculateHealthParams(parameters: HealthParamsArgs) {
|
|
|
357
695
|
const { principals, prices, assetsData, assetsConfig, poolConfig } = parameters;
|
|
358
696
|
|
|
359
697
|
const { ASSET_LIQUIDATION_THRESHOLD_SCALE } = poolConfig.masterConstants;
|
|
698
|
+
let activeHeCategory = determineHeCategory(assetsConfig, principals, poolConfig);
|
|
360
699
|
|
|
361
700
|
let totalSupply = 0n;
|
|
362
701
|
let totalDebt = 0n;
|
|
363
702
|
let totalLimit = 0n;
|
|
364
703
|
|
|
704
|
+
if (activeHeCategory > 0 && !exceedsStandardBorrowLimit(principals, assetsConfig, assetsData, prices, poolConfig.masterConstants)) {
|
|
705
|
+
activeHeCategory = -1;
|
|
706
|
+
}
|
|
707
|
+
|
|
365
708
|
for (const asset of poolConfig.poolAssetsConfig) {
|
|
366
709
|
if (!principals.has(asset.assetId)) continue;
|
|
367
710
|
const assetPrincipal = principals.get(asset.assetId)!;
|
|
@@ -380,8 +723,13 @@ export function calculateHealthParams(parameters: HealthParamsArgs) {
|
|
|
380
723
|
const assetBalance = presentValue(sRate, bRate, assetPrincipal, poolConfig.masterConstants);
|
|
381
724
|
const assetWorth = (assetBalance.amount * assetPrice) / assetScale;
|
|
382
725
|
if (assetBalance.type === BalanceType.supply) {
|
|
726
|
+
const liquidationThreshold =
|
|
727
|
+
activeHeCategory > 0 && assetConfig.heCategory === activeHeCategory
|
|
728
|
+
? assetConfig.heLiquidationThreshold
|
|
729
|
+
: assetConfig.liquidationThreshold;
|
|
730
|
+
|
|
383
731
|
totalSupply += assetWorth;
|
|
384
|
-
totalLimit += (assetWorth *
|
|
732
|
+
totalLimit += (assetWorth * liquidationThreshold) / ASSET_LIQUIDATION_THRESHOLD_SCALE;
|
|
385
733
|
} else if (assetBalance.type === BalanceType.borrow && assetConfig.dust < assetBalance.amount) {
|
|
386
734
|
totalDebt += assetWorth;
|
|
387
735
|
}
|
|
@@ -399,6 +747,7 @@ export function calculateHealthParams(parameters: HealthParamsArgs) {
|
|
|
399
747
|
totalDebt,
|
|
400
748
|
totalLimit,
|
|
401
749
|
totalSupply,
|
|
750
|
+
activeHeCategory,
|
|
402
751
|
isLiquidatable: _isLiquidable,
|
|
403
752
|
isBadDebt: _isBadDebt,
|
|
404
753
|
};
|
|
@@ -427,6 +776,11 @@ export function calculateLiquidationData(
|
|
|
427
776
|
let totalDebt = 0n;
|
|
428
777
|
let totalLimit = 0n;
|
|
429
778
|
|
|
779
|
+
let activeHeCategory = determineHeCategory(assetsConfig, principals, poolConfig);
|
|
780
|
+
if (activeHeCategory > 0 && !exceedsStandardBorrowLimit(principals, assetsConfig, assetsData, prices, poolConfig.masterConstants)) {
|
|
781
|
+
activeHeCategory = -1;
|
|
782
|
+
}
|
|
783
|
+
|
|
430
784
|
const { ASSET_SRATE_SCALE, ASSET_BRATE_SCALE, COLLATERAL_WORTH_THRESHOLD } = poolConfig.masterConstants;
|
|
431
785
|
|
|
432
786
|
for (const asset of poolConfig.poolAssetsConfig) {
|
|
@@ -441,8 +795,12 @@ export function calculateLiquidationData(
|
|
|
441
795
|
|
|
442
796
|
const assetWorth = (bigAbs(balance) * prices.get(asset.assetId)!) / 10n ** assetConfig.decimals;
|
|
443
797
|
if (balance > 0) {
|
|
444
|
-
|
|
445
|
-
|
|
798
|
+
const liquidationThreshold =
|
|
799
|
+
activeHeCategory > 0 && assetConfig.heCategory === activeHeCategory
|
|
800
|
+
? assetConfig.heLiquidationThreshold
|
|
801
|
+
: assetConfig.liquidationThreshold;
|
|
802
|
+
|
|
803
|
+
totalLimit += (assetWorth * liquidationThreshold) / poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE;
|
|
446
804
|
// get the greatest collateral
|
|
447
805
|
if (assetWorth > collateralValue) {
|
|
448
806
|
collateralValue = assetWorth;
|
|
@@ -516,46 +874,152 @@ export function calculateLiquidationData(
|
|
|
516
874
|
}
|
|
517
875
|
|
|
518
876
|
export function predictHealthFactor(args: PredictHealthFactorArgs): number {
|
|
519
|
-
const
|
|
877
|
+
const { principals, prices, assetsData, assetsConfig, poolConfig } = args;
|
|
520
878
|
const assetId = args.asset.assetId;
|
|
879
|
+
const changeType = args.balanceChangeType;
|
|
880
|
+
const currentAmount = args.amount;
|
|
521
881
|
|
|
522
|
-
const
|
|
523
|
-
const
|
|
882
|
+
const heCategoryByAssetId = new Map<bigint, number>();
|
|
883
|
+
for (const heConfig of poolConfig.poolAssetsHEConfig) {
|
|
884
|
+
for (const asset of heConfig.assets) {
|
|
885
|
+
heCategoryByAssetId.set(asset.assetId, heConfig.heCategory);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
524
888
|
|
|
525
|
-
|
|
526
|
-
let totalBorrow = Number(healthParams.totalDebt);
|
|
889
|
+
const projectedBalances = new Map<bigint, bigint>();
|
|
527
890
|
|
|
528
|
-
const
|
|
891
|
+
for (const asset of poolConfig.poolAssetsConfig) {
|
|
892
|
+
if (!principals.has(asset.assetId)) {
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
529
895
|
|
|
530
|
-
|
|
896
|
+
const assetPrincipal = principals.get(asset.assetId)!;
|
|
897
|
+
const assetConfig = assetsConfig.get(asset.assetId)!;
|
|
898
|
+
const assetData = assetsData.get(asset.assetId)!;
|
|
899
|
+
const balance = presentValue(assetData.sRate, assetData.bRate, assetPrincipal, poolConfig.masterConstants);
|
|
531
900
|
|
|
532
|
-
|
|
533
|
-
|
|
901
|
+
if (balance.type === BalanceType.supply) {
|
|
902
|
+
projectedBalances.set(asset.assetId, balance.amount);
|
|
903
|
+
} else if (balance.type === BalanceType.borrow) {
|
|
904
|
+
projectedBalances.set(asset.assetId, -balance.amount);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
534
907
|
|
|
535
908
|
if (currentAmount != null && currentAmount != 0n) {
|
|
909
|
+
const currentSignedBalance = projectedBalances.get(assetId) ?? 0n;
|
|
910
|
+
const actionAssetConfig = assetsConfig.get(assetId)!;
|
|
911
|
+
let newSignedBalance = currentSignedBalance;
|
|
912
|
+
|
|
536
913
|
if (changeType == BalanceChangeType.Borrow) {
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
(
|
|
540
|
-
|
|
541
|
-
|
|
914
|
+
const borrowWithFee =
|
|
915
|
+
currentAmount +
|
|
916
|
+
mulDivC(
|
|
917
|
+
currentAmount,
|
|
918
|
+
actionAssetConfig.originationFee,
|
|
919
|
+
poolConfig.masterConstants.ASSET_ORIGINATION_FEE_SCALE,
|
|
920
|
+
);
|
|
921
|
+
newSignedBalance -= borrowWithFee;
|
|
542
922
|
} else if (changeType == BalanceChangeType.Repay) {
|
|
543
|
-
|
|
923
|
+
newSignedBalance += currentAmount;
|
|
544
924
|
} else if (changeType == BalanceChangeType.Withdraw) {
|
|
545
|
-
|
|
546
|
-
(currentBalance * Number(assetConfig.liquidationThreshold)) /
|
|
547
|
-
Number(args.poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE);
|
|
925
|
+
newSignedBalance -= currentAmount;
|
|
548
926
|
} else if (changeType == BalanceChangeType.Supply) {
|
|
549
|
-
|
|
550
|
-
(currentBalance * Number(assetConfig.liquidationThreshold)) /
|
|
551
|
-
Number(args.poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE);
|
|
927
|
+
newSignedBalance += currentAmount;
|
|
552
928
|
}
|
|
929
|
+
|
|
930
|
+
projectedBalances.set(assetId, newSignedBalance);
|
|
553
931
|
}
|
|
554
|
-
|
|
555
|
-
|
|
932
|
+
|
|
933
|
+
const getHeCategoryForAsset = (assetID: bigint, assetConfig: AssetConfig): number => {
|
|
934
|
+
return heCategoryByAssetId.get(assetID) ?? assetConfig.heCategory;
|
|
935
|
+
};
|
|
936
|
+
|
|
937
|
+
let projectedActiveHeCategory = -1;
|
|
938
|
+
for (const asset of poolConfig.poolAssetsConfig) {
|
|
939
|
+
const signedBalance = projectedBalances.get(asset.assetId) ?? 0n;
|
|
940
|
+
if (signedBalance >= 0n) {
|
|
941
|
+
continue;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
const assetConfig = assetsConfig.get(asset.assetId)!;
|
|
945
|
+
const heCategory = getHeCategoryForAsset(asset.assetId, assetConfig);
|
|
946
|
+
if (heCategory <= 0) {
|
|
947
|
+
projectedActiveHeCategory = -1;
|
|
948
|
+
break;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
if (projectedActiveHeCategory === -1) {
|
|
952
|
+
projectedActiveHeCategory = heCategory;
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
if (projectedActiveHeCategory !== heCategory) {
|
|
957
|
+
projectedActiveHeCategory = -1;
|
|
958
|
+
break;
|
|
959
|
+
}
|
|
556
960
|
}
|
|
557
961
|
|
|
558
|
-
|
|
962
|
+
const calculateProjectedHealthFactor = (heCategoryForCalculation: number): number => {
|
|
963
|
+
let totalLimit = 0n;
|
|
964
|
+
let totalBorrow = 0n;
|
|
965
|
+
|
|
966
|
+
for (const asset of poolConfig.poolAssetsConfig) {
|
|
967
|
+
const signedBalance = projectedBalances.get(asset.assetId) ?? 0n;
|
|
968
|
+
if (signedBalance === 0n) {
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
const assetConfig = assetsConfig.get(asset.assetId)!;
|
|
973
|
+
const price = prices.get(asset.assetId)!;
|
|
974
|
+
const assetWorth = (bigAbs(signedBalance) * price) / 10n ** assetConfig.decimals;
|
|
975
|
+
|
|
976
|
+
if (signedBalance > 0n) {
|
|
977
|
+
const heCategory = getHeCategoryForAsset(asset.assetId, assetConfig);
|
|
978
|
+
const liquidationThreshold =
|
|
979
|
+
heCategoryForCalculation > 0 && heCategory === heCategoryForCalculation
|
|
980
|
+
? assetConfig.heLiquidationThreshold
|
|
981
|
+
: assetConfig.liquidationThreshold;
|
|
982
|
+
totalLimit +=
|
|
983
|
+
(assetWorth * liquidationThreshold) / poolConfig.masterConstants.ASSET_LIQUIDATION_THRESHOLD_SCALE;
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const borrowAmount = -signedBalance;
|
|
988
|
+
if (borrowAmount > assetConfig.dust) {
|
|
989
|
+
totalBorrow += assetWorth;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
if (totalLimit === 0n) {
|
|
994
|
+
return 1;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
return Math.min(Math.max(1 - Number(totalBorrow) / Number(totalLimit), 0), 1);
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
if (projectedActiveHeCategory > 0) {
|
|
1001
|
+
let standardBorrowLimit = 0n;
|
|
1002
|
+
let projectedTotalBorrow = 0n;
|
|
1003
|
+
for (const asset of poolConfig.poolAssetsConfig) {
|
|
1004
|
+
const signedBalance = projectedBalances.get(asset.assetId) ?? 0n;
|
|
1005
|
+
if (signedBalance === 0n) continue;
|
|
1006
|
+
const assetConfig = assetsConfig.get(asset.assetId)!;
|
|
1007
|
+
const price = prices.get(asset.assetId)!;
|
|
1008
|
+
const assetWorth = (bigAbs(signedBalance) * price) / 10n ** assetConfig.decimals;
|
|
1009
|
+
if (signedBalance > 0n) {
|
|
1010
|
+
standardBorrowLimit +=
|
|
1011
|
+
(assetWorth * assetConfig.collateralFactor) /
|
|
1012
|
+
poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE;
|
|
1013
|
+
} else if (-signedBalance > assetConfig.dust) {
|
|
1014
|
+
projectedTotalBorrow += assetWorth;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
if (projectedTotalBorrow > standardBorrowLimit) {
|
|
1019
|
+
return calculateProjectedHealthFactor(projectedActiveHeCategory);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return calculateProjectedHealthFactor(-1);
|
|
559
1023
|
}
|
|
560
1024
|
|
|
561
1025
|
/**
|