mars_params/types/
asset.rs1use cosmwasm_schema::cw_serde;
2use cosmwasm_std::{Addr, Api, Decimal, Uint128};
3use mars_utils::{
4 error::ValidationError,
5 helpers::{decimal_param_le_one, decimal_param_lt_one, validate_native_denom},
6};
7
8use crate::{
9 error::ContractResult,
10 execute::{assert_hls_lqt_gt_max_ltv, assert_lqt_gt_max_ltv},
11 types::hls::HlsParamsBase,
12};
13
14#[cw_serde]
15pub struct CmSettings<T> {
16 pub whitelisted: bool,
17 pub hls: Option<HlsParamsBase<T>>,
18}
19
20#[cw_serde]
21pub struct RedBankSettings {
22 pub deposit_enabled: bool,
23 pub borrow_enabled: bool,
24 pub deposit_cap: Uint128,
25}
26
27#[cw_serde]
36pub struct LiquidationBonus {
37 pub starting_lb: Decimal,
40 pub slope: Decimal,
43 pub min_lb: Decimal,
45 pub max_lb: Decimal,
48}
49
50impl LiquidationBonus {
51 pub fn validate(&self) -> Result<(), ValidationError> {
52 assert_starting_lb_within_range(self.starting_lb)?;
53 assert_lb_slope_within_range(self.slope)?;
54 assert_min_lb_within_range(self.min_lb)?;
55 assert_max_lb_within_range(self.max_lb)?;
56 assert_max_lb_gt_min_lb(self.min_lb, self.max_lb)?;
57 Ok(())
58 }
59}
60
61fn assert_starting_lb_within_range(b: Decimal) -> Result<(), ValidationError> {
62 if b > Decimal::percent(10) {
63 return Err(ValidationError::InvalidParam {
64 param_name: "starting_lb".to_string(),
65 invalid_value: b.to_string(),
66 predicate: "[0, 0.1]".to_string(),
67 });
68 }
69 Ok(())
70}
71
72fn assert_lb_slope_within_range(slope: Decimal) -> Result<(), ValidationError> {
73 if slope < Decimal::one() || slope > Decimal::from_ratio(5u8, 1u8) {
74 return Err(ValidationError::InvalidParam {
75 param_name: "slope".to_string(),
76 invalid_value: slope.to_string(),
77 predicate: "[1, 5]".to_string(),
78 });
79 }
80 Ok(())
81}
82
83fn assert_min_lb_within_range(min_lb: Decimal) -> Result<(), ValidationError> {
84 if min_lb > Decimal::percent(10) {
85 return Err(ValidationError::InvalidParam {
86 param_name: "min_lb".to_string(),
87 invalid_value: min_lb.to_string(),
88 predicate: "[0, 0.1]".to_string(),
89 });
90 }
91 Ok(())
92}
93
94fn assert_max_lb_within_range(max_lb: Decimal) -> Result<(), ValidationError> {
95 if max_lb < Decimal::percent(5) || max_lb > Decimal::percent(30) {
96 return Err(ValidationError::InvalidParam {
97 param_name: "max_lb".to_string(),
98 invalid_value: max_lb.to_string(),
99 predicate: "[0.05, 0.3]".to_string(),
100 });
101 }
102 Ok(())
103}
104
105fn assert_max_lb_gt_min_lb(min_lb: Decimal, max_lb: Decimal) -> Result<(), ValidationError> {
106 if min_lb > max_lb {
107 return Err(ValidationError::InvalidParam {
108 param_name: "max_lb".to_string(),
109 invalid_value: max_lb.to_string(),
110 predicate: format!("> {} (min LB)", min_lb),
111 });
112 }
113 Ok(())
114}
115
116#[cw_serde]
117pub struct AssetParamsBase<T> {
118 pub denom: String,
119 pub credit_manager: CmSettings<T>,
120 pub red_bank: RedBankSettings,
121 pub max_loan_to_value: Decimal,
122 pub liquidation_threshold: Decimal,
123 pub liquidation_bonus: LiquidationBonus,
124 pub protocol_liquidation_fee: Decimal,
125}
126
127pub type AssetParams = AssetParamsBase<Addr>;
128pub type AssetParamsUnchecked = AssetParamsBase<String>;
129
130impl From<AssetParams> for AssetParamsUnchecked {
131 fn from(p: AssetParams) -> Self {
132 Self {
133 denom: p.denom,
134 credit_manager: CmSettings {
135 whitelisted: p.credit_manager.whitelisted,
136 hls: p.credit_manager.hls.map(Into::into),
137 },
138 red_bank: p.red_bank,
139 max_loan_to_value: p.max_loan_to_value,
140 liquidation_threshold: p.liquidation_threshold,
141 liquidation_bonus: p.liquidation_bonus,
142 protocol_liquidation_fee: p.protocol_liquidation_fee,
143 }
144 }
145}
146
147impl AssetParamsUnchecked {
148 pub fn check(&self, api: &dyn Api) -> ContractResult<AssetParams> {
149 validate_native_denom(&self.denom)?;
150
151 decimal_param_lt_one(self.max_loan_to_value, "max_loan_to_value")?;
152 decimal_param_le_one(self.liquidation_threshold, "liquidation_threshold")?;
153 assert_lqt_gt_max_ltv(self.max_loan_to_value, self.liquidation_threshold)?;
154
155 self.liquidation_bonus.validate()?;
156 decimal_param_lt_one(self.protocol_liquidation_fee, "protocol_liquidation_fee")?;
157
158 if let Some(hls) = self.credit_manager.hls.as_ref() {
159 decimal_param_lt_one(hls.max_loan_to_value, "hls_max_loan_to_value")?;
160 decimal_param_le_one(hls.liquidation_threshold, "hls_liquidation_threshold")?;
161 assert_hls_lqt_gt_max_ltv(hls.max_loan_to_value, hls.liquidation_threshold)?;
162 }
163
164 let hls = self.credit_manager.hls.as_ref().map(|hls| hls.check(api)).transpose()?;
165
166 Ok(AssetParams {
167 denom: self.denom.clone(),
168 credit_manager: CmSettings {
169 whitelisted: self.credit_manager.whitelisted,
170 hls,
171 },
172 red_bank: self.red_bank.clone(),
173 max_loan_to_value: self.max_loan_to_value,
174 liquidation_threshold: self.liquidation_threshold,
175 liquidation_bonus: self.liquidation_bonus.clone(),
176 protocol_liquidation_fee: self.protocol_liquidation_fee,
177 })
178 }
179}