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
31pub 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#[cfg_attr(feature = "std", derive(eosio_scale_info::TypeInfo))]
64#[derive(Copy, Clone, Default, Eq, PartialEq)]
65pub struct SymbolCode {
66 pub value: u64,
68}
69
70impl SymbolCode {
71 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 pub fn value(&self) -> u64 {
88 self.value
89 }
90
91 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 pub fn is_valid(&self) -> bool {
109 return is_valid_symbol_code(self.value);
110 }
111}
112
113impl Packer for SymbolCode {
114 fn size(&self) -> usize {
116 return 8;
117 }
118
119 fn pack(&self, enc: &mut Encoder) -> usize {
121 self.value.pack(enc)
122 }
123
124 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#[cfg_attr(feature = "std", derive(eosio_scale_info::TypeInfo))]
135#[derive(Copy, Clone, Default, Eq, PartialEq)]
136pub struct Symbol {
137 value: u64,
139}
140
141impl Symbol {
142 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 pub fn value(&self) -> u64 {
162 self.value
163 }
164
165 pub fn code(&self) -> SymbolCode {
167 SymbolCode{value: self.value >> 8}
168 }
169
170 pub fn precision(&self) -> usize {
172 (self.value & 0xFF) as usize
173 }
174
175 pub fn to_string(&self) -> String {
177 self.precision().to_string() + "," + &self.code().to_string()
178 }
179
180 pub fn is_valid(&self) -> bool {
182 return self.code().is_valid();
183 }
184}
185
186impl Packer for Symbol {
187 fn size(&self) -> usize {
189 return 8;
190 }
191
192 fn pack(&self, enc: &mut Encoder) -> usize {
194 self.value.pack(enc)
195 }
196
197 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#[cfg_attr(feature = "std", derive(eosio_scale_info::TypeInfo))]
208#[derive(Copy, Clone, Default, Eq, PartialEq)]
209pub struct Asset {
210 amount: i64,
212 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 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 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 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 pub fn amount(&self) -> i64 {
312 self.amount
313 }
314
315 pub fn symbol(&self) -> Symbol {
317 self.symbol
318 }
319
320 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 pub fn is_valid(&self) -> bool {
341 return is_amount_within_range(self.amount) && self.symbol().is_valid();
342 }
343}
344
345impl 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 fn size(&self) -> usize {
402 return 16;
403 }
404
405 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 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#[cfg_attr(feature = "std", derive(eosio_scale_info::TypeInfo))]
429#[derive(Copy, Clone, Default, Eq, PartialEq)]
430pub struct ExtendedAsset {
431 quantity: Asset,
433 contract: Name,
435}
436
437impl ExtendedAsset {
438 pub fn new(quantity: Asset, contract: Name) -> Self {
440 Self{quantity, contract}
441 }
442
443 pub fn quantity(&self) -> Asset {
445 self.quantity
446 }
447
448 pub fn contract(&self) -> Name {
450 self.contract
451 }
452}
453
454impl Packer for ExtendedAsset {
455 fn size(&self) -> usize {
457 return 16 + 8;
458 }
459
460 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 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}