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