1
use crate::{
2
    msg::FeeRecipient,
3
    state::{remove_swap_route, store_swap_route, CONFIG},
4
    types::{Config, SwapRoute},
5
    ContractError,
6
    ContractError::CustomError,
7
};
8
use cosmwasm_std::{ensure, ensure_eq, Addr, Attribute, BankMsg, Coin, Deps, DepsMut, Env, Event, Response, StdResult};
9
use injective_cosmwasm::{InjectiveMsgWrapper, InjectiveQuerier, InjectiveQueryWrapper, MarketId};
10
use std::collections::HashSet;
11

            
12
16
pub fn save_config(deps: DepsMut<InjectiveQueryWrapper>, env: Env, admin: Addr, fee_recipient: FeeRecipient) -> StdResult<()> {
13
16
    let fee_recipient = match fee_recipient {
14
8
        FeeRecipient::Address(addr) => addr,
15
8
        FeeRecipient::SwapContract => env.contract.address,
16
    };
17
16
    let config = Config { fee_recipient, admin };
18
16
    config.to_owned().validate()?;
19

            
20
16
    CONFIG.save(deps.storage, &config)
21
16
}
22

            
23
56
pub fn verify_sender_is_admin(deps: Deps<InjectiveQueryWrapper>, sender: &Addr) -> Result<(), ContractError> {
24
56
    let config = CONFIG.load(deps.storage)?;
25
56
    ensure_eq!(&config.admin, sender, ContractError::Unauthorized {});
26
50
    Ok(())
27
56
}
28

            
29
4
pub fn update_config(
30
4
    deps: DepsMut<InjectiveQueryWrapper>,
31
4
    env: Env,
32
4
    sender: Addr,
33
4
    admin: Option<Addr>,
34
4
    fee_recipient: Option<FeeRecipient>,
35
4
) -> Result<Response<InjectiveMsgWrapper>, ContractError> {
36
4
    verify_sender_is_admin(deps.as_ref(), &sender)?;
37
2
    let mut config = CONFIG.load(deps.storage)?;
38
2
    let mut updated_config_event_attrs: Vec<Attribute> = Vec::new();
39
2
    if let Some(admin) = admin {
40
2
        config.admin = admin.clone();
41
2
        updated_config_event_attrs.push(Attribute::new("admin", admin.to_string()));
42
2
    }
43
2
    if let Some(fee_recipient) = fee_recipient {
44
2
        config.fee_recipient = match fee_recipient {
45
2
            FeeRecipient::Address(addr) => addr,
46
            FeeRecipient::SwapContract => env.contract.address,
47
        };
48
2
        updated_config_event_attrs.push(Attribute::new("fee_recipient", config.fee_recipient.to_string()));
49
    }
50
2
    CONFIG.save(deps.storage, &config)?;
51

            
52
2
    Ok(Response::new()
53
2
        .add_attribute("method", "update_config")
54
2
        .add_event(Event::new("config_updated").add_attributes(updated_config_event_attrs)))
55
4
}
56

            
57
pub fn withdraw_support_funds(
58
    deps: DepsMut<InjectiveQueryWrapper>,
59
    sender: Addr,
60
    coins: Vec<Coin>,
61
    target_address: Addr,
62
) -> Result<Response<InjectiveMsgWrapper>, ContractError> {
63
    verify_sender_is_admin(deps.as_ref(), &sender)?;
64
    let send_message = BankMsg::Send {
65
        to_address: target_address.to_string(),
66
        amount: coins,
67
    };
68
    let response = Response::new()
69
        .add_message(send_message)
70
        .add_attribute("method", "withdraw_support_funds")
71
        .add_attribute("target_address", target_address.to_string());
72
    Ok(response)
73
}
74

            
75
46
pub fn set_route(
76
46
    deps: DepsMut<InjectiveQueryWrapper>,
77
46
    sender: &Addr,
78
46
    source_denom: String,
79
46
    target_denom: String,
80
46
    route: Vec<MarketId>,
81
46
) -> Result<Response<InjectiveMsgWrapper>, ContractError> {
82
46
    verify_sender_is_admin(deps.as_ref(), sender)?;
83

            
84
44
    if source_denom == target_denom {
85
2
        return Err(ContractError::CustomError {
86
2
            val: "Cannot set a route with the same denom being source and target".to_string(),
87
2
        });
88
42
    }
89
42

            
90
42
    if route.is_empty() {
91
2
        return Err(ContractError::CustomError {
92
2
            val: "Route must have at least one step".to_string(),
93
2
        });
94
40
    }
95
40

            
96
40
    if route.clone().into_iter().collect::<HashSet<MarketId>>().len() < route.len() {
97
2
        return Err(ContractError::CustomError {
98
2
            val: "Route cannot have duplicate steps!".to_string(),
99
2
        });
100
38
    }
101
38

            
102
38
    let route = SwapRoute {
103
38
        steps: route,
104
38
        source_denom,
105
38
        target_denom,
106
38
    };
107
38
    verify_route_exists(deps.as_ref(), &route)?;
108
32
    store_swap_route(deps.storage, &route)?;
109

            
110
32
    Ok(Response::new().add_attribute("method", "set_route"))
111
46
}
112

            
113
38
fn verify_route_exists(deps: Deps<InjectiveQueryWrapper>, route: &SwapRoute) -> Result<(), ContractError> {
114
    struct MarketDenom {
115
        quote_denom: String,
116
        base_denom: String,
117
    }
118
38
    let mut denoms: Vec<MarketDenom> = Vec::new();
119
38
    let querier = InjectiveQuerier::new(&deps.querier);
120

            
121
62
    for market_id in route.steps.iter() {
122
62
        let market = querier.query_spot_market(market_id)?.market.ok_or(CustomError {
123
62
            val: format!("Market {} not found", market_id.as_str()).to_string(),
124
62
        })?;
125

            
126
60
        denoms.push(MarketDenom {
127
60
            quote_denom: market.quote_denom,
128
60
            base_denom: market.base_denom,
129
60
        })
130
    }
131

            
132
    // defensive programming
133
36
    ensure!(
134
36
        !denoms.is_empty(),
135
        CustomError {
136
            val: "No market denoms found".to_string()
137
        }
138
    );
139
36
    ensure!(
140
36
        denoms.first().unwrap().quote_denom == route.source_denom || denoms.first().unwrap().base_denom == route.source_denom,
141
2
        CustomError {
142
2
            val: "Source denom not found in first market".to_string()
143
2
        }
144
    );
145
34
    ensure!(
146
34
        denoms.last().unwrap().quote_denom == route.target_denom || denoms.last().unwrap().base_denom == route.target_denom,
147
2
        CustomError {
148
2
            val: "Target denom not found in last market".to_string()
149
2
        }
150
    );
151

            
152
32
    Ok(())
153
38
}
154

            
155
6
pub fn delete_route(
156
6
    deps: DepsMut<InjectiveQueryWrapper>,
157
6
    sender: &Addr,
158
6
    source_denom: String,
159
6
    target_denom: String,
160
6
) -> Result<Response<InjectiveMsgWrapper>, ContractError> {
161
6
    verify_sender_is_admin(deps.as_ref(), sender)?;
162
4
    remove_swap_route(deps.storage, &source_denom, &target_denom);
163
4

            
164
4
    Ok(Response::new().add_attribute("method", "delete_route"))
165
6
}