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

Move fixedpointmath macro logic to utils #164

Merged
merged 3 commits into from
Jul 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/fixedpointmath/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ description = "Math library to simulate FixedPoint computation in Solidity smart
[dependencies]

# External dependencies
ethers = "2.0.11"
ethers = { version = "2.0.11", default-features = false }
eyre = "0.6.8"
rand = "0.8.5"

Expand All @@ -24,4 +24,4 @@ test-utils = { path = "../test-utils" }
hyperdrive-wrappers = { path = "../hyperdrive-wrappers" }

[package.metadata.docs.rs]
rustdoc-args = ["--html-in-header", "assets/katex_header.html"]
rustdoc-args = ["--html-in-header", "assets/katex_header.html"]
14 changes: 13 additions & 1 deletion crates/fixedpointmath/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
mod macros;
mod utils;

use std::{
fmt,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Shr, Sub, SubAssign},
str::FromStr,
};

use ethers::types::{Sign, I256, U256};
Expand All @@ -12,7 +16,7 @@ use rand::{
},
Rng,
};
mod macros;
pub use utils::*;

/// A fixed point wrapper around the `U256` type from ethers-rs.
///
Expand Down Expand Up @@ -97,6 +101,14 @@ impl TryFrom<FixedPoint> for I256 {
}
}

impl FromStr for FixedPoint {
type Err = Error;

fn from_str(s: &str) -> Result<FixedPoint> {
Ok(FixedPoint(u256_from_str(s)?))
}
}

/// Math ///

impl Add for FixedPoint {
Expand Down
80 changes: 2 additions & 78 deletions crates/fixedpointmath/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,91 +11,15 @@
macro_rules! uint256 {
($number:expr) => {{
let str = stringify!($number);

// Parse a string into a mantissa and an exponent. The U256 arithmetic
// will overflow if the mantissa or the exponent are too large.
let mut found_dot = false;
let mut found_e = false;
let mut mantissa = ethers::types::U256::zero();
let mut exponent = ethers::types::U256::zero();
let mut decimals = 0;

for digit in str.chars() {
if digit.is_ascii_digit() {
let d = digit.to_digit(10).unwrap();
if !found_e {
mantissa = mantissa * 10 + d;
} else {
exponent = exponent * 10 + d;
}
if found_dot && !found_e {
decimals += 1;
}
} else if digit == 'e' && !found_e {
found_e = true;
} else if digit == '.' && !found_dot {
found_dot = true;
} else if digit != '_' {
panic!("Unexpected character: {digit}");
}
}

// Combine the mantissa and the exponent into a single U256. This will
// overflow if the exponent is too large. We also need to make sure that
// the final result is an integer.
let decimals = ethers::types::U256::from(decimals);
if exponent < decimals {
panic!("Exponent is too small: {exponent}");
}

mantissa * ethers::types::U256::from(10).pow(exponent - decimals)
$crate::u256_from_str(str).unwrap()
}};
}

#[macro_export]
macro_rules! int256 {
($number:expr) => {{
let str = stringify!($number);

// Parse a string into a mantissa and an exponent. The U256 arithmetic
// will overflow if the mantissa or the exponent are too large.
let mut sign = ethers::types::I256::one();
let mut found_dot = false;
let mut found_e = false;
let mut mantissa = ethers::types::I256::zero();
let mut exponent = 0;
let mut decimals = 0;

for digit in str.chars() {
if digit.is_ascii_digit() {
let d = digit.to_digit(10).unwrap();
if !found_e {
mantissa = mantissa * 10 + d;
} else {
exponent = exponent * 10 + d;
}
if found_dot && !found_e {
decimals += 1;
}
} else if digit == '-' {
sign = -ethers::types::I256::one();
} else if digit == 'e' && !found_e {
found_e = true;
} else if digit == '.' && !found_dot {
found_dot = true;
} else if digit != '_' {
panic!("Unexpected character: {digit}");
}
}

// Combine the mantissa and the exponent I256. This will
// overflow if the exponent is too large. We also need to make sure that
// the final result is an integer.
if exponent < decimals {
panic!("Exponent is too small: {exponent}");
}

sign * mantissa * ethers::types::I256::from(10).pow(exponent - decimals)
$crate::i256_from_str(str).unwrap()
}};
}

Expand Down
106 changes: 106 additions & 0 deletions crates/fixedpointmath/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use ethers::types::{I256, U256};
use eyre::{eyre, Result};

/// Parse a string into a U256 with support for scientific and decimal notation.
///
/// ## Example
///
/// ```
/// use ethers::types::U256;
/// use fixedpointmath::u256_from_str;
///
/// let u = u256_from_str("1.1e18").unwrap();
/// assert_eq!(u, U256::from(11) * U256::from(10).pow(17));
/// ```
pub fn u256_from_str(s: &str) -> Result<U256> {
// Parse a string into a mantissa and an exponent. The U256 arithmetic
// will overflow if the mantissa or the exponent are too large.
let mut found_dot = false;
let mut found_e = false;
let mut mantissa = ethers::types::U256::zero();
let mut exponent = ethers::types::U256::zero();
let mut decimals = 0;

for digit in s.chars() {
if digit.is_ascii_digit() {
let d = digit.to_digit(10).unwrap();
if !found_e {
mantissa = mantissa * 10 + d;
} else {
exponent = exponent * 10 + d;
}
if found_dot && !found_e {
decimals += 1;
}
} else if digit == 'e' && !found_e {
found_e = true;
} else if digit == '.' && !found_dot {
found_dot = true;
} else if digit != '_' {
return Err(eyre!("Unexpected character: {digit}"));
}
}

// Combine the mantissa and the exponent into a single U256. This will
// overflow if the exponent is too large. We also need to make sure that
// the final result is an integer.
let decimals = ethers::types::U256::from(decimals);
if exponent < decimals {
return Err(eyre!("Exponent is too small: {exponent}"));
}

Ok(mantissa * ethers::types::U256::from(10).pow(exponent - decimals))
}

/// Parse a string into an I256 with support for scientific and decimal notation.
///
/// ## Example
///
/// ```
/// use ethers::types::I256;
/// use fixedpointmath::i256_from_str;
///
/// let i = i256_from_str("-1.1e18").unwrap();
/// assert_eq!(i, -I256::from(11) * I256::from(10).pow(17));
/// ```
pub fn i256_from_str(s: &str) -> Result<I256> {
// Parse a string into a mantissa and an exponent. The U256 arithmetic
// will overflow if the mantissa or the exponent are too large.
let mut sign = ethers::types::I256::one();
let mut found_dot = false;
let mut found_e = false;
let mut mantissa = ethers::types::I256::zero();
let mut exponent = 0;
let mut decimals = 0;

for digit in s.chars() {
if digit.is_ascii_digit() {
let d = digit.to_digit(10).unwrap();
if !found_e {
mantissa = mantissa * 10 + d;
} else {
exponent = exponent * 10 + d;
}
if found_dot && !found_e {
decimals += 1;
}
} else if digit == '-' {
sign = -ethers::types::I256::one();
} else if digit == 'e' && !found_e {
found_e = true;
} else if digit == '.' && !found_dot {
found_dot = true;
} else if digit != '_' {
panic!("Unexpected character: {digit}");
}
}

// Combine the mantissa and the exponent I256. This will
// overflow if the exponent is too large. We also need to make sure that
// the final result is an integer.
if exponent < decimals {
panic!("Exponent is too small: {exponent}");
}

Ok(sign * mantissa * ethers::types::I256::from(10).pow(exponent - decimals))
}
Loading