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

[Maths] Use new maths functions in utilities #305

Merged
merged 8 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 2 additions & 3 deletions programs/uxd/src/mercurial_utils/calculate_lp_tokens_value.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::error::UxdError;
use crate::utils::checked_as_u64;
use anchor_lang::{
prelude::{Account, Clock, SolanaSysvar},
Result,
Expand All @@ -10,9 +11,7 @@ pub fn calculate_lp_tokens_value(
mercurial_vault_lp_mint_supply: u64,
lp_token_amount: u64,
) -> Result<u64> {
let current_time = u64::try_from(Clock::get()?.unix_timestamp)
.ok()
.ok_or(UxdError::MathOverflow)?;
let current_time = checked_as_u64(Clock::get()?.unix_timestamp)?;

Ok(mercurial_vault
.get_amount_by_share(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::{error::UxdError, utils::compute_value_for_single_share_ceil};
use crate::error::UxdError;
use crate::utils::checked_as_u64;
use crate::utils::compute_value_for_single_share_ceil;
use anchor_lang::{
prelude::{Account, Clock, SolanaSysvar},
Result,
Expand All @@ -11,9 +13,7 @@ pub fn calculate_possible_lp_token_precision_loss_collateral_value(
mercurial_vault: &Account<Vault>,
mercurial_vault_lp_mint_supply: u64,
) -> Result<u64> {
let current_time = u64::try_from(Clock::get()?.unix_timestamp)
.ok()
.ok_or(UxdError::MathOverflow)?;
let current_time = checked_as_u64(Clock::get()?.unix_timestamp)?;

// Calculate the price of 1 native LP token
// Do not use mercurial_vault.get_amount_by_share because it does not handle precision loss
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use anchor_lang::require;
use crate::error::UxdError;
use crate::utils::calculate_depositories_sum_value;
use crate::utils::checked_as_u64;
use crate::utils::checked_sub;
use crate::ROUTER_DEPOSITORIES_COUNT;

use super::compute_amount_less_fraction_floor;
Expand Down Expand Up @@ -36,10 +37,10 @@ pub fn calculate_depositories_mint_collateral_amount(
{
return Ok(0);
}
Ok(depository
.target_redeemable_amount
.checked_sub(depository_redeemable_amount_under_management)
.ok_or(UxdError::MathOverflow)?)
checked_sub(
depository.target_redeemable_amount,
depository_redeemable_amount_under_management,
)
})
.collect::<Result<Vec<u64>>>()?;

Expand All @@ -65,10 +66,10 @@ pub fn calculate_depositories_mint_collateral_amount(
let depositories_mint_collateral_amount = depositories_maximum_mintable_collateral_amount
.iter()
.map(|depository_mintable_collateral_amount| {
let other_depositories_maximum_mintable_collateral_amount =
total_maximum_mintable_collateral_amount
.checked_sub(*depository_mintable_collateral_amount)
.ok_or(UxdError::MathOverflow)?;
let other_depositories_maximum_mintable_collateral_amount = checked_sub(
total_maximum_mintable_collateral_amount,
*depository_mintable_collateral_amount,
)?;
compute_amount_less_fraction_floor(
requested_mint_collateral_amount,
other_depositories_maximum_mintable_collateral_amount,
Expand Down
69 changes: 36 additions & 33 deletions programs/uxd/src/utils/calculate_depositories_redeemable_amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use anchor_lang::require;

use crate::error::UxdError;
use crate::utils::calculate_depositories_sum_value;
use crate::utils::checked_add;
use crate::utils::checked_as_u64;
use crate::utils::checked_sub;
use crate::ROUTER_DEPOSITORIES_COUNT;

use super::compute_amount_less_fraction_floor;
Expand Down Expand Up @@ -42,9 +44,10 @@ pub fn calculate_depositories_redeemable_amount(
{
return Ok(0);
}
Ok(depository_redeemable_amount_under_management
.checked_sub(depository.target_redeemable_amount)
.ok_or(UxdError::MathOverflow)?)
checked_sub(
depository_redeemable_amount_under_management,
depository.target_redeemable_amount,
)
})
.collect::<Result<Vec<u64>>>()?;

Expand Down Expand Up @@ -82,9 +85,10 @@ pub fn calculate_depositories_redeemable_amount(
// -- to be able to fullfill the user's redeemable requested amount
// ---------------------------------------------------------------------

let total_overall_redeemable_amount = total_over_target_redeemable_amount
.checked_add(total_under_target_redeemable_amount)
.ok_or(UxdError::MathOverflow)?;
let total_overall_redeemable_amount = checked_add(
total_over_target_redeemable_amount,
total_under_target_redeemable_amount,
)?;
require!(
total_overall_redeemable_amount >= requested_redeemable_amount,
UxdError::InvalidRedeemableAmount
Expand All @@ -109,15 +113,16 @@ pub fn calculate_depositories_redeemable_amount(
requested_redeemable_amount,
total_over_target_redeemable_amount,
);
let requested_second_redeemable_amount = requested_redeemable_amount
.checked_sub(requested_first_redeemable_amount)
.ok_or(UxdError::MathOverflow)?;
let requested_second_redeemable_amount = checked_sub(
requested_redeemable_amount,
requested_first_redeemable_amount,
)?;
// First step, try to use the over_target amounts, weighted for each depository
let depository_first_redeemable_amount = if total_over_target_redeemable_amount > 0 {
let other_depositories_over_target_redeemable_amount =
total_over_target_redeemable_amount
.checked_sub(*depository_over_target_redeemable_amount)
.ok_or(UxdError::MathOverflow)?;
let other_depositories_over_target_redeemable_amount = checked_sub(
total_over_target_redeemable_amount,
*depository_over_target_redeemable_amount,
)?;
compute_amount_less_fraction_floor(
requested_first_redeemable_amount,
other_depositories_over_target_redeemable_amount,
Expand All @@ -128,10 +133,10 @@ pub fn calculate_depositories_redeemable_amount(
};
// Second step, anything under_target must be taken as backup
let depository_second_redeemable_amount = if total_under_target_redeemable_amount > 0 {
let other_depositories_under_target_redeemable_amount =
total_under_target_redeemable_amount
.checked_sub(*depository_under_target_redeemable_amount)
.ok_or(UxdError::MathOverflow)?;
let other_depositories_under_target_redeemable_amount = checked_sub(
total_under_target_redeemable_amount,
*depository_under_target_redeemable_amount,
)?;
compute_amount_less_fraction_floor(
requested_second_redeemable_amount,
other_depositories_under_target_redeemable_amount,
Expand All @@ -141,9 +146,10 @@ pub fn calculate_depositories_redeemable_amount(
0
};
// The combo of the two gives our depository amount
Ok(depository_first_redeemable_amount
.checked_add(depository_second_redeemable_amount)
.ok_or(UxdError::MathOverflow)?)
checked_add(
depository_first_redeemable_amount,
depository_second_redeemable_amount,
)
},
)
.collect::<Result<Vec<u64>>>()?;
Expand All @@ -158,27 +164,24 @@ pub fn calculate_depositories_redeemable_amount(
let total_redeemable_amount =
calculate_depositories_sum_value(&depositories_redeemable_amount)?;

let mut rounding_errors = requested_redeemable_amount
.checked_sub(total_redeemable_amount)
.ok_or(UxdError::MathOverflow)?;
let mut rounding_errors = checked_sub(requested_redeemable_amount, total_redeemable_amount)?;

for i in 0..depositories_info.len() {
let depository = &depositories_info[i];
if !depository.is_liquid {
continue;
}
let depository_remaining_after_redeem =
checked_as_u64(depository.redeemable_amount_under_management)?
.checked_sub(depositories_redeemable_amount[i])
.ok_or(UxdError::MathOverflow)?;
let depository_remaining_after_redeem = checked_sub(
checked_as_u64(depository.redeemable_amount_under_management)?,
depositories_redeemable_amount[i],
)?;
let depository_rounding_correction =
std::cmp::min(depository_remaining_after_redeem, rounding_errors);
depositories_redeemable_amount[i] = depositories_redeemable_amount[i]
.checked_add(depository_rounding_correction)
.ok_or(UxdError::MathOverflow)?;
rounding_errors = rounding_errors
.checked_sub(depository_rounding_correction)
.ok_or(UxdError::MathOverflow)?;
depositories_redeemable_amount[i] = checked_add(
depositories_redeemable_amount[i],
depository_rounding_correction,
)?;
rounding_errors = checked_sub(rounding_errors, depository_rounding_correction)?;
}

// Done
Expand Down
5 changes: 2 additions & 3 deletions programs/uxd/src/utils/calculate_depositories_sum_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use anchor_lang::prelude::Result;
use anchor_lang::require;

use crate::error::UxdError;
use crate::utils::checked_add;
use crate::ROUTER_DEPOSITORIES_COUNT;

/**
Expand All @@ -15,9 +16,7 @@ pub fn calculate_depositories_sum_value(depositories_values: &Vec<u64>) -> Resul
let sum = depositories_values
.iter()
.try_fold(0u64, |accumulator: u64, value: &u64| {
accumulator
.checked_add(*value)
.ok_or(UxdError::MathOverflow)
checked_add(accumulator, *value)
})?;
Ok(sum)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use anchor_lang::require;

use crate::error::UxdError;
use crate::utils::calculate_depositories_sum_value;
use crate::utils::checked_add;
use crate::utils::checked_as_u64;
use crate::utils::checked_sub;
use crate::BPS_POWER;
use crate::ROUTER_DEPOSITORIES_COUNT;

Expand Down Expand Up @@ -76,9 +78,10 @@ pub fn calculate_depositories_target_redeemable_amount(
if depository_raw_target_redeemable_amount <= depository_hard_cap_amount {
return Ok(0);
}
Ok(depository_raw_target_redeemable_amount
.checked_sub(*depository_hard_cap_amount)
.ok_or(UxdError::MathOverflow)?)
checked_sub(
*depository_raw_target_redeemable_amount,
*depository_hard_cap_amount,
)
},
)
.collect::<Result<Vec<u64>>>()?;
Expand All @@ -93,9 +96,10 @@ pub fn calculate_depositories_target_redeemable_amount(
if depository_raw_target_redeemable_amount >= depository_hard_cap_amount {
return Ok(0);
}
Ok(depository_hard_cap_amount
.checked_sub(*depository_raw_target_redeemable_amount)
.ok_or(UxdError::MathOverflow)?)
checked_sub(
*depository_hard_cap_amount,
*depository_raw_target_redeemable_amount,
)
},
)
.collect::<Result<Vec<u64>>>()?;
Expand Down Expand Up @@ -150,11 +154,13 @@ pub fn calculate_depositories_target_redeemable_amount(
} else {
0
};
let final_target = depository_raw_target_redeemable_amount
.checked_add(overflow_amount_reallocated_from_other_depositories)
.ok_or(UxdError::MathOverflow)?
.checked_sub(*depository_overflow_amount)
.ok_or(UxdError::MathOverflow)?;
let final_target = checked_sub(
checked_add(
*depository_raw_target_redeemable_amount,
overflow_amount_reallocated_from_other_depositories,
)?,
*depository_overflow_amount,
)?;
Ok(final_target)
},
)
Expand Down
25 changes: 8 additions & 17 deletions programs/uxd/src/utils/math/checked_add_u128_and_i128.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,21 @@
use crate::error::UxdError;
use crate::utils::checked_add;
use crate::utils::checked_as_u128;
use crate::utils::checked_sub;
use anchor_lang::prelude::*;

pub fn checked_add_u128_and_i128(value_before: u128, change_delta: i128) -> Result<u128> {
// In case of a simple positive change (increase), add the two positive values
if change_delta >= 0 {
let increase: u128 = u128::try_from(change_delta)
.ok()
.ok_or(UxdError::MathOverflow)?;
return Ok(value_before
.checked_add(increase)
.ok_or(UxdError::MathOverflow)?);
let increase: u128 = checked_as_u128(change_delta)?;
return checked_add(value_before, increase);
}
// In case of a negative change, substract the absolute value of the delta (decrease)
let decrease: u128 = if change_delta == i128::MIN {
// special case: i128::MIN does not have an i128 absolute value
u128::try_from(i128::MAX)
.ok()
.ok_or(UxdError::MathOverflow)?
.checked_add(1)
.ok_or(UxdError::MathOverflow)?
checked_add(checked_as_u128(i128::MAX)?, 1)?
} else {
u128::try_from(change_delta.checked_abs().ok_or(UxdError::MathOverflow)?)
.ok()
.ok_or(UxdError::MathOverflow)?
checked_as_u128(change_delta.checked_abs().ok_or(UxdError::MathOverflow)?)?
};
Ok(value_before
.checked_sub(decrease)
.ok_or(UxdError::MathOverflow)?)
checked_sub(value_before, decrease)
}
23 changes: 8 additions & 15 deletions programs/uxd/src/utils/math/compute_amount_fraction_ceil.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::error::UxdError;
use crate::utils::checked_as_u64;
use crate::utils::checked_ceil_div;
use crate::utils::checked_mul;
use anchor_lang::prelude::*;

// Rounding error may increase the returned amount.
Expand All @@ -12,19 +15,9 @@ pub fn compute_amount_fraction_ceil(
if fraction_numerator == 0 || amount == 0 {
return Ok(0);
}
let amount: u128 = amount.into();
let fraction_numerator: u128 = fraction_numerator.into();
let fraction_denominator: u128 = fraction_denominator.into();
let amount_fraction_ceil: u128 = amount
.checked_mul(fraction_numerator)
crypto-vincent marked this conversation as resolved.
Show resolved Hide resolved
.ok_or(UxdError::MathOverflow)?
.checked_sub(1)
.ok_or(UxdError::MathOverflow)?
.checked_div(fraction_denominator)
.ok_or(UxdError::MathOverflow)?
.checked_add(1)
.ok_or(UxdError::MathOverflow)?;
Ok(u64::try_from(amount_fraction_ceil)
.ok()
.ok_or(UxdError::MathOverflow)?)
let amount_fraction_ceil: u128 = checked_ceil_div::<u128>(
checked_mul::<u128>(u128::from(amount), u128::from(fraction_numerator))?,
u128::from(fraction_denominator),
)?;
checked_as_u64(amount_fraction_ceil)
}
34 changes: 19 additions & 15 deletions programs/uxd/src/utils/math/compute_amount_less_fraction_floor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::error::UxdError;
use crate::utils::checked_as_u64;
use crate::utils::checked_div;
use crate::utils::checked_mul;
use crate::utils::checked_sub;
use anchor_lang::prelude::*;

// Precision loss may lower the returned amount.
Expand All @@ -9,19 +13,19 @@ pub fn compute_amount_less_fraction_floor(
fraction_denominator: u64,
) -> Result<u64> {
require!(fraction_denominator > 0, UxdError::MathOverflow);
let amount: u128 = amount.into();
let fraction_numerator: u128 = fraction_numerator.into();
let fraction_denominator: u128 = fraction_denominator.into();
let amount_less_fraction: u128 = amount
.checked_mul(
fraction_denominator
.checked_sub(fraction_numerator)
.ok_or(UxdError::MathOverflow)?,
)
.ok_or(UxdError::MathOverflow)?
.checked_div(fraction_denominator)
.ok_or(UxdError::MathOverflow)?;
Ok(u64::try_from(amount_less_fraction)
.ok()
.ok_or(UxdError::MathOverflow)?)
require!(
fraction_denominator >= fraction_numerator,
UxdError::MathOverflow
);
let amount_less_fraction: u128 = checked_div::<u128>(
checked_mul::<u128>(
u128::from(amount),
checked_sub::<u128>(
u128::from(fraction_denominator),
u128::from(fraction_numerator),
)?,
)?,
u128::from(fraction_denominator),
)?;
checked_as_u64(amount_less_fraction)
}
Loading
Loading