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#[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////////////////////
48// CurrencySymbol //
49////////////////////
50
51/// Identifier of a currency, which could be either Ada (or tAda), or a native token represented by
52/// it's minting policy hash. A currency may be associated with multiple `AssetClass`es.
53#[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
79/// Serialize into hexadecimal string, or empty string if Ada
80/// It returns `lovelace` instead of the empty string when the alternate flag is used (e.g.: format!("{:#}", cs))
81impl 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
159/// Nom parser for CurrencySymbol
160/// Expects a hexadecimal string representation of 0 (Ada) or 28 bytes (NativeToken)
161pub(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///////////
171// Value //
172///////////
173
174/// A value that can contain multiple asset classes
175#[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    /// Create a Value containing only ada tokens, given the quantity in lovelace.
242    pub fn ada_value(amount: &BigInt) -> Self {
243        Self::token_value(&CurrencySymbol::Ada, &TokenName::ada(), amount)
244    }
245
246    /// Create a Value containing only the given quantity of the given token.
247    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    /// Lookup the quantity of the given token.
255    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    /// Lookup the quantity of ada(unit: lovelace).
263    pub fn get_ada_amount(&self) -> BigInt {
264        self.get_token_amount(&CurrencySymbol::Ada, &TokenName::ada())
265    }
266
267    /// Insert ada into a value by inserting or replacing old value
268    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    /// Create a new value by inserting a new token or replacing the existing quantity.
276    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    /// Insert a new token into the value, or replace the existing quantity.
289    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(&currency_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    /// Return true if the value don't have any entries.
315    pub fn is_empty(&self) -> bool {
316        self.0.is_empty()
317    }
318
319    /// Remove all tokens whose quantity is zero.
320    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            // Has negative entries?
328            .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    /// Apply a function to each token of the value, and use its result as the new amount.
337    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    /// Apply a predicate to tokens.
345    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    /// Apply a function to each token of the value. If the result is None, the token entry will be
353    /// removed.
354    ///
355    /// Note that if the name-quantity map of any given currency symbols is empty, the currency entry
356    /// will be removed from the top-level map entirely.
357    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    /// Create a vector with each distinct value
381    /// Warning: is the value is not normalized, the same asset class can appear twice
382    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
433/// Nom parser for a single entry in a Value
434/// Expects an integer quantity, followed by an asset class after a space character
435/// (space is not required for Ada)
436/// E.g.: 12 11223344556677889900112233445566778899001122334455667788.001122aabbcc
437pub(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
452/// Nom parser for Value
453/// Expects flat Value entries divided by a `+` sign
454/// E.g.: 123+12 11223344556677889900112233445566778899001122334455667788.001122aabbcc
455pub(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///////////////
719// TokenName //
720///////////////
721
722/// Name of a token. This can be any arbitrary bytearray
723#[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    /// Ada tokenname (empty bytestring)
730    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    /// Convert a UTF8 string into a TokenName (use from_str to convert from a hexadecimal string)
752    pub fn from_string(str: &str) -> Result<Self, ConversionError> {
753        TokenName::from_bytes(String::from(str).into_bytes())
754    }
755
756    /// Convert TokenName to string if it is a valid UTF8 bytestring
757    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
779/// Nom parser for TokenName
780/// Expects a hexadecimal string representation of up to 32
781pub(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
797/// Serialize into a hexadecimal string
798/// It tries to decode the token name from UTF8 when the alternate flag is used (e.g.: format!("{:#}", ac)),
799/// if failsed it prepends the hex value with `0x`
800impl 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////////////////
828// AssetClass //
829////////////////
830
831/// AssetClass is uniquely identifying a specific asset
832#[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
840/// Serialize into two hexadecimal strings divided by a . (e.g. aabbcc.001122)
841/// It tries to decode the token name from UTF8 when the alternate flag is used (e.g.: format!("{:#}", ac)),
842/// if failsed it prepends the hex value with `0x`
843impl 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
872/// Nom parser for AssetClass
873/// Expects a currency symbol and token name both in hexadecimal format, divided by a `.`
874/// In case the token name is empty, the divider is not required
875/// E.g.:
876///   - 11223344556677889900112233445566778899001122334455667788.001122aabbcc
877///   - 11223344556677889900112233445566778899001122334455667788
878pub(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////////////////
896// Lovelace //
897////////////////
898
899#[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);