builder_unlock/
query.rs

1#[cfg(not(feature = "library"))]
2use cosmwasm_std::entry_point;
3use cosmwasm_std::{to_json_binary, Addr, Binary, Deps, Env, Order, StdError, StdResult, Uint128};
4use cw_storage_plus::Bound;
5
6use astroport_governance::builder_unlock::{
7    AllocationParams, AllocationResponse, QueryMsg, SimulateWithdrawResponse, State,
8};
9use astroport_governance::{DEFAULT_LIMIT, MAX_LIMIT};
10
11use crate::error::ContractError;
12use crate::state::{Allocation, CONFIG, PARAMS, STATE, STATUS};
13
14/// Expose available contract queries.
15///
16/// ## Queries
17/// * **QueryMsg::Config {}** Return the contract configuration.
18///
19/// * **QueryMsg::State {}** Return the contract state (number of ASTRO that still need to be withdrawn).
20///
21/// * **QueryMsg::Allocation {}** Return the allocation details for a specific account.
22///
23/// * **QueryMsg::UnlockedTokens {}** Return the amount of unlocked ASTRO for a specific account.
24///
25/// * **QueryMsg::SimulateWithdraw {}** Return the result of a withdrawal simulation.
26#[cfg_attr(not(feature = "library"), entry_point)]
27pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
28    match msg {
29        QueryMsg::Config {} => to_json_binary(&CONFIG.load(deps.storage)?),
30        QueryMsg::State { timestamp } => to_json_binary(&query_state(deps, timestamp)?),
31        QueryMsg::Allocation { account, timestamp } => {
32            to_json_binary(&query_allocation(deps, account, timestamp)?)
33        }
34        QueryMsg::UnlockedTokens { account } => to_json_binary(
35            &query_tokens_unlocked(deps, env, account)
36                .map_err(|err| StdError::generic_err(err.to_string()))?,
37        ),
38        QueryMsg::SimulateWithdraw { account, timestamp } => to_json_binary(
39            &query_simulate_withdraw(deps, env, account, timestamp)
40                .map_err(|err| StdError::generic_err(err.to_string()))?,
41        ),
42        QueryMsg::Allocations { start_after, limit } => {
43            to_json_binary(&query_allocations(deps, start_after, limit)?)
44        }
45    }
46}
47
48/// Query either historical or current contract state.
49pub fn query_state(deps: Deps, timestamp: Option<u64>) -> StdResult<State> {
50    if let Some(timestamp) = timestamp {
51        // Loads state at specific timestamp. State changes reflected **after** block has been produced.
52        STATE.may_load_at_height(deps.storage, timestamp)
53    } else {
54        // Loads latest state. Can load allocation state at the current block timestamp.
55        STATE.may_load(deps.storage)
56    }
57    .map(|state| state.unwrap_or_default())
58}
59
60/// Return either historical or current information about a specific allocation.
61///
62/// * **account** account whose allocation we query.
63///
64/// * **timestamp** timestamp at which we query the allocation. Optional.
65pub fn query_allocation(
66    deps: Deps,
67    account: String,
68    timestamp: Option<u64>,
69) -> StdResult<AllocationResponse> {
70    let receiver = deps.api.addr_validate(&account)?;
71    let params = PARAMS
72        .may_load(deps.storage, &receiver)?
73        .unwrap_or_default();
74
75    let status = if let Some(timestamp) = timestamp {
76        // Loads allocation state at specific timestamp. State changes reflected **after** block has been produced.
77        STATUS
78            .may_load_at_height(deps.storage, &receiver, timestamp)?
79            .unwrap_or_default()
80    } else {
81        // Loads latest allocation state. Can load allocation state at the current block timestamp.
82        STATUS
83            .may_load(deps.storage, &receiver)?
84            .unwrap_or_default()
85    };
86
87    Ok(AllocationResponse { params, status })
88}
89
90/// Return information about a specific allocation.
91///
92/// * **start_after** account from which to start querying.
93///
94/// * **limit** max amount of entries to return.
95pub fn query_allocations(
96    deps: Deps,
97    start_after: Option<String>,
98    limit: Option<u32>,
99) -> StdResult<Vec<(Addr, AllocationParams)>> {
100    let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
101    let default_start;
102
103    let start = if let Some(start_after) = start_after {
104        default_start = deps.api.addr_validate(&start_after)?;
105        Some(Bound::exclusive(&default_start))
106    } else {
107        None
108    };
109
110    PARAMS
111        .range(deps.storage, start, None, Order::Ascending)
112        .take(limit)
113        .collect()
114}
115
116/// Return the total amount of unlocked tokens for a specific account.
117///
118/// * **account** account whose unlocked token amount we query.
119pub fn query_tokens_unlocked(
120    deps: Deps,
121    env: Env,
122    account: String,
123) -> Result<Uint128, ContractError> {
124    let receiver = deps.api.addr_validate(&account)?;
125    let block_ts = env.block.time.seconds();
126    let allocation = Allocation::must_load(deps.storage, block_ts, &receiver)?;
127
128    Ok(allocation.compute_unlocked_amount(block_ts))
129}
130
131/// Simulate a token withdrawal.
132///
133/// * **account** account for which we simulate a withdrawal.
134///
135/// * **timestamp** timestamp where we assume the account would withdraw.
136pub fn query_simulate_withdraw(
137    deps: Deps,
138    env: Env,
139    account: String,
140    timestamp: Option<u64>,
141) -> Result<SimulateWithdrawResponse, ContractError> {
142    let receiver = deps.api.addr_validate(&account)?;
143    let allocation = Allocation::must_load(deps.storage, env.block.time.seconds(), &receiver)?;
144    let timestamp = timestamp.unwrap_or_else(|| env.block.time.seconds());
145
146    Ok(allocation.compute_withdraw_amount(timestamp))
147}