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#[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#[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#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
33pub enum ContractStatusLevel {
34 NormalRun,
35 StopAllButRedeems,
36 StopAll,
37}
38
39#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
41pub struct ContractStatus {
42 pub status: ContractStatusLevel,
43}
44
45#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
47pub struct ExchangeRate {
48 pub rate: Uint128,
49 pub denom: String,
50}
51
52#[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#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
64pub struct Balance {
65 pub amount: Uint128,
66}
67
68#[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 pub block_time: Option<u64>,
81 pub block_height: Option<u64>,
82}
83
84#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
86pub struct TransferHistory {
87 pub total: Option<u64>,
88 pub txs: Vec<Tx>,
89}
90
91#[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#[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#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
126pub struct TransactionHistory {
127 pub total: Option<u64>,
128 pub txs: Vec<RichTx>,
129}
130
131#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
133pub struct Minters {
134 pub minters: Vec<HumanAddr>,
135}
136
137#[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 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 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#[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#[derive(Deserialize)]
246pub struct TokenInfoResponse {
247 pub token_info: TokenInfo,
248}
249
250#[derive(Deserialize)]
252pub struct TokenConfigResponse {
253 pub token_config: TokenConfig,
254}
255
256#[derive(Deserialize)]
258pub struct ContractStatusResponse {
259 pub contract_status: ContractStatus,
260}
261
262#[derive(Deserialize)]
264pub struct ExchangeRateResponse {
265 pub exchange_rate: ExchangeRate,
266}
267
268#[derive(Deserialize)]
270pub struct MintersResponse {
271 pub minters: Minters,
272}
273
274pub 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
293pub 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
312pub 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
335pub 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#[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
398pub 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#[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#[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
511pub 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}