croncat_mod_balances/
contract.rs

1use cosmwasm_std::entry_point;
2use cosmwasm_std::{
3    coin, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult,
4};
5#[cfg(not(feature = "library"))]
6use cw2::set_contract_version;
7use cw20::{Balance, BalanceResponse};
8use mod_sdk::types::QueryResponse;
9
10use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
11use crate::types::{BalanceComparator, HasBalanceComparator};
12use crate::ContractError;
13
14// version info for migration info
15const CONTRACT_NAME: &str = "crate:croncat-mod-balances";
16const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
17
18#[cfg_attr(not(feature = "library"), entry_point)]
19pub fn instantiate(
20    deps: DepsMut,
21    _env: Env,
22    _info: MessageInfo,
23    msg: InstantiateMsg,
24) -> Result<Response, StdError> {
25    let contract_version = msg.version.unwrap_or_else(|| CONTRACT_VERSION.to_string());
26    set_contract_version(deps.storage, CONTRACT_NAME, &contract_version)?;
27
28    Ok(Response::new().add_attribute("action", "instantiate"))
29}
30
31#[cfg_attr(not(feature = "library"), entry_point)]
32pub fn execute(
33    _deps: DepsMut,
34    _env: Env,
35    _info: MessageInfo,
36    _msg: ExecuteMsg,
37) -> Result<Response, ContractError> {
38    Err(ContractError::Noop)
39}
40
41#[cfg_attr(not(feature = "library"), entry_point)]
42pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
43    match msg {
44        QueryMsg::GetBalance { address, denom } => {
45            to_binary(&query_get_balance(deps, address, denom)?)
46        }
47        QueryMsg::GetCw20Balance {
48            cw20_contract,
49            address,
50        } => to_binary(&query_get_cw20_balance(deps, cw20_contract, address)?),
51        QueryMsg::HasBalanceComparator(HasBalanceComparator {
52            address,
53            required_balance,
54            comparator,
55        }) => to_binary(&query_has_balance_comparator(
56            deps,
57            address,
58            required_balance,
59            comparator,
60        )?),
61    }
62}
63
64/// Query: GetBalance
65/// Used as a helper method to get the native balance for an account
66///
67/// Response: QueryResponse
68/// Always returns true, even if balance is 0
69/// Data is the balance found for this account
70fn query_get_balance(deps: Deps, address: String, denom: String) -> StdResult<QueryResponse> {
71    let valid_addr = deps.api.addr_validate(&address)?;
72    let coin = deps.querier.query_balance(valid_addr, denom)?;
73    Ok(QueryResponse {
74        result: true,
75        data: to_binary(&coin)?,
76    })
77}
78
79/// Query: GetCw20Balance
80/// Used as a helper method to get the CW20 balance for an account
81///
82/// Response: QueryResponse
83/// Always returns true, even if balance is 0
84/// Data is the balance found for this account
85fn query_get_cw20_balance(
86    deps: Deps,
87    cw20_contract: String,
88    address: String,
89) -> StdResult<QueryResponse> {
90    let valid_cw20 = deps.api.addr_validate(&cw20_contract)?;
91    let valid_address = deps.api.addr_validate(&address)?;
92    let balance_response: BalanceResponse = deps.querier.query_wasm_smart(
93        valid_cw20,
94        &cw20::Cw20QueryMsg::Balance {
95            address: valid_address.to_string(),
96        },
97    )?;
98    let coin = coin(balance_response.balance.into(), cw20_contract);
99    Ok(QueryResponse {
100        result: true,
101        data: to_binary(&coin)?,
102    })
103}
104
105/// Query: HasBalanceComparator
106/// Used for comparing on-chain balance with a pre-defined input balance
107/// Comparator allows the flexibility of a single method implementation
108/// for all types of comparators: Equal, Not Equal, Greater Than,
109/// Greater Than Equal To, Less Than, Less Than Equal To
110/// If address doesn't exist, the query works as if the balance is zero
111///
112/// Response: QueryResponse
113/// Will never error, but default to returning false for logical use.
114fn query_has_balance_comparator(
115    deps: Deps,
116    address: String,
117    required_balance: Balance,
118    comparator: BalanceComparator,
119) -> StdResult<QueryResponse> {
120    let valid_address = deps.api.addr_validate(&address)?;
121
122    // NOTE: This implementation requires only 1 coin to be compared.
123    let (balance_amount, denom, required_amount) = match required_balance {
124        Balance::Native(required_native) => {
125            // Get the required denom from required_balance
126            // then loop the queried chain balances to find matching required denom
127            let native_vec = required_native.into_vec();
128            let native = native_vec.first().cloned();
129            if let Some(native) = native {
130                let balance = deps.querier.query_balance(valid_address, native.denom)?;
131                (balance.amount, balance.denom, native.amount)
132            } else {
133                return Ok(QueryResponse {
134                    result: false,
135                    data: Default::default(),
136                });
137            }
138        }
139        Balance::Cw20(required_cw20) => {
140            let balance_response: BalanceResponse = deps.querier.query_wasm_smart(
141                required_cw20.address.clone(),
142                &cw20::Cw20QueryMsg::Balance { address },
143            )?;
144            (
145                balance_response.balance,
146                required_cw20.address.to_string(),
147                required_cw20.amount,
148            )
149        }
150    };
151
152    let result = match comparator {
153        BalanceComparator::Eq => required_amount == balance_amount,
154        BalanceComparator::Ne => required_amount != balance_amount,
155        BalanceComparator::Gt => required_amount < balance_amount,
156        BalanceComparator::Gte => required_amount <= balance_amount,
157        BalanceComparator::Lt => required_amount > balance_amount,
158        BalanceComparator::Lte => required_amount >= balance_amount,
159    };
160
161    let coin = coin(balance_amount.into(), denom);
162    Ok(QueryResponse {
163        result,
164        data: to_binary(&coin)?,
165    })
166}