Skip to content

Commit

Permalink
changing-mint-maths
Browse files Browse the repository at this point in the history
  • Loading branch information
crypto-vincent committed Oct 22, 2023
1 parent 911446f commit 4afe6a1
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ use super::compute_amount_less_fraction_floor;
pub struct DepositoryInfoForMintCollateralAmount {
pub directly_mintable: bool,
pub target_redeemable_amount: u64,
pub redeemable_amount_under_management: u128,
pub redeemable_amount_under_management: u64,
pub redeemable_amount_under_management_cap: u64,
}

pub fn calculate_depositories_mint_collateral_amount(
requested_mint_collateral_amount: u64,
requested_collateral_amount: u64,
depositories_info: &Vec<DepositoryInfoForMintCollateralAmount>,
) -> Result<Vec<u64>> {
require!(
Expand All @@ -25,61 +26,127 @@ pub fn calculate_depositories_mint_collateral_amount(

// ---------------------------------------------------------------------
// -- Phase 1
// -- Calculate the maximum mintable collateral amount for each depository
// -- Calculate the under_target redeemable amount for each depository
// -- This amount is what we can mint into first, so that all depositories remain balanced
// ---------------------------------------------------------------------

let depositories_maximum_mintable_collateral_amount = depositories_info
let depositories_under_target_redeemable_amount = depositories_info
.iter()
.map(|depository| {
if !depository.directly_mintable {
return Ok(0);
}
let depository_redeemable_amount_under_management =
checked_as_u64(depository.redeemable_amount_under_management)?;
if depository.target_redeemable_amount <= depository_redeemable_amount_under_management
Ok(std::cmp::min(
depository_redeemable_amount_under_management,
depository.target_redeemable_amount,
))
})
.collect::<Result<Vec<u64>>>()?;

let total_under_target_redeemable_amount =
calculate_depositories_sum_value(&depositories_under_target_redeemable_amount)?;

// ---------------------------------------------------------------------
// -- Phase 2
// -- Calculate the under cap redeemable amount for each depository
// -- This is the amount of redeemable past the target and under the hard-cap of the depository
// -- This amount will be used as a last-ditch effort to fullfull the mint
// -- In case all mintable depositories are filled up to their target
// ---------------------------------------------------------------------

let depositories_under_cap_redeemable_amount = depositories_info
.iter()
.map(|depository| {
if !depository.directly_mintable {
return Ok(0);
}
if depository.redeemable_amount_under_management_cap
<= depository.target_redeemable_amount
{
return Ok(0);
}
Ok(depository
.target_redeemable_amount
.checked_sub(depository_redeemable_amount_under_management)
.redeemable_amount_under_management_cap
.checked_sub(depository.target_redeemable_amount)
.ok_or(UxdError::MathOverflow)?)
})
.collect::<Result<Vec<u64>>>()?;

let total_under_cap_redeemable_amount =
calculate_depositories_sum_value(&depositories_under_cap_redeemable_amount)?;

// ---------------------------------------------------------------------
// -- Phase 2
// -- Calculate the total amount we could possibly mint
// -- If this total is not enough, we abort
// -- Phase 3
// -- Check that we have enough available space in our depositories
// -- To be able to fullfill the user's mint requested amount
// ---------------------------------------------------------------------

let total_maximum_mintable_collateral_amount =
calculate_depositories_sum_value(&depositories_maximum_mintable_collateral_amount)?;
let total_overall_mintable_amount = total_under_target_redeemable_amount
.checked_add(total_under_cap_redeemable_amount)
.ok_or(UxdError::MathOverflow)?;
require!(
total_maximum_mintable_collateral_amount >= requested_mint_collateral_amount,
total_overall_mintable_amount >= requested_collateral_amount,
UxdError::DepositoriesTargerRedeemableAmountReached
);

// ---------------------------------------------------------------------
// -- Phase 3
// -- Calculate the actual minted amount per depository for the requested mint amount,
// -- it is a weighted slice of the total mintable amount, scaled by the requested mint amount
// -- Phase 4
// -- Compute the final amounts by:
// -- trying as a best-effort to keep the balance (step1)
// -- And when keeping the perfect balance is not possible,
// -- try to consume the under_cap amount fairly (step2)
// ---------------------------------------------------------------------

let depositories_mint_collateral_amount = depositories_maximum_mintable_collateral_amount
.iter()
.map(|depository_maximum_mintable_collateral_amount| {
let other_depositories_maximum_mintable_collateral_amount =
total_maximum_mintable_collateral_amount
.checked_sub(*depository_maximum_mintable_collateral_amount)
.ok_or(UxdError::MathOverflow)?;
compute_amount_less_fraction_floor(
requested_mint_collateral_amount,
other_depositories_maximum_mintable_collateral_amount,
total_maximum_mintable_collateral_amount,
)
})
.collect::<Result<Vec<u64>>>()?;
let depositories_mint_collateral_amount = std::iter::zip(
depositories_under_target_redeemable_amount.iter(),
depositories_under_cap_redeemable_amount.iter(),
)
.map(
|(depository_under_target_redeemable_amount, depository_under_cap_redeemable_amount)| {
// Step 1, try to mint any under_target amount to fill the depository to its target
let requested_primary_collateral_amount = std::cmp::min(
requested_collateral_amount,
total_under_target_redeemable_amount,
);
let depository_primary_collateral_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)?;
compute_amount_less_fraction_floor(
requested_primary_collateral_amount,
other_depositories_under_target_redeemable_amount,
total_under_target_redeemable_amount,
)?
} else {
0
};
// Step 2, when all depositories are to their targets, try to fill up to their caps fairly
let requested_backup_collateral_amount = requested_collateral_amount
.checked_sub(requested_primary_collateral_amount)
.ok_or(UxdError::MathOverflow)?;
let depository_backup_collateral_amount = if total_under_cap_redeemable_amount > 0 {
let other_depositories_under_cap_redeemable_amount =
total_under_cap_redeemable_amount
.checked_sub(*depository_under_cap_redeemable_amount)
.ok_or(UxdError::MathOverflow)?;
compute_amount_less_fraction_floor(
requested_backup_collateral_amount,
other_depositories_under_cap_redeemable_amount,
total_under_cap_redeemable_amount,
)?
} else {
0
};
// The combo of the two gives our depository amount
Ok(requested_primary_collateral_amount
.checked_add(requested_backup_collateral_amount)
.ok_or(UxdError::MathOverflow)?)
},
)
.collect::<Result<Vec<u64>>>()?;

// Done
Ok(depositories_mint_collateral_amount)
Expand Down
27 changes: 13 additions & 14 deletions programs/uxd/src/utils/calculate_depositories_redeemable_amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub fn calculate_depositories_redeemable_amount(
// -- Phase 2
// -- Calculate the under_target redeemable amount for each depository
// -- This amount is what remains redeemable after we redeemed the over_target amount
// -- This amount will be used as a last-ditch effort to fullfull the redeem when needed
// -- This amount will be used as a last-ditch effort to fullfill the redeem when needed
// ---------------------------------------------------------------------

let depositories_under_target_redeemable_amount = depositories_info
Expand Down Expand Up @@ -104,45 +104,44 @@ pub fn calculate_depositories_redeemable_amount(
)
.map(
|(depository_over_target_redeemable_amount, depository_under_target_redeemable_amount)| {
// Total possible redeemable amounts for both steps
let requested_first_redeemable_amount = std::cmp::min(
// Step 1, try to use the over_target amounts, weighted for each depository
let requested_primary_redeemable_amount = std::cmp::min(
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)?;
// 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 depository_primary_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)?;
compute_amount_less_fraction_floor(
requested_first_redeemable_amount,
requested_primary_redeemable_amount,
other_depositories_over_target_redeemable_amount,
total_over_target_redeemable_amount,
)?
} else {
0
};
// Second step, anything under_target must be taken as backup
let depository_second_redeemable_amount = if total_under_target_redeemable_amount > 0 {
// Step 2, anything under_target must be used as backup
let requested_backup_redeemable_amount = requested_redeemable_amount
.checked_sub(requested_primary_redeemable_amount)
.ok_or(UxdError::MathOverflow)?;
let depository_backup_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)?;
compute_amount_less_fraction_floor(
requested_second_redeemable_amount,
requested_backup_redeemable_amount,
other_depositories_under_target_redeemable_amount,
total_under_target_redeemable_amount,
)?
} else {
0
};
// The combo of the two gives our depository amount
Ok(depository_first_redeemable_amount
.checked_add(depository_second_redeemable_amount)
Ok(depository_primary_redeemable_amount
.checked_add(depository_backup_redeemable_amount)
.ok_or(UxdError::MathOverflow)?)
},
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Keypair;

use uxd::instructions::EditAlloyxVaultDepositoryFields;
use uxd::instructions::EditCredixLpDepositoryFields;
use uxd::instructions::EditIdentityDepositoryFields;
use uxd::instructions::EditMercurialVaultDepositoryFields;
use uxd::instructions::EditAlloyxVaultDepositoryFields;

use crate::integration_tests::api::program_context;
use crate::integration_tests::api::program_uxd;
Expand Down
2 changes: 1 addition & 1 deletion programs/uxd/tests/integration_tests/suites/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub mod test_credix_lp_depository_rebalance_illiquid;
pub mod test_credix_lp_depository_rebalance_liquid;
pub mod test_credix_lp_depository_rebalance_no_overflow;
pub mod test_credix_lp_depository_rebalance_under_requested;
pub mod test_ensure_devnet;
//pub mod test_ensure_devnet;
pub mod test_identity_depository_edit;
pub mod test_identity_depository_mint_and_redeem;
pub mod test_mercurial_vault_depository_edit;
Expand Down

0 comments on commit 4afe6a1

Please sign in to comment.