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#[cfg_attr(feature = "std", derive(eosio_scale_info::TypeInfo))]
59#[derive(Copy, Clone, Default, Eq, PartialEq)]
60pub struct SymbolCode {
61 value: u64,
63}
64
65impl SymbolCode {
66 pub fn new(value: u64) -> Self {
68 check(is_valid_symbol_code(value), "bad symbol value");
69 Self{value}
70 }
71
72 pub fn value(&self) -> u64 {
74 self.value
75 }
76
77 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 pub fn is_valid(&self) -> bool {
93 return is_valid_symbol_code(self.value);
94 }
95}
96
97impl Packer for SymbolCode {
98 fn size(&self) -> usize {
100 return 8;
101 }
102
103 fn pack(&self) -> Vec<u8> {
105 self.value.pack()
106 }
107
108 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#[cfg_attr(feature = "std", derive(eosio_scale_info::TypeInfo))]
119#[derive(Copy, Clone, Default, Eq, PartialEq)]
120pub struct Symbol {
121 value: u64,
123}
124
125impl Symbol {
126 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 pub fn value(&self) -> u64 {
146 self.value
147 }
148
149 pub fn code(&self) -> SymbolCode {
151 SymbolCode{value: self.value >> 8}
152 }
153
154 pub fn precision(&self) -> usize {
156 (self.value & 0xFF) as usize
157 }
158
159 pub fn to_string(&self) -> String {
161 self.precision().to_string() + "," + &self.code().to_string()
162 }
163
164 pub fn is_valid(&self) -> bool {
166 return self.code().is_valid();
167 }
168}
169
170impl Packer for Symbol {
171 fn size(&self) -> usize {
173 return 8;
174 }
175
176 fn pack(&self) -> Vec<u8> {
178 self.value.pack()
179 }
180
181 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#[cfg_attr(feature = "std", derive(eosio_scale_info::TypeInfo))]
192#[derive(Copy, Clone, Default, Eq, PartialEq)]
193pub struct Asset {
194 amount: i64,
196 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 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 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 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 pub fn amount(&self) -> i64 {
279 self.amount
280 }
281
282 pub fn symbol(&self) -> Symbol {
284 self.symbol
285 }
286
287 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 pub fn is_valid(&self) -> bool {
308 return is_amount_within_range(self.amount) && self.symbol().is_valid();
309 }
310}
311
312impl 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 fn size(&self) -> usize {
369 return 16;
370 }
371
372 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 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#[cfg_attr(feature = "std", derive(eosio_scale_info::TypeInfo))]
394#[derive(Copy, Clone, Default, Eq, PartialEq)]
395pub struct ExtendedAsset {
396 quantity: Asset,
398 contract: Name,
400}
401
402impl ExtendedAsset {
403 pub fn new(quantity: Asset, contract: Name) -> Self {
405 Self{quantity, contract}
406 }
407
408 pub fn quantity(&self) -> Asset {
410 self.quantity
411 }
412
413 pub fn contract(&self) -> Name {
415 self.contract
416 }
417}
418
419impl Packer for ExtendedAsset {
420 fn size(&self) -> usize {
422 return 16 + 8;
423 }
424
425 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 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}