antelope/chain/
asset.rs

1use core::ops;
2use serde::{de, Deserialize, Deserializer, Serialize};
3use std::fmt;
4use std::fmt::{Display, Formatter};
5
6use crate::chain::{name::Name, Decoder, Encoder, Packer};
7
8const MAX_AMOUNT: i64 = (1 << 62) - 1;
9const MAX_PRECISION: u8 = 18;
10
11/// Check if the given symbol code is valid.
12pub fn is_valid_symbol_code(sym: u64) -> bool {
13    let mut i: i32 = 0;
14    let mut tmp = sym;
15    if (sym >> 56) != 0 {
16        return false;
17    }
18
19    for j in 0..7 {
20        let c = (tmp & 0xFF) as u8;
21        if !c.is_ascii_uppercase() {
22            return false;
23        }
24
25        tmp >>= 8;
26        if (tmp & 0xFF) == 0 {
27            break;
28        }
29        i = j;
30    }
31    i += 1;
32
33    for _ in i..7 {
34        tmp >>= 8;
35        if (tmp & 0xFF) != 0 {
36            return false;
37        }
38    }
39    true
40}
41
42#[derive(Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
43pub struct SymbolCode {
44    pub value: u64,
45}
46
47impl SymbolCode {
48    pub fn new(sym: &str) -> Self {
49        let raw = sym.as_bytes();
50        assert!(raw.len() < 7 && !raw.is_empty(), "bad symbol name");
51
52        let mut value: u64 = 0;
53        for i in (0..raw.len()).rev() {
54            let c = raw[i];
55            assert!(c.is_ascii_uppercase(), "invalid symbol code character");
56            value <<= 8;
57            value |= c as u64;
58        }
59        Self { value }
60    }
61
62    pub fn value(&self) -> u64 {
63        self.value
64    }
65
66    pub fn as_string(&self) -> String {
67        let mut v: Vec<u8> = Vec::with_capacity(7);
68        let mut tmp = self.value;
69        for _ in 0..7 {
70            let c = (tmp & 0xff) as u8;
71            assert!(c.is_ascii_uppercase(), "invalid symbol character");
72            v.push(c);
73            tmp >>= 8;
74            if tmp == 0 {
75                break;
76            }
77        }
78        String::from_utf8(v).unwrap()
79    }
80
81    pub fn is_valid(&self) -> bool {
82        is_valid_symbol_code(self.value)
83    }
84}
85
86impl Display for SymbolCode {
87    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
88        write!(f, "{}", self.as_string())
89    }
90}
91
92impl Packer for SymbolCode {
93    fn size(&self) -> usize {
94        8
95    }
96
97    fn pack(&self, enc: &mut Encoder) -> usize {
98        self.value.pack(enc)
99    }
100
101    fn unpack(&mut self, data: &[u8]) -> usize {
102        assert!(
103            data.len() >= self.size(),
104            "SymbolCode.unpack: buffer overflow"
105        );
106        self.value.unpack(data);
107        assert!(self.is_valid(), "SymbolCode.unpack:: bad symbol code");
108        8
109    }
110}
111
112#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
113pub struct Symbol {
114    value: u64,
115}
116
117impl Symbol {
118    pub fn new(name: &str, precision: u8) -> Self {
119        let raw = name.as_bytes();
120        assert!(raw.len() < 7 && !raw.is_empty(), "bad symbol name");
121
122        let mut value: u64 = 0;
123        for i in (0..raw.len()).rev() {
124            let c = raw[i];
125            assert!(c.is_ascii_uppercase(), "invalid symbol character");
126            value <<= 8;
127            value |= c as u64;
128        }
129
130        value <<= 8;
131        value |= precision as u64;
132        Self { value }
133    }
134
135    pub fn value(&self) -> u64 {
136        self.value
137    }
138
139    pub fn code(&self) -> SymbolCode {
140        SymbolCode {
141            value: self.value >> 8,
142        }
143    }
144
145    pub fn precision(&self) -> usize {
146        (self.value & 0xFF) as usize
147    }
148
149    pub fn as_string(&self) -> String {
150        self.precision().to_string() + "," + &self.code().to_string()
151    }
152
153    pub fn is_valid(&self) -> bool {
154        self.code().is_valid()
155    }
156}
157
158impl Display for Symbol {
159    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
160        write!(f, "{}", self.as_string())
161    }
162}
163
164impl Packer for Symbol {
165    fn size(&self) -> usize {
166        8
167    }
168
169    fn pack(&self, enc: &mut Encoder) -> usize {
170        self.value.pack(enc)
171    }
172
173    fn unpack(&mut self, data: &[u8]) -> usize {
174        assert!(data.len() >= self.size(), "Symbol.unpack: buffer overflow");
175        self.value.unpack(data);
176        assert!(self.code().is_valid(), "Symbol.unpack: bad symbol value");
177        8
178    }
179}
180
181#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
182pub struct Asset {
183    amount: i64,
184    symbol: Symbol,
185}
186
187#[derive(Copy, Clone, Eq, PartialEq)]
188enum AssetStringParseStatus {
189    Initial,
190    FoundDot,
191    FoundSpace,
192}
193
194fn is_amount_within_range(amount: i64) -> bool {
195    (-MAX_AMOUNT..=MAX_AMOUNT).contains(&amount)
196}
197
198impl Asset {
199    pub fn new(amount: i64, symbol: Symbol) -> Self {
200        assert!(
201            is_amount_within_range(amount),
202            "magnitude of asset amount must be less than 2^62"
203        );
204        assert!(symbol.is_valid(), "invalid symbol name");
205        Self { amount, symbol }
206    }
207
208    pub fn from_string(s: &str) -> Self {
209        assert!(!s.is_empty(), "Asset.from_string: empty string");
210        let mut status = AssetStringParseStatus::Initial;
211        let mut raw = s.as_bytes();
212
213        let mut minus: bool = false;
214        let mut amount: i64 = 0;
215        let mut symbol: u64 = 0;
216        let mut precision: u8 = 0;
217        let mut raw_symbol: Vec<u8> = Vec::with_capacity(7);
218
219        if raw[0] == b'-' {
220            minus = true;
221            raw = &raw[1..];
222        }
223
224        for &c in raw {
225            if c == b'.' {
226                assert!(
227                    status == AssetStringParseStatus::Initial,
228                    "Asset.from_string: invalid dot character"
229                );
230                status = AssetStringParseStatus::FoundDot;
231                continue;
232            } else if c == b' ' {
233                assert!(
234                    status == AssetStringParseStatus::Initial
235                        || status == AssetStringParseStatus::FoundDot,
236                    "Asset.from_string: invalid space character"
237                );
238                // if status == AssetStringParseStatus::FoundDot {
239                //     assert!(precision > 0, "Asset.from_string: invalid precision");
240                // }
241                status = AssetStringParseStatus::FoundSpace;
242                continue;
243            }
244
245            match status {
246                AssetStringParseStatus::Initial => {
247                    assert!(c.is_ascii_digit(), "Asset.from_string: bad amount");
248                    amount *= 10;
249                    amount += (c - b'0') as i64;
250                    assert!(is_amount_within_range(amount), "bad amount");
251                }
252                AssetStringParseStatus::FoundDot => {
253                    assert!(c.is_ascii_digit(), "Asset.from_string: bad amount");
254                    amount *= 10;
255                    amount += (c - b'0') as i64;
256                    precision += 1;
257                    assert!(
258                        precision <= MAX_PRECISION,
259                        "Asset.from_string: bad precision"
260                    );
261                    assert!(is_amount_within_range(amount), "bad amount");
262                }
263                AssetStringParseStatus::FoundSpace => {
264                    assert!(c.is_ascii_uppercase(), "Asset.from_string: bad symbol");
265                    raw_symbol.push(c);
266                    assert!(raw_symbol.len() < 7, "Asset.from_string: bad symbol");
267                }
268            }
269        }
270
271        assert!(!raw_symbol.is_empty(), "Asset.from_string: bad symbol");
272
273        if minus {
274            amount = -amount;
275        }
276
277        raw_symbol.reverse();
278        for c in raw_symbol {
279            symbol <<= 8;
280            symbol |= c as u64;
281        }
282
283        symbol <<= 8;
284        symbol |= precision as u64;
285
286        Self {
287            amount,
288            symbol: Symbol { value: symbol },
289        }
290    }
291
292    pub fn amount(&self) -> i64 {
293        self.amount
294    }
295
296    pub fn symbol(&self) -> Symbol {
297        self.symbol
298    }
299
300    pub fn as_string(self) -> String {
301        let mut part1: i64 = self.amount;
302
303        for _ in 0..self.symbol.precision() {
304            part1 /= 10;
305        }
306
307        let mut part2: Vec<u8> = vec![0u8; self.symbol.precision()];
308
309        let mut tmp: i64 = self.amount;
310        for i in (0..self.symbol.precision()).rev() {
311            part2[i] = b'0' + (tmp % 10) as u8;
312            tmp /= 10;
313        }
314        let mut decimal = String::from_utf8(part2).unwrap();
315        if !decimal.is_empty() {
316            decimal = String::from(".") + decimal.as_str();
317        }
318
319        part1.to_string() + decimal.as_str() + " " + &self.symbol.code().to_string()
320    }
321
322    pub fn is_valid(&self) -> bool {
323        is_amount_within_range(self.amount) && self.symbol().is_valid()
324    }
325}
326
327impl Display for Asset {
328    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
329        write!(f, "{}", self.as_string())
330    }
331}
332
333// assert!(a.symbol.value == b.symbol.value, "symbol not the same");
334// let amount: i64 = a.amount + b.amount;
335// assert!(-MAX_AMOUNT <= amount, "addition underflow");
336// assert!(amount <= MAX_AMOUNT, "addition overflow");
337// return new Asset(amount, Symbol.fromU64(a.symbol.value));
338
339impl ops::Add for Asset {
340    type Output = Self;
341
342    fn add(self, rhs: Self) -> Self::Output {
343        assert!(self.symbol == rhs.symbol, "add: bad symbol");
344        let amount = self.amount + rhs.amount;
345        assert!(amount >= -MAX_AMOUNT, "addition underflow");
346        assert!(amount <= MAX_AMOUNT, "addition overflow");
347        Self {
348            amount,
349            symbol: self.symbol,
350        }
351    }
352}
353
354impl ops::AddAssign for Asset {
355    fn add_assign(&mut self, rhs: Asset) {
356        *self = *self + rhs;
357    }
358}
359
360impl ops::Sub for Asset {
361    type Output = Self;
362
363    fn sub(self, rhs: Self) -> Self::Output {
364        assert!(self.symbol == rhs.symbol, "sub: bad symbol");
365        let amount = self.amount() - rhs.amount();
366        assert!(amount >= -MAX_AMOUNT, "subtraction underflow");
367        assert!(amount <= MAX_AMOUNT, "subtraction overflow");
368        Self {
369            amount,
370            symbol: self.symbol,
371        }
372    }
373}
374
375impl ops::SubAssign for Asset {
376    fn sub_assign(&mut self, rhs: Asset) {
377        *self = *self - rhs;
378    }
379}
380
381impl Packer for Asset {
382    fn size(&self) -> usize {
383        16
384    }
385
386    fn pack(&self, enc: &mut Encoder) -> usize {
387        let pos = enc.get_size();
388
389        self.amount.pack(enc);
390        self.symbol.pack(enc);
391
392        enc.get_size() - pos
393    }
394
395    fn unpack(&mut self, data: &[u8]) -> usize {
396        assert!(data.len() >= self.size(), "Asset.unpack: buffer overflow");
397
398        let mut dec = Decoder::new(data);
399        dec.unpack(&mut self.amount);
400        assert!(
401            self.amount >= -MAX_AMOUNT && self.amount <= MAX_AMOUNT,
402            "Asset.unpack: bad asset amount"
403        );
404        dec.unpack(&mut self.symbol);
405        dec.get_pos()
406    }
407}
408
409pub(crate) fn deserialize_asset<'de, D>(deserializer: D) -> Result<Asset, D::Error>
410where
411    D: Deserializer<'de>,
412{
413    struct AssetVisitor;
414
415    impl<'de> de::Visitor<'de> for AssetVisitor {
416        type Value = Asset;
417
418        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
419            formatter.write_str("a string representing an asset in the format 'amount symbol_code'")
420        }
421
422        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
423        where
424            E: de::Error,
425        {
426            // Directly return the Asset instance since Asset::from_string does not produce errors.
427            Ok(Asset::from_string(value))
428        }
429    }
430
431    deserializer.deserialize_str(AssetVisitor)
432}
433
434#[derive(Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
435pub struct ExtendedAsset {
436    quantity: Asset,
437    contract: Name,
438}
439
440impl ExtendedAsset {
441    pub fn new(quantity: Asset, contract: Name) -> Self {
442        Self { quantity, contract }
443    }
444
445    pub fn quantity(&self) -> Asset {
446        self.quantity
447    }
448
449    pub fn contract(&self) -> Name {
450        self.contract
451    }
452}
453
454impl Packer for ExtendedAsset {
455    fn size(&self) -> usize {
456        16 + 8
457    }
458
459    fn pack(&self, enc: &mut Encoder) -> usize {
460        let pos = enc.get_size();
461
462        self.quantity.pack(enc);
463        self.contract.pack(enc);
464
465        enc.get_size() - pos
466    }
467
468    fn unpack(&mut self, data: &[u8]) -> usize {
469        assert!(
470            data.len() >= self.size(),
471            "ExtendedAsset.unpack: buffer overflow"
472        );
473
474        let mut dec = Decoder::new(data);
475        dec.unpack(&mut self.quantity);
476        dec.unpack(&mut self.contract);
477        dec.get_pos()
478    }
479}