Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #249 divisibility arbitrary percision #251

Closed
wants to merge 7 commits into from
74 changes: 74 additions & 0 deletions primitives/src/big_num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,62 @@ use std::str::FromStr;
use num::rational::Ratio;
use num::{BigUint, CheckedSub, Integer};
use num_derive::{Num, NumOps, One, Zero};
use num_traits::Pow;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

/// DAI has precision of 18 decimals
/// For CPM we have 3 decimals precision, but that's for 1000 (3 decimals more)
/// This in terms means we need 18 - (3 + 3) = 12 decimals precision
pub const GLOBAL_MULTIPLIER: Multiplier = Multiplier(12);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this not 10**12 rather than 12? it would save you from having to do a pow operation

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! I will impl it.


#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
/// Multiplier - Pow of 10 (10**n)
pub struct Multiplier(u64);

impl Mul<PrecisionU64> for Multiplier {
type Output = BigUint;

fn mul(self, rhs: PrecisionU64) -> Self::Output {
let real_multiplier = BigUint::from(10u8).pow(BigUint::from(GLOBAL_MULTIPLIER.0));

real_multiplier * rhs.0
}
}

impl Into<BigNum> for Multiplier {
fn into(self) -> BigNum {
BigNum(self.into())
}
}

impl Into<BigUint> for Multiplier {
fn into(self) -> BigUint {
BigUint::from(10u8).pow(BigUint::from(self.0))
}
}

///
// @TODO: (De)serialize
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PrecisionU64(u64);

impl Into<BigNum> for PrecisionU64 {
fn into(self) -> BigNum {
BigNum(GLOBAL_MULTIPLIER * self)
}
}

impl From<BigNum> for PrecisionU64 {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use From with default of 0 or should we impl TryFrom and fail if this conversion cannot be done?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TryFrom would be better as 0 may be magic :( I think error should be handled properly

fn from(bignum: BigNum) -> Self {
let precision = bignum
.div_floor(&GLOBAL_MULTIPLIER.into())
.to_u64()
.unwrap_or(0);

Self(precision)
}
}

#[derive(
Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, NumOps, One, Zero, Num,
)]
Expand Down Expand Up @@ -283,4 +337,24 @@ mod test {
let expected: BigNum = 11.into();
assert_eq!(expected, &big_num * &ratio);
}

#[test]
fn precision_u64_to_bignum() {
let precision = PrecisionU64(5);
let bignum = precision.into();

assert_eq!(BigNum::from(5_000_000_000_000), bignum)
}

#[test]
fn bignum_to_precision_u64() {
// less than the multiplier 12
let zero_bignum = BigNum::from(900_000_000_000);
// it should floor to 0
assert_eq!(PrecisionU64(0), PrecisionU64::from(zero_bignum));

let bignum = BigNum::from(5_000_000_000_000);

assert_eq!(PrecisionU64(5), PrecisionU64::from(bignum))
}
}