Skip to main content

hermit_toolkit_hmip20/
query.rs

1use core::fmt;
2use schemars::JsonSchema;
3use serde::{de::DeserializeOwned, Deserialize, Serialize};
4
5use cosmwasm_std::{
6    to_binary, Coin, HumanAddr, Querier, QueryRequest, StdError, StdResult, Uint128, WasmQuery,
7};
8
9use hermit_toolkit_utils::space_pad;
10
11/// TokenInfo response
12#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
13pub struct TokenInfo {
14    pub name: String,
15    pub symbol: String,
16    pub decimals: u8,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub total_supply: Option<Uint128>,
19}
20
21/// TokenConfig response
22#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
23pub struct TokenConfig {
24    pub public_total_supply: bool,
25    pub deposit_enabled: bool,
26    pub redeem_enabled: bool,
27    pub mint_enabled: bool,
28    pub burn_enabled: bool,
29}
30
31/// Contract status
32#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
33pub enum ContractStatusLevel {
34    NormalRun,
35    StopAllButRedeems,
36    StopAll,
37}
38
39/// ContractStatus Response
40#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
41pub struct ContractStatus {
42    pub status: ContractStatusLevel,
43}
44
45/// ExchangeRate response
46#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
47pub struct ExchangeRate {
48    pub rate: Uint128,
49    pub denom: String,
50}
51
52/// Allowance response
53#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
54pub struct Allowance {
55    pub spender: HumanAddr,
56    pub owner: HumanAddr,
57    pub allowance: Uint128,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub expiration: Option<u64>,
60}
61
62/// Balance response
63#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
64pub struct Balance {
65    pub amount: Uint128,
66}
67
68/// Transaction data
69#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
70pub struct Tx {
71    pub id: u64,
72    pub from: HumanAddr,
73    pub sender: HumanAddr,
74    pub receiver: HumanAddr,
75    pub coins: Coin,
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub memo: Option<String>,
78    // The block time and block height are optional so that the JSON schema
79    // reflects that some SNIP-20 contracts may not include this info.
80    pub block_time: Option<u64>,
81    pub block_height: Option<u64>,
82}
83
84/// TransferHistory response
85#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
86pub struct TransferHistory {
87    pub total: Option<u64>,
88    pub txs: Vec<Tx>,
89}
90
91/// Types of transactions for RichTx
92#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
93#[serde(rename_all = "snake_case")]
94pub enum TxAction {
95    Transfer {
96        from: HumanAddr,
97        sender: HumanAddr,
98        recipient: HumanAddr,
99    },
100    Mint {
101        minter: HumanAddr,
102        recipient: HumanAddr,
103    },
104    Burn {
105        burner: HumanAddr,
106        owner: HumanAddr,
107    },
108    Deposit {},
109    Redeem {},
110}
111
112/// Rich transaction data used for TransactionHistory
113#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
114pub struct RichTx {
115    pub id: u64,
116    pub action: TxAction,
117    pub coins: Coin,
118    #[serde(skip_serializing_if = "Option::is_none")]
119    pub memo: Option<String>,
120    pub block_time: u64,
121    pub block_height: u64,
122}
123
124/// TransactionHistory response
125#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
126pub struct TransactionHistory {
127    pub total: Option<u64>,
128    pub txs: Vec<RichTx>,
129}
130
131/// Minters response
132#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
133pub struct Minters {
134    pub minters: Vec<HumanAddr>,
135}
136
137/// SNIP20 queries
138#[derive(Serialize, Clone, Debug, Eq, PartialEq)]
139#[serde(rename_all = "snake_case")]
140pub enum QueryMsg {
141    TokenInfo {},
142    TokenConfig {},
143    ContractStatus {},
144    ExchangeRate {},
145    Allowance {
146        owner: HumanAddr,
147        spender: HumanAddr,
148        key: String,
149    },
150    Balance {
151        address: HumanAddr,
152        key: String,
153    },
154    TransferHistory {
155        address: HumanAddr,
156        key: String,
157        page: Option<u32>,
158        page_size: u32,
159    },
160    TransactionHistory {
161        address: HumanAddr,
162        key: String,
163        page: Option<u32>,
164        page_size: u32,
165    },
166    Minters {},
167}
168
169impl fmt::Display for QueryMsg {
170    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171        match *self {
172            QueryMsg::TokenInfo { .. } => write!(f, "TokenInfo"),
173            QueryMsg::TokenConfig { .. } => write!(f, "TokenConfig"),
174            QueryMsg::ContractStatus { .. } => write!(f, "ContractStatus"),
175            QueryMsg::ExchangeRate { .. } => write!(f, "ExchangeRate"),
176            QueryMsg::Allowance { .. } => write!(f, "Allowance"),
177            QueryMsg::Balance { .. } => write!(f, "Balance"),
178            QueryMsg::TransferHistory { .. } => write!(f, "TransferHistory"),
179            QueryMsg::TransactionHistory { .. } => write!(f, "TransactionHistory"),
180            QueryMsg::Minters { .. } => write!(f, "Minters"),
181        }
182    }
183}
184
185impl QueryMsg {
186    /// Returns a StdResult<T>, where T is the "Response" type that wraps the query answer
187    ///
188    /// # Arguments
189    ///
190    /// * `querier` - a reference to the Querier dependency of the querying contract
191    /// * `block_size` - pad the message to blocks of this size
192    /// * `callback_code_hash` - String holding the code hash of the contract being queried
193    /// * `contract_addr` - address of the contract being queried
194    pub fn query<Q: Querier, T: DeserializeOwned>(
195        &self,
196        querier: &Q,
197        mut block_size: usize,
198        callback_code_hash: String,
199        contract_addr: HumanAddr,
200    ) -> StdResult<T> {
201        // can not have block size of 0
202        if block_size == 0 {
203            block_size = 1;
204        }
205        let mut msg = to_binary(self)?;
206        space_pad(&mut msg.0, block_size);
207        querier
208            .query(&QueryRequest::Wasm(WasmQuery::Smart {
209                contract_addr,
210                callback_code_hash,
211                msg,
212            }))
213            .map_err(|err| {
214                StdError::generic_err(format!("Error performing {} query: {}", self, err))
215            })
216    }
217}
218
219/// enum used to screen for a ViewingKeyError response from an authenticated query
220#[derive(Deserialize)]
221#[serde(rename_all = "snake_case")]
222pub enum AuthenticatedQueryResponse {
223    Allowance {
224        spender: HumanAddr,
225        owner: HumanAddr,
226        allowance: Uint128,
227        expiration: Option<u64>,
228    },
229    Balance {
230        amount: Uint128,
231    },
232    TransferHistory {
233        txs: Vec<Tx>,
234        total: Option<u64>,
235    },
236    TransactionHistory {
237        txs: Vec<RichTx>,
238        total: Option<u64>,
239    },
240    ViewingKeyError {
241        msg: String,
242    },
243}
244/// wrapper to deserialize TokenInfo response
245#[derive(Deserialize)]
246pub struct TokenInfoResponse {
247    pub token_info: TokenInfo,
248}
249
250/// wrapper to deserialize TokenConfig response
251#[derive(Deserialize)]
252pub struct TokenConfigResponse {
253    pub token_config: TokenConfig,
254}
255
256/// wrapper to deserialize ContractStatus response
257#[derive(Deserialize)]
258pub struct ContractStatusResponse {
259    pub contract_status: ContractStatus,
260}
261
262/// wrapper to deserialize ExchangeRate response
263#[derive(Deserialize)]
264pub struct ExchangeRateResponse {
265    pub exchange_rate: ExchangeRate,
266}
267
268/// wrapper to deserialize Minters response
269#[derive(Deserialize)]
270pub struct MintersResponse {
271    pub minters: Minters,
272}
273
274/// Returns a StdResult<TokenInfo> from performing TokenInfo query
275///
276/// # Arguments
277///
278/// * `querier` - a reference to the Querier dependency of the querying contract
279/// * `block_size` - pad the message to blocks of this size
280/// * `callback_code_hash` - String holding the code hash of the contract being queried
281/// * `contract_addr` - address of the contract being queried
282pub fn token_info_query<Q: Querier>(
283    querier: &Q,
284    block_size: usize,
285    callback_code_hash: String,
286    contract_addr: HumanAddr,
287) -> StdResult<TokenInfo> {
288    let answer: TokenInfoResponse =
289        QueryMsg::TokenInfo {}.query(querier, block_size, callback_code_hash, contract_addr)?;
290    Ok(answer.token_info)
291}
292
293/// Returns a StdResult<TokenConfig> from performing TokenConfig query
294///
295/// # Arguments
296///
297/// * `querier` - a reference to the Querier dependency of the querying contract
298/// * `block_size` - pad the message to blocks of this size
299/// * `callback_code_hash` - String holding the code hash of the contract being queried
300/// * `contract_addr` - address of the contract being queried
301pub fn token_config_query<Q: Querier>(
302    querier: &Q,
303    block_size: usize,
304    callback_code_hash: String,
305    contract_addr: HumanAddr,
306) -> StdResult<TokenConfig> {
307    let answer: TokenConfigResponse =
308        QueryMsg::TokenConfig {}.query(querier, block_size, callback_code_hash, contract_addr)?;
309    Ok(answer.token_config)
310}
311
312/// Returns a StdResult<ContractStatus> from performing ContractStatus query
313///
314/// # Arguments
315///
316/// * `querier` - a reference to the Querier dependency of the querying contract
317/// * `block_size` - pad the message to blocks of this size
318/// * `callback_code_hash` - String holding the code hash of the contract being queried
319/// * `contract_addr` - address of the contract being queried
320pub fn contract_status_query<Q: Querier>(
321    querier: &Q,
322    block_size: usize,
323    callback_code_hash: String,
324    contract_addr: HumanAddr,
325) -> StdResult<ContractStatus> {
326    let answer: ContractStatusResponse = QueryMsg::ContractStatus {}.query(
327        querier,
328        block_size,
329        callback_code_hash,
330        contract_addr,
331    )?;
332    Ok(answer.contract_status)
333}
334
335/// Returns a StdResult<ExchangeRate> from performing ExchangeRate query
336///
337/// # Arguments
338///
339/// * `querier` - a reference to the Querier dependency of the querying contract
340/// * `block_size` - pad the message to blocks of this size
341/// * `callback_code_hash` - String holding the code hash of the contract being queried
342/// * `contract_addr` - address of the contract being queried
343pub fn exchange_rate_query<Q: Querier>(
344    querier: &Q,
345    block_size: usize,
346    callback_code_hash: String,
347    contract_addr: HumanAddr,
348) -> StdResult<ExchangeRate> {
349    let answer: ExchangeRateResponse =
350        QueryMsg::ExchangeRate {}.query(querier, block_size, callback_code_hash, contract_addr)?;
351    Ok(answer.exchange_rate)
352}
353
354/// Returns a StdResult<Allowance> from performing Allowance query
355///
356/// # Arguments
357///
358/// * `querier` - a reference to the Querier dependency of the querying contract
359/// * `owner` - the address that owns the tokens
360/// * `spender` - the address allowed to send/burn tokens
361/// * `key` - String holding the authentication key needed to view the allowance
362/// * `block_size` - pad the message to blocks of this size
363/// * `callback_code_hash` - String holding the code hash of the contract being queried
364/// * `contract_addr` - address of the contract being queried
365#[allow(clippy::too_many_arguments)]
366pub fn allowance_query<Q: Querier>(
367    querier: &Q,
368    owner: HumanAddr,
369    spender: HumanAddr,
370    key: String,
371    block_size: usize,
372    callback_code_hash: String,
373    contract_addr: HumanAddr,
374) -> StdResult<Allowance> {
375    let answer: AuthenticatedQueryResponse = QueryMsg::Allowance {
376        owner,
377        spender,
378        key,
379    }
380        .query(querier, block_size, callback_code_hash, contract_addr)?;
381    match answer {
382        AuthenticatedQueryResponse::Allowance {
383            spender,
384            owner,
385            allowance,
386            expiration,
387        } => Ok(Allowance {
388            spender,
389            owner,
390            allowance,
391            expiration,
392        }),
393        AuthenticatedQueryResponse::ViewingKeyError { .. } => Err(StdError::unauthorized()),
394        _ => Err(StdError::generic_err("Invalid Allowance query response")),
395    }
396}
397
398/// Returns a StdResult<Balance> from performing Balance query
399///
400/// # Arguments
401///
402/// * `querier` - a reference to the Querier dependency of the querying contract
403/// * `address` - the address whose balance should be displayed
404/// * `key` - String holding the authentication key needed to view the balance
405/// * `block_size` - pad the message to blocks of this size
406/// * `callback_code_hash` - String holding the code hash of the contract being queried
407/// * `contract_addr` - address of the contract being queried
408pub fn balance_query<Q: Querier>(
409    querier: &Q,
410    address: HumanAddr,
411    key: String,
412    block_size: usize,
413    callback_code_hash: String,
414    contract_addr: HumanAddr,
415) -> StdResult<Balance> {
416    let answer: AuthenticatedQueryResponse = QueryMsg::Balance { address, key }.query(
417        querier,
418        block_size,
419        callback_code_hash,
420        contract_addr,
421    )?;
422    match answer {
423        AuthenticatedQueryResponse::Balance { amount } => Ok(Balance { amount }),
424        AuthenticatedQueryResponse::ViewingKeyError { .. } => Err(StdError::unauthorized()),
425        _ => Err(StdError::generic_err("Invalid Balance query response")),
426    }
427}
428
429/// Returns a StdResult<TransferHistory> from performing TransferHistory query
430///
431/// # Arguments
432///
433/// * `querier` - a reference to the Querier dependency of the querying contract
434/// * `address` - the address whose transaction history should be displayed
435/// * `key` - String holding the authentication key needed to view transactions
436/// * `page` - Optional u32 representing the page number of transactions to display
437/// * `page_size` - u32 number of transactions to return
438/// * `block_size` - pad the message to blocks of this size
439/// * `callback_code_hash` - String holding the code hash of the contract being queried
440/// * `contract_addr` - address of the contract being queried
441#[allow(clippy::too_many_arguments)]
442pub fn transfer_history_query<Q: Querier>(
443    querier: &Q,
444    address: HumanAddr,
445    key: String,
446    page: Option<u32>,
447    page_size: u32,
448    block_size: usize,
449    callback_code_hash: String,
450    contract_addr: HumanAddr,
451) -> StdResult<TransferHistory> {
452    let answer: AuthenticatedQueryResponse = QueryMsg::TransferHistory {
453        address,
454        key,
455        page,
456        page_size,
457    }
458        .query(querier, block_size, callback_code_hash, contract_addr)?;
459    match answer {
460        AuthenticatedQueryResponse::TransferHistory { txs, total } => {
461            Ok(TransferHistory { txs, total })
462        }
463        AuthenticatedQueryResponse::ViewingKeyError { .. } => Err(StdError::unauthorized()),
464        _ => Err(StdError::generic_err(
465            "Invalid TransferHistory query response",
466        )),
467    }
468}
469
470/// Returns a StdResult<TransactionHistory> from performing TransactionHistory query
471///
472/// # Arguments
473///
474/// * `querier` - a reference to the Querier dependency of the querying contract
475/// * `address` - the address whose transaction history should be displayed
476/// * `key` - String holding the authentication key needed to view transactions
477/// * `page` - Optional u32 representing the page number of transactions to display
478/// * `page_size` - u32 number of transactions to return
479/// * `block_size` - pad the message to blocks of this size
480/// * `callback_code_hash` - String holding the code hash of the contract being queried
481/// * `contract_addr` - address of the contract being queried
482#[allow(clippy::too_many_arguments)]
483pub fn transaction_history_query<Q: Querier>(
484    querier: &Q,
485    address: HumanAddr,
486    key: String,
487    page: Option<u32>,
488    page_size: u32,
489    block_size: usize,
490    callback_code_hash: String,
491    contract_addr: HumanAddr,
492) -> StdResult<TransactionHistory> {
493    let answer: AuthenticatedQueryResponse = QueryMsg::TransactionHistory {
494        address,
495        key,
496        page,
497        page_size,
498    }
499        .query(querier, block_size, callback_code_hash, contract_addr)?;
500    match answer {
501        AuthenticatedQueryResponse::TransactionHistory { txs, total } => {
502            Ok(TransactionHistory { txs, total })
503        }
504        AuthenticatedQueryResponse::ViewingKeyError { .. } => Err(StdError::unauthorized()),
505        _ => Err(StdError::generic_err(
506            "Invalid TransactionHistory query response",
507        )),
508    }
509}
510
511/// Returns a StdResult<Minters> from performing Minters query
512///
513/// # Arguments
514///
515/// * `querier` - a reference to the Querier dependency of the querying contract
516/// * `block_size` - pad the message to blocks of this size
517/// * `callback_code_hash` - String holding the code hash of the contract being queried
518/// * `contract_addr` - address of the contract being queried
519pub fn minters_query<Q: Querier>(
520    querier: &Q,
521    block_size: usize,
522    callback_code_hash: String,
523    contract_addr: HumanAddr,
524) -> StdResult<Minters> {
525    let answer: MintersResponse =
526        QueryMsg::Minters {}.query(querier, block_size, callback_code_hash, contract_addr)?;
527    Ok(answer.minters)
528}