levana_perpswap_cosmos/number/
types.rs

1//! This module provides raw data types and just enough functionality to
2//! construct them. It exports smart contructors to the rest of the crate to
3//! ensure that invariants are never violated.
4
5use anyhow::{Context, Result};
6use cosmwasm_std::{Decimal256, OverflowError, Uint128, Uint256};
7use std::{
8    fmt::Display,
9    ops::{Add, Sub},
10    str::FromStr,
11};
12
13/// needed to get around the orphan rule
14#[cfg(feature = "arbitrary")]
15pub fn arbitrary_decimal_256(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Decimal256> {
16    let bytes: [u8; 32] = u.arbitrary()?;
17    let value = Uint256::new(bytes);
18    Ok(Decimal256::new(value))
19}
20
21/// needed to get around the orphan rule
22#[cfg(feature = "arbitrary")]
23pub fn arbitrary_decimal_256_option(
24    u: &mut arbitrary::Unstructured,
25) -> arbitrary::Result<Option<Decimal256>> {
26    let bytes: Option<[u8; 32]> = u.arbitrary()?;
27    Ok(bytes.map(|bytes| {
28        let value = Uint256::new(bytes);
29        Decimal256::new(value)
30    }))
31}
32
33/// Generalizes any newtype wrapper around a [Decimal256].
34pub trait UnsignedDecimal:
35    Display
36    + std::fmt::Debug
37    + serde::Serialize
38    + serde::de::DeserializeOwned
39    + Copy
40    + Ord
41    + FromStr
42    + Default
43{
44    /// Convert into the underlying [Decimal256].
45    fn into_decimal256(self) -> Decimal256;
46
47    /// Convert from a [Decimal256].
48    fn from_decimal256(src: Decimal256) -> Self;
49
50    /// Check if the underlying value is 0.
51    fn is_zero(&self) -> bool {
52        self.into_decimal256().is_zero()
53    }
54
55    /// Add two values together
56    fn checked_add(self, rhs: Self) -> Result<Self, OverflowError> {
57        self.into_decimal256()
58            .checked_add(rhs.into_decimal256())
59            .map(Self::from_decimal256)
60    }
61
62    /// Try to add a signed value to this, erroring if it results in a negative result.
63    fn checked_add_signed(self, rhs: Signed<Self>) -> Result<Self> {
64        self.into_signed()
65            .checked_add(rhs)?
66            .try_into_non_negative_value()
67            .with_context(|| format!("{self} + {rhs}"))
68    }
69
70    /// Subtract two values
71    fn checked_sub(self, rhs: Self) -> Result<Self, OverflowError> {
72        self.into_decimal256()
73            .checked_sub(rhs.into_decimal256())
74            .map(Self::from_decimal256)
75    }
76
77    /// Try to convert from a general purpose [Number]
78    fn try_from_number(Signed { value, negative }: Signed<Decimal256>) -> anyhow::Result<Self> {
79        if negative {
80            Err(anyhow::anyhow!(
81                "try_from_number: received a negative value"
82            ))
83        } else {
84            Ok(Self::from_decimal256(value))
85        }
86    }
87
88    /// convert into a general purpose [Number]
89    fn into_number(self) -> Signed<Decimal256> {
90        Signed::new_positive(self.into_decimal256())
91    }
92
93    /// Convert into a positive [Signed] value.
94    fn into_signed(self) -> Signed<Self> {
95        Signed::new_positive(self)
96    }
97
98    /// The value 0
99    fn zero() -> Self {
100        Self::from_decimal256(Decimal256::zero())
101    }
102
103    /// The value 2
104    fn two() -> Self {
105        Self::from_decimal256(Decimal256::from_atomics(2u128, 0).unwrap())
106    }
107
108    /// Difference between two values
109    fn diff(self, rhs: Self) -> Self {
110        Self::from_decimal256(if self > rhs {
111            self.into_decimal256() - rhs.into_decimal256()
112        } else {
113            rhs.into_decimal256() - self.into_decimal256()
114        })
115    }
116
117    /// Is the delta between these less than the epsilon value?
118    ///
119    /// Epsilon is `10^-7`
120    fn approx_eq(self, rhs: Self) -> bool {
121        self.diff(rhs).into_decimal256() < Decimal256::from_ratio(1u32, 10_000_000u32)
122    }
123
124    // Note: we do _not_ include multiplication and division, since some operations
125    // (like multiplying two Collateral values together) are non-sensical.
126}
127
128impl UnsignedDecimal for Decimal256 {
129    fn into_decimal256(self) -> Decimal256 {
130        self
131    }
132
133    fn from_decimal256(src: Decimal256) -> Self {
134        src
135    }
136}
137
138macro_rules! unsigned {
139    ($t:tt) => {
140        // Avoid using cw_serde because Decimal256 has a bad Debug impl
141        #[derive(
142            PartialEq,
143            Eq,
144            PartialOrd,
145            Ord,
146            Clone,
147            Copy,
148            Default,
149            serde::Serialize,
150            serde::Deserialize,
151            schemars::JsonSchema,
152        )]
153        /// Unsigned value
154        pub struct $t(Decimal256);
155
156        impl $t {
157            /// Zero value
158            pub const fn zero() -> Self {
159                Self(Decimal256::zero())
160            }
161
162            /// One value
163            pub const fn one() -> Self {
164                Self(Decimal256::one())
165            }
166        }
167
168        impl UnsignedDecimal for $t {
169            fn into_decimal256(self) -> Decimal256 {
170                self.0
171            }
172
173            fn from_decimal256(src: Decimal256) -> Self {
174                Self(src)
175            }
176        }
177
178        impl Display for $t {
179            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
180                write!(f, "{}", self.0)
181            }
182        }
183
184        impl std::fmt::Debug for $t {
185            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
186                write!(f, "{}({})", stringify!($t), self.0)
187            }
188        }
189
190        impl FromStr for $t {
191            type Err = anyhow::Error;
192
193            fn from_str(s: &str) -> Result<Self> {
194                parse_decimal256(s).map(Self::from_decimal256)
195            }
196        }
197
198        impl TryFrom<&str> for $t {
199            type Error = anyhow::Error;
200
201            fn try_from(value: &str) -> Result<Self> {
202                value.parse()
203            }
204        }
205
206        impl TryFrom<String> for Signed<$t> {
207            type Error = anyhow::Error;
208
209            fn try_from(value: String) -> Result<Self> {
210                value.parse()
211            }
212        }
213
214        impl TryFrom<&str> for Signed<$t> {
215            type Error = anyhow::Error;
216
217            fn try_from(value: &str) -> Result<Self> {
218                value.parse()
219            }
220        }
221
222        impl TryFrom<String> for $t {
223            type Error = anyhow::Error;
224
225            fn try_from(value: String) -> Result<Self> {
226                value.parse()
227            }
228        }
229
230        impl Add for $t {
231            type Output = anyhow::Result<Self, OverflowError>;
232
233            fn add(self, rhs: Self) -> Self::Output {
234                Ok(Self(self.0.checked_add(rhs.0)?))
235            }
236        }
237
238        impl Sub for $t {
239            type Output = anyhow::Result<Self, OverflowError>;
240
241            fn sub(self, rhs: Self) -> Self::Output {
242                Ok(Self(self.0.checked_sub(rhs.0)?))
243            }
244        }
245
246        impl From<u64> for $t {
247            fn from(src: u64) -> Self {
248                u128::from(src).into()
249            }
250        }
251
252        impl From<u128> for $t {
253            fn from(src: u128) -> Self {
254                Self::from_decimal256(Decimal256::from_ratio(src, 1u32))
255            }
256        }
257
258        #[cfg(feature = "arbitrary")]
259        impl<'a> arbitrary::Arbitrary<'a> for $t {
260            fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
261                Ok(Self::from_decimal256(arbitrary_decimal_256(u)?))
262            }
263        }
264
265        impl $t {
266            /// Floor the current value with given decimal precision
267            pub fn floor_with_precision(&self, precision: u32) -> Self {
268                // Adjust precision based on given value and chuck in array
269                let factor = Decimal256::one().atomics() / Uint256::from_u128(10).pow(precision);
270                let raw = self.0.atomics() / factor * factor;
271
272                Self(Decimal256::new(raw))
273            }
274        }
275    };
276}
277
278fn parse_decimal256(s: &str) -> Result<Decimal256> {
279    s.parse()
280        .with_context(|| format!("Unable to parse unsigned decimal from {s}"))
281}
282
283unsigned!(Collateral);
284unsigned!(Notional);
285unsigned!(Base);
286unsigned!(Quote);
287unsigned!(Usd);
288unsigned!(LpToken);
289unsigned!(FarmingToken);
290unsigned!(LvnToken);
291unsigned!(LockdropShares);
292
293/// Wrap up any [UnsignedDecimal] to provide negative values too.
294#[derive(PartialEq, Eq, Clone, Copy)]
295pub struct Signed<T> {
296    value: T,
297    /// Invariant: must always be false if value is 0
298    negative: bool,
299}
300
301impl<T: UnsignedDecimal> Default for Signed<T> {
302    fn default() -> Self {
303        Signed {
304            value: T::default(),
305            negative: false,
306        }
307    }
308}
309
310impl<T> From<T> for Signed<T> {
311    fn from(value: T) -> Self {
312        Signed {
313            value,
314            negative: false,
315        }
316    }
317}
318
319impl<T: UnsignedDecimal> Signed<T> {
320    pub(crate) fn value(self) -> T {
321        self.value
322    }
323
324    /// Strictly less than 0, returns false on 0
325    pub fn is_negative(&self) -> bool {
326        self.negative
327    }
328
329    /// create a new positive Number with the given value
330    pub(crate) fn new_positive(value: T) -> Self {
331        Self {
332            value,
333            negative: false,
334        }
335    }
336
337    /// create a new negative Number with the given value
338    pub(crate) fn new_negative(value: T) -> Self {
339        Self {
340            value,
341            negative: !value.is_zero(),
342        }
343    }
344
345    /// Convert into a general purpose [Number].
346    pub fn into_number(self) -> Signed<Decimal256> {
347        Signed {
348            value: self.value.into_decimal256(),
349            negative: self.negative,
350        }
351    }
352
353    /// convert from a general purpose [Number].
354    pub fn from_number(src: Signed<Decimal256>) -> Self {
355        Signed {
356            value: T::from_decimal256(src.value),
357            negative: src.negative,
358        }
359    }
360
361    /// The value 0
362    pub fn zero() -> Self {
363        Signed::new_positive(T::zero())
364    }
365
366    /// The value 2
367    pub fn two() -> Self {
368        Signed::new_positive(T::two())
369    }
370
371    /// If the value is positive or zero, return the inner `T`. Otherwise return `None`.
372    pub fn try_into_non_negative_value(self) -> Option<T> {
373        if self.is_negative() {
374            None
375        } else {
376            Some(self.value())
377        }
378    }
379
380    /// Try to convert into a non-zero value
381    pub fn try_into_non_zero(self) -> Option<NonZero<T>> {
382        self.try_into_non_negative_value().and_then(NonZero::new)
383    }
384}
385
386impl Signed<Decimal256> {
387    /// The maximum allowed
388    pub const MAX: Self = Self {
389        value: Decimal256::MAX,
390        negative: false,
391    };
392
393    /// The minimum allowed
394    pub const MIN: Number = Number {
395        value: Decimal256::MAX,
396        negative: true,
397    };
398
399    /// 1 as a Number
400    pub const ONE: Number = Number {
401        value: Decimal256::one(),
402        negative: false,
403    };
404
405    /// -1 as a Number
406    pub const NEG_ONE: Number = Number {
407        value: Decimal256::one(),
408        negative: true,
409    };
410
411    /// 0 as a Number
412    pub const ZERO: Number = Number {
413        value: Decimal256::zero(),
414        negative: false,
415    };
416
417    /// Default epsilon used for approximate comparisons
418    // dev hint: if you want to get a new value
419    // print out a Number.abs_value_as_array()
420    // then just set that here
421    pub const EPS_E7: Number = Number {
422        // 18 digits precision - 7 digits == 11 zeros
423        value: Decimal256::raw(100_000_000_000),
424        negative: false,
425    };
426
427    /// An alternate epsilon that can be used for approximate comparisons
428    pub const EPS_E6: Number = Number {
429        value: Decimal256::raw(1_000_000_000_000),
430        negative: false,
431    };
432
433    /// Another alternate epsilon that can be used for approximate comparisons
434    /// where the rounding error is due to the Decimal256 representation
435    /// as opposed to, say, token precision
436    pub const EPS_E17: Number = Number {
437        // 18 digits precision - 17 digits == 1 zero
438        value: Decimal256::raw(10),
439        negative: false,
440    };
441}
442
443impl<T: UnsignedDecimal> std::ops::Neg for Signed<T> {
444    type Output = Self;
445
446    fn neg(mut self) -> Self {
447        if !self.value.is_zero() {
448            self.negative = !self.negative;
449        }
450        self
451    }
452}
453
454#[cfg(feature = "arbitrary")]
455impl<'a> arbitrary::Arbitrary<'a> for Signed<Decimal256> {
456    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
457        Ok(Self {
458            value: arbitrary_decimal_256(u)?,
459            negative: u.arbitrary()?,
460        })
461    }
462}
463
464#[cfg(feature = "arbitrary")]
465impl<'a> arbitrary::Arbitrary<'a> for Signed<Collateral> {
466    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
467        Ok(Self {
468            value: u.arbitrary()?,
469            negative: u.arbitrary()?,
470        })
471    }
472}
473
474#[cfg(feature = "arbitrary")]
475impl<'a> arbitrary::Arbitrary<'a> for Signed<Notional> {
476    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
477        Ok(Self {
478            value: u.arbitrary()?,
479            negative: u.arbitrary()?,
480        })
481    }
482}
483
484/// A signed number type with high fidelity.
485///
486/// Similar in spirit to cosmwasm_bignumber::Decimal256 - it is
487/// a more ergonomic wrapper around cosmwasm-std by making more things public
488/// but we also add negative values and other methods as-needed
489///
490/// MANY OF THE METHODS ARE COPY/PASTE FROM cosmwasm_std
491/// the hope is that this is a temporary hack until `cosmwasm_math` lands
492pub type Number = Signed<Decimal256>;
493
494/// Ensure that the inner value is never 0.
495#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Debug)]
496pub struct NonZero<T>(T);
497
498#[cfg(feature = "arbitrary")]
499impl<'a> arbitrary::Arbitrary<'a> for NonZero<Decimal256> {
500    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
501        let v = arbitrary_decimal_256(u)?;
502        if v.is_zero() {
503            Ok(Self(Decimal256::one()))
504        } else {
505            Ok(Self(v))
506        }
507    }
508}
509#[cfg(feature = "arbitrary")]
510impl<'a> arbitrary::Arbitrary<'a> for NonZero<LpToken> {
511    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
512        NonZero::<Decimal256>::arbitrary(u).map(|v| Self(LpToken::from_decimal256(v.0)))
513    }
514}
515#[cfg(feature = "arbitrary")]
516impl<'a> arbitrary::Arbitrary<'a> for NonZero<Collateral> {
517    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
518        NonZero::<Decimal256>::arbitrary(u).map(|v| Self(Collateral::from_decimal256(v.0)))
519    }
520}
521
522/// A special case of [NonZero] which stores a big endian array of data.
523///
524/// Purpose: this is intended to be used as a key in a cw-storage-plus `Map`. This wouldn't be necessary if cw-storage-plus allowed non-reference
525
526/// A [Number] which is always greater than zero.
527///
528/// This is useful for representing things like price.
529pub type NumberGtZero = NonZero<Decimal256>;
530
531impl<T: UnsignedDecimal> NonZero<T> {
532    /// Convert into a big-endian array.
533    pub fn to_be_bytes(&self) -> [u8; 32] {
534        self.0.into_decimal256().atomics().to_be_bytes()
535    }
536
537    /// Convert raw bytes into this value.
538    ///
539    /// Intended for use with cw-storage-plus.
540    pub fn from_be_bytes(bytes: [u8; 32]) -> Option<Self> {
541        if bytes == [0; 32] {
542            None
543        } else {
544            Some(NonZero(T::from_decimal256(Decimal256::new(
545                Uint256::from_be_bytes(bytes),
546            ))))
547        }
548    }
549
550    /// Get the underlying raw value.
551    pub fn raw(self) -> T {
552        self.0
553    }
554
555    /// Turn into a signed value.
556    pub fn into_signed(self) -> Signed<T> {
557        self.0.into()
558    }
559
560    /// Try to convert a raw value into a [NonZero].
561    pub fn new(src: T) -> Option<Self> {
562        if src.is_zero() {
563            None
564        } else {
565            Some(NonZero(src))
566        }
567    }
568
569    /// Convert into a general purpose [Decimal256].
570    pub fn into_decimal256(self) -> Decimal256 {
571        self.0.into_decimal256()
572    }
573
574    /// Convert into `NonZero<Decimal>`
575    pub fn into_number_gt_zero(self) -> NumberGtZero {
576        NonZero::<Decimal256>(self.into_decimal256())
577    }
578
579    /// Convert into a general purpose [Number].
580    pub fn into_number(self) -> Signed<Decimal256> {
581        self.0.into_number()
582    }
583
584    /// Try to convert a general purpose [Number] into this type.
585    pub fn try_from_number(src: Signed<Decimal256>) -> Option<Self> {
586        T::try_from_number(src).ok().and_then(NonZero::new)
587    }
588
589    /// Try to convert a general purpose [Decimal256] into this type.
590    pub fn try_from_decimal(src: Decimal256) -> Option<Self> {
591        NonZero::new(T::from_decimal256(src))
592    }
593
594    /// Try to convert a signed value into a non-zero.
595    pub fn try_from_signed(src: Signed<T>) -> Result<Self> {
596        src.try_into_non_negative_value()
597            .and_then(NonZero::new)
598            .with_context(|| format!("Could not converted signed value {src} into NonZero"))
599    }
600
601    /// Add an unsigned value to this non-zero
602    ///
603    /// This can fail due to overflow error, but is guaranteed to not give a
604    /// value of 0.
605    pub fn checked_add(self, rhs: T) -> Result<Self> {
606        self.raw()
607            .checked_add(rhs)
608            .context("NonZero::checked_add overflow")
609            .map(|x| NonZero::new(x).expect("Impossible! NonZero::checked_add returned 0"))
610    }
611
612    /// Subtract an unsigned value.
613    ///
614    /// This can fail if the result would be either negative or zero.
615    pub fn checked_sub(self, rhs: T) -> Result<Self> {
616        self.raw()
617            .checked_sub(rhs)
618            .ok()
619            .and_then(NonZero::new)
620            .with_context(|| format!("NonZero::checked_sub: cannot perform {self} - {rhs}"))
621    }
622
623    /// Try to add a signed value to this, erroring if it results in a negative
624    /// or zero result.
625    pub fn checked_add_signed(self, rhs: Signed<T>) -> Result<Self> {
626        NonZero::try_from_signed(self.into_signed().checked_add(rhs)?)
627            .with_context(|| format!("{self} + {rhs}"))
628    }
629
630    /// Try to subtract a signed value from this, erroring if it results in a
631    /// negative or zero result.
632    pub fn checked_sub_signed(self, rhs: Signed<T>) -> Result<Self> {
633        NonZero::try_from_signed(self.into_signed().checked_add(-rhs)?)
634            .with_context(|| format!("{self} - {rhs}"))
635    }
636
637    /// The value 1.
638    pub fn one() -> Self {
639        Self(T::from_decimal256(Decimal256::one()))
640    }
641}
642
643impl<T: UnsignedDecimal> From<NonZero<T>> for Signed<T> {
644    fn from(src: NonZero<T>) -> Self {
645        Signed::new_positive(src.0)
646    }
647}
648
649impl<T: UnsignedDecimal> TryFrom<Signed<T>> for NonZero<T> {
650    type Error = anyhow::Error;
651
652    fn try_from(value: Signed<T>) -> Result<Self, Self::Error> {
653        if value.is_strictly_positive() {
654            Ok(NonZero(value.value()))
655        } else {
656            Err(anyhow::anyhow!(
657                "Cannot convert Signed to NonZero, value is {value}"
658            ))
659        }
660    }
661}
662
663impl Collateral {
664    /// Multiply by the given [Decimal256]
665    pub fn checked_mul_dec(self, rhs: Decimal256) -> Result<Collateral> {
666        self.0
667            .checked_mul(rhs)
668            .map(Collateral)
669            .with_context(|| format!("Collateral::checked_mul_dec failed on {self} * {rhs}"))
670    }
671
672    /// Divide by the given [Decimal256]
673    pub fn checked_div_dec(self, rhs: Decimal256) -> Result<Collateral> {
674        self.0
675            .checked_div(rhs)
676            .map(Collateral)
677            .with_context(|| format!("Collateral::checked_div_dec failed on {self} * {rhs}"))
678    }
679
680    /// Divide by a non-zero decimal.
681    pub fn div_non_zero_dec(self, rhs: NonZero<Decimal256>) -> Collateral {
682        Collateral::from_decimal256(self.into_decimal256() / rhs.into_decimal256())
683    }
684
685    /// Divide by a non-zero collateral, returning a ratio between the two.
686    pub fn div_non_zero(self, rhs: NonZero<Collateral>) -> Decimal256 {
687        self.into_decimal256() / rhs.into_decimal256()
688    }
689}
690
691impl Usd {
692    /// Multiply by the given [Decimal256]
693    pub fn checked_mul_dec(self, rhs: Decimal256) -> Result<Usd> {
694        self.0
695            .checked_mul(rhs)
696            .map(Usd)
697            .with_context(|| format!("Usd::checked_mul_ratio failed on {self} * {rhs}"))
698    }
699}
700
701impl NonZero<Collateral> {
702    /// Multiply by the given non-zero decimal.
703    pub fn checked_mul_non_zero(self, rhs: NonZero<Decimal256>) -> Result<NonZero<Collateral>> {
704        self.0.checked_mul_dec(rhs.raw()).map(|x| {
705            debug_assert!(!x.is_zero());
706            NonZero(x)
707        })
708    }
709
710    /// Divide two non-zero collateral values to get the ratio between them.
711    ///
712    /// This can be used for cases like calculating the max gains.
713    pub fn checked_div_collateral(self, rhs: NonZero<Collateral>) -> Result<NonZero<Decimal256>> {
714        Ok(NonZero(
715            self.into_decimal256().checked_div(rhs.into_decimal256())?,
716        ))
717    }
718}
719
720impl<T: UnsignedDecimal> Signed<T> {
721    /// Multiply by a raw number
722    pub fn checked_mul_number(self, rhs: Signed<Decimal256>) -> Result<Self> {
723        self.into_number().checked_mul(rhs).map(Self::from_number)
724    }
725}
726
727/// How much to divide an atomic value by to get to an LP token amount.
728/// The token uses 6 digits of precision, and Decimal256 uses 18 digits of precision.
729/// So to truncate the Decimal256's atomic representation to the Uint128 representation,
730/// we need to remove 12 digits (18 - 6).
731const LP_TOKEN_DIVIDER: u64 = 1_000_000_000_000;
732
733impl LpToken {
734    /// The hard-coded precision of the LP and xLP token contracts.
735    pub const PRECISION: u8 = 6;
736
737    /// Convert into a u128 representation for contract interactions.
738    ///
739    /// Note that this is a lossy conversion, and will truncate some data.
740    pub fn into_u128(self) -> Result<u128> {
741        Ok(Uint128::try_from(
742            self.into_decimal256()
743                .atomics()
744                .checked_div(LP_TOKEN_DIVIDER.into())?,
745        )?
746        .u128())
747    }
748
749    /// Convert from a u128 representation.
750    pub fn from_u128(x: u128) -> Result<Self> {
751        Ok(LpToken::from_decimal256(Decimal256::from_atomics(
752            x,
753            Self::PRECISION.into(),
754        )?))
755    }
756}
757
758impl LvnToken {
759    /// Multiply by the given [Decimal256]
760    pub fn checked_mul_dec(self, rhs: Decimal256) -> Result<LvnToken> {
761        self.0
762            .checked_mul(rhs)
763            .map(LvnToken)
764            .with_context(|| format!("LvnToken::checked_mul failed on {self} * {rhs}"))
765    }
766
767    /// Divide by the given [Decimal256]
768    pub fn checked_div_dec(self, rhs: Decimal256) -> Result<LvnToken> {
769        self.0
770            .checked_div(rhs)
771            .map(LvnToken)
772            .with_context(|| format!("LvnToken::checked_div failed on {self} / {rhs}"))
773    }
774}
775
776#[cfg(test)]
777mod tests {
778    use super::*;
779
780    #[test]
781    fn lp_token_u128_roundtrip() {
782        assert_eq!(
783            LpToken::from_str("12.3456789")
784                .unwrap()
785                .into_u128()
786                .unwrap(),
787            12345678
788        );
789        assert_eq!(
790            LpToken::from_str("12.345678").unwrap(),
791            LpToken::from_u128(12345678).unwrap(),
792        );
793    }
794
795    #[test]
796    fn floor_unsigned_type_with_precision() {
797        unsigned!(UnsignedStruct);
798
799        assert_eq!(
800            UnsignedStruct::from_str("12.3456789")
801                .unwrap()
802                .floor_with_precision(2),
803            UnsignedStruct::from_str("12.34").unwrap()
804        );
805    }
806}