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