Skip to content

Commit

Permalink
fix: move premium redeem to vault registry
Browse files Browse the repository at this point in the history
  • Loading branch information
nakul1010 committed Dec 6, 2023
1 parent c6e1948 commit 652d338
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 65 deletions.
29 changes: 5 additions & 24 deletions crates/redeem/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@ pub(crate) mod vault_registry {
use crate::DefaultVaultId;
use currency::Amount;
use frame_support::dispatch::{DispatchError, DispatchResult};
use primitives::VaultCurrencyPair;
use vault_registry::types::{CurrencyId, CurrencySource, DefaultVault, UnsignedFixedPoint};
use vault_registry::types::{CurrencyId, CurrencySource, DefaultVault};

pub fn get_backing_collateral<T: crate::Config>(vault_id: &DefaultVaultId<T>) -> Result<Amount<T>, DispatchError> {
<vault_registry::Pallet<T>>::get_backing_collateral(vault_id)
pub fn max_premium_for_vault<T: crate::Config>(
vault_id: &DefaultVaultId<T>,
) -> Result<Amount<T>, DispatchError> {
<vault_registry::Pallet<T>>::get_vault_max_premium_redeem(vault_id)
}

pub fn get_liquidated_collateral<T: crate::Config>(
Expand Down Expand Up @@ -81,12 +82,6 @@ pub(crate) mod vault_registry {
<vault_registry::Pallet<T>>::get_vault_from_id(vault_id)
}

pub fn vault_to_be_backed_tokens<T: crate::Config>(
vault_id: &DefaultVaultId<T>,
) -> Result<Amount<T>, DispatchError> {
<vault_registry::Pallet<T>>::vault_to_be_backed_tokens(vault_id)
}

pub fn try_increase_to_be_redeemed_tokens<T: crate::Config>(
vault_id: &DefaultVaultId<T>,
amount: &Amount<T>,
Expand Down Expand Up @@ -178,20 +173,6 @@ pub(crate) mod vault_registry {
) -> Result<(Amount<T>, Amount<T>), DispatchError> {
<vault_registry::Pallet<T>>::decrease_to_be_replaced_tokens(vault_id, tokens)
}

pub fn get_global_secure_threshold<T: crate::Config>(
currency_pair: &VaultCurrencyPair<CurrencyId<T>>,
) -> Result<UnsignedFixedPoint<T>, DispatchError> {
<vault_registry::Pallet<T>>::get_global_secure_threshold(currency_pair)
}

pub fn required_collateral<T: crate::Config>(
vault_id: &DefaultVaultId<T>,
to_be_backed_tokens: &Amount<T>,
secure_threshold: UnsignedFixedPoint<T>,
) -> Result<Amount<T>, DispatchError> {
<vault_registry::Pallet<T>>::required_collateral(vault_id, to_be_backed_tokens, secure_threshold)
}
}

#[cfg_attr(test, mockable)]
Expand Down
36 changes: 1 addition & 35 deletions crates/redeem/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ use frame_system::{ensure_root, ensure_signed};
use oracle::OracleKey;
use sp_core::H256;
use sp_runtime::{
traits::{CheckedDiv, CheckedSub},
ArithmeticError, FixedPointNumber,
};
use sp_std::{convert::TryInto, vec::Vec};
Expand Down Expand Up @@ -508,46 +507,13 @@ impl<T: Config> Pallet<T> {
let below_premium_redeem = ext::vault_registry::is_vault_below_premium_threshold::<T>(&vault_id)?;
let currency_id = vault_id.collateral_currency();

// Still ToDos: change rpc, comments,
let premium_collateral = if below_premium_redeem {
// The goal of premium redeems is to get the vault back the a healthy collateralization ratio. As such,
// we only award a premium for the amount of tokens required to get the vault back to secure threshold.

// The CollateralizationRate is defined as `totalCollateral / convertToCollateral(totalTokens)`
// When paying a premium, the collateralization rate gets updated according to the following formula:
// `NewCollateralization = (oldCol - awardedPremium) / ( oldTokens*EXCH - awardedPremium/FEE)`

// To calculate the maximum premium we are willing to pay, we set the newCollateralization to
// the secure threshold, which gives:
// `SECURE = (oldCol - awardedPremium) / (oldTokens*EXCH - awardedPremium/FEE)``
// We can rewrite this formula to calculate the `premium` amount that would get us to the secure
// threshold: `maxPremium = (oldTokens * EXCH * SECURE - oldCol) * (FEE / (SECURE -
// FEE))` Which can be interpreted as:
// `maxPremium = missingCollateral * (FEE / (SECURE - FEE))

// Note that to prevent repeated premium redeems while waiting for execution, we use to_be_backed_tokens
// for `oldCol`, which takes into account pending issues and redeems

let to_be_backed_tokens = ext::vault_registry::vault_to_be_backed_tokens(&vault_id)?;
let global_secure_threshold = ext::vault_registry::get_global_secure_threshold::<T>(&vault_id.currencies)?;
let premium_redeem_rate = ext::fee::premium_redeem_reward_rate::<T>();
let required_collateral =
ext::vault_registry::required_collateral(&vault_id, &to_be_backed_tokens, global_secure_threshold)?;
let current_collateral = ext::vault_registry::get_backing_collateral::<T>(&vault_id)?;
let missing_collateral = required_collateral.checked_sub(&current_collateral)?;
let factor = premium_redeem_rate
.checked_div(
&global_secure_threshold
.checked_sub(&premium_redeem_rate)
.ok_or(ArithmeticError::Underflow)?,
)
.ok_or(ArithmeticError::DivisionByZero)?;
let max_premium = missing_collateral.checked_mul(&factor)?;

let redeem_amount_wrapped_in_collateral = user_to_be_received_btc.convert_to(currency_id)?;
let premium_for_redeem_amount = redeem_amount_wrapped_in_collateral
.checked_rounded_mul(&premium_redeem_rate, Rounding::NearestPrefUp)?;

let max_premium = ext::vault_registry::max_premium_for_vault(&vault_id)?;
max_premium.min(&premium_for_redeem_amount)?
} else {
Amount::zero(currency_id)
Expand Down
6 changes: 6 additions & 0 deletions crates/vault-registry/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,15 @@ pub(crate) mod capacity {
#[cfg_attr(test, mockable)]
pub(crate) mod fee {
use crate::DefaultVaultId;
use fee::types::UnsignedFixedPoint;
use frame_support::dispatch::DispatchResult;

pub fn distribute_all_vault_rewards<T: crate::Config>(vault_id: &DefaultVaultId<T>) -> DispatchResult {
<fee::Pallet<T>>::distribute_all_vault_rewards(vault_id)
}

pub fn premium_redeem_reward_rate<T: crate::Config>() -> UnsignedFixedPoint<T> {
<fee::Pallet<T>>::premium_redeem_reward_rate()
}

}
59 changes: 53 additions & 6 deletions crates/vault-registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,54 @@ impl<T: Config> Pallet<T> {
ext::staking::total_current_stake::<T>(vault_id)
}

/// Calculate the maximum premium that can be given by a vault.
///
/// # Arguments
/// * `vault_id` - The identifier of the vault for which the maximum premium is being calculated.
///
/// # Returns
/// Returns a `Result` containing the calculated maximum premium as an `Amount<T>`.
pub fn get_vault_max_premium_redeem(vault_id: &DefaultVaultId<T>) -> Result<Amount<T>, DispatchError> {
// The goal of premium redeems is to get the vault back the a healthy collateralization ratio. As such,
// we only award a premium for the amount of tokens required to get the vault back to secure threshold.

// The CollateralizationRate is defined as `totalCollateral / convertToCollateral(totalTokens)`
// When paying a premium, the collateralization rate gets updated according to the following formula:
// `NewCollateralization = (oldCol - awardedPremium) / ( oldTokens*EXCH - awardedPremium/FEE)`

// To calculate the maximum premium we are willing to pay, we set the newCollateralization to
// the global secure threshold, which gives:
// `SECURE = (oldCol - awardedPremium) / (oldTokens*EXCH - awardedPremium/FEE)``
// We can rewrite this formula to calculate the `premium` amount that would get us to the secure
// threshold: `maxPremium = (oldTokens * EXCH * SECURE - oldCol) * (FEE / (SECURE -
// FEE))` Which can be interpreted as:
// `maxPremium = missingCollateral * (FEE / (SECURE - FEE))

// Note that to prevent repeated premium redeems while waiting for execution, we use to_be_backed_tokens
// for `oldCol`, which takes into account pending issues and redeems
let to_be_backed_tokens = Self::vault_to_be_backed_tokens(&vault_id)?;
let global_secure_threshold = Self::get_global_secure_threshold(&vault_id.currencies)?;
let mut premium_redeem_rate = ext::fee::premium_redeem_reward_rate::<T>();
premium_redeem_rate = premium_redeem_rate.saturating_plus_one();

let required_collateral =
Self::required_collateral(&vault_id, &to_be_backed_tokens, global_secure_threshold)?;

let current_collateral = Self::get_backing_collateral(&vault_id)?;
let missing_collateral = required_collateral.checked_sub(&current_collateral)?;

let factor = premium_redeem_rate
.checked_div(
&global_secure_threshold
.checked_sub(&premium_redeem_rate)
.ok_or(ArithmeticError::Underflow)?,
)
.ok_or(ArithmeticError::DivisionByZero)?;

let max_premium = missing_collateral.checked_mul(&factor)?;
Ok(max_premium)
}

pub fn get_liquidated_collateral(vault_id: &DefaultVaultId<T>) -> Result<Amount<T>, DispatchError> {
let vault = Self::get_vault_from_id(vault_id)?;
Ok(Amount::new(vault.liquidated_collateral, vault_id.currencies.collateral))
Expand Down Expand Up @@ -1636,13 +1684,12 @@ impl<T: Config> Pallet<T> {
/// The redeemable tokens are the currently vault.issued_tokens - the vault.to_be_redeemed_tokens
pub fn get_premium_redeem_vaults() -> Result<Vec<(DefaultVaultId<T>, Amount<T>)>, DispatchError> {
let mut suitable_vaults = Vaults::<T>::iter()
.filter_map(|(vault_id, vault)| {
let rich_vault: RichVault<T> = vault.into();

let redeemable_tokens = rich_vault.redeemable_tokens().ok()?;
.filter_map(|(vault_id, _vault)| {
let max_premium_in_collateral = Self::get_vault_max_premium_redeem(&vault_id).ok()?;
let premium_redeemable_tokens = max_premium_in_collateral.convert_to(vault_id.wrapped_currency()).ok()?;

if !redeemable_tokens.is_zero() && Self::is_vault_below_premium_threshold(&vault_id).unwrap_or(false) {
Some((vault_id, redeemable_tokens))
if !premium_redeemable_tokens.is_zero() && Self::is_vault_below_premium_threshold(&vault_id).unwrap_or(false) {
Some((vault_id, premium_redeemable_tokens))
} else {
None
}
Expand Down

0 comments on commit 652d338

Please sign in to comment.