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::with_capacity(0)),
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 a new token into the value, or replace the existing quantity.
266    pub fn insert_token(&self, cs: &CurrencySymbol, tn: &TokenName, a: &BigInt) -> Self {
267        let mut result_map = self.0.clone();
268
269        result_map
270            .entry(cs.clone())
271            .and_modify(|tn_map| {
272                tn_map
273                    .entry(tn.clone())
274                    .and_modify(|old_a| {
275                        old_a.clone_from(a);
276                    })
277                    .or_insert_with(|| a.clone());
278            })
279            .or_insert_with(|| singleton((tn.clone(), a.clone())));
280
281        Self(result_map)
282    }
283
284    /// Return true if the value don't have any entries.
285    pub fn is_empty(&self) -> bool {
286        self.0.is_empty()
287    }
288
289    /// Remove all tokens whose quantity is zero.
290    pub fn normalize(self) -> Self {
291        self.filter(|_, _, a| a.is_zero().not())
292    }
293
294    pub fn is_subset(&self, b: &Value) -> bool {
295        (b - self)
296            .normalize()
297            // Has negative entries?
298            .filter(|_, _, amount| amount < &BigInt::from(0u32))
299            .is_empty()
300    }
301
302    pub fn is_pure_ada(&self) -> bool {
303        self.0.iter().all(|(cs, _)| cs == &CurrencySymbol::Ada)
304    }
305
306    /// Apply a function to each token of the value, and use its result as the new amount.
307    pub fn map_amount<F>(self, mut f: F) -> Self
308    where
309        F: FnMut(&CurrencySymbol, &TokenName, &BigInt) -> BigInt,
310    {
311        self.filter_map_amount(|cs, tn, a| Some(f(cs, tn, a)))
312    }
313
314    /// Apply a predicate to tokens.
315    pub fn filter<F>(self, mut f: F) -> Self
316    where
317        F: FnMut(&CurrencySymbol, &TokenName, &BigInt) -> bool,
318    {
319        self.filter_map_amount(|cs, tn, a| f(cs, tn, a).then(|| a.clone()))
320    }
321
322    /// Apply a function to each token of the value. If the result is None, the token entry will be
323    /// removed.
324    ///
325    /// Note that if the name-quantity map of any given currency symbols is empty, the currency entry
326    /// will be removed from the top-level map entirely.
327    pub fn filter_map_amount<F>(self, mut f: F) -> Self
328    where
329        F: FnMut(&CurrencySymbol, &TokenName, &BigInt) -> Option<BigInt>,
330    {
331        Value(
332            (self.0)
333                .into_iter()
334                .filter_map(|(cs, tn_map)| {
335                    let filtered_tn_map = tn_map
336                        .into_iter()
337                        .filter_map(|(tn, a)| f(&cs, &tn, &a).map(|a| (tn, a)))
338                        .collect::<BTreeMap<TokenName, BigInt>>();
339
340                    if filtered_tn_map.is_empty() {
341                        None
342                    } else {
343                        Some((cs.clone(), filtered_tn_map))
344                    }
345                })
346                .collect(),
347        )
348    }
349
350    /// Create a vector with each distinct value
351    /// Warning: is the value is not normalized, the same asset class can appear twice
352    pub fn flatten(&self) -> Vec<(&CurrencySymbol, &TokenName, &BigInt)> {
353        self.0
354            .iter()
355            .flat_map(|(currency_symbol, assets)| {
356                assets
357                    .iter()
358                    .map(move |(token_name, amount)| (currency_symbol, token_name, amount))
359            })
360            .collect()
361    }
362
363    pub fn unflatten(list: &[(CurrencySymbol, TokenName, BigInt)]) -> Self {
364        list.iter()
365            .fold(Value::new(), |v, (cs, tn, am)| v.insert_token(cs, tn, am))
366    }
367}
368
369impl fmt::Display for Value {
370    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371        let mut it = self
372            .0
373            .iter()
374            .flat_map(|(currency_symbol, assets)| {
375                assets
376                    .iter()
377                    .map(move |(token_name, amount)| (currency_symbol, token_name, amount))
378            })
379            .peekable();
380        while let Some((cur_sym, tn, amount)) = it.next() {
381            if cur_sym.is_ada() {
382                amount.fmt(f)?;
383            } else if tn.is_empty() {
384                amount.fmt(f)?;
385                " ".fmt(f)?;
386                cur_sym.fmt(f)?;
387            } else {
388                amount.fmt(f)?;
389                " ".fmt(f)?;
390                cur_sym.fmt(f)?;
391                ".".fmt(f)?;
392                tn.fmt(f)?;
393            }
394            if it.peek().is_some() {
395                "+".fmt(f)?;
396            }
397        }
398
399        Ok(())
400    }
401}
402
403/// Nom parser for a single entry in a Value
404/// Expects an integer quantity, followed by an asset class after a space character
405/// (space is not required for Ada)
406/// E.g.: 12 11223344556677889900112233445566778899001122334455667788.001122aabbcc
407pub(crate) fn flat_value(
408    input: &str,
409) -> IResult<&str, (CurrencySymbol, TokenName, BigInt), VerboseError<&str>> {
410    map(
411        tuple((big_int, opt(preceded(char(' '), asset_class)))),
412        |(amount, asset_class)| match asset_class {
413            None => (CurrencySymbol::Ada, TokenName::ada(), amount),
414            Some(AssetClass {
415                currency_symbol,
416                token_name,
417            }) => (currency_symbol, token_name, amount),
418        },
419    )(input)
420}
421
422/// Nom parser for Value
423/// Expects flat Value entries divided by a `+` sign
424/// E.g.: 123+12 11223344556677889900112233445566778899001122334455667788.001122aabbcc
425pub(crate) fn value(input: &str) -> IResult<&str, Value, VerboseError<&str>> {
426    map(
427        separated_list0(tuple((space0, char('+'))), flat_value),
428        |flat_values| Value::unflatten(&flat_values),
429    )(input)
430}
431
432impl FromStr for Value {
433    type Err = ConversionError;
434
435    fn from_str(s: &str) -> Result<Self, Self::Err> {
436        all_consuming(value)(s)
437            .finish()
438            .map_err(|err| {
439                ConversionError::ParseError(anyhow!("Error while parsing Value '{}': {}", s, err))
440            })
441            .map(|(_, cs)| cs)
442    }
443}
444
445impl Zero for Value {
446    fn zero() -> Self {
447        Default::default()
448    }
449
450    fn is_zero(&self) -> bool {
451        self.is_empty()
452    }
453}
454
455impl_op!(+ |a: &Value, b: &Value| -> Value { a.clone() + b.clone() });
456impl_op!(+ |a: &Value, b: Value| -> Value { a.clone() + b });
457impl_op!(+ |a: Value, b: &Value| -> Value { a + b.clone() });
458
459impl Add<Value> for Value {
460    type Output = Value;
461
462    fn add(self, rhs: Value) -> Self::Output {
463        Value(union_btree_maps_with(
464            |lhs, rhs| union_btree_maps_with(|lhs, rhs| lhs + rhs, lhs, rhs),
465            self.0,
466            rhs.0,
467        ))
468    }
469}
470
471impl Neg for Value {
472    type Output = Value;
473
474    fn neg(self) -> Self::Output {
475        self.map_amount(|_, _, a| a.neg())
476    }
477}
478
479impl Neg for &Value {
480    type Output = Value;
481
482    fn neg(self) -> Self::Output {
483        self.clone().neg()
484    }
485}
486
487impl_op!(-|a: &Value, b: &Value| -> Value { a.clone() - b.clone() });
488impl_op!(-|a: &Value, b: Value| -> Value { a.clone() - b });
489impl_op!(-|a: Value, b: &Value| -> Value { a - b.clone() });
490
491impl Sub<Value> for Value {
492    type Output = Value;
493
494    fn sub(self, rhs: Value) -> Self::Output {
495        self.add(rhs.neg())
496    }
497}
498
499impl_op!(*|a: &BigInt, b: &Value| -> Value { b * a });
500impl_op_commutative!(*|a: Value, b: BigInt| -> Value { &a * &b });
501impl_op_commutative!(*|a: &Value, b: BigInt| -> Value { a * &b });
502impl_op_commutative!(*|a: Value, b: &BigInt| -> Value { &a * b });
503
504impl_op_commutative!(*|a: &Value, b: i8| -> Value { a * BigInt::from(b) });
505impl_op_commutative!(*|a: &Value, b: i16| -> Value { a * BigInt::from(b) });
506impl_op_commutative!(*|a: &Value, b: i32| -> Value { a * BigInt::from(b) });
507impl_op_commutative!(*|a: &Value, b: i64| -> Value { a * BigInt::from(b) });
508
509impl_op_commutative!(*|a: &Value, b: u8| -> Value { a * BigInt::from(b) });
510impl_op_commutative!(*|a: &Value, b: u16| -> Value { a * BigInt::from(b) });
511impl_op_commutative!(*|a: &Value, b: u32| -> Value { a * BigInt::from(b) });
512impl_op_commutative!(*|a: &Value, b: u64| -> Value { a * BigInt::from(b) });
513
514impl Mul<&BigInt> for &Value {
515    type Output = Value;
516
517    fn mul(self, rhs: &BigInt) -> Self::Output {
518        Value(
519            self.0
520                .iter()
521                .map(|(cs, tn_map)| {
522                    (
523                        cs.clone(),
524                        tn_map.iter().map(|(tn, q)| (tn.clone(), q * rhs)).collect(),
525                    )
526                })
527                .collect(),
528        )
529    }
530}
531
532impl Sum<Value> for Value {
533    fn sum<I: Iterator<Item = Value>>(iter: I) -> Self {
534        iter.fold(Zero::zero(), Add::add)
535    }
536}
537
538impl<'a> Sum<&'a Value> for Value {
539    fn sum<I: Iterator<Item = &'a Value>>(iter: I) -> Self {
540        iter.fold(Zero::zero(), Add::add)
541    }
542}
543
544impl IsPlutusData for Value {
545    fn to_plutus_data(&self) -> PlutusData {
546        self.0.to_plutus_data()
547    }
548
549    fn from_plutus_data(data: &PlutusData) -> Result<Self, PlutusDataError> {
550        IsPlutusData::from_plutus_data(data).map(Self)
551    }
552}
553
554impl FromCSL<csl::Assets> for BTreeMap<TokenName, BigInt> {
555    fn from_csl(value: &csl::Assets) -> Self {
556        let keys = value.keys();
557        (0..keys.len()).fold(BTreeMap::new(), |mut acc, idx| {
558            let asset_name = keys.get(idx);
559            if let Some(quantity) = value.get(&asset_name) {
560                acc.insert(
561                    TokenName::from_csl(&asset_name),
562                    BigInt::from_csl(&quantity),
563                );
564            }
565            acc
566        })
567    }
568}
569
570impl TryFromPLA<BTreeMap<TokenName, BigInt>> for csl::Assets {
571    fn try_from_pla(val: &BTreeMap<TokenName, BigInt>) -> Result<Self, TryFromPLAError> {
572        val.iter().try_fold(csl::Assets::new(), |mut acc, (k, v)| {
573            acc.insert(&k.try_to_csl()?, &v.try_to_csl()?);
574            Ok(acc)
575        })
576    }
577}
578
579impl FromCSL<csl::MultiAsset> for Value {
580    fn from_csl(value: &csl::MultiAsset) -> Self {
581        let keys = value.keys();
582        Value((0..keys.len()).fold(BTreeMap::new(), |mut acc, idx| {
583            let script_hash = keys.get(idx);
584            if let Some(assets) = value.get(&script_hash) {
585                let assets = BTreeMap::from_csl(&assets);
586                acc.insert(
587                    CurrencySymbol::NativeToken(MintingPolicyHash::from_csl(&script_hash)),
588                    assets,
589                );
590            }
591            acc
592        }))
593    }
594}
595
596impl FromCSL<csl::Value> for Value {
597    fn from_csl(value: &csl::Value) -> Self {
598        let lovelaces = BigInt::from_csl(&value.coin());
599        let mut pla_value = Value::ada_value(&lovelaces);
600        if let Some(multi_asset) = value.multiasset() {
601            pla_value = &pla_value + &Value::from_csl(&multi_asset)
602        }
603        pla_value
604    }
605}
606
607impl TryFromPLA<Value> for csl::Value {
608    fn try_from_pla(val: &Value) -> Result<Self, TryFromPLAError> {
609        let coin: csl::Coin = val
610            .0
611            .get(&CurrencySymbol::Ada)
612            .and_then(|m| m.get(&TokenName::ada()))
613            .map_or(Ok(csl::BigNum::zero()), TryToCSL::try_to_csl)?;
614
615        let m_ass = val
616            .0
617            .iter()
618            .filter_map(|(cs, tn_map)| match &cs {
619                CurrencySymbol::Ada => None,
620                CurrencySymbol::NativeToken(h) => Some((h, tn_map)),
621            })
622            .try_fold(csl::MultiAsset::new(), |mut acc, (cs, ass)| {
623                acc.insert(&cs.try_to_csl()?, &ass.try_to_csl()?);
624                Ok(acc)
625            })?;
626
627        let mut v = csl::Value::new(&coin);
628
629        v.set_multiasset(&m_ass);
630
631        Ok(v)
632    }
633}
634
635impl FromCSL<csl::MintAssets> for BTreeMap<TokenName, BigInt> {
636    fn from_csl(m_ass: &csl::MintAssets) -> Self {
637        let keys = m_ass.keys();
638        (0..keys.len())
639            .map(|idx| {
640                let key = keys.get(idx);
641                let value = m_ass.get(&key).unwrap();
642                (TokenName::from_csl(&key), BigInt::from_csl(&value))
643            })
644            .collect()
645    }
646}
647
648impl FromCSL<csl::MintsAssets> for BTreeMap<TokenName, BigInt> {
649    fn from_csl(value: &csl::MintsAssets) -> Self {
650        (0..value.len())
651            .map(|idx| value.get(idx).unwrap())
652            .fold(BTreeMap::new(), |acc, m| {
653                let ass = BTreeMap::from_csl(&m);
654                union_b_tree_maps_with(|l, r| l + r, [&acc, &ass])
655            })
656    }
657}
658
659impl TryFromPLA<BTreeMap<TokenName, BigInt>> for csl::MintAssets {
660    fn try_from_pla(val: &BTreeMap<TokenName, BigInt>) -> Result<Self, TryFromPLAError> {
661        val.iter()
662            .try_fold(csl::MintAssets::new(), |mut acc, (k, v)| {
663                acc.insert(&k.try_to_csl()?, &v.try_to_csl()?)
664                    .map_err(TryFromPLAError::CSLJsError)?;
665                Ok(acc)
666            })
667    }
668}
669
670impl FromCSL<csl::Mint> for Value {
671    fn from_csl(mint: &csl::Mint) -> Self {
672        let keys = mint.keys();
673        Value(
674            (0..keys.len())
675                .map(|idx| {
676                    let sh = keys.get(idx);
677                    let ass = mint.get(&sh).unwrap_or(csl::MintsAssets::new());
678                    (
679                        CurrencySymbol::NativeToken(MintingPolicyHash::from_csl(&sh)),
680                        BTreeMap::from_csl(&ass),
681                    )
682                })
683                .collect::<BTreeMap<CurrencySymbol, BTreeMap<TokenName, BigInt>>>(),
684        )
685    }
686}
687
688///////////////
689// TokenName //
690///////////////
691
692/// Name of a token. This can be any arbitrary bytearray
693#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
694#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
695#[cfg_attr(feature = "lbf", derive(Json))]
696pub struct TokenName(pub LedgerBytes);
697
698impl TokenName {
699    /// Ada tokenname (empty bytestring)
700    pub fn ada() -> TokenName {
701        TokenName(LedgerBytes(Vec::with_capacity(0)))
702    }
703
704    pub fn is_empty(&self) -> bool {
705        self.0 .0.is_empty()
706    }
707
708    pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, ConversionError> {
709        if bytes.len() <= 32 {
710            Ok(TokenName(LedgerBytes(bytes)))
711        } else {
712            Err(ConversionError::invalid_bytestring_length(
713                "TokenName",
714                32,
715                "less than or equal to",
716                &bytes,
717            ))
718        }
719    }
720
721    /// Convert a UTF8 string into a TokenName (use from_str to convert from a hexadecimal string)
722    pub fn from_string(str: &str) -> Result<Self, ConversionError> {
723        TokenName::from_bytes(String::from(str).into_bytes())
724    }
725
726    /// Convert TokenName to string if it is a valid UTF8 bytestring
727    pub fn try_into_string(self) -> Result<String, std::string::FromUtf8Error> {
728        String::from_utf8(self.0 .0)
729    }
730}
731
732impl FromStr for TokenName {
733    type Err = ConversionError;
734
735    fn from_str(s: &str) -> Result<Self, Self::Err> {
736        all_consuming(token_name)(s)
737            .finish()
738            .map_err(|err| {
739                ConversionError::ParseError(anyhow!(
740                    "Error while parsing TokenName '{}': {}",
741                    s,
742                    err
743                ))
744            })
745            .map(|(_, cs)| cs)
746    }
747}
748
749/// Nom parser for TokenName
750/// Expects a hexadecimal string representation of up to 32
751pub(crate) fn token_name(input: &str) -> IResult<&str, TokenName, VerboseError<&str>> {
752    map_res(ledger_bytes, |LedgerBytes(bytes)| {
753        TokenName::from_bytes(bytes)
754    })(input)
755}
756
757impl IsPlutusData for TokenName {
758    fn to_plutus_data(&self) -> PlutusData {
759        self.0.to_plutus_data()
760    }
761
762    fn from_plutus_data(data: &PlutusData) -> Result<Self, PlutusDataError> {
763        IsPlutusData::from_plutus_data(data).map(Self)
764    }
765}
766
767/// Serialize into a hexadecimal string
768/// It tries to decode the token name from UTF8 when the alternate flag is used (e.g.: format!("{:#}", ac)),
769/// if failsed it prepends the hex value with `0x`
770impl fmt::Display for TokenName {
771    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
772        if f.alternate() {
773            let utf8_str = std::str::from_utf8(&self.0 .0);
774
775            match utf8_str {
776                Ok(str) => write!(f, "{}", str),
777                Err(_) => write!(f, "0x{}", self.0),
778            }
779        } else {
780            write!(f, "{}", self.0)
781        }
782    }
783}
784
785impl FromCSL<csl::AssetName> for TokenName {
786    fn from_csl(value: &csl::AssetName) -> Self {
787        TokenName(LedgerBytes(value.name()))
788    }
789}
790
791impl TryFromPLA<TokenName> for csl::AssetName {
792    fn try_from_pla(val: &TokenName) -> Result<Self, TryFromPLAError> {
793        csl::AssetName::new(val.0 .0.to_owned()).map_err(TryFromPLAError::CSLJsError)
794    }
795}
796
797////////////////
798// AssetClass //
799////////////////
800
801/// AssetClass is uniquely identifying a specific asset
802#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
803#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
804#[cfg_attr(feature = "lbf", derive(Json))]
805pub struct AssetClass {
806    pub currency_symbol: CurrencySymbol,
807    pub token_name: TokenName,
808}
809
810/// Serialize into two hexadecimal strings divided by a . (e.g. aabbcc.001122)
811/// It tries to decode the token name from UTF8 when the alternate flag is used (e.g.: format!("{:#}", ac)),
812/// if failsed it prepends the hex value with `0x`
813impl fmt::Display for AssetClass {
814    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
815        if self.token_name.is_empty() {
816            self.currency_symbol.fmt(f)
817        } else {
818            self.currency_symbol.fmt(f)?;
819            ".".fmt(f)?;
820            self.token_name.fmt(f)
821        }
822    }
823}
824
825impl FromStr for AssetClass {
826    type Err = ConversionError;
827
828    fn from_str(s: &str) -> Result<Self, Self::Err> {
829        all_consuming(asset_class)(s)
830            .finish()
831            .map_err(|err| {
832                ConversionError::ParseError(anyhow!(
833                    "Error while parsing AssetClass '{}': {}",
834                    s,
835                    err
836                ))
837            })
838            .map(|(_, cs)| cs)
839    }
840}
841
842/// Nom parser for AssetClass
843/// Expects a currency symbol and token name both in hexadecimal format, divided by a `.`
844/// In case the token name is empty, the divider is not required
845/// E.g.:
846///   - 11223344556677889900112233445566778899001122334455667788.001122aabbcc
847///   - 11223344556677889900112233445566778899001122334455667788
848pub(crate) fn asset_class(input: &str) -> IResult<&str, AssetClass, VerboseError<&str>> {
849    let (input, cs) = currency_symbol(input)?;
850
851    let (input, tn) = alt((
852        preceded(eof, success(TokenName::ada())),
853        preceded(char('.'), token_name),
854    ))(input)?;
855
856    Ok((
857        input,
858        AssetClass {
859            currency_symbol: cs,
860            token_name: tn,
861        },
862    ))
863}
864
865////////////////
866// Lovelace //
867////////////////
868
869#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
870#[is_plutus_data_derive_strategy = "Newtype"]
871#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
872#[cfg_attr(feature = "lbf", derive(Json))]
873pub struct Lovelace(pub BigInt);