mars_core/red_bank/
mod.rs

1pub mod interest_rate_models;
2pub mod msg;
3
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8use cosmwasm_std::{Addr, Uint128};
9
10use crate::asset::AssetType;
11use crate::error::MarsError;
12use crate::helpers::decimal_param_le_one;
13use crate::math::decimal::Decimal;
14
15use self::interest_rate_models::InterestRateModel;
16
17/// Global configuration
18#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
19pub struct Config {
20    /// Contract owner
21    pub owner: Addr,
22    /// Address provider returns addresses for all protocol contracts
23    pub address_provider_address: Addr,
24    /// maToken code id used to instantiate new tokens
25    pub ma_token_code_id: u64,
26    /// Maximum percentage of outstanding debt that can be covered by a liquidator
27    pub close_factor: Decimal,
28}
29
30impl Config {
31    pub fn validate(&self) -> Result<(), MarsError> {
32        decimal_param_le_one(&self.close_factor, "close_factor")?;
33
34        Ok(())
35    }
36}
37
38/// RedBank global state
39#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
40pub struct GlobalState {
41    /// Market count
42    pub market_count: u32,
43}
44
45/// Asset markets
46#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
47pub struct Market {
48    /// Market index (Bit position on data)
49    pub index: u32,
50    /// maToken contract address
51    pub ma_token_address: Addr,
52    /// Indicated whether the asset is native or a cw20 token
53    pub asset_type: AssetType,
54
55    /// Max uusd that can be borrowed per uusd collateral when using the asset as collateral
56    pub max_loan_to_value: Decimal,
57    /// uusd amount in debt position per uusd of asset collateral that if surpassed makes the user's position liquidatable.
58    pub liquidation_threshold: Decimal,
59    /// Bonus amount of collateral liquidator get when repaying user's debt (Will get collateral
60    /// from user in an amount equal to debt repayed + bonus)
61    pub liquidation_bonus: Decimal,
62    /// Portion of the borrow rate that is kept as protocol rewards
63    pub reserve_factor: Decimal,
64
65    /// model (params + internal state) that defines how interest rate behaves
66    pub interest_rate_model: InterestRateModel,
67
68    /// Borrow index (Used to compute borrow interest)
69    pub borrow_index: Decimal,
70    /// Liquidity index (Used to compute deposit interest)
71    pub liquidity_index: Decimal,
72    /// Rate charged to borrowers
73    pub borrow_rate: Decimal,
74    /// Rate paid to depositors
75    pub liquidity_rate: Decimal,
76    /// Timestamp (seconds) where indexes and where last updated
77    pub indexes_last_updated: u64,
78
79    /// Total debt scaled for the market's currency
80    pub debt_total_scaled: Uint128,
81
82    /// If false cannot do any action (deposit/withdraw/borrow/repay/liquidate)
83    pub active: bool,
84    /// If false cannot deposit
85    pub deposit_enabled: bool,
86    /// If false cannot borrow
87    pub borrow_enabled: bool,
88}
89
90impl Market {
91    pub fn validate(&self) -> Result<(), MarketError> {
92        decimal_param_le_one(&self.max_loan_to_value, "max_loan_to_value")?;
93        decimal_param_le_one(&self.liquidation_threshold, "liquidation_threshold")?;
94        decimal_param_le_one(&self.liquidation_bonus, "liquidation_bonus")?;
95
96        // liquidation_threshold should be greater than max_loan_to_value
97        if self.liquidation_threshold <= self.max_loan_to_value {
98            return Err(MarketError::InvalidLiquidationThreshold {
99                liquidation_threshold: self.liquidation_threshold,
100                max_loan_to_value: self.max_loan_to_value,
101            });
102        }
103
104        Ok(())
105    }
106}
107
108impl Default for Market {
109    fn default() -> Self {
110        let dynamic_ir_model = interest_rate_models::InterestRateModel::Dynamic {
111            params: interest_rate_models::DynamicInterestRateModelParams {
112                min_borrow_rate: Decimal::zero(),
113                max_borrow_rate: Decimal::one(),
114                kp_1: Default::default(),
115                optimal_utilization_rate: Default::default(),
116                kp_augmentation_threshold: Default::default(),
117                kp_2: Default::default(),
118
119                update_threshold_txs: 1,
120                update_threshold_seconds: 0,
121            },
122            state: interest_rate_models::DynamicInterestRateModelState {
123                txs_since_last_borrow_rate_update: 0,
124                borrow_rate_last_updated: 0,
125            },
126        };
127
128        Market {
129            index: 0,
130            ma_token_address: crate::helpers::zero_address(),
131            liquidity_index: Decimal::one(),
132            borrow_index: Decimal::one(),
133            borrow_rate: Default::default(),
134            liquidity_rate: Default::default(),
135            max_loan_to_value: Default::default(),
136            reserve_factor: Default::default(),
137            indexes_last_updated: 0,
138            debt_total_scaled: Default::default(),
139            asset_type: AssetType::Native,
140            liquidation_threshold: Decimal::one(),
141            liquidation_bonus: Decimal::zero(),
142            interest_rate_model: dynamic_ir_model,
143            active: true,
144            deposit_enabled: true,
145            borrow_enabled: true,
146        }
147    }
148}
149
150#[derive(Error, Debug, PartialEq)]
151pub enum MarketError {
152    #[error("{0}")]
153    Mars(#[from] MarsError),
154
155    #[error("liquidation_threshold should be greater than max_loan_to_value. liquidation_threshold: {liquidation_threshold:?}, max_loan_to_value: {max_loan_to_value:?}")]
156    InvalidLiquidationThreshold {
157        liquidation_threshold: Decimal,
158        max_loan_to_value: Decimal,
159    },
160}
161
162/// Data for individual users
163#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
164pub struct User {
165    /// bits representing borrowed assets. 1 on the corresponding bit means asset is
166    /// being borrowed
167    pub borrowed_assets: Uint128,
168    /// bits representing collateral assets. 1 on the corresponding bit means asset is
169    /// being used as collateral
170    pub collateral_assets: Uint128,
171}
172
173impl Default for User {
174    fn default() -> Self {
175        User {
176            borrowed_assets: Uint128::zero(),
177            collateral_assets: Uint128::zero(),
178        }
179    }
180}
181
182/// Debt for each asset and user
183#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
184pub struct Debt {
185    /// Scaled debt amount
186    pub amount_scaled: Uint128,
187
188    /// Marker for uncollateralized debt
189    pub uncollateralized: bool,
190}
191
192#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
193#[serde(rename_all = "snake_case")]
194pub enum UserHealthStatus {
195    NotBorrowing,
196    Borrowing(Decimal),
197}
198// We define a custom struct for each query response
199#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
200pub struct ConfigResponse {
201    pub owner: Addr,
202    pub address_provider_address: Addr,
203    pub ma_token_code_id: u64,
204    pub market_count: u32,
205    pub close_factor: Decimal,
206}
207
208#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
209pub struct MarketsListResponse {
210    pub markets_list: Vec<MarketInfo>,
211}
212
213#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
214pub struct MarketInfo {
215    /// Asset denom
216    pub denom: String,
217    /// Either denom if native asset or contract address if cw20
218    pub asset_label: String,
219    /// Bytes used as key on the kv store for data related to the asset
220    pub asset_reference: Vec<u8>,
221    /// Indicated whether the asset is native or a cw20 token
222    pub asset_type: AssetType,
223    /// Address for the corresponding maToken
224    pub ma_token_address: Addr,
225}
226
227#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
228pub struct UserDebtResponse {
229    pub debts: Vec<UserAssetDebtResponse>,
230}
231
232#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
233pub struct UserAssetDebtResponse {
234    /// Asset denom
235    pub denom: String,
236    /// Either denom if native asset or contract address if cw20
237    pub asset_label: String,
238    /// Bytes used as key on the kv store for data related to the asset
239    pub asset_reference: Vec<u8>,
240    /// Indicated whether the asset is native or a cw20 token
241    pub asset_type: AssetType,
242    /// Scaled debt amount stored in contract state
243    pub amount_scaled: Uint128,
244    /// Underlying asset amount that is actually owed at the current block
245    pub amount: Uint128,
246}
247
248#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
249pub struct UserCollateralResponse {
250    pub collateral: Vec<UserAssetCollateralResponse>,
251}
252
253#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
254pub struct UserAssetCollateralResponse {
255    /// Asset denom
256    pub denom: String,
257    /// Either denom if native asset or contract address if cw20
258    pub asset_label: String,
259    /// Bytes used as key on the kv store for data related to the asset
260    pub asset_reference: Vec<u8>,
261    /// Indicated whether the asset is native or a cw20 token
262    pub asset_type: AssetType,
263    /// Wether the user is using asset as collateral or not
264    pub enabled: bool,
265}
266
267#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
268pub struct UserPositionResponse {
269    pub total_collateral_in_uusd: Uint128,
270    pub total_debt_in_uusd: Uint128,
271    /// Total debt minus the uncollateralized debt
272    pub total_collateralized_debt_in_uusd: Uint128,
273    pub max_debt_in_uusd: Uint128,
274    pub weighted_liquidation_threshold_in_uusd: Uint128,
275    pub health_status: UserHealthStatus,
276}