astroport_emissions_controller/
query.rs

1use std::collections::HashSet;
2
3#[cfg(not(feature = "library"))]
4use cosmwasm_std::entry_point;
5use cosmwasm_std::{to_json_binary, Binary, Deps, Env, Order, StdError, StdResult};
6use cw_storage_plus::Bound;
7use itertools::Itertools;
8use neutron_sdk::bindings::query::NeutronQuery;
9
10use astroport_governance::emissions_controller::consts::MAX_PAGE_LIMIT;
11use astroport_governance::emissions_controller::hub::{
12    QueryMsg, SimulateTuneResponse, UserInfoResponse,
13};
14
15use crate::error::ContractError;
16use crate::state::{
17    get_active_outposts, get_all_outposts, CONFIG, POOLS_BLACKLIST, POOLS_WHITELIST, TUNE_INFO,
18    USER_INFO, VOTED_POOLS,
19};
20use crate::utils::simulate_tune;
21
22/// Expose available contract queries.
23#[cfg_attr(not(feature = "library"), entry_point)]
24pub fn query(deps: Deps<NeutronQuery>, env: Env, msg: QueryMsg) -> Result<Binary, ContractError> {
25    match msg {
26        QueryMsg::UserInfo { user, timestamp } => {
27            let block_time = env.block.time.seconds();
28            let timestamp = timestamp.unwrap_or(block_time);
29            let user_info = match timestamp {
30                timestamp if timestamp == block_time => USER_INFO.may_load(deps.storage, &user),
31                timestamp => USER_INFO.may_load_at_height(deps.storage, &user, timestamp),
32            }?
33            .unwrap_or_default();
34
35            let applied_votes = user_info
36                .votes
37                .iter()
38                .filter_map(|(pool, weight)| {
39                    let data = if timestamp == block_time {
40                        VOTED_POOLS.may_load(deps.storage, pool)
41                    } else {
42                        VOTED_POOLS.may_load_at_height(deps.storage, pool, timestamp)
43                    };
44
45                    match data {
46                        Ok(Some(pool_info)) if pool_info.init_ts <= user_info.vote_ts => {
47                            Some(Ok((pool.clone(), *weight)))
48                        }
49                        Err(err) => Some(Err(err)),
50                        _ => None,
51                    }
52                })
53                .try_collect()?;
54
55            let response = UserInfoResponse {
56                vote_ts: user_info.vote_ts,
57                voting_power: user_info.voting_power,
58                votes: user_info.votes,
59                applied_votes,
60            };
61
62            Ok(to_json_binary(&response)?)
63        }
64        QueryMsg::TuneInfo { timestamp } => {
65            let block_time = env.block.time.seconds();
66            let timestamp = timestamp.unwrap_or(block_time);
67            let tune_info = match timestamp {
68                timestamp if timestamp == block_time => TUNE_INFO.may_load(deps.storage),
69                timestamp => TUNE_INFO.may_load_at_height(deps.storage, timestamp),
70            }?
71            .ok_or_else(|| StdError::generic_err(format!("Tune info not found at {timestamp}")))?;
72            Ok(to_json_binary(&tune_info)?)
73        }
74        QueryMsg::Config {} => Ok(to_json_binary(&CONFIG.load(deps.storage)?)?),
75        QueryMsg::VotedPool { pool, timestamp } => {
76            let block_time = env.block.time.seconds();
77            let timestamp = timestamp.unwrap_or(block_time);
78            let voted_pool = match timestamp {
79                timestamp if timestamp == block_time => VOTED_POOLS.may_load(deps.storage, &pool),
80                timestamp => VOTED_POOLS.may_load_at_height(deps.storage, &pool, timestamp),
81            }?
82            .ok_or_else(|| StdError::generic_err(format!("Voted pool not found at {timestamp}")))?;
83            Ok(to_json_binary(&voted_pool)?)
84        }
85        QueryMsg::VotedPools { limit, start_after } => {
86            let limit = limit.unwrap_or(MAX_PAGE_LIMIT) as usize;
87            let voted_pools = VOTED_POOLS
88                .range(
89                    deps.storage,
90                    start_after.as_ref().map(|s| Bound::exclusive(s.as_str())),
91                    None,
92                    Order::Ascending,
93                )
94                .take(limit)
95                .collect::<StdResult<Vec<_>>>()?;
96            Ok(to_json_binary(&voted_pools)?)
97        }
98        QueryMsg::ListOutposts {} => {
99            let outposts = get_all_outposts(deps.storage)?.into_iter().collect_vec();
100            Ok(to_json_binary(&outposts)?)
101        }
102        QueryMsg::QueryWhitelist { limit, start_after } => {
103            let limit = limit.unwrap_or(MAX_PAGE_LIMIT) as usize;
104            let pools_whitelist = POOLS_WHITELIST
105                .load(deps.storage)?
106                .into_iter()
107                .skip_while(|pool| {
108                    if let Some(start_after) = &start_after {
109                        pool != start_after
110                    } else {
111                        false
112                    }
113                })
114                .take(limit)
115                .collect_vec();
116
117            let pools_whitelist = if start_after.is_some() {
118                &pools_whitelist[1..]
119            } else {
120                &pools_whitelist
121            };
122
123            Ok(to_json_binary(pools_whitelist)?)
124        }
125        QueryMsg::QueryBlacklist { limit, start_after } => {
126            let limit = limit.unwrap_or(MAX_PAGE_LIMIT) as usize;
127            let start_after = start_after.as_ref().map(|s| Bound::exclusive(s.as_str()));
128            let pools_blacklist = POOLS_BLACKLIST
129                .keys(deps.storage, start_after, None, Order::Ascending)
130                .take(limit)
131                .collect::<StdResult<Vec<_>>>()?;
132
133            Ok(to_json_binary(&pools_blacklist)?)
134        }
135        QueryMsg::CheckWhitelist { lp_tokens } => {
136            let whitelist = POOLS_WHITELIST.load(deps.storage)?;
137            let is_whitelisted = lp_tokens
138                .into_iter()
139                .map(|lp_token| {
140                    let is_whitelisted = whitelist.contains(&lp_token);
141                    (lp_token, is_whitelisted)
142                })
143                .collect_vec();
144            Ok(to_json_binary(&is_whitelisted)?)
145        }
146        QueryMsg::SimulateTune {} => {
147            let deps = deps.into_empty();
148
149            let voted_pools = VOTED_POOLS
150                .keys(deps.storage, None, None, Order::Ascending)
151                .collect::<StdResult<HashSet<_>>>()?;
152            let outposts = get_active_outposts(deps.storage)?;
153            let config = CONFIG.load(deps.storage)?;
154
155            let tune_result = simulate_tune(
156                deps,
157                &voted_pools,
158                &outposts,
159                env.block.time.seconds(),
160                &config,
161            )?;
162            Ok(to_json_binary(&SimulateTuneResponse {
163                new_emissions_state: tune_result.new_emissions_state,
164                next_pools_grouped: tune_result.next_pools_grouped,
165            })?)
166        }
167    }
168}