code/common/src/currency.rs

currency.rs

712 lines · 17,912 bytes · 19a6485969RawGitHub
use core::fmt::{self, Display, Formatter};
use core::ops::{
    Add, AddAssign, BitAnd, BitOr, BitXor, Div, DivAssign, Mul, MulAssign, Not, Rem, RemAssign,
    Shl, Shr, Sub, SubAssign,
};

#[cfg(feature = "approx")]
use approx::AbsDiffEq;
use codec::{
    Compact, CompactAs, Decode, DecodeWithMemTracking, Encode, Error as CodecError, MaxEncodedLen,
};
use frame_support::pallet_prelude::*;
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
use substrate_fixed::traits::{Fixed, ToFixed};
use subtensor_macros::freeze_struct;

pub use num_traits::{
    CheckedShl, CheckedShr, FromPrimitive, Num, NumCast, NumOps, PrimInt, Saturating, Signed,
    ToPrimitive, Unsigned, checked_pow,
};
use sp_arithmetic::per_things::Rounding;
use sp_arithmetic::rational::MultiplyRational;
use sp_arithmetic::traits::{
    Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub, One, Zero,
};

#[cfg(feature = "std")]
use sp_rpc::number::NumberOrHex;

#[freeze_struct("fe2aa2d7fcb480e8")]
#[repr(transparent)]
#[derive(
    Deserialize,
    Serialize,
    Clone,
    Copy,
    Decode,
    DecodeWithMemTracking,
    Default,
    Encode,
    Eq,
    Hash,
    MaxEncodedLen,
    Ord,
    PartialEq,
    PartialOrd,
    RuntimeDebug,
    TypeInfo,
)]
pub struct AlphaBalance(u64);

#[freeze_struct("a99f2483a97121fc")]
#[repr(transparent)]
#[derive(
    Deserialize,
    Serialize,
    Clone,
    Copy,
    Decode,
    DecodeWithMemTracking,
    Default,
    Encode,
    Eq,
    Hash,
    MaxEncodedLen,
    Ord,
    PartialEq,
    PartialOrd,
    RuntimeDebug,
    TypeInfo,
)]
pub struct TaoBalance(u64);

// implements traits required by the Currency trait (ToFixed + Into<u64> + From<u64>) and CompactAs
// and Display. It expects a wrapper structure for u64 (CurrencyT(u64)).
// TypeInfo is derived on the structs themselves so the type identity (path) is preserved in the
// runtime metadata, letting SDKs generate distinct TaoBalance/AlphaBalance types instead of bare u64.
macro_rules! impl_currency_reqs {
    ($currency_type:ident) => {
        impl $currency_type {
            pub const fn new(inner: u64) -> Self {
                Self(inner)
            }
        }

        impl Display for $currency_type {
            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
                Display::fmt(&self.0, f)
            }
        }

        impl CompactAs for $currency_type {
            type As = u64;

            fn encode_as(&self) -> &Self::As {
                &self.0
            }

            fn decode_from(v: Self::As) -> Result<Self, CodecError> {
                Ok(Self(v))
            }
        }

        impl From<Compact<$currency_type>> for $currency_type {
            fn from(c: Compact<$currency_type>) -> Self {
                c.0
            }
        }

        impl From<$currency_type> for u64 {
            fn from(val: $currency_type) -> Self {
                val.0
            }
        }

        impl From<u64> for $currency_type {
            fn from(value: u64) -> Self {
                Self(value)
            }
        }

        impl From<u32> for $currency_type {
            fn from(value: u32) -> Self {
                Self(value as u64)
            }
        }

        // TODO: This is a lossy conversion, maybe it should use try_from
        impl From<i32> for $currency_type {
            fn from(value: i32) -> Self {
                Self(value.unsigned_abs().into())
            }
        }

        impl From<u8> for $currency_type {
            fn from(value: u8) -> Self {
                Self(value as u64)
            }
        }

        impl ToFixed for $currency_type {
            fn to_fixed<F: Fixed>(self) -> F {
                self.0.to_fixed()
            }

            fn checked_to_fixed<F: Fixed>(self) -> Option<F> {
                self.0.checked_to_fixed()
            }

            fn saturating_to_fixed<F: Fixed>(self) -> F {
                self.0.saturating_to_fixed()
            }
            fn wrapping_to_fixed<F: Fixed>(self) -> F {
                self.0.wrapping_to_fixed()
            }

            fn overflowing_to_fixed<F: Fixed>(self) -> (F, bool) {
                self.0.overflowing_to_fixed()
            }
        }

        impl Zero for $currency_type {
            fn zero() -> Self {
                Self(0)
            }
            fn is_zero(&self) -> bool {
                Into::<u64>::into(*self) == 0
            }
        }

        impl One for $currency_type {
            fn one() -> Self {
                Self(1)
            }
        }
    };
}

macro_rules! impl_arithmetic_operators {
    ($currency_type:ident) => {
        impl Add for $currency_type {
            type Output = Self;

            #[allow(clippy::arithmetic_side_effects)]
            fn add(self, rhs: Self) -> Self::Output {
                let lhs_u64: u64 = self.into();
                let rhs_u64: u64 = rhs.into();
                (lhs_u64 + rhs_u64).into()
            }
        }

        impl Sub for $currency_type {
            type Output = Self;

            #[allow(clippy::arithmetic_side_effects)]
            fn sub(self, rhs: Self) -> Self::Output {
                let lhs_u64: u64 = self.into();
                let rhs_u64: u64 = rhs.into();
                (lhs_u64 - rhs_u64).into()
            }
        }

        impl Mul for $currency_type {
            type Output = Self;

            #[allow(clippy::arithmetic_side_effects)]
            fn mul(self, rhs: Self) -> Self::Output {
                let lhs_u64: u64 = self.into();
                let rhs_u64: u64 = rhs.into();
                (lhs_u64 * rhs_u64).into()
            }
        }

        impl Div for $currency_type {
            type Output = Self;

            #[allow(clippy::arithmetic_side_effects)]
            fn div(self, rhs: Self) -> Self::Output {
                let lhs_u64: u64 = self.into();
                let rhs_u64: u64 = rhs.into();
                (lhs_u64 / rhs_u64).into()
            }
        }

        impl Rem for $currency_type {
            type Output = Self;

            #[allow(clippy::arithmetic_side_effects)]
            fn rem(self, rhs: Self) -> Self::Output {
                let lhs_u64: u64 = self.into();
                let rhs_u64: u64 = rhs.into();
                (lhs_u64 % rhs_u64).into()
            }
        }

        impl AddAssign for $currency_type {
            #[allow(clippy::arithmetic_side_effects)]
            fn add_assign(&mut self, rhs: Self) {
                *self = *self + rhs;
            }
        }

        impl SubAssign for $currency_type {
            #[allow(clippy::arithmetic_side_effects)]
            fn sub_assign(&mut self, rhs: Self) {
                *self = *self - rhs;
            }
        }

        impl MulAssign for $currency_type {
            #[allow(clippy::arithmetic_side_effects)]
            fn mul_assign(&mut self, rhs: Self) {
                *self = *self * rhs;
            }
        }

        impl DivAssign for $currency_type {
            #[allow(clippy::arithmetic_side_effects)]
            fn div_assign(&mut self, rhs: Self) {
                *self = *self / rhs;
            }
        }
    };
}

macro_rules! impl_approx {
    ($currency_type:ident) => {
        #[cfg(feature = "approx")]
        impl AbsDiffEq<Self> for $currency_type {
            type Epsilon = Self;

            fn default_epsilon() -> Self::Epsilon {
                u64::default_epsilon().into()
            }

            fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
                u64::abs_diff_eq(
                    &Into::<u64>::into(*self),
                    &Into::<u64>::into(*other),
                    epsilon.into(),
                )
            }

            fn abs_diff_ne(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
                u64::abs_diff_ne(
                    &Into::<u64>::into(*self),
                    &Into::<u64>::into(*other),
                    epsilon.into(),
                )
            }
        }
    };
}

pub trait Token:
    ToFixed
    + Into<u64>
    + From<u64>
    + Clone
    + Copy
    + Eq
    + NumOps
    + Ord
    + PartialEq
    + PartialOrd
    + Display
    + Zero
    + One
{
    const MAX: Self;
    const ZERO: Self;

    fn to_u64(&self) -> u64 {
        (*self).into()
    }

    fn saturating_add(&self, rhv: Self) -> Self {
        Into::<u64>::into(*self).saturating_add(rhv.into()).into()
    }

    #[allow(clippy::arithmetic_side_effects)]
    fn saturating_div(&self, rhv: Self) -> Self {
        Into::<u64>::into(*self).saturating_div(rhv.into()).into()
    }

    fn saturating_sub(&self, rhv: Self) -> Self {
        Into::<u64>::into(*self).saturating_sub(rhv.into()).into()
    }

    fn saturating_mul(&self, rhv: Self) -> Self {
        Into::<u64>::into(*self).saturating_mul(rhv.into()).into()
    }
}

impl_arithmetic_operators!(AlphaBalance);
impl_approx!(AlphaBalance);
impl_currency_reqs!(AlphaBalance);

impl_arithmetic_operators!(TaoBalance);
impl_approx!(TaoBalance);
impl_currency_reqs!(TaoBalance);

impl Token for AlphaBalance {
    const MAX: Self = Self(u64::MAX);
    const ZERO: Self = Self(0);
}

impl Token for TaoBalance {
    const MAX: Self = Self(u64::MAX);
    const ZERO: Self = Self(0);
}

// // Required explicitly by the bound
// impl From<u32> for TaoBalance {
//     fn from(x: u32) -> Self { Self(x as u64) }
// }

impl Bounded for TaoBalance {
    fn min_value() -> Self {
        Self(u64::MIN)
    }
    fn max_value() -> Self {
        Self(u64::MAX)
    }
}

impl Saturating for TaoBalance {
    fn saturating_add(self, rhs: Self) -> Self {
        Self(self.0.saturating_add(rhs.0))
    }
    fn saturating_sub(self, rhs: Self) -> Self {
        Self(self.0.saturating_sub(rhs.0))
    }
}

//////////////////

impl CheckedNeg for TaoBalance {
    fn checked_neg(&self) -> Option<TaoBalance> {
        Some(*self)
    }
}

impl CheckedRem for TaoBalance {
    fn checked_rem(&self, rhs: &Self) -> Option<Self> {
        let lhs_u64: u64 = (*self).into();
        let rhs_u64: u64 = (*rhs).into();
        lhs_u64.checked_rem(rhs_u64).map(Into::into)
    }
}

impl Shl<u32> for TaoBalance {
    type Output = Self;

    #[allow(clippy::arithmetic_side_effects)]
    fn shl(self, rhs: u32) -> Self::Output {
        let lhs: u64 = self.into();
        // Define semantics: saturate to 0 on oversized shift (instead of panic).
        // Alternatively, you could debug_assert! and return 0 in release.
        let shifted = lhs.checked_shl(rhs).unwrap_or(0);
        shifted.into()
    }
}

impl Shr<u32> for TaoBalance {
    type Output = Self;

    #[allow(clippy::arithmetic_side_effects)]
    fn shr(self, rhs: u32) -> Self::Output {
        let lhs: u64 = self.into();
        let shifted = lhs.checked_shr(rhs).unwrap_or(0);
        shifted.into()
    }
}

impl Shl<usize> for TaoBalance {
    type Output = Self;

    #[allow(clippy::arithmetic_side_effects)]
    fn shl(self, rhs: usize) -> Self::Output {
        let lhs: u64 = self.into();
        // Define semantics: saturate to 0 on oversized shift (instead of panic).
        // Alternatively, you could debug_assert! and return 0 in release.
        let shifted = lhs.checked_shl(rhs as u32).unwrap_or(0);
        shifted.into()
    }
}

impl Shr<usize> for TaoBalance {
    type Output = Self;

    #[allow(clippy::arithmetic_side_effects)]
    fn shr(self, rhs: usize) -> Self::Output {
        let lhs: u64 = self.into();
        let shifted = lhs.checked_shr(rhs as u32).unwrap_or(0);
        shifted.into()
    }
}

impl Not for TaoBalance {
    type Output = Self;
    fn not(self) -> Self {
        Self(!self.0)
    }
}
impl BitAnd for TaoBalance {
    type Output = Self;
    fn bitand(self, rhs: Self) -> Self {
        Self(self.0 & rhs.0)
    }
}
impl BitOr for TaoBalance {
    type Output = Self;
    fn bitor(self, rhs: Self) -> Self {
        Self(self.0 | rhs.0)
    }
}

impl BitXor for TaoBalance {
    type Output = Self;
    fn bitxor(self, rhs: Self) -> Self {
        Self(self.0 ^ rhs.0)
    }
}

impl CheckedAdd for TaoBalance {
    fn checked_add(&self, rhs: &Self) -> Option<Self> {
        self.0.checked_add(rhs.0).map(Self)
    }
}
impl CheckedSub for TaoBalance {
    fn checked_sub(&self, rhs: &Self) -> Option<Self> {
        self.0.checked_sub(rhs.0).map(Self)
    }
}

impl CheckedMul for TaoBalance {
    fn checked_mul(&self, rhs: &Self) -> Option<Self> {
        self.0.checked_mul(rhs.0).map(Self)
    }
}

impl CheckedDiv for TaoBalance {
    fn checked_div(&self, rhs: &Self) -> Option<Self> {
        self.0.checked_div(rhs.0).map(Self)
    }
}

#[allow(clippy::arithmetic_side_effects)]
impl CheckedShl for TaoBalance {
    fn checked_shl(&self, rhs: u32) -> Option<Self> {
        // Validate first (so we can return None), then use the operator as requested.
        let lhs: u64 = (*self).into();
        lhs.checked_shl(rhs)?;
        Some((*self) << rhs)
    }
}

#[allow(clippy::arithmetic_side_effects)]
impl CheckedShr for TaoBalance {
    fn checked_shr(&self, rhs: u32) -> Option<Self> {
        let lhs: u64 = (*self).into();
        lhs.checked_shr(rhs)?;
        Some((*self) >> rhs)
    }
}

impl RemAssign for TaoBalance {
    #[allow(clippy::arithmetic_side_effects)]
    fn rem_assign(&mut self, rhs: Self) {
        *self = *self % rhs;
    }
}

impl PrimInt for TaoBalance {
    fn count_ones(self) -> u32 {
        self.0.count_ones()
    }
    fn count_zeros(self) -> u32 {
        self.0.count_zeros()
    }
    fn leading_zeros(self) -> u32 {
        self.0.leading_zeros()
    }
    fn trailing_zeros(self) -> u32 {
        self.0.trailing_zeros()
    }
    fn rotate_left(self, n: u32) -> Self {
        Self(self.0.rotate_left(n))
    }
    fn rotate_right(self, n: u32) -> Self {
        Self(self.0.rotate_right(n))
    }
    fn signed_shl(self, n: u32) -> Self {
        // For unsigned integers, num_traits defines signed_shl/shr as normal shifts.
        Self(self.0.wrapping_shl(n))
    }
    fn signed_shr(self, n: u32) -> Self {
        Self(self.0.wrapping_shr(n))
    }
    fn unsigned_shl(self, n: u32) -> Self {
        Self(self.0.wrapping_shl(n))
    }
    fn unsigned_shr(self, n: u32) -> Self {
        Self(self.0.wrapping_shr(n))
    }
    fn swap_bytes(self) -> Self {
        Self(self.0.swap_bytes())
    }
    fn from_be(x: Self) -> Self {
        Self(u64::from_be(x.0))
    }
    fn from_le(x: Self) -> Self {
        Self(u64::from_le(x.0))
    }
    fn to_be(self) -> Self {
        Self(self.0.to_be())
    }
    fn to_le(self) -> Self {
        Self(self.0.to_le())
    }
    fn pow(self, exp: u32) -> Self {
        Self(self.0.pow(exp))
    }
}

impl Unsigned for TaoBalance {}

impl Num for TaoBalance {
    type FromStrRadixErr = <u64 as Num>::FromStrRadixErr;

    fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
        u64::from_str_radix(s, radix).map(Self)
    }
}

impl NumCast for TaoBalance {
    fn from<T: ToPrimitive>(n: T) -> Option<Self> {
        n.to_u64().map(Self)
    }
}

impl ToPrimitive for TaoBalance {
    fn to_i64(&self) -> Option<i64> {
        self.0.to_i64()
    }
    fn to_u64(&self) -> Option<u64> {
        Some(self.0)
    }
    fn to_u128(&self) -> Option<u128> {
        Some(self.0 as u128)
    }
    fn to_usize(&self) -> Option<usize> {
        self.0.to_usize()
    }
}

impl FromPrimitive for TaoBalance {
    fn from_u64(n: u64) -> Option<Self> {
        Some(Self(n))
    }
    fn from_u128(n: u128) -> Option<Self> {
        if n <= u64::MAX as u128 {
            Some(Self(n as u64))
        } else {
            None
        }
    }
    fn from_usize(n: usize) -> Option<Self> {
        Some(Self(n as u64))
    }
    fn from_i64(n: i64) -> Option<Self> {
        if n >= 0 { Some(Self(n as u64)) } else { None }
    }
}

impl MultiplyRational for TaoBalance {
    fn multiply_rational(self, n: Self, d: Self, r: Rounding) -> Option<Self> {
        let a: u64 = self.into();
        let n: u64 = n.into();
        let d: u64 = d.into();
        a.multiply_rational(n, d, r).map(Into::into)
    }
}

impl From<u16> for TaoBalance {
    fn from(x: u16) -> Self {
        TaoBalance(x as u64)
    }
}
impl From<u128> for TaoBalance {
    fn from(n: u128) -> Self {
        if n <= u64::MAX as u128 {
            Self(n as u64)
        } else {
            Self(u64::MAX)
        }
    }
}
impl From<usize> for TaoBalance {
    fn from(n: usize) -> Self {
        Self(n as u64)
    }
}
impl From<sp_core::U256> for TaoBalance {
    fn from(n: sp_core::U256) -> Self {
        if let Ok(n_u64) = n.try_into() {
            Self(n_u64)
        } else {
            Self(u64::MAX)
        }
    }
}

#[allow(clippy::from_over_into)]
impl Into<usize> for TaoBalance {
    fn into(self) -> usize {
        self.0 as usize
    }
}

#[allow(clippy::from_over_into)]
impl Into<u128> for TaoBalance {
    fn into(self) -> u128 {
        self.0 as u128
    }
}

#[allow(clippy::from_over_into)]
impl Into<u32> for TaoBalance {
    fn into(self) -> u32 {
        self.0 as u32
    }
}

#[allow(clippy::from_over_into)]
impl Into<u16> for TaoBalance {
    fn into(self) -> u16 {
        self.0 as u16
    }
}

#[allow(clippy::from_over_into)]
impl Into<u8> for TaoBalance {
    fn into(self) -> u8 {
        self.0 as u8
    }
}

#[allow(clippy::from_over_into)]
impl Into<sp_core::U256> for TaoBalance {
    fn into(self) -> sp_core::U256 {
        sp_core::U256::from(self.0)
    }
}

#[allow(clippy::from_over_into)]
#[cfg(feature = "std")]
impl Into<NumberOrHex> for TaoBalance {
    fn into(self) -> NumberOrHex {
        NumberOrHex::Number(self.0)
    }
}

pub struct ConstTao<const N: u64>;

impl<const N: u64> Get<TaoBalance> for ConstTao<N> {
    fn get() -> TaoBalance {
        TaoBalance::new(N)
    }
}