Skip to content

Commit

Permalink
Merge pull request #30 from malik672/add_recoverable_errors
Browse files Browse the repository at this point in the history
Add recoverable errors
  • Loading branch information
malik672 authored Jan 11, 2024
2 parents 6a2fcea + edcdb3f commit 034cd2a
Show file tree
Hide file tree
Showing 15 changed files with 296 additions and 147 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
/.DS_Store
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "uniswap-sdk-core"
version = "0.8.0"
version = "0.9.0"
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"
Expand All @@ -18,3 +18,4 @@ num-integer = "0.1.45"
num-rational = "0.4.1"
num-traits = "0.2.17"
regex = "1.10.2"
thiserror = "1.0.56"
Binary file added src/.DS_Store
Binary file not shown.
132 changes: 93 additions & 39 deletions src/entities/fractions/currency_amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,30 @@ pub struct CurrencyMeta<T: CurrencyTrait> {
// Implementation of methods for CurrencyAmount
impl<T: CurrencyTrait> CurrencyAmount<T> {
// Constructor method for creating a new currency amount
fn new(currency: T, numerator: impl Into<BigInt>, denominator: impl Into<BigInt>) -> Self {
fn new(
currency: T,
numerator: impl Into<BigInt>,
denominator: impl Into<BigInt>,
) -> Result<Self, Error> {
let numerator = numerator.into();
let denominator = denominator.into();
// Ensure the amount does not exceed MAX_UINT256
assert!(numerator.div_floor(&denominator).le(&MAX_UINT256), "AMOUNT");
if !numerator.div_floor(&denominator).le(&MAX_UINT256) {
return Err(Error::MaxUint);
}
let exponent = currency.decimals();
FractionTrait::new(
Ok(FractionTrait::new(
numerator,
denominator,
CurrencyMeta {
currency,
decimal_scale: BigUint::from(10u64).pow(exponent as u32),
},
)
))
}

// Returns a new currency amount instance from the unitless amount of token (raw amount)
pub fn from_raw_amount(currency: T, raw_amount: impl Into<BigInt>) -> CurrencyAmount<T> {
pub fn from_raw_amount(currency: T, raw_amount: impl Into<BigInt>) -> Result<Self, Error> {
Self::new(currency, raw_amount, 1)
}

Expand All @@ -40,12 +46,12 @@ impl<T: CurrencyTrait> CurrencyAmount<T> {
currency: T,
numerator: impl Into<BigInt>,
denominator: impl Into<BigInt>,
) -> CurrencyAmount<T> {
) -> Result<Self, Error> {
Self::new(currency, numerator, denominator)
}

// Multiplication of currency amount by another fractional amount
pub fn multiply<M>(&self, other: &impl FractionTrait<M>) -> Self {
pub fn multiply<M>(&self, other: &impl FractionTrait<M>) -> Result<Self, Error> {
let multiplied = self.as_fraction() * other.as_fraction();
Self::from_fractional_amount(
self.meta.currency.clone(),
Expand All @@ -55,7 +61,7 @@ impl<T: CurrencyTrait> CurrencyAmount<T> {
}

// Division of currency amount by another fractional amount
pub fn divide<M>(&self, other: &impl FractionTrait<M>) -> Self {
pub fn divide<M>(&self, other: &impl FractionTrait<M>) -> Result<Self, Error> {
let divided = self.as_fraction() / other.as_fraction();
Self::from_fractional_amount(
self.meta.currency.clone(),
Expand All @@ -73,8 +79,10 @@ impl<T: CurrencyTrait> CurrencyAmount<T> {
}

// Addition of another currency amount to the current amount
pub fn add(&self, other: &Self) -> Self {
assert!(self.meta.currency.equals(&other.meta.currency), "CURRENCY");
pub fn add(&self, other: &Self) -> Result<Self, Error> {
if !self.meta.currency.equals(&other.meta.currency) {
return Err(Error::NotEqual("CURRENCY: not equal".to_owned()));
}
let added = self.as_fraction() + other.as_fraction();
Self::from_fractional_amount(
self.meta.currency.clone(),
Expand All @@ -84,8 +92,10 @@ impl<T: CurrencyTrait> CurrencyAmount<T> {
}

// Subtraction of another currency amount from the current amount
pub fn subtract(&self, other: &Self) -> Self {
assert!(self.meta.currency.equals(&other.meta.currency), "CURRENCY");
pub fn subtract(&self, other: &Self) -> Result<Self, Error> {
if !self.meta.currency.equals(&other.meta.currency) {
return Err(Error::NotEqual("CURRENCY: not equal".to_owned()));
}
let subtracted = self.as_fraction() - other.as_fraction();
Self::from_fractional_amount(
self.meta.currency.clone(),
Expand All @@ -95,30 +105,43 @@ impl<T: CurrencyTrait> CurrencyAmount<T> {
}

// Convert the currency amount to a string with a specified number of significant digits
pub fn to_significant(&self, significant_digits: u8, rounding: Rounding) -> String {
pub fn to_significant(
&self,
significant_digits: u8,
rounding: Rounding,
) -> Result<String, Error> {
(self.as_fraction() / Fraction::new(self.meta.decimal_scale.clone(), 1))
.to_significant(significant_digits, rounding)
}

// Convert the currency amount to a string with a fixed number of decimal places
pub fn to_fixed(&self, decimal_places: u8, rounding: Rounding) -> String {
assert!(decimal_places <= self.meta.currency.decimals(), "DECIMALS");
(self.as_fraction() / Fraction::new(self.meta.decimal_scale.clone(), 1))
.to_fixed(decimal_places, rounding)
pub fn to_fixed(&self, decimal_places: u8, rounding: Rounding) -> Result<String, Error> {
if !decimal_places <= self.meta.currency.decimals() {
return Err(Error::NotEqual(format!(
"{} should be less than or equal to {}",
decimal_places,
self.meta.currency.decimals()
)));
}

Ok(
(self.as_fraction() / Fraction::new(self.meta.decimal_scale.clone(), 1))
.to_fixed(decimal_places, rounding),
)
}
}

// Implementation for a specific type of CurrencyAmount (Token)
impl CurrencyAmount<Token> {
// Wrap the currency amount if the currency is not native
pub fn wrapped(&self) -> CurrencyAmount<Token> {
pub fn wrapped(&self) -> Result<CurrencyAmount<Token>, Error> {
match &self.meta.currency.is_native() {
true => Self::from_fractional_amount(
self.meta.currency.wrapped(),
self.numerator().clone(),
self.denominator().clone(),
),
false => self.clone(),
false => Ok(self.clone()),
}
}
}
Expand All @@ -142,22 +165,25 @@ mod tests {
#[test]
fn test_constructor() {
let amount = CurrencyAmount::from_raw_amount(TOKEN18.clone(), 100);
assert_eq!(amount.quotient(), 100.into());
assert_eq!(amount.unwrap().quotient(), 100.into());
}

#[test]
fn test_quotient() {
let amount =
CurrencyAmount::from_raw_amount(TOKEN18.clone(), 100).multiply(&Percent::new(15, 100));
assert_eq!(amount.quotient(), BigInt::from(15));
let amount = CurrencyAmount::from_raw_amount(TOKEN18.clone(), 100)
.unwrap()
.multiply(&Percent::new(15, 100));
assert_eq!(amount.unwrap().quotient(), BigInt::from(15));
}

#[test]
fn test_ether() {
let ether = Ether::on_chain(1);
let amount = CurrencyAmount::from_raw_amount(Currency::NativeCurrency(ether.clone()), 100);
assert_eq!(amount.quotient(), 100.into());
let amount =
CurrencyAmount::from_raw_amount(Currency::NativeCurrency(ether.clone()), 100).unwrap();
assert_eq!(amount.clone().quotient(), 100.into());
assert!(amount
.clone()
.meta
.currency
.equals(&Currency::NativeCurrency(ether.clone())));
Expand All @@ -166,81 +192,109 @@ mod tests {
#[test]
fn test_token_amount_max_uint256() {
let amount = CurrencyAmount::from_raw_amount(TOKEN18.clone(), MAX_UINT256.clone());
assert_eq!(amount.quotient(), MAX_UINT256.clone());
assert_eq!(amount.unwrap().quotient(), MAX_UINT256.clone());
}

#[test]
#[should_panic(expected = "AMOUNT")]
fn test_token_amount_exceeds_max_uint256() {
let _ = CurrencyAmount::from_raw_amount(TOKEN18.clone(), MAX_UINT256.clone() + 1);
let _w = CurrencyAmount::from_raw_amount(TOKEN18.clone(), MAX_UINT256.clone() + 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 _ = CurrencyAmount::from_fractional_amount(TOKEN18.clone(), numerator, 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);
assert_eq!(amount.numerator(), &numerator);
assert_eq!(amount.unwrap().numerator(), &numerator);
}

#[test]
#[should_panic(expected = "DECIMALS")]
fn to_fixed_decimals_exceeds_currency_decimals() {
let amount = CurrencyAmount::from_raw_amount(TOKEN0.clone(), 1000);
let _ = amount.to_fixed(3, Rounding::RoundDown);
let _w = amount.unwrap().to_fixed(3, Rounding::RoundDown);
assert!(_w.is_err(), "DECIMALS");
}

#[test]
fn to_fixed_0_decimals() {
let amount = CurrencyAmount::from_raw_amount(TOKEN0.clone(), 123456);
assert_eq!(amount.to_fixed(0, Rounding::RoundDown), "123456");
assert_eq!(
amount.unwrap().to_fixed(0, Rounding::RoundDown).unwrap(),
"123456"
);
}

#[test]
fn to_fixed_18_decimals() {
let amount = CurrencyAmount::from_raw_amount(TOKEN18.clone(), 1e15 as i64);
assert_eq!(amount.to_fixed(9, Rounding::RoundDown), "0.001000000");
assert_eq!(
amount.unwrap().to_fixed(9, Rounding::RoundDown).unwrap(),
"0.001000000"
);
}

#[test]
fn to_significant_does_not_throw() {
let amount = CurrencyAmount::from_raw_amount(TOKEN0.clone(), 1000);
assert_eq!(amount.to_significant(3, Rounding::RoundDown), "1000");
assert_eq!(
amount
.unwrap()
.to_significant(3, Rounding::RoundDown)
.unwrap(),
"1000"
);
}

#[test]
fn to_significant_0_decimals() {
let amount = CurrencyAmount::from_raw_amount(TOKEN0.clone(), 123456);
assert_eq!(amount.to_significant(4, Rounding::RoundDown), "123400");
assert_eq!(
amount
.unwrap()
.to_significant(4, Rounding::RoundDown)
.unwrap(),
"123400"
);
}

#[test]
fn to_significant_18_decimals() {
let amount = CurrencyAmount::from_raw_amount(TOKEN18.clone(), 1e15 as i64);
assert_eq!(amount.to_significant(9, Rounding::RoundDown), "0.001");
assert_eq!(
amount
.unwrap()
.to_significant(9, Rounding::RoundDown)
.unwrap(),
"0.001"
);
}

#[test]
fn to_exact_does_not_throw() {
let amount = CurrencyAmount::from_raw_amount(TOKEN0.clone(), 1000);
assert_eq!(amount.to_exact(), "1000");
assert_eq!(amount.unwrap().to_exact(), "1000");
}

#[test]
fn to_exact_0_decimals() {
let amount = CurrencyAmount::from_raw_amount(TOKEN0.clone(), 123456);
assert_eq!(amount.to_exact(), "123456");
let amount = CurrencyAmount::from_raw_amount(TOKEN0.clone(), 123456).unwrap();
println!("{:?}", amount.clone().to_exact());
assert_eq!(amount.clone().to_exact(), "123456");
}

#[test]
fn to_exact_18_decimals() {
let amount = CurrencyAmount::from_raw_amount(TOKEN18.clone(), 123e13 as i64);
assert_eq!(amount.to_exact(), "0.00123");
assert_eq!(amount.unwrap().to_exact(), "0.00123");
}
}
Loading

0 comments on commit 034cd2a

Please sign in to comment.