plutus_ledger_api/v1/
value.rs

1//! Types related to Cardano values, such as Ada and native tokens.
2
3use 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////////////////////
46// CurrencySymbol //
47////////////////////
48
49/// Identifier of a currency, which could be either Ada (or tAda), or a native token represented by
50/// it's minting policy hash. A currency may be associated with multiple `AssetClass`es.
51#[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
77/// Serialize into hexadecimal string, or empty string if Ada
78/// It returns `lovelace` instead of the empty string when the alternate flag is used (e.g.: format!("{:#}", cs))
79impl 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
157/// Nom parser for CurrencySymbol
158/// Expects a hexadecimal string representation of 0 (Ada) or 28 bytes (NativeToken)
159pub(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///////////
169// Value //
170///////////
171
172/// A value that can contain multiple asset classes
173#[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    /// Create a Value containing only ada tokens, given the quantity in lovelace.
240    pub fn ada_value(amount: &BigInt) -> Self {
241        Self::token_value(&CurrencySymbol::Ada, &TokenName::ada(), amount)
242    }
243
244    /// Create a Value containing only the given quantity of the given token.
245    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    /// Lookup the quantity of the given token.
253    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    /// Lookup the quantity of ada(unit: lovelace).
261    pub fn get_ada_amount(&self) -> BigInt {
262        self.get_token_amount(&CurrencySymbol::Ada, &TokenName::ada())
263    }
264
265    /// Insert ada into a value by inserting or replacing old value
266    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    /// Create a new value by inserting a new token or replacing the existing quantity.
274    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    /// Insert a new token into the value, or replace the existing quantity.
287    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(&currency_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    /// Return true if the value don't have any entries.
313    pub fn is_empty(&self) -> bool {
314        self.0.is_empty()
315    }
316
317    /// Remove all tokens whose quantity is zero.
318    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            // Has negative entries?
326            .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    /// Apply a function to each token of the value, and use its result as the new amount.
335    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    /// Apply a predicate to tokens.
343    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    /// Apply a function to each token of the value. If the result is None, the token entry will be
351    /// removed.
352    ///
353    /// Note that if the name-quantity map of any given currency symbols is empty, the currency entry
354    /// will be removed from the top-level map entirely.
355    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    /// Create a vector with each distinct value
379    /// Warning: is the value is not normalized, the same asset class can appear twice
380    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
431/// Nom parser for a single entry in a Value
432/// Expects an integer quantity, followed by an asset class after a space character
433/// (space is not required for Ada)
434/// E.g.: 12 11223344556677889900112233445566778899001122334455667788.001122aabbcc
435pub(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
450/// Nom parser for Value
451/// Expects flat Value entries divided by a `+` sign
452/// E.g.: 123+12 11223344556677889900112233445566778899001122334455667788.001122aabbcc
453pub(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///////////////
717// TokenName //
718///////////////
719
720/// Name of a token. This can be any arbitrary bytearray
721#[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    /// Ada tokenname (empty bytestring)
728    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    /// Convert a UTF8 string into a TokenName (use from_str to convert from a hexadecimal string)
750    pub fn from_string(str: &str) -> Result<Self, ConversionError> {
751        TokenName::from_bytes(String::from(str).into_bytes())
752    }
753
754    /// Convert TokenName to string if it is a valid UTF8 bytestring
755    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
777/// Nom parser for TokenName
778/// Expects a hexadecimal string representation of up to 32
779pub(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
795/// Serialize into a hexadecimal string
796/// It tries to decode the token name from UTF8 when the alternate flag is used (e.g.: format!("{:#}", ac)),
797/// if failsed it prepends the hex value with `0x`
798impl 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////////////////
826// AssetClass //
827////////////////
828
829/// AssetClass is uniquely identifying a specific asset
830#[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
838/// Serialize into two hexadecimal strings divided by a . (e.g. aabbcc.001122)
839/// It tries to decode the token name from UTF8 when the alternate flag is used (e.g.: format!("{:#}", ac)),
840/// if failsed it prepends the hex value with `0x`
841impl 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
870/// Nom parser for AssetClass
871/// Expects a currency symbol and token name both in hexadecimal format, divided by a `.`
872/// In case the token name is empty, the divider is not required
873/// E.g.:
874///   - 11223344556677889900112233445566778899001122334455667788.001122aabbcc
875///   - 11223344556677889900112233445566778899001122334455667788
876pub(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////////////////
894// Lovelace //
895////////////////
896
897#[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);