1use std::str::FromStr;
4use std::string::String;
5use std::{
6 collections::BTreeMap,
7 iter::Sum,
8 ops::{Add, Mul, Neg, Not, Sub},
9};
10use std::{fmt, ops};
11
12use anyhow::anyhow;
13use cardano_serialization_lib as csl;
14#[cfg(feature = "lbf")]
15use lbr_prelude::json::{Error, Json, JsonType};
16use nom::combinator::{map, opt};
17use nom::{
18 branch::alt,
19 character::complete::{char, space0},
20 combinator::{all_consuming, eof, map_res, success},
21 error::{context, VerboseError},
22 multi::separated_list0,
23 sequence::preceded,
24 sequence::tuple,
25 Finish, IResult,
26};
27use num_bigint::BigInt;
28use num_traits::Zero;
29#[cfg(feature = "serde")]
30use serde::{Deserialize, Serialize};
31#[cfg(feature = "lbf")]
32use serde_json;
33
34use crate as plutus_ledger_api;
35use crate::aux::{big_int, singleton, union_b_tree_maps_with, union_btree_maps_with};
36use crate::csl::csl_to_pla::FromCSL;
37use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL};
38use crate::error::ConversionError;
39use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError};
40use crate::v1::crypto::LedgerBytes;
41use crate::v1::script::{MintingPolicyHash, ScriptHash};
42
43use super::crypto::ledger_bytes;
44
45#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
52#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
53pub enum CurrencySymbol {
54 Ada,
55 NativeToken(MintingPolicyHash),
56}
57
58impl CurrencySymbol {
59 pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, ConversionError> {
60 if bytes.is_empty() {
61 Ok(CurrencySymbol::Ada)
62 } else {
63 Ok(CurrencySymbol::NativeToken(MintingPolicyHash::from_bytes(
64 bytes,
65 )?))
66 }
67 }
68
69 pub fn is_ada(&self) -> bool {
70 match self {
71 CurrencySymbol::Ada => true,
72 CurrencySymbol::NativeToken(_) => false,
73 }
74 }
75}
76
77impl fmt::Display for CurrencySymbol {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 match self {
82 CurrencySymbol::Ada => {
83 if f.alternate() {
84 write!(f, "lovelace")
85 } else {
86 write!(f, "")
87 }
88 }
89 CurrencySymbol::NativeToken(symbol) => write!(f, "{}", symbol.0 .0),
90 }
91 }
92}
93
94impl FromStr for CurrencySymbol {
95 type Err = ConversionError;
96
97 fn from_str(s: &str) -> Result<Self, Self::Err> {
98 all_consuming(currency_symbol)(s)
99 .finish()
100 .map_err(|err| {
101 ConversionError::ParseError(anyhow!(
102 "Error while parsing CurrencySymbol '{}': {}",
103 s,
104 err
105 ))
106 })
107 .map(|(_, cs)| cs)
108 }
109}
110
111impl IsPlutusData for CurrencySymbol {
112 fn to_plutus_data(&self) -> PlutusData {
113 match self {
114 CurrencySymbol::NativeToken(policy_hash) => policy_hash.to_plutus_data(),
115 CurrencySymbol::Ada => PlutusData::Bytes(Vec::new()),
116 }
117 }
118
119 fn from_plutus_data(data: &PlutusData) -> Result<Self, PlutusDataError> {
120 IsPlutusData::from_plutus_data(data).map(|bytes: LedgerBytes| {
121 if bytes.0.is_empty() {
122 CurrencySymbol::Ada
123 } else {
124 CurrencySymbol::NativeToken(MintingPolicyHash(ScriptHash(bytes)))
125 }
126 })
127 }
128}
129
130#[cfg(feature = "lbf")]
131impl Json for CurrencySymbol {
132 fn to_json(&self) -> serde_json::Value {
133 match self {
134 CurrencySymbol::Ada => serde_json::Value::String(String::new()),
135 CurrencySymbol::NativeToken(policy_hash) => policy_hash.to_json(),
136 }
137 }
138
139 fn from_json(value: &serde_json::Value) -> Result<Self, Error> {
140 match value.clone() {
141 serde_json::Value::String(str) => {
142 if str.is_empty() {
143 Ok(CurrencySymbol::Ada)
144 } else {
145 Ok(CurrencySymbol::NativeToken(Json::from_json(value)?))
146 }
147 }
148 _ => Err(Error::UnexpectedJsonType {
149 wanted: JsonType::String,
150 got: JsonType::from(value),
151 parser: "Plutus.V1.CurrencySymbol".to_owned(),
152 }),
153 }
154 }
155}
156
157pub(crate) fn currency_symbol(input: &str) -> IResult<&str, CurrencySymbol, VerboseError<&str>> {
160 context(
161 "currency symbol",
162 map_res(ledger_bytes, |LedgerBytes(bytes)| {
163 CurrencySymbol::from_bytes(bytes)
164 }),
165 )(input)
166}
167
168#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
174#[cfg_attr(feature = "lbf", derive(Json))]
175pub struct Value(pub BTreeMap<CurrencySymbol, BTreeMap<TokenName, BigInt>>);
176
177#[cfg(feature = "serde")]
178mod value_serde {
179 use std::collections::BTreeMap;
180
181 use num_bigint::BigInt;
182 use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
183
184 use super::{CurrencySymbol, TokenName, Value};
185
186 struct Assets(BTreeMap<TokenName, BigInt>);
187
188 impl Serialize for Value {
189 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
190 where
191 S: Serializer,
192 {
193 serializer.collect_seq(
194 self.0
195 .iter()
196 .map(|(cur_sym, assets)| (cur_sym, Assets(assets.to_owned()))),
197 )
198 }
199 }
200
201 impl<'de> Deserialize<'de> for Value {
202 fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
203 where
204 D: Deserializer<'de>,
205 {
206 let vec: Vec<(CurrencySymbol, Assets)> = Vec::deserialize(deserializer)?;
207
208 Ok(Value(
209 vec.into_iter().map(|(cs, assets)| (cs, assets.0)).collect(),
210 ))
211 }
212 }
213
214 impl Serialize for Assets {
215 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
216 where
217 S: Serializer,
218 {
219 serializer.collect_seq(self.0.iter())
220 }
221 }
222
223 impl<'de> Deserialize<'de> for Assets {
224 fn deserialize<D>(deserializer: D) -> Result<Assets, D::Error>
225 where
226 D: Deserializer<'de>,
227 {
228 let vec: Vec<(TokenName, BigInt)> = Vec::deserialize(deserializer)?;
229
230 Ok(Assets(vec.into_iter().collect()))
231 }
232 }
233}
234
235impl Value {
236 pub fn new() -> Self {
237 Value(BTreeMap::new())
238 }
239 pub fn ada_value(amount: &BigInt) -> Self {
241 Self::token_value(&CurrencySymbol::Ada, &TokenName::ada(), amount)
242 }
243
244 pub fn token_value(cs: &CurrencySymbol, tn: &TokenName, amount: &BigInt) -> Self {
246 Value(singleton((
247 cs.clone(),
248 singleton((tn.clone(), amount.clone())),
249 )))
250 }
251
252 pub fn get_token_amount(&self, cs: &CurrencySymbol, tn: &TokenName) -> BigInt {
254 self.0
255 .get(cs)
256 .and_then(|tn_map| tn_map.get(tn))
257 .map_or(BigInt::zero(), Clone::clone)
258 }
259
260 pub fn get_ada_amount(&self) -> BigInt {
262 self.get_token_amount(&CurrencySymbol::Ada, &TokenName::ada())
263 }
264
265 pub fn insert_ada_mut(&mut self, amount: BigInt) {
267 self.0.insert(
268 CurrencySymbol::Ada,
269 BTreeMap::from([(TokenName::ada(), amount)]),
270 );
271 }
272
273 pub fn insert_token(
275 &self,
276 currency_symbol: &CurrencySymbol,
277 token_name: &TokenName,
278 amount: &BigInt,
279 ) -> Self {
280 let mut result_map = self.clone();
281
282 result_map.insert_token_mut(currency_symbol.clone(), token_name.clone(), amount.clone());
283
284 result_map
285 }
286 pub fn insert_token_mut(
288 &mut self,
289 currency_symbol: CurrencySymbol,
290 token_name: TokenName,
291 amount: BigInt,
292 ) {
293 let tn_map = self.0.get_mut(¤cy_symbol);
294
295 match tn_map {
296 None => {
297 self.0
298 .insert(currency_symbol, singleton((token_name, amount)));
299 }
300 Some(tn_map) => {
301 let val = tn_map.get_mut(&token_name);
302 match val {
303 None => {
304 tn_map.insert(token_name, amount);
305 }
306 Some(old_amount) => *old_amount = amount,
307 }
308 }
309 }
310 }
311
312 pub fn is_empty(&self) -> bool {
314 self.0.is_empty()
315 }
316
317 pub fn normalize(self) -> Self {
319 self.filter(|_, _, a| a.is_zero().not())
320 }
321
322 pub fn is_subset(&self, b: &Value) -> bool {
323 (b - self)
324 .normalize()
325 .filter(|_, _, amount| amount < &BigInt::from(0u32))
327 .is_empty()
328 }
329
330 pub fn is_pure_ada(&self) -> bool {
331 self.0.iter().all(|(cs, _)| cs == &CurrencySymbol::Ada)
332 }
333
334 pub fn map_amount<F>(self, mut f: F) -> Self
336 where
337 F: FnMut(&CurrencySymbol, &TokenName, &BigInt) -> BigInt,
338 {
339 self.filter_map_amount(|cs, tn, a| Some(f(cs, tn, a)))
340 }
341
342 pub fn filter<F>(self, mut f: F) -> Self
344 where
345 F: FnMut(&CurrencySymbol, &TokenName, &BigInt) -> bool,
346 {
347 self.filter_map_amount(|cs, tn, a| f(cs, tn, a).then(|| a.clone()))
348 }
349
350 pub fn filter_map_amount<F>(self, mut f: F) -> Self
356 where
357 F: FnMut(&CurrencySymbol, &TokenName, &BigInt) -> Option<BigInt>,
358 {
359 Value(
360 (self.0)
361 .into_iter()
362 .filter_map(|(cs, tn_map)| {
363 let filtered_tn_map = tn_map
364 .into_iter()
365 .filter_map(|(tn, a)| f(&cs, &tn, &a).map(|a| (tn, a)))
366 .collect::<BTreeMap<TokenName, BigInt>>();
367
368 if filtered_tn_map.is_empty() {
369 None
370 } else {
371 Some((cs.clone(), filtered_tn_map))
372 }
373 })
374 .collect(),
375 )
376 }
377
378 pub fn flatten(&self) -> Vec<(&CurrencySymbol, &TokenName, &BigInt)> {
381 self.0
382 .iter()
383 .flat_map(|(currency_symbol, assets)| {
384 assets
385 .iter()
386 .map(move |(token_name, amount)| (currency_symbol, token_name, amount))
387 })
388 .collect()
389 }
390
391 pub fn unflatten(list: &[(CurrencySymbol, TokenName, BigInt)]) -> Self {
392 list.iter()
393 .fold(Value::new(), |v, (cs, tn, am)| v.insert_token(cs, tn, am))
394 }
395}
396
397impl fmt::Display for Value {
398 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
399 let mut it = self
400 .0
401 .iter()
402 .flat_map(|(currency_symbol, assets)| {
403 assets
404 .iter()
405 .map(move |(token_name, amount)| (currency_symbol, token_name, amount))
406 })
407 .peekable();
408 while let Some((cur_sym, tn, amount)) = it.next() {
409 if cur_sym.is_ada() {
410 amount.fmt(f)?;
411 } else if tn.is_empty() {
412 amount.fmt(f)?;
413 " ".fmt(f)?;
414 cur_sym.fmt(f)?;
415 } else {
416 amount.fmt(f)?;
417 " ".fmt(f)?;
418 cur_sym.fmt(f)?;
419 ".".fmt(f)?;
420 tn.fmt(f)?;
421 }
422 if it.peek().is_some() {
423 "+".fmt(f)?;
424 }
425 }
426
427 Ok(())
428 }
429}
430
431pub(crate) fn flat_value(
436 input: &str,
437) -> IResult<&str, (CurrencySymbol, TokenName, BigInt), VerboseError<&str>> {
438 map(
439 tuple((big_int, opt(preceded(char(' '), asset_class)))),
440 |(amount, asset_class)| match asset_class {
441 None => (CurrencySymbol::Ada, TokenName::ada(), amount),
442 Some(AssetClass {
443 currency_symbol,
444 token_name,
445 }) => (currency_symbol, token_name, amount),
446 },
447 )(input)
448}
449
450pub(crate) fn value(input: &str) -> IResult<&str, Value, VerboseError<&str>> {
454 map(
455 separated_list0(tuple((space0, char('+'))), flat_value),
456 |flat_values| Value::unflatten(&flat_values),
457 )(input)
458}
459
460impl FromStr for Value {
461 type Err = ConversionError;
462
463 fn from_str(s: &str) -> Result<Self, Self::Err> {
464 all_consuming(value)(s)
465 .finish()
466 .map_err(|err| {
467 ConversionError::ParseError(anyhow!("Error while parsing Value '{}': {}", s, err))
468 })
469 .map(|(_, cs)| cs)
470 }
471}
472
473impl Zero for Value {
474 fn zero() -> Self {
475 Default::default()
476 }
477
478 fn is_zero(&self) -> bool {
479 self.is_empty()
480 }
481}
482
483impl_op!(+ |a: &Value, b: &Value| -> Value { a.clone() + b.clone() });
484impl_op!(+ |a: &Value, b: Value| -> Value { a.clone() + b });
485impl_op!(+ |a: Value, b: &Value| -> Value { a + b.clone() });
486
487impl Add<Value> for Value {
488 type Output = Value;
489
490 fn add(self, rhs: Value) -> Self::Output {
491 Value(union_btree_maps_with(
492 |lhs, rhs| union_btree_maps_with(|lhs, rhs| lhs + rhs, lhs, rhs),
493 self.0,
494 rhs.0,
495 ))
496 }
497}
498
499impl Neg for Value {
500 type Output = Value;
501
502 fn neg(self) -> Self::Output {
503 self.map_amount(|_, _, a| a.neg())
504 }
505}
506
507impl Neg for &Value {
508 type Output = Value;
509
510 fn neg(self) -> Self::Output {
511 self.clone().neg()
512 }
513}
514
515impl_op!(-|a: &Value, b: &Value| -> Value { a.clone() - b.clone() });
516impl_op!(-|a: &Value, b: Value| -> Value { a.clone() - b });
517impl_op!(-|a: Value, b: &Value| -> Value { a - b.clone() });
518
519impl Sub<Value> for Value {
520 type Output = Value;
521
522 fn sub(self, rhs: Value) -> Self::Output {
523 self.add(rhs.neg())
524 }
525}
526
527impl_op!(*|a: &BigInt, b: &Value| -> Value { b * a });
528impl_op_commutative!(*|a: Value, b: BigInt| -> Value { &a * &b });
529impl_op_commutative!(*|a: &Value, b: BigInt| -> Value { a * &b });
530impl_op_commutative!(*|a: Value, b: &BigInt| -> Value { &a * b });
531
532impl_op_commutative!(*|a: &Value, b: i8| -> Value { a * BigInt::from(b) });
533impl_op_commutative!(*|a: &Value, b: i16| -> Value { a * BigInt::from(b) });
534impl_op_commutative!(*|a: &Value, b: i32| -> Value { a * BigInt::from(b) });
535impl_op_commutative!(*|a: &Value, b: i64| -> Value { a * BigInt::from(b) });
536
537impl_op_commutative!(*|a: &Value, b: u8| -> Value { a * BigInt::from(b) });
538impl_op_commutative!(*|a: &Value, b: u16| -> Value { a * BigInt::from(b) });
539impl_op_commutative!(*|a: &Value, b: u32| -> Value { a * BigInt::from(b) });
540impl_op_commutative!(*|a: &Value, b: u64| -> Value { a * BigInt::from(b) });
541
542impl Mul<&BigInt> for &Value {
543 type Output = Value;
544
545 fn mul(self, rhs: &BigInt) -> Self::Output {
546 Value(
547 self.0
548 .iter()
549 .map(|(cs, tn_map)| {
550 (
551 cs.clone(),
552 tn_map.iter().map(|(tn, q)| (tn.clone(), q * rhs)).collect(),
553 )
554 })
555 .collect(),
556 )
557 }
558}
559
560impl Sum<Value> for Value {
561 fn sum<I: Iterator<Item = Value>>(iter: I) -> Self {
562 iter.fold(Zero::zero(), Add::add)
563 }
564}
565
566impl<'a> Sum<&'a Value> for Value {
567 fn sum<I: Iterator<Item = &'a Value>>(iter: I) -> Self {
568 iter.fold(Zero::zero(), Add::add)
569 }
570}
571
572impl IsPlutusData for Value {
573 fn to_plutus_data(&self) -> PlutusData {
574 self.0.to_plutus_data()
575 }
576
577 fn from_plutus_data(data: &PlutusData) -> Result<Self, PlutusDataError> {
578 IsPlutusData::from_plutus_data(data).map(Self)
579 }
580}
581
582impl FromCSL<csl::Assets> for BTreeMap<TokenName, BigInt> {
583 fn from_csl(value: &csl::Assets) -> Self {
584 let keys = value.keys();
585 (0..keys.len()).fold(BTreeMap::new(), |mut acc, idx| {
586 let asset_name = keys.get(idx);
587 if let Some(quantity) = value.get(&asset_name) {
588 acc.insert(
589 TokenName::from_csl(&asset_name),
590 BigInt::from_csl(&quantity),
591 );
592 }
593 acc
594 })
595 }
596}
597
598impl TryFromPLA<BTreeMap<TokenName, BigInt>> for csl::Assets {
599 fn try_from_pla(val: &BTreeMap<TokenName, BigInt>) -> Result<Self, TryFromPLAError> {
600 val.iter().try_fold(csl::Assets::new(), |mut acc, (k, v)| {
601 acc.insert(&k.try_to_csl()?, &v.try_to_csl()?);
602 Ok(acc)
603 })
604 }
605}
606
607impl FromCSL<csl::MultiAsset> for Value {
608 fn from_csl(value: &csl::MultiAsset) -> Self {
609 let keys = value.keys();
610 Value((0..keys.len()).fold(BTreeMap::new(), |mut acc, idx| {
611 let script_hash = keys.get(idx);
612 if let Some(assets) = value.get(&script_hash) {
613 let assets = BTreeMap::from_csl(&assets);
614 acc.insert(
615 CurrencySymbol::NativeToken(MintingPolicyHash::from_csl(&script_hash)),
616 assets,
617 );
618 }
619 acc
620 }))
621 }
622}
623
624impl FromCSL<csl::Value> for Value {
625 fn from_csl(value: &csl::Value) -> Self {
626 let lovelaces = BigInt::from_csl(&value.coin());
627 let mut pla_value = Value::ada_value(&lovelaces);
628 if let Some(multi_asset) = value.multiasset() {
629 pla_value = &pla_value + &Value::from_csl(&multi_asset)
630 }
631 pla_value
632 }
633}
634
635impl TryFromPLA<Value> for csl::Value {
636 fn try_from_pla(val: &Value) -> Result<Self, TryFromPLAError> {
637 let coin: csl::Coin = val
638 .0
639 .get(&CurrencySymbol::Ada)
640 .and_then(|m| m.get(&TokenName::ada()))
641 .map_or(Ok(csl::BigNum::zero()), TryToCSL::try_to_csl)?;
642
643 let m_ass = val
644 .0
645 .iter()
646 .filter_map(|(cs, tn_map)| match &cs {
647 CurrencySymbol::Ada => None,
648 CurrencySymbol::NativeToken(h) => Some((h, tn_map)),
649 })
650 .try_fold(csl::MultiAsset::new(), |mut acc, (cs, ass)| {
651 acc.insert(&cs.try_to_csl()?, &ass.try_to_csl()?);
652 Ok(acc)
653 })?;
654
655 let mut v = csl::Value::new(&coin);
656
657 v.set_multiasset(&m_ass);
658
659 Ok(v)
660 }
661}
662
663impl FromCSL<csl::MintAssets> for BTreeMap<TokenName, BigInt> {
664 fn from_csl(m_ass: &csl::MintAssets) -> Self {
665 let keys = m_ass.keys();
666 (0..keys.len())
667 .map(|idx| {
668 let key = keys.get(idx);
669 let value = m_ass.get(&key).unwrap();
670 (TokenName::from_csl(&key), BigInt::from_csl(&value))
671 })
672 .collect()
673 }
674}
675
676impl FromCSL<csl::MintsAssets> for BTreeMap<TokenName, BigInt> {
677 fn from_csl(value: &csl::MintsAssets) -> Self {
678 (0..value.len())
679 .map(|idx| value.get(idx).unwrap())
680 .fold(BTreeMap::new(), |acc, m| {
681 let ass = BTreeMap::from_csl(&m);
682 union_b_tree_maps_with(|l, r| l + r, [&acc, &ass])
683 })
684 }
685}
686
687impl TryFromPLA<BTreeMap<TokenName, BigInt>> for csl::MintAssets {
688 fn try_from_pla(val: &BTreeMap<TokenName, BigInt>) -> Result<Self, TryFromPLAError> {
689 val.iter()
690 .try_fold(csl::MintAssets::new(), |mut acc, (k, v)| {
691 acc.insert(&k.try_to_csl()?, &v.try_to_csl()?)
692 .map_err(TryFromPLAError::CSLJsError)?;
693 Ok(acc)
694 })
695 }
696}
697
698impl FromCSL<csl::Mint> for Value {
699 fn from_csl(mint: &csl::Mint) -> Self {
700 let keys = mint.keys();
701 Value(
702 (0..keys.len())
703 .map(|idx| {
704 let sh = keys.get(idx);
705 let ass = mint.get(&sh).unwrap_or(csl::MintsAssets::new());
706 (
707 CurrencySymbol::NativeToken(MintingPolicyHash::from_csl(&sh)),
708 BTreeMap::from_csl(&ass),
709 )
710 })
711 .collect::<BTreeMap<CurrencySymbol, BTreeMap<TokenName, BigInt>>>(),
712 )
713 }
714}
715
716#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
722#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
723#[cfg_attr(feature = "lbf", derive(Json))]
724pub struct TokenName(pub LedgerBytes);
725
726impl TokenName {
727 pub fn ada() -> TokenName {
729 TokenName(LedgerBytes(Vec::new()))
730 }
731
732 pub fn is_empty(&self) -> bool {
733 self.0 .0.is_empty()
734 }
735
736 pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, ConversionError> {
737 if bytes.len() <= 32 {
738 Ok(TokenName(LedgerBytes(bytes)))
739 } else {
740 Err(ConversionError::invalid_bytestring_length(
741 "TokenName",
742 32,
743 "less than or equal to",
744 &bytes,
745 ))
746 }
747 }
748
749 pub fn from_string(str: &str) -> Result<Self, ConversionError> {
751 TokenName::from_bytes(String::from(str).into_bytes())
752 }
753
754 pub fn try_into_string(self) -> Result<String, std::string::FromUtf8Error> {
756 String::from_utf8(self.0 .0)
757 }
758}
759
760impl FromStr for TokenName {
761 type Err = ConversionError;
762
763 fn from_str(s: &str) -> Result<Self, Self::Err> {
764 all_consuming(token_name)(s)
765 .finish()
766 .map_err(|err| {
767 ConversionError::ParseError(anyhow!(
768 "Error while parsing TokenName '{}': {}",
769 s,
770 err
771 ))
772 })
773 .map(|(_, cs)| cs)
774 }
775}
776
777pub(crate) fn token_name(input: &str) -> IResult<&str, TokenName, VerboseError<&str>> {
780 map_res(ledger_bytes, |LedgerBytes(bytes)| {
781 TokenName::from_bytes(bytes)
782 })(input)
783}
784
785impl IsPlutusData for TokenName {
786 fn to_plutus_data(&self) -> PlutusData {
787 self.0.to_plutus_data()
788 }
789
790 fn from_plutus_data(data: &PlutusData) -> Result<Self, PlutusDataError> {
791 IsPlutusData::from_plutus_data(data).map(Self)
792 }
793}
794
795impl fmt::Display for TokenName {
799 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
800 if f.alternate() {
801 let utf8_str = std::str::from_utf8(&self.0 .0);
802
803 match utf8_str {
804 Ok(str) => write!(f, "{}", str),
805 Err(_) => write!(f, "0x{}", self.0),
806 }
807 } else {
808 write!(f, "{}", self.0)
809 }
810 }
811}
812
813impl FromCSL<csl::AssetName> for TokenName {
814 fn from_csl(value: &csl::AssetName) -> Self {
815 TokenName(LedgerBytes(value.name()))
816 }
817}
818
819impl TryFromPLA<TokenName> for csl::AssetName {
820 fn try_from_pla(val: &TokenName) -> Result<Self, TryFromPLAError> {
821 csl::AssetName::new(val.0 .0.to_owned()).map_err(TryFromPLAError::CSLJsError)
822 }
823}
824
825#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
831#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
832#[cfg_attr(feature = "lbf", derive(Json))]
833pub struct AssetClass {
834 pub currency_symbol: CurrencySymbol,
835 pub token_name: TokenName,
836}
837
838impl fmt::Display for AssetClass {
842 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
843 if self.token_name.is_empty() {
844 self.currency_symbol.fmt(f)
845 } else {
846 self.currency_symbol.fmt(f)?;
847 ".".fmt(f)?;
848 self.token_name.fmt(f)
849 }
850 }
851}
852
853impl FromStr for AssetClass {
854 type Err = ConversionError;
855
856 fn from_str(s: &str) -> Result<Self, Self::Err> {
857 all_consuming(asset_class)(s)
858 .finish()
859 .map_err(|err| {
860 ConversionError::ParseError(anyhow!(
861 "Error while parsing AssetClass '{}': {}",
862 s,
863 err
864 ))
865 })
866 .map(|(_, cs)| cs)
867 }
868}
869
870pub(crate) fn asset_class(input: &str) -> IResult<&str, AssetClass, VerboseError<&str>> {
877 let (input, cs) = currency_symbol(input)?;
878
879 let (input, tn) = alt((
880 preceded(eof, success(TokenName::ada())),
881 preceded(char('.'), token_name),
882 ))(input)?;
883
884 Ok((
885 input,
886 AssetClass {
887 currency_symbol: cs,
888 token_name: tn,
889 },
890 ))
891}
892
893#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
898#[is_plutus_data_derive_strategy = "Newtype"]
899#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
900#[cfg_attr(feature = "lbf", derive(Json))]
901pub struct Lovelace(pub BigInt);