starfleit/
token.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use cosmwasm_std::{StdError, StdResult, Uint128};
5use cw20::{Cw20Coin, MinterResponse};
6
7/// TokenContract InstantiateMsg
8#[derive(Serialize, Deserialize, JsonSchema)]
9pub struct InstantiateMsg {
10    pub name: String,
11    pub symbol: String,
12    pub decimals: u8,
13    pub initial_balances: Vec<Cw20Coin>,
14    pub mint: Option<MinterResponse>,
15}
16
17impl InstantiateMsg {
18    pub fn get_cap(&self) -> Option<Uint128> {
19        self.mint.as_ref().and_then(|v| v.cap)
20    }
21
22    pub fn validate(&self) -> StdResult<()> {
23        // Check name, symbol, decimals
24        if !is_valid_name(&self.name) {
25            return Err(StdError::generic_err(
26                "Name is not in the expected format (3-50 UTF-8 bytes)",
27            ));
28        }
29        if !is_valid_symbol(&self.symbol) {
30            return Err(StdError::generic_err(
31                "Ticker symbol is not in expected format [a-zA-Z\\-]{3,12}",
32            ));
33        }
34        if self.decimals > 18 {
35            return Err(StdError::generic_err("Decimals must not exceed 18"));
36        }
37        Ok(())
38    }
39}
40
41fn is_valid_name(name: &str) -> bool {
42    let bytes = name.as_bytes();
43    if bytes.len() < 3 || bytes.len() > 50 {
44        return false;
45    }
46    true
47}
48
49fn is_valid_symbol(symbol: &str) -> bool {
50    let bytes = symbol.as_bytes();
51    if bytes.len() < 3 || bytes.len() > 12 {
52        return false;
53    }
54    for byte in bytes.iter() {
55        if (*byte != 45) && (*byte < 65 || *byte > 90) && (*byte < 97 || *byte > 122) {
56            return false;
57        }
58    }
59    true
60}
61
62#[cfg(test)]
63mod test {
64    use super::*;
65
66    #[test]
67    fn get_cap() {
68        let msg = InstantiateMsg {
69            decimals: 6u8,
70            initial_balances: vec![],
71            mint: Some(MinterResponse {
72                cap: Some(Uint128::from(1u128)),
73                minter: "minter0000".to_string(),
74            }),
75            name: "test_token".to_string(),
76            symbol: "TNT".to_string(),
77        };
78
79        assert_eq!(msg.get_cap(), Some(Uint128::from(1u128)))
80    }
81
82    #[test]
83    fn validate() {
84        let valid_msg = InstantiateMsg {
85            decimals: 6u8,
86            initial_balances: vec![],
87            mint: Some(MinterResponse {
88                cap: Some(Uint128::from(1u128)),
89                minter: "minter0000".to_string(),
90            }),
91            name: "test_token".to_string(),
92            symbol: "TNT".to_string(),
93        };
94
95        assert_eq!(valid_msg.validate(), Ok(()));
96
97        let name_invalid_msg = InstantiateMsg {
98            decimals: 6u8,
99            initial_balances: vec![],
100            mint: Some(MinterResponse {
101                cap: Some(Uint128::from(1u128)),
102                minter: "minter0000".to_string(),
103            }),
104            name: "a".to_string(),
105            symbol: "TNT".to_string(),
106        };
107
108        assert_eq!(
109            name_invalid_msg.validate(),
110            Err(StdError::generic_err(
111                "Name is not in the expected format (3-50 UTF-8 bytes)",
112            ))
113        );
114
115        let symbol_invalid_msg = InstantiateMsg {
116            decimals: 6u8,
117            initial_balances: vec![],
118            mint: Some(MinterResponse {
119                cap: Some(Uint128::from(1u128)),
120                minter: "minter0000".to_string(),
121            }),
122            name: "test_token".to_string(),
123            symbol: "TN".to_string(),
124        };
125
126        assert_eq!(
127            symbol_invalid_msg.validate(),
128            Err(StdError::generic_err(
129                "Ticker symbol is not in expected format [a-zA-Z\\-]{3,12}",
130            ))
131        );
132
133        let decimal_invalid_msg = InstantiateMsg {
134            decimals: 20u8,
135            initial_balances: vec![],
136            mint: Some(MinterResponse {
137                cap: Some(Uint128::from(1u128)),
138                minter: "minter0000".to_string(),
139            }),
140            name: "test_token".to_string(),
141            symbol: "TNT".to_string(),
142        };
143
144        assert_eq!(
145            decimal_invalid_msg.validate(),
146            Err(StdError::generic_err("Decimals must not exceed 18"))
147        );
148    }
149}