1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use cosmwasm_std::{Decimal, DepsMut, MessageInfo, Response};
use mars_utils::{error::ValidationError, helpers::validate_native_denom};

use crate::{
    error::ContractError,
    state::{ASSET_PARAMS, MAX_CLOSE_FACTOR, OWNER, VAULT_CONFIGS},
    types::{AssetParamsUpdate, VaultConfigUpdate},
};

pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

pub fn update_max_close_factor(
    deps: DepsMut,
    info: MessageInfo,
    max_close_factor: Decimal,
) -> Result<Response, ContractError> {
    OWNER.assert_owner(deps.storage, &info.sender)?;

    assert_mcf(max_close_factor)?;
    MAX_CLOSE_FACTOR.save(deps.storage, &max_close_factor)?;

    let response = Response::new()
        .add_attribute("action", "update_max_close_factor")
        .add_attribute("value", max_close_factor.to_string());

    Ok(response)
}

pub fn update_asset_params(
    deps: DepsMut,
    info: MessageInfo,
    update: AssetParamsUpdate,
) -> Result<Response, ContractError> {
    OWNER.assert_owner(deps.storage, &info.sender)?;

    let mut response = Response::new().add_attribute("action", "update_asset_param");

    match update {
        AssetParamsUpdate::AddOrUpdate {
            denom,
            params,
        } => {
            validate_native_denom(&denom)?;
            params.validate()?;

            ASSET_PARAMS.save(deps.storage, &denom, &params)?;
            response = response
                .add_attribute("action_type", "add_or_update")
                .add_attribute("denom", denom);
        }
    }

    Ok(response)
}

pub fn update_vault_config(
    deps: DepsMut,
    info: MessageInfo,
    update: VaultConfigUpdate,
) -> Result<Response, ContractError> {
    OWNER.assert_owner(deps.storage, &info.sender)?;

    let mut response = Response::new().add_attribute("action", "update_vault_config");

    match update {
        VaultConfigUpdate::AddOrUpdate {
            addr,
            config,
        } => {
            let checked = deps.api.addr_validate(&addr)?;
            config.validate()?;
            VAULT_CONFIGS.save(deps.storage, &checked, &config)?;
            response =
                response.add_attribute("action_type", "add_or_update").add_attribute("addr", addr);
        }
        VaultConfigUpdate::Remove {
            addr,
        } => {
            let checked = deps.api.addr_validate(&addr)?;
            VAULT_CONFIGS.remove(deps.storage, &checked);
            response = response.add_attribute("action_type", "remove").add_attribute("addr", addr);
        }
    }

    Ok(response)
}

pub fn assert_mcf(param_value: Decimal) -> Result<(), ValidationError> {
    if !param_value.le(&Decimal::one()) {
        Err(ValidationError::InvalidParam {
            param_name: "max-close-factor".to_string(),
            invalid_value: "max-close-factor".to_string(),
            predicate: "<= 1".to_string(),
        })
    } else {
        Ok(())
    }
}

/// liquidation_threshold should be greater than or equal to max_loan_to_value
pub fn assert_lqt_gt_max_ltv(
    max_ltv: Decimal,
    liq_threshold: Decimal,
) -> Result<(), ValidationError> {
    if liq_threshold <= max_ltv {
        return Err(ValidationError::InvalidParam {
            param_name: "liquidation_threshold".to_string(),
            invalid_value: liq_threshold.to_string(),
            predicate: format!("> {} (max LTV)", max_ltv),
        });
    }
    Ok(())
}

pub fn assert_hls_lqt_gt_max_ltv(
    max_ltv: Decimal,
    liq_threshold: Decimal,
) -> Result<(), ValidationError> {
    if liq_threshold <= max_ltv {
        return Err(ValidationError::InvalidParam {
            param_name: "hls_liquidation_threshold".to_string(),
            invalid_value: liq_threshold.to_string(),
            predicate: format!("> {} (hls max LTV)", max_ltv),
        });
    }
    Ok(())
}