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
11pub 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 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
333impl 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 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}