miden_objects/asset/
token_symbol.rs1use alloc::string::String;
2
3use super::{AssetError, Felt};
4
5#[derive(Default, Clone, Copy, Debug)]
6pub struct TokenSymbol(Felt);
7
8impl TokenSymbol {
9 pub const MAX_SYMBOL_LENGTH: usize = 6;
10 pub const MAX_ENCODED_VALUE: u64 = 26u64.pow(TokenSymbol::MAX_SYMBOL_LENGTH as u32);
11
12 pub fn new(symbol: &str) -> Result<Self, AssetError> {
13 let felt = encode_symbol_to_felt(symbol)?;
14 Ok(Self(felt))
15 }
16
17 pub fn to_str(&self) -> String {
18 decode_felt_to_symbol(self.0)
19 }
20}
21
22impl From<TokenSymbol> for Felt {
23 fn from(symbol: TokenSymbol) -> Self {
24 symbol.0
25 }
26}
27
28impl TryFrom<&str> for TokenSymbol {
29 type Error = AssetError;
30
31 fn try_from(symbol: &str) -> Result<Self, Self::Error> {
32 TokenSymbol::new(symbol)
33 }
34}
35
36impl TryFrom<Felt> for TokenSymbol {
37 type Error = AssetError;
38
39 fn try_from(felt: Felt) -> Result<Self, Self::Error> {
40 if felt.as_int() >= TokenSymbol::MAX_ENCODED_VALUE {
42 return Err(AssetError::TokenSymbolError(format!(
43 "token symbol value {} cannot exceed {}",
44 felt.as_int(),
45 TokenSymbol::MAX_ENCODED_VALUE
46 )));
47 }
48 Ok(TokenSymbol(felt))
49 }
50}
51
52fn encode_symbol_to_felt(s: &str) -> Result<Felt, AssetError> {
57 if s.is_empty() || s.len() > TokenSymbol::MAX_SYMBOL_LENGTH {
58 return Err(AssetError::TokenSymbolError(format!(
59 "token symbol of length {} is not between 1 and 6 characters long",
60 s.len()
61 )));
62 } else if s.chars().any(|c| !c.is_ascii_uppercase()) {
63 return Err(AssetError::TokenSymbolError(format!(
64 "token symbol {} contains characters that are not uppercase ASCII",
65 s
66 )));
67 }
68
69 let mut encoded_value = 0;
70 for char in s.chars() {
71 let digit = char as u64 - b'A' as u64;
72 assert!(digit < 26);
73 encoded_value = encoded_value * 26 + digit;
74 }
75
76 Ok(Felt::new(encoded_value))
77}
78
79fn decode_felt_to_symbol(encoded_felt: Felt) -> String {
80 let encoded_value = encoded_felt.as_int();
81 assert!(encoded_value < 26u64.pow(TokenSymbol::MAX_SYMBOL_LENGTH as u32));
82
83 let mut decoded_string = String::new();
84 let mut remaining_value = encoded_value;
85
86 for _ in 0..6 {
87 let digit = (remaining_value % 26) as u8;
88 let char = (digit + b'A') as char;
89 decoded_string.insert(0, char);
90 remaining_value /= 26;
91 }
92
93 decoded_string
94}
95
96#[test]
99fn test_token_symbol_decoding_encoding() {
100 let symbols = vec!["AAAAAA", "AAAAAB", "AAAAAC", "AAAAAD", "AAAAAE", "AAAAAF", "AAAAAG"];
101 for symbol in symbols {
102 let token_symbol = TokenSymbol::try_from(symbol).unwrap();
103 let decoded_symbol = TokenSymbol::to_str(&token_symbol);
104 assert_eq!(symbol, decoded_symbol);
105 }
106
107 let symbol = "";
108 let felt = encode_symbol_to_felt(symbol);
109 assert!(felt.is_err());
110
111 let symbol = "ABCDEFG";
112 let felt = encode_symbol_to_felt(symbol);
113 assert!(felt.is_err());
114
115 let symbol = "$$$";
116 let felt = encode_symbol_to_felt(symbol);
117 assert!(felt.is_err());
118
119 let symbol = "ABCDEF";
120 let token_symbol = TokenSymbol::try_from(symbol);
121 assert!(token_symbol.is_ok());
122 let token_symbol_felt: Felt = token_symbol.unwrap().into();
123 assert_eq!(token_symbol_felt, encode_symbol_to_felt(symbol).unwrap());
124}