hmip20/
msg.rs

1#![allow(clippy::field_reassign_with_default)] // This is triggered in `#[derive(JsonSchema)]`
2
3use hermit_toolkit_permit::Permit;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7use cosmwasm_std::{Binary, HumanAddr, StdError, StdResult, Uint128};
8
9use crate::batch;
10use crate::transaction_history::{RichTx, Tx};
11use crate::viewing_key::ViewingKey;
12
13#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
14pub struct InitHook {
15    pub msg: Binary,
16    pub contract_addr: HumanAddr,
17    pub code_hash: String,
18}
19
20#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema)]
21pub struct InitialBalance {
22    pub address: HumanAddr,
23    pub amount: Uint128,
24}
25
26// 初始化消息结构体
27#[derive(Serialize, Deserialize, JsonSchema)]
28pub struct InitMsg {
29    pub name: String,
30    pub admin: Option<HumanAddr>,
31    pub symbol: String,
32    pub decimals: u8,
33    pub initial_balances: Option<Vec<InitialBalance>>,
34    pub prng_seed: Binary,
35    pub config: Option<InitConfig>,
36    pub init_hook: Option<InitHook>,
37}
38
39impl InitMsg {
40    pub fn config(&self) -> InitConfig {
41        self.config.clone().unwrap_or_default()
42    }
43}
44
45// 初始化配置
46#[derive(Serialize, Deserialize, JsonSchema, Clone, Default, Debug)]
47#[serde(rename_all = "snake_case")]
48pub struct InitConfig {
49    public_total_supply: Option<bool>,
50    enable_deposit: Option<bool>,
51    enable_redeem: Option<bool>,
52    enable_mint: Option<bool>,
53    enable_burn: Option<bool>,
54}
55
56impl InitConfig {
57    pub fn public_total_supply(&self) -> bool {
58        self.public_total_supply.unwrap_or(false)
59    }
60
61    pub fn deposit_enabled(&self) -> bool {
62        self.enable_deposit.unwrap_or(false)
63    }
64
65    pub fn redeem_enabled(&self) -> bool {
66        self.enable_redeem.unwrap_or(false)
67    }
68
69    pub fn mint_enabled(&self) -> bool {
70        self.enable_mint.unwrap_or(false)
71    }
72
73    pub fn burn_enabled(&self) -> bool {
74        self.enable_burn.unwrap_or(false)
75    }
76}
77
78///////////////////////////////////////////////////
79//                  Handle Msg
80///////////////////////////////////////////////////
81#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
82#[serde(rename_all = "snake_case")]
83pub enum HandleMsg {
84    Redeem {
85        amount: Uint128,
86        denom: Option<String>,
87        padding: Option<String>,
88    },
89    Deposit {
90        padding: Option<String>,
91    },
92
93    // bash erc20 stuff
94    Transfer {
95        recipient: HumanAddr,
96        amount: Uint128,
97        memo: Option<String>,
98        padding: Option<String>,
99    },
100    Send {
101        recipient: HumanAddr,
102        recipient_code_hash: Option<String>,
103        amount: Uint128,
104        msg: Option<Binary>,
105        memo: Option<String>,
106        padding: Option<String>,
107    },
108    BatchTransfer {
109        actions: Vec<batch::TransferAction>,
110        padding: Option<String>,
111    },
112    BatchSend {
113        actions: Vec<batch::SendAction>,
114        padding: Option<String>,
115    },
116    Burn {
117        amount: Uint128,
118        memo: Option<String>,
119        padding: Option<String>,
120    },
121    RegisterReceive {
122        code_hash: String,
123        padding: Option<String>,
124    },
125    CreateViewingKey {
126        entropy: String,
127        padding: Option<String>,
128    },
129    SetViewingKey {
130        key: String,
131        padding: Option<String>,
132    },
133
134    // allowance
135    IncreaseAllowance {
136        spender: HumanAddr,
137        amount: Uint128,
138        expiration: Option<u64>,
139        padding: Option<String>,
140    },
141    DecreaseAllowance {
142        spender: HumanAddr,
143        amount: Uint128,
144        expiration: Option<u64>,
145        padding: Option<String>,
146    },
147    TransferFrom {
148        owner: HumanAddr,
149        recipient: HumanAddr,
150        amount: Uint128,
151        memo: Option<String>,
152        padding: Option<String>,
153    },
154    SendFrom {
155        owner: HumanAddr,
156        recipient: HumanAddr,
157        recipient_code_hash: Option<String>,
158        amount: Uint128,
159        msg: Option<Binary>,
160        memo: Option<String>,
161        padding: Option<String>,
162    },
163    BatchTransferFrom {
164        actions: Vec<batch::TransferFromAction>,
165        padding: Option<String>,
166    },
167    BatchSendFrom {
168        actions: Vec<batch::SendFromAction>,
169        padding: Option<String>,
170    },
171    BurnFrom {
172        owner: HumanAddr,
173        amount: Uint128,
174        memo: Option<String>,
175        padding: Option<String>,
176    },
177    BatchBurnFrom {
178        actions: Vec<batch::BurnFromAction>,
179        padding: Option<String>,
180    },
181
182    // mint
183    Mint {
184        recipient: HumanAddr,
185        amount: Uint128,
186        memo: Option<String>,
187        padding: Option<String>,
188    },
189    BatchMint {
190        actions: Vec<batch::MintAction>,
191        padding: Option<String>,
192    },
193    AddMinters {
194        minters: Vec<HumanAddr>,
195        padding: Option<String>,
196    },
197    RemoveMinters {
198        minters: Vec<HumanAddr>,
199        padding: Option<String>,
200    },
201    SetMinters {
202        minters: Vec<HumanAddr>,
203        padding: Option<String>,
204    },
205
206    // admin
207    ChangeAdmin {
208        address: HumanAddr,
209        padding: Option<String>,
210    },
211    SetContractStatus {
212        level: ContractStatusLevel,
213        padding: Option<String>,
214    },
215
216    // permit
217    RevokePermit {
218        permit_name: String,
219        padding: Option<String>,
220    },
221}
222
223#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
224#[serde(rename_all = "snake_case")]
225pub enum HandleAnswer {
226    // native
227    Deposit {
228        status: ResponseStatus,
229    },
230    Redeem {
231        status: ResponseStatus,
232    },
233
234    // base
235    Transfer {
236        status: ResponseStatus,
237    },
238    Send {
239        status: ResponseStatus,
240    },
241    BatchTransfer {
242        status: ResponseStatus,
243    },
244    BatchSend {
245        status: ResponseStatus,
246    },
247    Burn {
248        status: ResponseStatus,
249    },
250    RegisterReceive {
251        status: ResponseStatus,
252    },
253    CreateViewingKey {
254        key: ViewingKey,
255    },
256    SetViewingKey {
257        status: ResponseStatus,
258    },
259
260    // Allowance
261    IncreaseAllowance {
262        spender: HumanAddr,
263        owner: HumanAddr,
264        allowance: Uint128,
265    },
266    DecreaseAllowance {
267        spender: HumanAddr,
268        owner: HumanAddr,
269        allowance: Uint128,
270    },
271    TransferFrom {
272        status: ResponseStatus,
273    },
274    SendFrom {
275        status: ResponseStatus,
276    },
277    BatchTransferFrom {
278        status: ResponseStatus,
279    },
280    BatchSendFrom {
281        status: ResponseStatus,
282    },
283    BurnFrom {
284        status: ResponseStatus,
285    },
286    BatchBurnFrom {
287        status: ResponseStatus,
288    },
289
290    // mint
291    Mint {
292        status: ResponseStatus,
293    },
294    BatchMint {
295        status: ResponseStatus,
296    },
297    AddMinters {
298        status: ResponseStatus,
299    },
300    RemoveMinters {
301        status: ResponseStatus,
302    },
303    SetMinters {
304        status: ResponseStatus,
305    },
306
307    // other
308    ChangeAdmin {
309        status: ResponseStatus,
310    },
311    SetContractStatus {
312        status: ResponseStatus,
313    },
314
315    // Permit
316    RevokePermit {
317        status: ResponseStatus,
318    },
319}
320
321///////////////////////////////////////////////////
322//                  Query Msg
323///////////////////////////////////////////////////
324#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
325#[serde(rename_all = "snake_case")]
326pub enum QueryMsg {
327    TokenInfo {},
328    TokenConfig {},
329    ContractStatus {},
330    ExchangeRate {},
331    Allowance {
332        owner: HumanAddr,
333        spender: HumanAddr,
334        key: String,
335    },
336    Balance {
337        address: HumanAddr,
338        key: String,
339    },
340    TransferHistory {
341        address: HumanAddr,
342        key: String,
343        page: Option<u32>,
344        page_size: u32,
345    },
346    TransactionHistory {
347        address: HumanAddr,
348        key: String,
349        page: Option<u32>,
350        page_size: u32,
351    },
352    Minters {},
353    WithPermit {
354        permit: Permit,
355        query: QueryWithPermit,
356    },
357}
358
359impl QueryMsg {
360    pub fn get_validation_params(&self) -> (Vec<&HumanAddr>, ViewingKey) {
361        match self {
362            Self::Balance { address, key } => (vec![address], ViewingKey(key.clone())),
363            Self::TransferHistory { address, key, .. } => (vec![address], ViewingKey(key.clone())),
364            Self::TransactionHistory { address, key, .. } => {
365                (vec![address], ViewingKey(key.clone()))
366            }
367            Self::Allowance {
368                owner,
369                spender,
370                key,
371                ..
372            } => (vec![owner, spender], ViewingKey(key.clone())),
373            _ => panic!("This query type does not require authentication"),
374        }
375    }
376}
377
378/////////////////////////////////////////
379//             permit
380/////////////////////////////////////////
381#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
382#[serde(rename_all = "snake_case")]
383pub enum QueryWithPermit {
384    Allowance {
385        owner: HumanAddr,
386        spender: HumanAddr,
387    },
388    Balance {},
389    TransferHistory {
390        page: Option<u32>,
391        page_size: u32,
392    },
393    TransactionHistory {
394        page: Option<u32>,
395        page_size: u32,
396    },
397}
398
399#[derive(Serialize, Deserialize, JsonSchema, Debug)]
400#[serde(rename_all = "snake_case")]
401pub enum QueryAnswer {
402    TokenInfo {
403        name: String,
404        symbol: String,
405        decimals: u8,
406        total_supply: Option<Uint128>,
407    },
408    TokenConfig {
409        public_total_supply: bool,
410        deposit_enabled: bool,
411        redeem_enabled: bool,
412        mint_enabled: bool,
413        burn_enabled: bool,
414    },
415    ContractStatus {
416        status: ContractStatusLevel,
417    },
418    ExchangeRate {
419        rate: Uint128,
420        denom: String,
421    },
422    Allowance {
423        spender: HumanAddr,
424        owner: HumanAddr,
425        allowance: Uint128,
426        expiration: Option<u64>,
427    },
428    Balance {
429        amount: Uint128,
430    },
431    TransferHistory {
432        txs: Vec<Tx>,
433        total: Option<u64>,
434    },
435    TransactionHistory {
436        txs: Vec<RichTx>,
437        total: Option<u64>,
438    },
439    ViewingKeyError {
440        msg: String,
441    },
442    Minters {
443        minters: Vec<HumanAddr>,
444    },
445}
446
447#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema)]
448pub struct CreateViewingKeyResponse {
449    pub key: String,
450}
451
452#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
453#[serde(rename_all = "snake_case")]
454pub enum ResponseStatus {
455    Success,
456    Failure,
457}
458
459#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
460#[serde(rename_all = "snake_case")]
461pub enum ContractStatusLevel {
462    NormalRun,
463    StopAllButRedeems,
464    StopAll,
465}
466
467pub fn status_level_to_u8(status_level: ContractStatusLevel) -> u8 {
468    match status_level {
469        ContractStatusLevel::NormalRun => 0,
470        ContractStatusLevel::StopAllButRedeems => 1,
471        ContractStatusLevel::StopAll => 2,
472    }
473}
474
475pub fn u8_to_status_level(status_level: u8) -> StdResult<ContractStatusLevel> {
476    match status_level {
477        0 => Ok(ContractStatusLevel::NormalRun),
478        1 => Ok(ContractStatusLevel::StopAllButRedeems),
479        2 => Ok(ContractStatusLevel::StopAll),
480        _ => Err(StdError::generic_err("Invalid state level")),
481    }
482}
483
484// Take a Vec<u8> and pad it up to a multiple of `block_size`, using spaces at the end.
485pub fn space_pad(block_size: usize, message: &mut Vec<u8>) -> &mut Vec<u8> {
486    let len = message.len();
487    let surplus = len % block_size;
488    if surplus == 0 {
489        return message;
490    }
491
492    let missing = block_size - surplus;
493    message.reserve(missing);
494    message.extend(std::iter::repeat(b' ').take(missing));
495    message
496}
497
498#[cfg(test)]
499mod tests {
500    use super::*;
501    use cosmwasm_std::{from_slice, StdResult};
502
503    #[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
504    #[serde(rename_all = "snake_case")]
505    pub enum Something {
506        Var { padding: Option<String> },
507    }
508
509    #[test]
510    fn test_deserialization_of_missing_option_fields() -> StdResult<()> {
511        let input = b"{ \"var\": {} }";
512        let obj: Something = from_slice(input)?;
513        assert_eq!(
514            obj,
515            Something::Var { padding: None },
516            "unexpected value: {:?}",
517            obj
518        );
519        Ok(())
520    }
521}