Skip to content

Commit

Permalink
Merge pull request #93 from malik672/fastnum
Browse files Browse the repository at this point in the history
perf!: refactor types and replace `num-bigint` with `fastnum`
  • Loading branch information
malik672 authored Jan 5, 2025
2 parents 1d2acdc + bba761d commit 91ab1d4
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 110 deletions.
9 changes: 4 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
[package]
name = "uniswap-sdk-core"
version = "3.3.0"
version = "4.0.0-rc"
edition = "2021"
authors = ["malik <[email protected]>", "Shuhui Luo <twitter.com/aureliano_law>"]
description = "The Uniswap SDK Core in Rust provides essential functionality for interacting with the Uniswap decentralized exchange"
license = "MIT"

[dependencies]
alloy-primitives = { version = ">=0.8.5", features = ["map-fxhash"] }
bigdecimal = "0.4.5"
derive_more = { version = "1.0.0", features = ["deref"] }
eth_checksum = { version = "0.1.2", optional = true }
fastnum = { version = "0.1.9", default-features = false, features = ["libm"] }
lazy_static = "1.5"
num-bigint = "0.4"
num-integer = "0.1"
regex = { version = "1.11", optional = true }
thiserror = { version = "2", default-features = false }

[features]
std = ["thiserror/std"]
default = []
std = ["fastnum/std", "thiserror/std"]
validate_parse_address = ["eth_checksum", "regex"]

[lib]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Add this to your Cargo.toml

```
[dependencies]
uniswap-sdk-core = "3.3.0"
uniswap-sdk-core = "4.0.0"
```

And this to your code:
Expand Down
9 changes: 2 additions & 7 deletions src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use crate::prelude::*;
use alloy_primitives::U256;
use lazy_static::lazy_static;
use num_bigint::Sign;

/// Represents the various types of trades.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
Expand All @@ -27,8 +25,5 @@ pub enum Rounding {
RoundUp,
}

lazy_static! {
/// Represents the maximum amount contained in a uint256
pub static ref MAX_UINT256: BigInt =
BigInt::from_biguint(Sign::Plus, BigUint::from_bytes_le(&U256::MAX.as_le_bytes()));
}
/// Represents the maximum amount contained in a uint256
pub const MAX_UINT256: BigInt = to_big_int(U256::MAX);
34 changes: 16 additions & 18 deletions src/entities/fractions/currency_amount.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::prelude::*;
use alloc::string::ToString;
use core::ops::Div;
use num_integer::Integer;
use fastnum::i512;

/// Currency amount struct that represents a rational amount of a currency
pub type CurrencyAmount<T> = FractionLike<CurrencyMeta<T>>;
Expand All @@ -12,7 +11,7 @@ pub struct CurrencyMeta<T: BaseCurrency> {
/// The currency associated with this metadata
pub currency: T,
/// The scale factor for the currency's decimal places
pub decimal_scale: BigUint,
pub decimal_scale: BigInt,
}

impl<T: BaseCurrency> CurrencyAmount<T> {
Expand All @@ -26,7 +25,7 @@ impl<T: BaseCurrency> CurrencyAmount<T> {
let numerator = numerator.into();
let denominator = denominator.into();
// Ensure the amount does not exceed MAX_UINT256
if numerator.div_floor(&denominator) > *MAX_UINT256 {
if numerator.div_floor(denominator) > MAX_UINT256 {
return Err(Error::UintOverflow);
}
let exponent = currency.decimals();
Expand All @@ -35,7 +34,7 @@ impl<T: BaseCurrency> CurrencyAmount<T> {
denominator,
CurrencyMeta {
currency,
decimal_scale: BigUint::from(10_u64).pow(exponent as u32),
decimal_scale: i512!(10).pow(exponent as u32),
},
))
}
Expand Down Expand Up @@ -81,8 +80,8 @@ impl<T: BaseCurrency> CurrencyAmount<T> {
/// Convert the currency amount to a string with exact precision
#[inline]
pub fn to_exact(&self) -> String {
BigDecimal::from(self.quotient())
.div(BigDecimal::from(BigInt::from(self.decimal_scale.clone())))
to_big_decimal(self.quotient())
.div(to_big_decimal(self.decimal_scale))
.to_string()
}

Expand Down Expand Up @@ -117,7 +116,7 @@ impl<T: BaseCurrency> CurrencyAmount<T> {
significant_digits: u8,
rounding: Option<Rounding>,
) -> Result<String, Error> {
(self.as_fraction() / Fraction::new(self.decimal_scale.clone(), 1)).to_significant(
(self.as_fraction() / Fraction::new(self.decimal_scale, 1)).to_significant(
significant_digits,
Some(rounding.unwrap_or(Rounding::RoundDown)),
)
Expand All @@ -134,7 +133,7 @@ impl<T: BaseCurrency> CurrencyAmount<T> {
return Err(Error::Invalid("DECIMALS"));
}
Ok(
(self.as_fraction() / Fraction::new(self.decimal_scale.clone(), 1)).to_fixed(
(self.as_fraction() / Fraction::new(self.decimal_scale, 1)).to_fixed(
decimal_places,
Some(rounding.unwrap_or(Rounding::RoundDown)),
),
Expand All @@ -146,8 +145,8 @@ impl<T: BaseCurrency> CurrencyAmount<T> {
pub fn wrapped(&self) -> Result<CurrencyAmount<&Token>, Error> {
CurrencyAmount::from_fractional_amount(
self.currency.wrapped(),
self.numerator().clone(),
self.denominator().clone(),
self.numerator(),
self.denominator(),
)
}
}
Expand Down Expand Up @@ -193,31 +192,30 @@ mod tests {

#[test]
fn test_token_amount_max_uint256() {
let amount = CurrencyAmount::from_raw_amount(TOKEN18.clone(), MAX_UINT256.clone()).unwrap();
let amount = CurrencyAmount::from_raw_amount(TOKEN18.clone(), MAX_UINT256).unwrap();
assert_eq!(amount.quotient(), MAX_UINT256.clone());
}

#[test]
#[should_panic(expected = "AMOUNT")]
fn test_token_amount_exceeds_max_uint256() {
let _w = CurrencyAmount::from_raw_amount(TOKEN18.clone(), MAX_UINT256.clone() + 1);
let _w = CurrencyAmount::from_raw_amount(TOKEN18.clone(), MAX_UINT256 + BigInt::from(1));
assert!(_w.is_ok(), "AMOUNT");
}

#[test]
#[should_panic(expected = "AMOUNT")]
fn test_token_amount_quotient_exceeds_max_uint256() {
let numerator: BigInt = (MAX_UINT256.clone() + 1) * 2;
let numerator: BigInt = (MAX_UINT256 + BigInt::from(1)) * BigInt::from(2);
let _w = CurrencyAmount::from_fractional_amount(TOKEN18.clone(), numerator, 2);
assert!(_w.is_ok(), "AMOUNT");
}

#[test]
fn test_token_amount_numerator_gt_uint256() {
let numerator: BigInt = MAX_UINT256.clone() + 2;
let amount =
CurrencyAmount::from_fractional_amount(TOKEN18.clone(), numerator.clone(), 2).unwrap();
assert_eq!(amount.numerator(), &numerator);
let numerator: BigInt = MAX_UINT256 + BigInt::from(2);
let amount = CurrencyAmount::from_fractional_amount(TOKEN18.clone(), numerator, 2).unwrap();
assert_eq!(amount.numerator(), numerator);
}

#[test]
Expand Down
73 changes: 34 additions & 39 deletions src/entities/fractions/fraction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ use alloc::string::ToString;
use core::{
cmp::Ordering,
hash::{Hash, Hasher},
num::NonZeroU64,
ops::{Add, Div, Mul, Sub},
};
use derive_more::Deref;
use num_integer::Integer;
use fastnum::i512;

/// Struct representing a fraction with metadata
#[derive(Clone, Debug, Deref)]
Expand All @@ -24,7 +23,7 @@ impl<M: Default> Default for FractionLike<M> {
fn default() -> Self {
Self {
numerator: BigInt::ZERO,
denominator: BigInt::from(1),
denominator: i512!(1),
meta: M::default(),
}
}
Expand Down Expand Up @@ -85,10 +84,10 @@ pub trait FractionBase<M: Clone>: Sized {
fn meta(&self) -> &M;

/// Accessor method for retrieving numerator
fn numerator(&self) -> &BigInt;
fn numerator(&self) -> BigInt;

/// Accessor method for retrieving the denominator
fn denominator(&self) -> &BigInt;
fn denominator(&self) -> BigInt;

/// Returns the floor division quotient of the fraction
#[inline]
Expand All @@ -101,25 +100,21 @@ pub trait FractionBase<M: Clone>: Sized {
fn remainder(&self) -> Self {
Self::new(
self.numerator() % self.denominator(),
self.denominator().clone(),
self.denominator(),
self.meta().clone(),
)
}

/// Returns the inverted fraction
#[inline]
fn invert(&self) -> Self {
Self::new(
self.denominator().clone(),
self.numerator().clone(),
self.meta().clone(),
)
Self::new(self.denominator(), self.numerator(), self.meta().clone())
}

/// Converts the fraction to a [`BigDecimal`]
#[inline]
fn to_decimal(&self) -> BigDecimal {
BigDecimal::from(self.numerator().clone()).div(BigDecimal::from(self.denominator().clone()))
to_big_decimal(self.numerator()) / to_big_decimal(self.denominator())
}

/// Converts the fraction to a string with a specified number of significant digits and rounding
Expand All @@ -134,12 +129,13 @@ pub trait FractionBase<M: Clone>: Sized {
return Err(Error::Invalid("SIGNIFICANT_DIGITS"));
}
let rounding_strategy = to_rounding_strategy(rounding.unwrap_or_default());
let quotient = self.to_decimal().with_precision_round(
NonZeroU64::new(significant_digits as u64).unwrap(),
rounding_strategy,
);
let quotient = self.to_decimal().with_rounding_mode(rounding_strategy);
let integer_digits = quotient.digits_count() as i16 - quotient.fractional_digits_count();
let quotient = quotient
.round(significant_digits as i16 - integer_digits)
.reduce();

Ok(quotient.normalized().to_string())
Ok(quotient.to_string())
}

/// Converts the fraction to a string with a fixed number of decimal places and rounding
Expand All @@ -148,14 +144,15 @@ pub trait FractionBase<M: Clone>: Sized {
fn to_fixed(&self, decimal_places: u8, rounding: Option<Rounding>) -> String {
let rounding_strategy = to_rounding_strategy(rounding.unwrap_or_default());
self.to_decimal()
.with_scale_round(decimal_places as i64, rounding_strategy)
.with_rounding_mode(rounding_strategy)
.round(decimal_places as i16)
.to_string()
}

/// Helper method for converting any superclass back to a simple [`Fraction`]
#[inline]
fn as_fraction(&self) -> Fraction {
Fraction::new(self.numerator().clone(), self.denominator().clone())
Fraction::new(self.numerator(), self.denominator())
}
}

Expand All @@ -182,22 +179,22 @@ impl<M: Clone> FractionBase<M> for FractionLike<M> {

/// Accessor method for retrieving the numerator
#[inline]
fn numerator(&self) -> &BigInt {
&self.numerator
fn numerator(&self) -> BigInt {
self.numerator
}

/// Accessor method for retrieving the denominator
#[inline]
fn denominator(&self) -> &BigInt {
&self.denominator
fn denominator(&self) -> BigInt {
self.denominator
}
}

impl<M: PartialEq> PartialEq for FractionLike<M> {
/// Checks if the current fraction is equal to another fraction
#[inline]
fn eq(&self, other: &Self) -> bool {
&self.numerator * &other.denominator == &other.numerator * &self.denominator
self.numerator * other.denominator == other.numerator * self.denominator
&& self.meta == other.meta
}
}
Expand All @@ -217,7 +214,7 @@ impl<M: Hash> Hash for FractionLike<M> {
impl<M: PartialEq> Ord for FractionLike<M> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(&self.numerator * &other.denominator).cmp(&(&other.numerator * &self.denominator))
(self.numerator * other.denominator).cmp(&(other.numerator * self.denominator))
}
}

Expand All @@ -241,7 +238,7 @@ impl<M: Clone> Add for FractionLike<M> {
)
} else {
FractionBase::new(
self.numerator * &other.denominator + other.numerator * &self.denominator,
self.numerator * other.denominator + other.numerator * self.denominator,
self.denominator * other.denominator,
self.meta,
)
Expand All @@ -256,14 +253,14 @@ impl<M: Clone> Add<&Self> for FractionLike<M> {
fn add(self, other: &Self) -> Self::Output {
if self.denominator == other.denominator {
FractionBase::new(
self.numerator + &other.numerator,
self.numerator + other.numerator,
self.denominator,
self.meta,
)
} else {
FractionBase::new(
self.numerator * &other.denominator + &other.numerator * &self.denominator,
self.denominator * &other.denominator,
self.numerator * other.denominator + other.numerator * self.denominator,
self.denominator * other.denominator,
self.meta,
)
}
Expand All @@ -283,7 +280,7 @@ impl<M: Clone> Sub for FractionLike<M> {
)
} else {
FractionBase::new(
self.numerator * &other.denominator - other.numerator * &self.denominator,
self.numerator * other.denominator - other.numerator * self.denominator,
self.denominator * other.denominator,
self.meta,
)
Expand All @@ -298,14 +295,14 @@ impl<M: Clone> Sub<&Self> for FractionLike<M> {
fn sub(self, other: &Self) -> Self::Output {
if self.denominator == other.denominator {
FractionBase::new(
self.numerator - &other.numerator,
self.numerator - other.numerator,
self.denominator,
self.meta,
)
} else {
FractionBase::new(
self.numerator * &other.denominator - &other.numerator * &self.denominator,
self.denominator * &other.denominator,
self.numerator * other.denominator - other.numerator * self.denominator,
self.denominator * other.denominator,
self.meta,
)
}
Expand All @@ -331,8 +328,8 @@ impl<M: Clone> Mul<&Self> for FractionLike<M> {
#[inline]
fn mul(self, other: &Self) -> Self::Output {
FractionBase::new(
self.numerator * &other.numerator,
self.denominator * &other.denominator,
self.numerator * other.numerator,
self.denominator * other.denominator,
self.meta,
)
}
Expand All @@ -341,7 +338,6 @@ impl<M: Clone> Mul<&Self> for FractionLike<M> {
impl<M: Clone> Div for FractionLike<M> {
type Output = Self;

/// There's little to no possibility of an error, so unwrap can be used
#[inline]
fn div(self, other: Self) -> Self::Output {
FractionBase::new(
Expand All @@ -355,12 +351,11 @@ impl<M: Clone> Div for FractionLike<M> {
impl<M: Clone> Div<&Self> for FractionLike<M> {
type Output = Self;

/// There's little to no possibility of an error, so unwrap can be used
#[inline]
fn div(self, other: &Self) -> Self::Output {
FractionBase::new(
self.numerator * &other.denominator,
self.denominator * &other.numerator,
self.numerator * other.denominator,
self.denominator * other.numerator,
self.meta,
)
}
Expand Down
Loading

0 comments on commit 91ab1d4

Please sign in to comment.