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
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_gte_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(())
}