contract_extrinsics/
balance.rs

1// Copyright (C) Use Ink (UK) Ltd.
2// This file is part of cargo-contract.
3//
4// cargo-contract is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// cargo-contract is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with cargo-contract.  If not, see <http://www.gnu.org/licenses/>.
16
17use std::{
18    fmt::Display,
19    result::Result::Ok,
20    str::FromStr,
21};
22
23use rust_decimal::{
24    Decimal,
25    prelude::FromPrimitive,
26};
27use serde_json::json;
28use subxt::{
29    Config,
30    backend::{
31        legacy::LegacyRpcMethods,
32        rpc::RpcClient,
33    },
34};
35
36use anyhow::{
37    Context,
38    Result,
39    anyhow,
40};
41use url::Url;
42
43use crate::url_to_string;
44
45/// Represents different formats of a balance
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub enum BalanceVariant<Balance> {
48    /// Default format: no symbol, no token_decimals
49    Default(Balance),
50    /// Denominated format: symbol and token_decimals are present
51    Denominated(DenominatedBalance),
52}
53
54#[derive(Debug, Clone)]
55pub struct TokenMetadata {
56    /// Number of token_decimals used for denomination
57    pub token_decimals: usize,
58    /// Token symbol
59    pub symbol: String,
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct DenominatedBalance {
64    value: Decimal,
65    unit: UnitPrefix,
66    symbol: String,
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub enum UnitPrefix {
71    Giga,
72    Mega,
73    Kilo,
74    One,
75    Milli,
76    Micro,
77    Nano,
78}
79
80impl TokenMetadata {
81    /// Query [TokenMetadata] through the node's RPC
82    pub async fn query<C: Config>(url: &Url) -> Result<Self> {
83        let rpc_cli = RpcClient::from_url(url_to_string(url)).await?;
84        let rpc = LegacyRpcMethods::<C>::new(rpc_cli.clone());
85        let sys_props = rpc.system_properties().await?;
86
87        let default_decimals = json!(12);
88        let default_units = json!("UNIT");
89        let token_decimals = sys_props
90            .get("tokenDecimals")
91            .unwrap_or(&default_decimals)
92            .as_u64()
93            .context("error converting decimal to u64")?
94            as usize;
95        let symbol = sys_props
96            .get("tokenSymbol")
97            .unwrap_or(&default_units)
98            .as_str()
99            .context("error converting symbol to string")?;
100        Ok(Self {
101            token_decimals,
102            symbol: symbol.to_string(),
103        })
104    }
105}
106
107impl<Balance> FromStr for BalanceVariant<Balance>
108where
109    Balance: FromStr,
110{
111    type Err = anyhow::Error;
112
113    /// Attempts to parse the balance either in plain or denominated formats
114    /// If the balance is provide without the token symbol,
115    /// then it is treated as raw.
116    /// Otherwise, the balance is attempted to be parsed in a denominated format
117    fn from_str(input: &str) -> Result<Self, Self::Err> {
118        let input = input.replace('_', "");
119        // if we cannot parse the balance in raw format
120        // it means it is in a denominated format
121        let result = match input.parse::<Balance>() {
122            Ok(balance) => BalanceVariant::Default(balance),
123            Err(_) => BalanceVariant::Denominated(DenominatedBalance::from_str(&input)?),
124        };
125        Ok(result)
126    }
127}
128
129impl FromStr for DenominatedBalance {
130    type Err = anyhow::Error;
131
132    fn from_str(value: &str) -> Result<Self, Self::Err> {
133        let symbols = value
134            .trim_start_matches(|ch: char| ch.is_numeric() || ch == '.' || ch == ',');
135        let unit_char = symbols
136            .chars()
137            .next()
138            .context("no units or symbols present")?;
139        let unit: UnitPrefix = match unit_char {
140            'G' => UnitPrefix::Giga,
141            'M' => UnitPrefix::Mega,
142            'k' => UnitPrefix::Kilo,
143            'm' => UnitPrefix::Milli,
144            '\u{3bc}' => UnitPrefix::Micro,
145            'n' => UnitPrefix::Nano,
146            _ => UnitPrefix::One,
147        };
148        let symbol = if unit != UnitPrefix::One {
149            let (start, _) = symbols
150                .char_indices()
151                .nth(1)
152                .context("cannot find the first char's index")?;
153            symbols[start..].to_string()
154        } else {
155            String::new()
156        };
157        let value = value.trim_end_matches(|ch: char| ch.is_alphabetic());
158        let value = Decimal::from_str_exact(value)
159            .context("Error while parsing the value. Please denominate and normalize the balance first.")?
160            .normalize();
161        Ok(Self {
162            value,
163            unit,
164            symbol,
165        })
166    }
167}
168
169impl<Balance> BalanceVariant<Balance>
170where
171    Balance: From<u128> + Clone,
172{
173    /// Converts BalanceVariant into Balance.
174    ///
175    /// It is a reverse process of `from<T: Into<u128>>()`
176    ///
177    /// Throws Error if `value` is of nigher precision that allowed.
178    ///
179    /// ```rust
180    /// use contract_extrinsics::{
181    ///     BalanceVariant,
182    ///     TokenMetadata,
183    /// };
184    /// let decimals = 6;
185    /// let tm = TokenMetadata {
186    ///     token_decimals: decimals,
187    ///     symbol: String::from("DOT"),
188    /// };
189    /// let sample_den_balance: BalanceVariant<u128> = "0.4\u{3bc}DOT".parse().unwrap();
190    /// let result = sample_den_balance.denominate_balance(&tm);
191    /// assert!(result.is_err());
192    /// ```
193    ///
194    /// Otherwise, [Balance] is returned:
195    /// ```rust
196    /// use contract_extrinsics::{
197    ///     BalanceVariant,
198    ///     TokenMetadata,
199    /// };
200    /// let decimals = 6;
201    /// let tm = TokenMetadata {
202    ///     token_decimals: decimals,
203    ///     symbol: String::from("DOT"),
204    /// };
205    /// let sample_den_balance: BalanceVariant<u128> = "4123\u{3bc}DOT".parse().unwrap();
206    /// let balance = 4123;
207    /// let result = sample_den_balance.denominate_balance(&tm).unwrap();
208    /// assert_eq!(balance, result);
209    /// ```
210    pub fn denominate_balance(&self, token_metadata: &TokenMetadata) -> Result<Balance> {
211        match self {
212            BalanceVariant::Default(balance) => Ok(balance.clone()),
213            BalanceVariant::Denominated(den_balance) => {
214                let zeros: usize = (token_metadata.token_decimals as isize
215                    + match den_balance.unit {
216                        UnitPrefix::Giga => 9,
217                        UnitPrefix::Mega => 6,
218                        UnitPrefix::Kilo => 3,
219                        UnitPrefix::One => 0,
220                        UnitPrefix::Milli => -3,
221                        UnitPrefix::Micro => -6,
222                        UnitPrefix::Nano => -9,
223                    })
224                .try_into()?;
225                let multiple =
226                    Decimal::from_str_exact(&format!("1{}", "0".repeat(zeros)))?;
227                let fract_scale = den_balance.value.fract().scale();
228                let mantissa_difference = zeros as isize - fract_scale as isize;
229                if mantissa_difference < 0 {
230                    return Err(anyhow!(
231                        "Given precision of a Balance value is higher than allowed"
232                    ))
233                }
234                let balance: u128 = den_balance
235                    .value
236                    .checked_mul(multiple)
237                    .context("error while converting balance to raw format. Overflow during multiplication!")?
238                    .try_into()?;
239                Ok(balance.into())
240            }
241        }
242    }
243
244    /// # Summary
245    /// Display token units in a denominated format.
246    ///
247    /// I takes `value` of `Into<u128>` and [TokenMetadata]
248    /// and calculates the value in an denominated format
249    /// by manipulating the token_decimals.
250    ///
251    /// If the number is divisible by 10^(`token_decimals` + `unit_zeros`),
252    /// It sets the `UnitPrefix` and divides the `value` into `Decimal`
253    ///
254    /// If no [TokenMetadata] was present, than that means
255    /// that [Balance] is to be displayed in *normal* format
256    /// and `BalanceVariant::Default` is returned
257    ///
258    /// # Examples
259    /// ```rust
260    /// use contract_extrinsics::{
261    ///     BalanceVariant,
262    ///     TokenMetadata,
263    /// };
264    /// let decimals = 10;
265    /// let tm = TokenMetadata {
266    ///     token_decimals: decimals,
267    ///     symbol: String::from("DOT"),
268    /// };
269    /// let sample_den_balance: BalanceVariant<u128> = "500.5MDOT".parse().unwrap();
270    /// let balance: u128 = 5_005_000_000_000_000_000;
271    /// let den_balance = BalanceVariant::from(balance, Some(&tm)).unwrap();
272    /// assert_eq!(sample_den_balance, den_balance);
273    /// ```
274    pub fn from<T: Into<u128>>(
275        value: T,
276        token_metadata: Option<&TokenMetadata>,
277    ) -> Result<Self> {
278        let n: u128 = value.into();
279
280        if let Some(token_metadata) = token_metadata {
281            if n == 0 {
282                return Ok(BalanceVariant::Denominated(DenominatedBalance {
283                    value: Decimal::ZERO,
284                    unit: UnitPrefix::One,
285                    symbol: token_metadata.symbol.clone(),
286                }))
287            }
288
289            let number_of_digits = n.to_string().len();
290
291            let giga_units_zeros = token_metadata.token_decimals + 9;
292            let mega_units_zeros = token_metadata.token_decimals + 6;
293            let kilo_units_zeros = token_metadata.token_decimals + 3;
294            let one_unit_zeros = token_metadata.token_decimals;
295            let milli_units_zeros = token_metadata.token_decimals.checked_sub(3);
296            let micro_units_zeros = token_metadata.token_decimals.checked_sub(6);
297            let nano_units_zeros = token_metadata.token_decimals.checked_sub(9);
298
299            let unit: UnitPrefix;
300            let zeros: usize;
301            if (giga_units_zeros + 1..).contains(&number_of_digits) {
302                zeros = giga_units_zeros;
303                unit = UnitPrefix::Giga;
304            } else if (mega_units_zeros + 1..=giga_units_zeros)
305                .contains(&number_of_digits)
306            {
307                zeros = mega_units_zeros;
308                unit = UnitPrefix::Mega;
309            } else if (kilo_units_zeros + 1..=mega_units_zeros)
310                .contains(&number_of_digits)
311            {
312                zeros = kilo_units_zeros;
313                unit = UnitPrefix::Kilo;
314            } else if (one_unit_zeros + 1..=kilo_units_zeros).contains(&number_of_digits)
315            {
316                zeros = one_unit_zeros;
317                unit = UnitPrefix::One;
318            } else if milli_units_zeros.is_some()
319                && (milli_units_zeros.unwrap() + 1..=one_unit_zeros)
320                    .contains(&number_of_digits)
321            {
322                zeros = match milli_units_zeros {
323                    Some(val) => val,
324                    None => return Err(anyhow!("the number is checked to be >= 0. qed")),
325                };
326                unit = UnitPrefix::Milli;
327            } else if milli_units_zeros.is_some()
328                && micro_units_zeros.is_some()
329                && (micro_units_zeros.unwrap() + 1..=milli_units_zeros.unwrap())
330                    .contains(&number_of_digits)
331            {
332                zeros = match micro_units_zeros {
333                    Some(val) => val,
334                    None => return Err(anyhow!("the number is checked to be >= 0. qed")),
335                };
336                unit = UnitPrefix::Micro;
337            } else if nano_units_zeros.is_some() {
338                zeros = match nano_units_zeros {
339                    Some(val) => val,
340                    None => return Err(anyhow!("the number is checked to be >= 0. qed")),
341                };
342                unit = UnitPrefix::Nano;
343            } else {
344                return Err(anyhow!("Invalid denomination"))
345            }
346            let multiple = Decimal::from_str_exact(&format!("1{}", "0".repeat(zeros)))?;
347            let value = Decimal::from_u128(n)
348                .context("value can not be converted into decimal")?
349                / multiple;
350
351            let den_balance = DenominatedBalance {
352                value,
353                unit,
354                symbol: token_metadata.symbol.clone(),
355            };
356
357            Ok(BalanceVariant::Denominated(den_balance))
358        } else {
359            Ok(BalanceVariant::Default(n.into()))
360        }
361    }
362}
363
364impl<Balance> Display for BalanceVariant<Balance>
365where
366    Balance: Display + Clone,
367{
368    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
369        match self {
370            BalanceVariant::Default(balance) => f.write_str(&balance.to_string()),
371            BalanceVariant::Denominated(input) => f.write_str(&input.to_string()),
372        }
373    }
374}
375
376impl Display for DenominatedBalance {
377    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
378        let prefix = match self.unit {
379            UnitPrefix::Giga => "G",
380            UnitPrefix::Mega => "M",
381            UnitPrefix::Kilo => "k",
382            UnitPrefix::One => "",
383            UnitPrefix::Milli => "m",
384            UnitPrefix::Micro => "μ",
385            UnitPrefix::Nano => "n",
386        };
387        f.write_fmt(format_args!("{}{}{}", self.value, prefix, self.symbol))
388    }
389}
390
391#[cfg(test)]
392mod tests {
393    use ink_env::{
394        DefaultEnvironment,
395        Environment,
396    };
397
398    use super::*;
399
400    #[test]
401    fn correct_balances_parses_success() {
402        assert!(
403            BalanceVariant::<<DefaultEnvironment as Environment>::Balance>::from_str(
404                "500DOT"
405            )
406            .is_ok(),
407            "<500DOT> was not parsed correctly"
408        );
409        assert!(
410            BalanceVariant::<<DefaultEnvironment as Environment>::Balance>::from_str(
411                "500"
412            )
413            .is_ok(),
414            "<500> was not parsed correctly"
415        );
416        assert!(
417            BalanceVariant::<<DefaultEnvironment as Environment>::Balance>::from_str(
418                "1.0"
419            )
420            .is_err(),
421            "<1.0> was not parsed correctly. Units must be provided"
422        );
423        assert!(
424            BalanceVariant::<<DefaultEnvironment as Environment>::Balance>::from_str(
425                "1.0DOT"
426            )
427            .is_ok(),
428            "<1.0DOt> was not parsed correctly"
429        );
430    }
431
432    #[test]
433    fn incorrect_balances() {
434        assert!(
435            BalanceVariant::<<DefaultEnvironment as Environment>::Balance>::from_str(
436                "500%"
437            )
438            .is_err(),
439            "expected to fail parsing incorrect balance"
440        );
441    }
442
443    #[test]
444    fn balance_variant_denominated_success() {
445        let tm = TokenMetadata {
446            token_decimals: 10,
447            symbol: String::from("DOT"),
448        };
449        let bv =
450            BalanceVariant::<<DefaultEnvironment as Environment>::Balance>::from_str(
451                "500MDOT",
452            )
453            .expect("successful parsing. qed");
454        assert!(
455            bv.denominate_balance(&tm).is_ok(),
456            "balances could not be denominated correctly"
457        );
458    }
459
460    #[test]
461    fn balance_variant_denominated_equal() {
462        let decimals = 10;
463        let tm = TokenMetadata {
464            token_decimals: decimals,
465            symbol: String::from("DOT"),
466        };
467        let balance: <DefaultEnvironment as Environment>::Balance =
468            500 * 1_000_000 * 10_000_000_000;
469        let bv =
470            BalanceVariant::<<DefaultEnvironment as Environment>::Balance>::from_str(
471                "500MDOT",
472            )
473            .expect("successful parsing. qed");
474        let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed");
475        assert_eq!(balance, balance_parsed);
476    }
477
478    #[test]
479    fn balance_variant_denominated_equal_fraction() {
480        let decimals = 10;
481        let tm = TokenMetadata {
482            token_decimals: decimals,
483            symbol: String::from("DOT"),
484        };
485        let balance: <DefaultEnvironment as Environment>::Balance =
486            5_005_000_000_000_000_000;
487        let bv = BalanceVariant::from_str("500.5MDOT").expect("successful parsing. qed");
488        let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed");
489        assert_eq!(balance, balance_parsed);
490    }
491
492    #[test]
493    fn balance_variant_denominated_equal_small_units() {
494        let decimals = 10;
495        let tm = TokenMetadata {
496            token_decimals: decimals,
497            symbol: String::from("DOT"),
498        };
499        let balance: <DefaultEnvironment as Environment>::Balance = 5_005_000;
500        let bv = BalanceVariant::from_str("500.5μDOT").expect("successful parsing. qed");
501        let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed");
502        assert_eq!(balance, balance_parsed);
503    }
504    #[test]
505    fn smallest_value() {
506        let decimals = 10;
507        let tm = TokenMetadata {
508            token_decimals: decimals,
509            symbol: String::from("DOT"),
510        };
511        let balance: <DefaultEnvironment as Environment>::Balance = 1;
512        let bv = BalanceVariant::from_str("0.1nDOT").expect("successful parsing. qed");
513        let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed");
514        assert_eq!(balance, balance_parsed);
515    }
516
517    #[test]
518    fn value_less_than_precision() {
519        // here we test if the user tries to input the denominated balance
520        // which results in value less than zero
521        let decimals = 10;
522        let tm = TokenMetadata {
523            token_decimals: decimals,
524            symbol: String::from("DOT"),
525        };
526        let bv =
527            BalanceVariant::<<DefaultEnvironment as Environment>::Balance>::from_str(
528                "0.01546nDOT",
529            )
530            .expect("successful parsing. qed");
531        let balance_parsed = bv.denominate_balance(&tm);
532        assert!(balance_parsed.is_err())
533    }
534
535    #[test]
536    fn giga() {
537        let decimals = 10;
538        let tm = TokenMetadata {
539            token_decimals: decimals,
540            symbol: String::from("DOT"),
541        };
542        let balance: <DefaultEnvironment as Environment>::Balance =
543            5_005_000_000_000_000_000_000;
544        let bv = BalanceVariant::from_str("500.5GDOT").expect("successful parsing. qed");
545        let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed");
546        assert_eq!(balance, balance_parsed);
547    }
548
549    #[test]
550    fn kilo() {
551        let decimals = 10;
552        let tm = TokenMetadata {
553            token_decimals: decimals,
554            symbol: String::from("DOT"),
555        };
556        let balance: <DefaultEnvironment as Environment>::Balance = 5_005_000_000_000_000;
557        let bv = BalanceVariant::from_str("500.5kDOT").expect("successful parsing. qed");
558        let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed");
559        assert_eq!(balance, balance_parsed);
560    }
561
562    #[test]
563    fn unit() {
564        let decimals = 10;
565        let tm = TokenMetadata {
566            token_decimals: decimals,
567            symbol: String::from("DOT"),
568        };
569        let balance: <DefaultEnvironment as Environment>::Balance = 5_005_000_000_000;
570        let bv = BalanceVariant::from_str("500.5DOT").expect("successful parsing. qed");
571        let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed");
572        assert_eq!(balance, balance_parsed);
573    }
574
575    #[test]
576    fn milli() {
577        let decimals = 10;
578        let tm = TokenMetadata {
579            token_decimals: decimals,
580            symbol: String::from("DOT"),
581        };
582        let balance: <DefaultEnvironment as Environment>::Balance = 5_005_000_000;
583        let bv = BalanceVariant::from_str("500.5mDOT").expect("successful parsing. qed");
584        let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed");
585        assert_eq!(balance, balance_parsed);
586    }
587    #[test]
588    fn micro() {
589        let decimals = 10;
590        let tm = TokenMetadata {
591            token_decimals: decimals,
592            symbol: String::from("DOT"),
593        };
594        let balance: <DefaultEnvironment as Environment>::Balance = 5_005_000;
595        let bv = BalanceVariant::from_str("500.5μDOT").expect("successful parsing. qed");
596        let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed");
597        assert_eq!(balance, balance_parsed);
598    }
599    #[test]
600    fn nano() {
601        let decimals = 10;
602        let tm = TokenMetadata {
603            token_decimals: decimals,
604            symbol: String::from("DOT"),
605        };
606        let balance: <DefaultEnvironment as Environment>::Balance = 5_005;
607        let bv = BalanceVariant::from_str("500.5nDOT").expect("successful parsing. qed");
608        let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed");
609        assert_eq!(balance, balance_parsed);
610    }
611
612    #[test]
613    fn different_digits() {
614        let decimals = 10;
615        let tm = TokenMetadata {
616            token_decimals: decimals,
617            symbol: String::from("DOT"),
618        };
619        let balance: <DefaultEnvironment as Environment>::Balance = 5_235_456_210_000_000;
620        let bv =
621            BalanceVariant::from_str("523.545621kDOT").expect("successful parsing. qed");
622        let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed");
623        assert_eq!(balance, balance_parsed);
624    }
625
626    #[test]
627    fn non_standard_token_decimals() {
628        let decimals = 10;
629        let tm = TokenMetadata {
630            token_decimals: decimals,
631            symbol: String::from("DOT"),
632        };
633        let balance: <DefaultEnvironment as Environment>::Balance = 50_015_000_000_000;
634        let bv = BalanceVariant::from_str("5001.5DOT").expect("successful parsing. qed");
635        let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed");
636        assert_eq!(balance, balance_parsed);
637    }
638
639    #[test]
640    fn small_number_of_decimals_zero() {
641        let decimals = 6;
642        let tm = TokenMetadata {
643            token_decimals: decimals,
644            symbol: String::from("DOT"),
645        };
646        let bv =
647            BalanceVariant::<<DefaultEnvironment as Environment>::Balance>::from_str(
648                "0.4μDOT",
649            )
650            .expect("successful parsing. qed");
651        let balance_parsed = bv.denominate_balance(&tm);
652        assert!(balance_parsed.is_err())
653    }
654
655    #[test]
656    fn big_input_to_denominate() {
657        // max value of Decimal:MAX is 79_228_162_514_264_337_593_543_950_335
658        let s = "79_228_162_514_264_337_593_543_950_336DOT";
659        let bv =
660            BalanceVariant::<<DefaultEnvironment as Environment>::Balance>::from_str(s);
661        assert!(bv.is_err())
662    }
663
664    #[test]
665    fn big_input_to_raw() {
666        // max value of Decimal:MAX is 79_228_162_514_264_337_593_543_950_335
667        let s = "79_228_162_514_264_337_593_543_950_336";
668        let bv =
669            BalanceVariant::<<DefaultEnvironment as Environment>::Balance>::from_str(s);
670        assert!(bv.is_ok())
671    }
672
673    #[test]
674    fn convert_from_u128() {
675        let decimals = 6;
676        let tm = TokenMetadata {
677            token_decimals: decimals,
678            symbol: String::from("DOT"),
679        };
680        let balance = 532_500_000_000_u128;
681        let denominated_balance = BalanceVariant::<
682            <DefaultEnvironment as Environment>::Balance,
683        >::from(balance, Some(&tm))
684        .expect("successful conversion");
685        let sample = BalanceVariant::Denominated(DenominatedBalance {
686            value: Decimal::new(5325, 1),
687            unit: UnitPrefix::Kilo,
688            symbol: String::from("DOT"),
689        });
690        assert_eq!(sample, denominated_balance);
691    }
692
693    #[test]
694    fn convert_one_from_u128() {
695        let decimals = 10;
696        let tm = TokenMetadata {
697            token_decimals: decimals,
698            symbol: String::from("DOT"),
699        };
700        let balance = 532_500_000_000_u128;
701        let denominated_balance = BalanceVariant::<
702            <DefaultEnvironment as Environment>::Balance,
703        >::from(balance, Some(&tm))
704        .expect("successful conversion");
705        let sample = BalanceVariant::Denominated(DenominatedBalance {
706            value: Decimal::new(5325, 2),
707            unit: UnitPrefix::One,
708            symbol: String::from("DOT"),
709        });
710        assert_eq!(sample, denominated_balance);
711    }
712
713    #[test]
714    fn convert_small_from_u128() {
715        let decimals = 10;
716        let tm = TokenMetadata {
717            token_decimals: decimals,
718            symbol: String::from("DOT"),
719        };
720        // 10_000_000_000 - One
721        // 10_000_000 - Milli
722        // 10_000 - Micro
723        // 532_500 - 52.25 Micro
724        let balance = 532_500_u128;
725        let denominated_balance = BalanceVariant::<
726            <DefaultEnvironment as Environment>::Balance,
727        >::from(balance, Some(&tm))
728        .expect("successful conversion");
729        let sample = BalanceVariant::Denominated(DenominatedBalance {
730            value: Decimal::new(5325, 2),
731            unit: UnitPrefix::Micro,
732            symbol: String::from("DOT"),
733        });
734        assert_eq!(sample, denominated_balance);
735    }
736}