rust_chain/
asset.rs

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