-
Notifications
You must be signed in to change notification settings - Fork 8
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
add_recoverable_error + bump version to 0.9.0 #28
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove from git indexing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove from git indexing. |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -14,54 +14,66 @@ 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 { field: "AMOUNT" }); | ||||||
shuhuiluo marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} | ||||||
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> { | ||||||
Self::new(currency, raw_amount, 1) | ||||||
pub fn from_raw_amount( | ||||||
currency: T, | ||||||
raw_amount: impl Into<BigInt>, | ||||||
) -> Result<CurrencyAmount<T>, Error> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
nit |
||||||
Self::new(currency, raw_amount, 1).map_err(|err| Error::CreationError(format!("{}", err))) | ||||||
shuhuiluo marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} | ||||||
|
||||||
// Construct a currency amount with a denominator that is not equal to 1 | ||||||
// Construct a currency amoun t with a denominator that is not equal to 1 | ||||||
malik672 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
pub fn from_fractional_amount( | ||||||
currency: T, | ||||||
numerator: impl Into<BigInt>, | ||||||
denominator: impl Into<BigInt>, | ||||||
) -> CurrencyAmount<T> { | ||||||
) -> Result<CurrencyAmount<T>, Error> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
nit |
||||||
Self::new(currency, numerator, denominator) | ||||||
.map_err(|err| Error::CreationError(format!("{}", err))) | ||||||
shuhuiluo marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} | ||||||
|
||||||
// Multiplication of currency amount by another fractional amount | ||||||
pub fn multiply<M>(&self, other: &impl FractionTrait<M>) -> Self { | ||||||
let multiplied = self.as_fraction() * other.as_fraction(); | ||||||
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(), | ||||||
multiplied.numerator().clone(), | ||||||
multiplied.denominator().clone(), | ||||||
multiplied.clone().numerator().clone(), | ||||||
multiplied.clone().denominator().clone(), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't get the purpose of these clones. It affects nothing but performance. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fraction::new takes ownership of the data the only way to do this i know is to clone There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
) | ||||||
.map_err(|err| Error::CreateFractionalError(format!("{}", err))) | ||||||
} | ||||||
|
||||||
// Division of currency amount by another fractional amount | ||||||
pub fn divide<M>(&self, other: &impl FractionTrait<M>) -> Self { | ||||||
let divided = self.as_fraction() / other.as_fraction(); | ||||||
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(), | ||||||
divided.numerator().clone(), | ||||||
divided.denominator().clone(), | ||||||
) | ||||||
.map_err(|err| Error::CreateFractionalError(format!("{}", err))) | ||||||
} | ||||||
|
||||||
// Convert the currency amount to a string with exact precision | ||||||
|
@@ -73,52 +85,73 @@ 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"); | ||||||
let added = self.as_fraction() + other.as_fraction(); | ||||||
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(), | ||||||
added.numerator().clone(), | ||||||
added.denominator().clone(), | ||||||
added.clone().numerator().clone(), | ||||||
added.clone().denominator().clone(), | ||||||
) | ||||||
.map_err(|err| Error::CreateFractionalError(format!("{}", err))) | ||||||
} | ||||||
|
||||||
// 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"); | ||||||
let subtracted = self.as_fraction() - other.as_fraction(); | ||||||
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(), | ||||||
subtracted.numerator().clone(), | ||||||
subtracted.denominator().clone(), | ||||||
subtracted.clone().numerator().clone(), | ||||||
subtracted.clone().denominator().clone(), | ||||||
) | ||||||
.map_err(|err| Error::CreateFractionalError(format!("{}", err))) | ||||||
} | ||||||
|
||||||
// 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 { | ||||||
(self.as_fraction() / Fraction::new(self.meta.decimal_scale.clone(), 1)) | ||||||
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) | ||||||
.map_err(|err| Error::CreateFractionalError(format!("{}", err))) | ||||||
} | ||||||
|
||||||
// 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(), | ||||||
) | ||||||
.map_err(|err| Error::CreateFractionalError(format!("{}", err))), | ||||||
false => Ok(self.clone()), | ||||||
} | ||||||
} | ||||||
} | ||||||
|
@@ -142,22 +175,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).unwrap()); | ||||||
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()))); | ||||||
|
@@ -166,81 +202,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_err(), "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_err(), "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"); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is it commented out? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lmao, forgot I ever did something like this |
||||||
} | ||||||
|
||||||
#[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"); | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add to
.gitignore
.