diff --git a/Cargo.toml b/Cargo.toml index 83cd8ee78..e69bc3b5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ keywords = [ anyhow = "1.0.71" bech32 = "0.9.1" cosmwasm-schema = "1.2.6" -cosmwasm-std = "1.2.6" +cosmwasm-std = "1.3.1" cw2 = "1.1.0" cw-storage-plus = "1.0.1" cw-utils = "1.0.1" diff --git a/contracts/red-bank/src/error.rs b/contracts/red-bank/src/error.rs index 8f07ce8b1..54b1972ac 100644 --- a/contracts/red-bank/src/error.rs +++ b/contracts/red-bank/src/error.rs @@ -1,4 +1,6 @@ -use cosmwasm_std::{CheckedFromRatioError, CheckedMultiplyFractionError, OverflowError, StdError}; +use cosmwasm_std::{ + CheckedFromRatioError, CheckedMultiplyFractionError, DivideByZeroError, OverflowError, StdError, +}; use cw_utils::PaymentError; use mars_health::error::HealthError; use mars_liquidation::error::LiquidationError; @@ -33,6 +35,9 @@ pub enum ContractError { #[error("{0}")] CheckedMultiplyFraction(#[from] CheckedMultiplyFractionError), + #[error("{0}")] + DivideByZero(#[from] DivideByZeroError), + #[error("{0}")] Health(#[from] HealthError), diff --git a/contracts/red-bank/src/health.rs b/contracts/red-bank/src/health.rs index ae8b3b775..aaced7409 100644 --- a/contracts/red-bank/src/health.rs +++ b/contracts/red-bank/src/health.rs @@ -112,7 +112,7 @@ pub fn get_user_positions_map( user_addr: &Addr, oracle_addr: &Addr, params_addr: &Addr, -) -> StdResult> { +) -> Result, ContractError> { let block_time = env.block.time.seconds(); // Find all denoms that the user has a collateral or debt position in diff --git a/contracts/red-bank/src/interest_rates.rs b/contracts/red-bank/src/interest_rates.rs index d25f83236..d0efc4c80 100644 --- a/contracts/red-bank/src/interest_rates.rs +++ b/contracts/red-bank/src/interest_rates.rs @@ -1,6 +1,6 @@ use std::str; -use cosmwasm_std::{Addr, Decimal, Env, Event, Response, StdResult, Storage, Uint128}; +use cosmwasm_std::{Addr, Decimal, Env, Event, Response, Storage, Uint128}; use mars_interest_rate::{ calculate_applied_linear_interest_rate, compute_scaled_amount, compute_underlying_amount, get_underlying_debt_amount, get_underlying_liquidity_amount, ScalingOperation, @@ -26,7 +26,7 @@ pub fn apply_accumulated_interests( rewards_collector_addr: &Addr, incentives_addr: &Addr, mut response: Response, -) -> StdResult { +) -> Result { let current_timestamp = env.block.time.seconds(); let previous_borrow_index = market.borrow_index; diff --git a/contracts/red-bank/src/query.rs b/contracts/red-bank/src/query.rs index e9a74ab16..44abb98ea 100644 --- a/contracts/red-bank/src/query.rs +++ b/contracts/red-bank/src/query.rs @@ -93,7 +93,7 @@ pub fn query_user_debt( block: &BlockInfo, user_addr: Addr, denom: String, -) -> StdResult { +) -> Result { let Debt { amount_scaled, uncollateralized, @@ -117,7 +117,7 @@ pub fn query_user_debts( user_addr: Addr, start_after: Option, limit: Option, -) -> StdResult> { +) -> Result, ContractError> { let block_time = block.time.seconds(); let start = start_after.map(|denom| Bound::ExclusiveRaw(denom.into_bytes())); @@ -151,7 +151,7 @@ pub fn query_user_collateral( user_addr: Addr, account_id: Option, denom: String, -) -> StdResult { +) -> Result { let acc_id = account_id.unwrap_or("".to_string()); let Collateral { @@ -178,7 +178,7 @@ pub fn query_user_collaterals( account_id: Option, start_after: Option, limit: Option, -) -> StdResult> { +) -> Result, ContractError> { let block_time = block.time.seconds(); let start = start_after.map(|denom| Bound::ExclusiveRaw(denom.into_bytes())); @@ -213,9 +213,9 @@ pub fn query_scaled_liquidity_amount( env: Env, denom: String, amount: Uint128, -) -> StdResult { +) -> Result { let market = MARKETS.load(deps.storage, &denom)?; - get_scaled_liquidity_amount(amount, &market, env.block.time.seconds()) + Ok(get_scaled_liquidity_amount(amount, &market, env.block.time.seconds())?) } pub fn query_scaled_debt_amount( @@ -223,9 +223,9 @@ pub fn query_scaled_debt_amount( env: Env, denom: String, amount: Uint128, -) -> StdResult { +) -> Result { let market = MARKETS.load(deps.storage, &denom)?; - get_scaled_debt_amount(amount, &market, env.block.time.seconds()) + Ok(get_scaled_debt_amount(amount, &market, env.block.time.seconds())?) } pub fn query_underlying_liquidity_amount( @@ -233,9 +233,9 @@ pub fn query_underlying_liquidity_amount( env: Env, denom: String, amount_scaled: Uint128, -) -> StdResult { +) -> Result { let market = MARKETS.load(deps.storage, &denom)?; - get_underlying_liquidity_amount(amount_scaled, &market, env.block.time.seconds()) + Ok(get_underlying_liquidity_amount(amount_scaled, &market, env.block.time.seconds())?) } pub fn query_underlying_debt_amount( @@ -243,9 +243,9 @@ pub fn query_underlying_debt_amount( env: Env, denom: String, amount_scaled: Uint128, -) -> StdResult { +) -> Result { let market = MARKETS.load(deps.storage, &denom)?; - get_underlying_debt_amount(amount_scaled, &market, env.block.time.seconds()) + Ok(get_underlying_debt_amount(amount_scaled, &market, env.block.time.seconds())?) } pub fn query_user_position( diff --git a/contracts/red-bank/tests/test_borrow.rs b/contracts/red-bank/tests/test_borrow.rs index 98314002e..7daa4ba90 100644 --- a/contracts/red-bank/tests/test_borrow.rs +++ b/contracts/red-bank/tests/test_borrow.rs @@ -18,7 +18,6 @@ use mars_red_bank::{ }; use mars_red_bank_types::red_bank::{ExecuteMsg, Market}; use mars_testing::{mock_env, mock_env_at_block_time, MockEnvParams}; -use mars_utils::math; use crate::helpers::th_default_asset_params; @@ -965,10 +964,10 @@ fn borrow_collateral_check() { .unwrap() * exchange_rate_3); let exceeding_borrow_amount = - math::divide_uint128_by_decimal(max_borrow_allowed_in_base_asset, exchange_rate_2).unwrap() + max_borrow_allowed_in_base_asset.checked_div_floor(exchange_rate_2).unwrap() + Uint128::from(100_u64); let permissible_borrow_amount = - math::divide_uint128_by_decimal(max_borrow_allowed_in_base_asset, exchange_rate_2).unwrap() + max_borrow_allowed_in_base_asset.checked_div_floor(exchange_rate_2).unwrap() - Uint128::from(100_u64); // borrow above the allowed amount given current collateral, should fail diff --git a/contracts/red-bank/tests/test_misc.rs b/contracts/red-bank/tests/test_misc.rs index d710b694c..f1c6a361d 100644 --- a/contracts/red-bank/tests/test_misc.rs +++ b/contracts/red-bank/tests/test_misc.rs @@ -20,7 +20,6 @@ use mars_red_bank::{ }; use mars_red_bank_types::red_bank::{Debt, ExecuteMsg, Market}; use mars_testing::{mock_env, mock_env_at_block_time, MockEnvParams}; -use mars_utils::math; use crate::helpers::th_default_asset_params; @@ -338,11 +337,9 @@ fn update_asset_collateral() { * token_2_exchange_rate; let weighted_liquidation_threshold_in_base_asset = token_1_weighted_lt_in_base_asset + token_2_weighted_lt_in_base_asset; - let max_debt_for_valid_hf = math::divide_uint128_by_decimal( - weighted_liquidation_threshold_in_base_asset, - token_3_exchange_rate, - ) - .unwrap(); + let max_debt_for_valid_hf = weighted_liquidation_threshold_in_base_asset + .checked_div_floor(token_3_exchange_rate) + .unwrap(); let token_3_debt_scaled = get_scaled_debt_amount( max_debt_for_valid_hf, &market_3_initial, diff --git a/contracts/red-bank/tests/test_withdraw.rs b/contracts/red-bank/tests/test_withdraw.rs index 235fea0ea..4496a9c86 100644 --- a/contracts/red-bank/tests/test_withdraw.rs +++ b/contracts/red-bank/tests/test_withdraw.rs @@ -23,7 +23,6 @@ use mars_red_bank_types::{ red_bank::{Collateral, Debt, ExecuteMsg, Market}, }; use mars_testing::{mock_env_at_block_time, MarsMockQuerier}; -use mars_utils::math; use crate::helpers::th_default_asset_params; @@ -637,13 +636,12 @@ fn how_much_to_withdraw(suite: &HealthCheckTestSuite, block_time: u64) -> Uint12 * prices[1]; // How much to withdraw in base asset to have health factor equal to one - let how_much_to_withdraw_in_base_asset = math::divide_uint128_by_decimal( - weighted_liquidation_threshold_in_base_asset - total_collateralized_debt_in_base_asset, - asset_params[2].liquidation_threshold, - ) - .unwrap(); + let how_much_to_withdraw_in_base_asset = (weighted_liquidation_threshold_in_base_asset + - total_collateralized_debt_in_base_asset) + .checked_div_floor(asset_params[2].liquidation_threshold) + .unwrap(); - math::divide_uint128_by_decimal(how_much_to_withdraw_in_base_asset, prices[2]).unwrap() + how_much_to_withdraw_in_base_asset.checked_div_floor(prices[2]).unwrap() } #[test] diff --git a/packages/interest-rate/src/lib.rs b/packages/interest-rate/src/lib.rs index 8f8a37591..e8631da1c 100644 --- a/packages/interest-rate/src/lib.rs +++ b/packages/interest-rate/src/lib.rs @@ -1,6 +1,5 @@ use cosmwasm_std::{Decimal, StdError, StdResult, Uint128}; -use mars_red_bank_types::red_bank::Market; -use mars_utils::math; +use mars_red_bank_types::{error::MarsError, red_bank::Market}; /// Scaling factor used to keep more precision during division / multiplication by index. pub const SCALING_FACTOR: Uint128 = Uint128::new(1_000_000); @@ -31,7 +30,7 @@ pub fn get_scaled_liquidity_amount( amount: Uint128, market: &Market, timestamp: u64, -) -> StdResult { +) -> Result { compute_scaled_amount( amount, get_updated_liquidity_index(market, timestamp)?, @@ -48,7 +47,7 @@ pub fn get_underlying_liquidity_amount( amount_scaled: Uint128, market: &Market, timestamp: u64, -) -> StdResult { +) -> Result { compute_underlying_amount( amount_scaled, get_updated_liquidity_index(market, timestamp)?, @@ -68,7 +67,7 @@ pub fn get_scaled_debt_amount( amount: Uint128, market: &Market, timestamp: u64, -) -> StdResult { +) -> Result { compute_scaled_amount( amount, get_updated_borrow_index(market, timestamp)?, @@ -85,7 +84,7 @@ pub fn get_underlying_debt_amount( amount_scaled: Uint128, market: &Market, timestamp: u64, -) -> StdResult { +) -> Result { compute_underlying_amount( amount_scaled, get_updated_borrow_index(market, timestamp)?, @@ -108,12 +107,12 @@ pub fn compute_scaled_amount( amount: Uint128, index: Decimal, scaling_operation: ScalingOperation, -) -> StdResult { +) -> Result { // Scale by SCALING_FACTOR to have better precision let scaled_amount = amount.checked_mul(SCALING_FACTOR)?; match scaling_operation { - ScalingOperation::Truncate => math::divide_uint128_by_decimal(scaled_amount, index), - ScalingOperation::Ceil => math::divide_uint128_by_decimal_and_ceil(scaled_amount, index), + ScalingOperation::Truncate => Ok(scaled_amount.checked_div_floor(index)?), + ScalingOperation::Ceil => Ok(scaled_amount.checked_div_ceil(index)?), } } @@ -123,7 +122,7 @@ pub fn compute_underlying_amount( scaled_amount: Uint128, index: Decimal, scaling_operation: ScalingOperation, -) -> StdResult { +) -> Result { // Multiply scaled amount by decimal (index) let before_scaling_factor = scaled_amount * index; @@ -131,7 +130,8 @@ pub fn compute_underlying_amount( match scaling_operation { ScalingOperation::Truncate => Ok(before_scaling_factor.checked_div(SCALING_FACTOR)?), ScalingOperation::Ceil => { - math::uint128_checked_div_with_ceil(before_scaling_factor, SCALING_FACTOR) + let scaling_factor_dec = Decimal::from_ratio(SCALING_FACTOR, Uint128::one()); + Ok(before_scaling_factor.checked_div_ceil(scaling_factor_dec)?) } } } diff --git a/packages/types/src/error.rs b/packages/types/src/error.rs index 0a2c667db..e9cb63111 100644 --- a/packages/types/src/error.rs +++ b/packages/types/src/error.rs @@ -1,4 +1,6 @@ -use cosmwasm_std::StdError; +use cosmwasm_std::{ + CheckedFromRatioError, CheckedMultiplyFractionError, DivideByZeroError, OverflowError, StdError, +}; use thiserror::Error; #[derive(Error, Debug, PartialEq)] @@ -22,6 +24,18 @@ pub enum MarsError { Deserialize { target_type: String, }, + + #[error("{0}")] + Overflow(#[from] OverflowError), + + #[error("{0}")] + DivideByZero(#[from] DivideByZeroError), + + #[error("{0}")] + CheckedFromRatio(#[from] CheckedFromRatioError), + + #[error("{0}")] + CheckedMultiplyFraction(#[from] CheckedMultiplyFractionError), } impl From for StdError { diff --git a/packages/types/src/red_bank/interest_rate_model.rs b/packages/types/src/red_bank/interest_rate_model.rs index 9e2af3129..d94e1c7e2 100644 --- a/packages/types/src/red_bank/interest_rate_model.rs +++ b/packages/types/src/red_bank/interest_rate_model.rs @@ -1,6 +1,8 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Decimal, StdError, StdResult}; -use mars_utils::{error::ValidationError, helpers::decimal_param_le_one, math}; +use cosmwasm_std::Decimal; +use mars_utils::{error::ValidationError, helpers::decimal_param_le_one}; + +use crate::error::MarsError; #[cw_serde] #[derive(Eq, Default)] @@ -21,28 +23,26 @@ impl InterestRateModel { Ok(()) } - pub fn get_borrow_rate(&self, current_utilization_rate: Decimal) -> StdResult { + pub fn get_borrow_rate(&self, current_utilization_rate: Decimal) -> Result { let new_borrow_rate = if current_utilization_rate <= self.optimal_utilization_rate { if current_utilization_rate.is_zero() { - // prevent division by zero when optimal_utilization_rate is zero + // prevent division by zero when current_utilization_rate is zero self.base } else { // The borrow interest rates increase slowly with utilization self.base - + self.slope_1.checked_mul(math::divide_decimal_by_decimal( - current_utilization_rate, - self.optimal_utilization_rate, - )?)? + + self.slope_1.checked_mul( + current_utilization_rate.checked_div(self.optimal_utilization_rate)?, + )? } } else { // The borrow interest rates increase sharply with utilization self.base + self.slope_1 - + math::divide_decimal_by_decimal( - self.slope_2 - .checked_mul(current_utilization_rate - self.optimal_utilization_rate)?, - Decimal::one() - self.optimal_utilization_rate, - )? + + self + .slope_2 + .checked_mul(current_utilization_rate - self.optimal_utilization_rate)? + .checked_div(Decimal::one() - self.optimal_utilization_rate)? }; Ok(new_borrow_rate) } @@ -52,12 +52,11 @@ impl InterestRateModel { borrow_rate: Decimal, current_utilization_rate: Decimal, reserve_factor: Decimal, - ) -> StdResult { - borrow_rate + ) -> Result { + Ok(borrow_rate .checked_mul(current_utilization_rate)? // This operation should not underflow as reserve_factor is checked to be <= 1 - .checked_mul(Decimal::one() - reserve_factor) - .map_err(StdError::from) + .checked_mul(Decimal::one() - reserve_factor)?) } } @@ -91,21 +90,13 @@ mod tests { market.update_interest_rates(utilization_rate).unwrap(); - let expected_borrow_rate = model.base - + math::divide_decimal_by_decimal( - model.slope_1.checked_mul(utilization_rate).unwrap(), - model.optimal_utilization_rate, - ) - .unwrap(); + let expected_borrow_rate = + model.base + model.slope_1 * utilization_rate / model.optimal_utilization_rate; assert_eq!(market.borrow_rate, expected_borrow_rate); assert_eq!( market.liquidity_rate, - expected_borrow_rate - .checked_mul(utilization_rate) - .unwrap() - .checked_mul(Decimal::one() - reserve_factor) - .unwrap() + expected_borrow_rate * utilization_rate * (Decimal::one() - reserve_factor) ); } @@ -124,11 +115,7 @@ mod tests { let new_borrow_rate = model.get_borrow_rate(current_utilization_rate).unwrap(); let expected_borrow_rate = model.base - + math::divide_decimal_by_decimal( - model.slope_1.checked_mul(current_utilization_rate).unwrap(), - model.optimal_utilization_rate, - ) - .unwrap(); + + model.slope_1 * current_utilization_rate / model.optimal_utilization_rate; assert_eq!(new_borrow_rate, expected_borrow_rate); } @@ -139,11 +126,7 @@ mod tests { let new_borrow_rate = model.get_borrow_rate(current_utilization_rate).unwrap(); let expected_borrow_rate = model.base - + math::divide_decimal_by_decimal( - model.slope_1.checked_mul(current_utilization_rate).unwrap(), - model.optimal_utilization_rate, - ) - .unwrap(); + + model.slope_1 * current_utilization_rate / model.optimal_utilization_rate; assert_eq!(new_borrow_rate, expected_borrow_rate); } @@ -155,14 +138,8 @@ mod tests { let expected_borrow_rate = model.base + model.slope_1 - + math::divide_decimal_by_decimal( - model - .slope_2 - .checked_mul(current_utilization_rate - model.optimal_utilization_rate) - .unwrap(), - Decimal::one() - model.optimal_utilization_rate, - ) - .unwrap(); + + model.slope_2 * (current_utilization_rate - model.optimal_utilization_rate) + / (Decimal::one() - model.optimal_utilization_rate); assert_eq!(new_borrow_rate, expected_borrow_rate); } @@ -179,9 +156,7 @@ mod tests { let current_utilization_rate = Decimal::percent(100); let new_borrow_rate = model.get_borrow_rate(current_utilization_rate).unwrap(); - let expected_borrow_rate = Decimal::percent(7); - - assert_eq!(new_borrow_rate, expected_borrow_rate); + assert_eq!(new_borrow_rate, Decimal::percent(7)); } // current utilization rate == 0% and optimal utilization rate == 0% @@ -196,9 +171,7 @@ mod tests { let current_utilization_rate = Decimal::percent(0); let new_borrow_rate = model.get_borrow_rate(current_utilization_rate).unwrap(); - let expected_borrow_rate = Decimal::percent(2); - - assert_eq!(new_borrow_rate, expected_borrow_rate); + assert_eq!(new_borrow_rate, Decimal::percent(2)); } // current utilization rate == 20% and optimal utilization rate == 0% @@ -213,9 +186,8 @@ mod tests { let current_utilization_rate = Decimal::percent(20); let new_borrow_rate = model.get_borrow_rate(current_utilization_rate).unwrap(); - let expected_borrow_rate = model.base - + model.slope_1 - + model.slope_2.checked_mul(current_utilization_rate).unwrap(); + let expected_borrow_rate = + model.base + model.slope_1 + model.slope_2 * current_utilization_rate; assert_eq!(new_borrow_rate, expected_borrow_rate); } diff --git a/packages/utils/src/lib.rs b/packages/utils/src/lib.rs index cc73f475f..17d5321cc 100644 --- a/packages/utils/src/lib.rs +++ b/packages/utils/src/lib.rs @@ -1,3 +1,2 @@ pub mod error; pub mod helpers; -pub mod math; diff --git a/packages/utils/src/math.rs b/packages/utils/src/math.rs deleted file mode 100644 index 19c9d64ad..000000000 --- a/packages/utils/src/math.rs +++ /dev/null @@ -1,261 +0,0 @@ -use std::convert::TryInto; - -use cosmwasm_std::{ - CheckedFromRatioError, Decimal, Fraction, OverflowError, OverflowOperation, StdError, - StdResult, Uint128, Uint256, -}; - -pub fn uint128_checked_div_with_ceil( - numerator: Uint128, - denominator: Uint128, -) -> StdResult { - let mut result = numerator.checked_div(denominator)?; - - if !numerator.checked_rem(denominator)?.is_zero() { - result += Uint128::from(1_u128); - } - - Ok(result) -} - -/// Divide 'a' by 'b'. -pub fn divide_decimal_by_decimal(a: Decimal, b: Decimal) -> StdResult { - Decimal::checked_from_ratio(a.numerator(), b.numerator()).map_err(|e| match e { - CheckedFromRatioError::Overflow => StdError::overflow(OverflowError { - operation: OverflowOperation::Mul, - operand1: a.numerator().to_string(), - operand2: a.denominator().to_string(), - }), - CheckedFromRatioError::DivideByZero => { - StdError::divide_by_zero(cosmwasm_std::DivideByZeroError { - operand: b.to_string(), - }) - } - }) -} - -/// Divide Uint128 by Decimal. -/// (Uint128 / numerator / denominator) is equal to (Uint128 * denominator / numerator). -pub fn divide_uint128_by_decimal(a: Uint128, b: Decimal) -> StdResult { - // (Uint128 / numerator / denominator) is equal to (Uint128 * denominator / numerator). - let numerator_u256 = a.full_mul(b.denominator()); - let denominator_u256 = Uint256::from(b.numerator()); - - let result_u256 = numerator_u256 / denominator_u256; - - let result = result_u256.try_into()?; - Ok(result) -} - -/// Divide Uint128 by Decimal, rounding up to the nearest integer. -pub fn divide_uint128_by_decimal_and_ceil(a: Uint128, b: Decimal) -> StdResult { - // (Uint128 / numerator / denominator) is equal to (Uint128 * denominator / numerator). - let numerator_u256 = a.full_mul(b.denominator()); - let denominator_u256 = Uint256::from(b.numerator()); - - let mut result_u256 = numerator_u256 / denominator_u256; - - if numerator_u256.checked_rem(denominator_u256)? > Uint256::zero() { - result_u256 += Uint256::from(1_u32); - } - - let result = result_u256.try_into()?; - Ok(result) -} - -/// Multiply Uint128 by Decimal, rounding up to the nearest integer. -pub fn multiply_uint128_by_decimal_and_ceil(a: Uint128, b: Decimal) -> StdResult { - let numerator_u256 = a.full_mul(b.numerator()); - let denominator_u256 = Uint256::from(b.denominator()); - - let mut result_u256 = numerator_u256 / denominator_u256; - - if numerator_u256.checked_rem(denominator_u256)? > Uint256::zero() { - result_u256 += Uint256::from(1_u32); - } - - let result = result_u256.try_into()?; - Ok(result) -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use cosmwasm_std::{ConversionOverflowError, OverflowOperation}; - - use super::*; - - const DECIMAL_FRACTIONAL: Uint128 = Uint128::new(1_000_000_000_000_000_000u128); // 1*10**18 - const DECIMAL_FRACTIONAL_SQUARED: Uint128 = - Uint128::new(1_000_000_000_000_000_000_000_000_000_000_000_000u128); // (1*10**18)**2 = 1*10**36 - - #[test] - fn test_uint128_checked_div_with_ceil() { - let a = Uint128::new(120u128); - let b = Uint128::zero(); - uint128_checked_div_with_ceil(a, b).unwrap_err(); - - let a = Uint128::new(120u128); - let b = Uint128::new(60_u128); - let c = uint128_checked_div_with_ceil(a, b).unwrap(); - assert_eq!(c, Uint128::new(2u128)); - - let a = Uint128::new(120u128); - let b = Uint128::new(119_u128); - let c = uint128_checked_div_with_ceil(a, b).unwrap(); - assert_eq!(c, Uint128::new(2u128)); - - let a = Uint128::new(120u128); - let b = Uint128::new(120_u128); - let c = uint128_checked_div_with_ceil(a, b).unwrap(); - assert_eq!(c, Uint128::new(1u128)); - - let a = Uint128::new(120u128); - let b = Uint128::new(121_u128); - let c = uint128_checked_div_with_ceil(a, b).unwrap(); - assert_eq!(c, Uint128::new(1u128)); - - let a = Uint128::zero(); - let b = Uint128::new(121_u128); - let c = uint128_checked_div_with_ceil(a, b).unwrap(); - assert_eq!(c, Uint128::zero()); - } - - #[test] - fn checked_decimal_division() { - let a = Decimal::from_ratio(99988u128, 100u128); - let b = Decimal::from_ratio(24997u128, 100u128); - let c = divide_decimal_by_decimal(a, b).unwrap(); - assert_eq!(c, Decimal::from_str("4.0").unwrap()); - - let a = Decimal::from_ratio(123456789u128, 1000000u128); - let b = Decimal::from_ratio(33u128, 1u128); - let c = divide_decimal_by_decimal(a, b).unwrap(); - assert_eq!(c, Decimal::from_str("3.741114818181818181").unwrap()); - - let a = Decimal::MAX; - let b = Decimal::MAX; - let c = divide_decimal_by_decimal(a, b).unwrap(); - assert_eq!(c, Decimal::one()); - - // Note: DivideByZeroError is not public so we just check if dividing by zero returns error - let a = Decimal::one(); - let b = Decimal::zero(); - divide_decimal_by_decimal(a, b).unwrap_err(); - - let a = Decimal::MAX; - let b = Decimal::from_ratio(1u128, DECIMAL_FRACTIONAL); - let res_error = divide_decimal_by_decimal(a, b).unwrap_err(); - assert_eq!( - res_error, - OverflowError::new(OverflowOperation::Mul, Uint128::MAX, DECIMAL_FRACTIONAL).into() - ); - } - - #[test] - fn test_divide_uint128_by_decimal() { - let a = Uint128::new(120u128); - let b = Decimal::from_ratio(120u128, 15u128); - let c = divide_uint128_by_decimal(a, b).unwrap(); - assert_eq!(c, Uint128::new(15u128)); - - let a = Uint128::new(DECIMAL_FRACTIONAL.u128()); - let b = Decimal::from_ratio(DECIMAL_FRACTIONAL.u128(), 1u128); - let c = divide_uint128_by_decimal(a, b).unwrap(); - assert_eq!(c, Uint128::new(1u128)); - - let a = Uint128::new(DECIMAL_FRACTIONAL.u128()); - let b = Decimal::from_ratio(1u128, DECIMAL_FRACTIONAL.u128()); - let c = divide_uint128_by_decimal(a, b).unwrap(); - assert_eq!(c, Uint128::new(DECIMAL_FRACTIONAL_SQUARED.u128())); - - let a = Uint128::MAX; - let b = Decimal::one(); - let c = divide_uint128_by_decimal(a, b).unwrap(); - assert_eq!(c, Uint128::MAX); - - let a = Uint128::new(1_000_000_000_000_000_000); - let b = Decimal::from_ratio(1u128, DECIMAL_FRACTIONAL); - let c = divide_uint128_by_decimal(a, b).unwrap(); - assert_eq!(c, Uint128::new(1_000_000_000_000_000_000_000_000_000_000_000_000)); - - // Division is truncated - let a = Uint128::new(100); - let b = Decimal::from_ratio(3u128, 1u128); - let c = divide_uint128_by_decimal(a, b).unwrap(); - assert_eq!(c, Uint128::new(33)); - - let a = Uint128::new(75); - let b = Decimal::from_ratio(100u128, 1u128); - let c = divide_uint128_by_decimal(a, b).unwrap(); - assert_eq!(c, Uint128::new(0)); - - // Overflow - let a = Uint128::MAX; - let b = Decimal::from_ratio(1_u128, 10_u128); - let res_error = divide_uint128_by_decimal(a, b).unwrap_err(); - assert_eq!( - res_error, - ConversionOverflowError::new( - "Uint256", - "Uint128", - "3402823669209384634633746074317682114550" - ) - .into() - ); - } - - #[test] - fn test_divide_uint128_by_decimal_and_ceil() { - let a = Uint128::new(120u128); - let b = Decimal::from_ratio(120u128, 15u128); - let c = divide_uint128_by_decimal_and_ceil(a, b).unwrap(); - assert_eq!(c, Uint128::new(15u128)); - - let a = Uint128::new(DECIMAL_FRACTIONAL.u128()); - let b = Decimal::from_ratio(DECIMAL_FRACTIONAL.u128(), 1u128); - let c = divide_uint128_by_decimal_and_ceil(a, b).unwrap(); - assert_eq!(c, Uint128::new(1u128)); - - let a = Uint128::new(DECIMAL_FRACTIONAL.u128()); - let b = Decimal::from_ratio(1u128, DECIMAL_FRACTIONAL.u128()); - let c = divide_uint128_by_decimal_and_ceil(a, b).unwrap(); - assert_eq!(c, Uint128::new(DECIMAL_FRACTIONAL_SQUARED.u128())); - - let a = Uint128::MAX; - let b = Decimal::one(); - let c = divide_uint128_by_decimal_and_ceil(a, b).unwrap(); - assert_eq!(c, Uint128::MAX); - - let a = Uint128::new(1_000_000_000_000_000_000); - let b = Decimal::from_ratio(1u128, DECIMAL_FRACTIONAL); - let c = divide_uint128_by_decimal_and_ceil(a, b).unwrap(); - assert_eq!(c, Uint128::new(1_000_000_000_000_000_000_000_000_000_000_000_000)); - - // Division is rounded up - let a = Uint128::new(100); - let b = Decimal::from_ratio(3u128, 1u128); - let c = divide_uint128_by_decimal_and_ceil(a, b).unwrap(); - assert_eq!(c, Uint128::new(34)); - - let a = Uint128::new(75); - let b = Decimal::from_ratio(100u128, 1u128); - let c = divide_uint128_by_decimal_and_ceil(a, b).unwrap(); - assert_eq!(c, Uint128::new(1)); - - // Overflow - let a = Uint128::MAX; - let b = Decimal::from_ratio(1_u128, 10_u128); - let res_error = divide_uint128_by_decimal_and_ceil(a, b).unwrap_err(); - assert_eq!( - res_error, - ConversionOverflowError::new( - "Uint256", - "Uint128", - "3402823669209384634633746074317682114550" - ) - .into() - ); - } -}