1
1
use std:: cmp:: min;
2
2
3
3
use cosmwasm_schema:: cw_serde;
4
- use cosmwasm_std:: { Coin , Decimal , Uint128 } ;
4
+ use cosmwasm_std:: { Coin , Decimal , Fraction , Uint128 } ;
5
5
use mars_types:: {
6
6
credit_manager:: Positions ,
7
7
health:: {
8
8
AccountKind , BorrowTarget , Health ,
9
9
HealthError :: {
10
- MissingHLSParams , MissingParams , MissingPrice , MissingVaultConfig , MissingVaultValues ,
10
+ MissingAmount , MissingHLSParams , MissingParams , MissingPrice , MissingVaultConfig ,
11
+ MissingVaultValues ,
11
12
} ,
12
- HealthResult , SwapKind ,
13
+ HealthResult , LiquidationPriceKind , SwapKind ,
13
14
} ,
14
15
params:: { AssetParams , CmSettings , VaultConfig } ,
15
16
} ;
@@ -31,7 +32,7 @@ pub struct HealthComputer {
31
32
}
32
33
33
34
impl HealthComputer {
34
- pub fn compute_health ( & self ) -> mars_types :: health :: HealthResult < Health > {
35
+ pub fn compute_health ( & self ) -> HealthResult < Health > {
35
36
let CollateralValue {
36
37
total_collateral_value,
37
38
max_ltv_adjusted_collateral,
@@ -403,6 +404,72 @@ impl HealthComputer {
403
404
Ok ( max_borrow_amount)
404
405
}
405
406
407
+ pub fn liquidation_price (
408
+ & self ,
409
+ denom : & str ,
410
+ kind : & LiquidationPriceKind ,
411
+ ) -> HealthResult < Uint128 > {
412
+ let collateral_ltv_value = self . total_collateral_value ( ) ?. max_ltv_adjusted_collateral ;
413
+ let total_debt_value = self . total_debt_value ( ) ?;
414
+ if total_debt_value. is_zero ( ) {
415
+ return Ok ( Uint128 :: zero ( ) ) ;
416
+ }
417
+
418
+ let current_price =
419
+ self . denoms_data . prices . get ( denom) . ok_or ( MissingPrice ( denom. to_string ( ) ) ) ?;
420
+
421
+ if total_debt_value >= collateral_ltv_value {
422
+ return Ok ( Uint128 :: one ( ) * * current_price) ;
423
+ }
424
+
425
+ match kind {
426
+ LiquidationPriceKind :: Asset => {
427
+ let asset_amount = self . get_coin_from_deposits_and_lends ( denom) ?. amount ;
428
+ if asset_amount. is_zero ( ) {
429
+ return Err ( MissingAmount ( denom. to_string ( ) ) ) ;
430
+ }
431
+
432
+ let asset_ltv = self . get_coin_max_ltv ( denom) ?;
433
+
434
+ let asset_ltv_value =
435
+ asset_amount. checked_mul_floor ( current_price. checked_mul ( asset_ltv) ?) ?;
436
+ let debt_with_asset_ltv_value = total_debt_value. checked_add ( asset_ltv_value) ?;
437
+
438
+ if debt_with_asset_ltv_value <= collateral_ltv_value {
439
+ return Ok ( Uint128 :: zero ( ) ) ;
440
+ }
441
+
442
+ let debt_without = debt_with_asset_ltv_value - collateral_ltv_value;
443
+
444
+ // liquidation_price = (debt_value - collateral_ltv_value + asset_ltv_value) / (asset_amount * asset_ltv)
445
+ Ok ( Uint128 :: one ( )
446
+ * Decimal :: checked_from_ratio ( debt_without, asset_amount) ?. checked_mul (
447
+ Decimal :: from_ratio ( asset_ltv. denominator ( ) , asset_ltv. numerator ( ) ) ,
448
+ ) ?)
449
+ }
450
+
451
+ LiquidationPriceKind :: Debt => {
452
+ let debt_amount = self
453
+ . positions
454
+ . debts
455
+ . iter ( )
456
+ . find ( |c| c. denom == denom)
457
+ . ok_or ( MissingAmount ( denom. to_string ( ) ) ) ?
458
+ . amount ;
459
+ if debt_amount. is_zero ( ) {
460
+ return Err ( MissingAmount ( denom. to_string ( ) ) ) ;
461
+ }
462
+
463
+ // Liquidation_price = (collateral_ltv_value - total_debt_value + debt_value_asset / asset_amount
464
+ let debt_value = debt_amount. checked_mul_ceil ( * current_price) ?;
465
+ let net_collateral_value_without_debt =
466
+ collateral_ltv_value. checked_add ( debt_value) ?. checked_sub ( total_debt_value) ?;
467
+
468
+ Ok ( net_collateral_value_without_debt / debt_amount)
469
+ }
470
+ }
471
+ }
472
+
406
473
fn total_debt_value ( & self ) -> HealthResult < Uint128 > {
407
474
let mut total = Uint128 :: zero ( ) ;
408
475
for debt in & self . positions . debts {
0 commit comments