Skip to content

Commit

Permalink
fix: change premium calculation formula.
Browse files Browse the repository at this point in the history
  • Loading branch information
nakul1010 committed Dec 4, 2023
1 parent aeaba1d commit 3dfeb00
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 46 deletions.
15 changes: 15 additions & 0 deletions crates/fee/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ pub mod pallet {
TryIntoIntError,
/// Value exceeds the expected upper bound for storage fields in this pallet.
AboveMaxExpectedValue,
/// Subtraction of the premium redeem fee from a value failed.
PremiumRedeemSubtractionFailed,
}

#[pallet::hooks]
Expand Down Expand Up @@ -427,6 +429,19 @@ impl<T: Config> Pallet<T> {
amount.checked_rounded_mul(&<PremiumRedeemFee<T>>::get(), Rounding::NearestPrefUp)
}

/// Apply a premium redeem discount to the given unsigned fixed-point value
///
/// # Arguments
///
/// * `amount` - amount in collateral (at current exchange rate)
pub fn apply_premium_redeem_discount(
amount: &UnsignedFixedPoint<T>,
) -> Result<UnsignedFixedPoint<T>, DispatchError> {
Ok(amount
.checked_sub(&<PremiumRedeemFee<T>>::get())
.ok_or(Error::<T>::PremiumRedeemSubtractionFailed)?)
}

/// Calculate punishment fee for a Vault that fails to execute a redeem
/// request before the expiry.
///
Expand Down
2 changes: 1 addition & 1 deletion crates/fee/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use scale_info::TypeInfo;

pub(crate) type BalanceOf<T> = <T as currency::Config>::Balance;

pub(crate) type UnsignedFixedPoint<T> = <T as currency::Config>::UnsignedFixedPoint;
pub type UnsignedFixedPoint<T> = <T as currency::Config>::UnsignedFixedPoint;

pub(crate) type DefaultVaultId<T> = VaultId<<T as frame_system::Config>::AccountId, CurrencyId<T>>;

Expand Down
22 changes: 14 additions & 8 deletions crates/redeem/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub(crate) mod vault_registry {
use crate::DefaultVaultId;
use currency::Amount;
use frame_support::dispatch::{DispatchError, DispatchResult};
use vault_registry::types::{CurrencyId, CurrencySource, DefaultVault};
use vault_registry::types::{CurrencyId, CurrencySource, DefaultVault, UnsignedFixedPoint};

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)
Expand Down Expand Up @@ -92,13 +92,6 @@ pub(crate) mod vault_registry {
<vault_registry::Pallet<T>>::vault_capacity_at_secure_threshold(vault_id)
}

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

pub fn try_increase_to_be_redeemed_tokens<T: crate::Config>(
vault_id: &DefaultVaultId<T>,
amount: &Amount<T>,
Expand Down Expand Up @@ -190,6 +183,12 @@ 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_secure_threshold<T: crate::Config>(
vault_id: &DefaultVaultId<T>,
) -> Result<UnsignedFixedPoint<T>, DispatchError> {
<vault_registry::Pallet<T>>::get_secure_threshold(vault_id)
}
}

#[cfg_attr(test, mockable)]
Expand Down Expand Up @@ -230,6 +229,7 @@ pub(crate) mod oracle {
#[cfg_attr(test, mockable)]
pub(crate) mod fee {
use currency::Amount;
use fee::types::UnsignedFixedPoint;
use frame_support::dispatch::{DispatchError, DispatchResult};

pub fn fee_pool_account_id<T: crate::Config>() -> T::AccountId {
Expand All @@ -251,4 +251,10 @@ pub(crate) mod fee {
pub fn get_premium_redeem_fee<T: crate::Config>(amount: &Amount<T>) -> Result<Amount<T>, DispatchError> {
<fee::Pallet<T>>::get_premium_redeem_fee(amount)
}

pub fn apply_premium_redeem_discount<T: crate::Config>(
amount: &UnsignedFixedPoint<T>,
) -> Result<UnsignedFixedPoint<T>, DispatchError> {
<fee::Pallet<T>>::apply_premium_redeem_discount(amount)
}
}
43 changes: 20 additions & 23 deletions crates/redeem/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub use crate::types::{DefaultRedeemRequest, RedeemRequest, RedeemRequestStatus}
use crate::types::{BalanceOf, RedeemRequestExt, Version};
use bitcoin::types::FullTransactionProof;
use btc_relay::BtcAddress;
use currency::Amount;
use currency::{Amount, Rounding};
use frame_support::{
dispatch::{DispatchError, DispatchResult},
ensure,
Expand Down Expand Up @@ -456,6 +456,7 @@ mod self_redeem {
Ok(())
}
}

// "Internal" functions, callable by code.
#[cfg_attr(test, mockable)]
impl<T: Config> Pallet<T> {
Expand Down Expand Up @@ -507,35 +508,31 @@ impl<T: Config> Pallet<T> {
let premium_collateral = if below_premium_redeem {
// Calculate the secure vault capacity
let secure_vault_capacity = ext::vault_registry::vault_capacity_at_secure_threshold(&vault_id)?;
let issued_tokens = ext::vault_registry::vault_to_be_backed_tokens(&vault_id)?;
let to_be_backed_tokens = ext::vault_registry::vault_to_be_backed_tokens(&vault_id)?;
let difference_in_tokens_to_reach_secure_threshold =
issued_tokens.saturating_sub(&secure_vault_capacity)?;
to_be_backed_tokens.saturating_sub(&secure_vault_capacity)?;

// Calculate collateral after paying the premium redeem fee
let backing_collateral = ext::vault_registry::get_backing_collateral(&vault_id)?;
let premium_redeem_fee = ext::fee::get_premium_redeem_fee::<T>(
&difference_in_tokens_to_reach_secure_threshold.convert_to(currency_id)?,
)?;
if difference_in_tokens_to_reach_secure_threshold.gt(&user_to_be_received_btc)? {
let premium_tokens_in_collateral = user_to_be_received_btc.convert_to(currency_id)?;

let collateral_after_premium_redeem = backing_collateral.saturating_sub(&premium_redeem_fee)?;
ext::fee::get_premium_redeem_fee::<T>(&premium_tokens_in_collateral)?
} else {
// Formula = max_premium_collateral = (FEE * (oldTokens * EXCH * SECURE - oldCol)) / (SECURE - FEE)

// Calculate the issued tokens that can be backed after paying the premium redeem fee
let issue_backed_after_premium_redeem =
ext::vault_registry::vault_capacity_at_secure_threshold_based_on_collateral(
&vault_id,
collateral_after_premium_redeem.clone(),
)?;
let backing_collateral = ext::vault_registry::get_backing_collateral(&vault_id)?;

let tokens_remaining_after_premium_redeem =
issued_tokens.saturating_sub(&issue_backed_after_premium_redeem)?;
let secure_threshold = ext::vault_registry::get_secure_threshold::<T>(&vault_id)?;

// Calculate the actual premium tokens and convert to collateral
let actual_premium_tokens = tokens_remaining_after_premium_redeem.min(&user_to_be_received_btc)?;
let premium_tokens_in_collateral = actual_premium_tokens.convert_to(currency_id)?;
let issued_tokens_in_collateral = to_be_backed_tokens.convert_to(currency_id)?; // oldTokens * EXCH

// Calculate the premium redeem fee for the premium tokens in collateral
let premium_collateral = ext::fee::get_premium_redeem_fee::<T>(&premium_tokens_in_collateral)?;
premium_collateral
let token_exchange_value =
issued_tokens_in_collateral.checked_rounded_mul(&secure_threshold, Rounding::NearestPrefUp)?; // oldTokens * EXCH * SECURE

ext::fee::get_premium_redeem_fee::<T>(
&token_exchange_value.saturating_sub(&backing_collateral)?, // (oldCol - oldTokens * EXCH * SECURE)
)? // FEE * (oldTokens * EXCH * SECURE - oldCol))
.checked_div(&ext::fee::apply_premium_redeem_discount::<T>(&secure_threshold)?)? // (SECURE - FEE)
}
} else {
Amount::zero(currency_id)
};
Expand Down
24 changes: 14 additions & 10 deletions crates/vault-registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,20 @@ impl<T: Config> Pallet<T> {
Ok(())
}

/// Get the secure threshold for a specified vault.
/// # Arguments
/// * `vault_id` - the id of the vault from which to issue tokens
///
/// # Returns
/// Returns the secure threshold of the specified vault or an error if the vault retrieval fails.
///
/// # Errors
/// * `VaultNotFound` - if no vault exists for the given `vault_id`
pub fn get_secure_threshold(vault_id: &DefaultVaultId<T>) -> Result<UnsignedFixedPoint<T>, DispatchError> {
let vault = Self::get_rich_vault_from_id(&vault_id)?;
vault.get_secure_threshold()
}

/// Adds an amount tokens to the to-be-redeemed tokens balance of a vault.
/// This function serves as a prevention against race conditions in the
/// redeem and replace procedures. If, for example, a vault would receive
Expand Down Expand Up @@ -1897,16 +1911,6 @@ impl<T: Config> Pallet<T> {
Self::calculate_max_wrapped_from_collateral_for_threshold(&collateral, wrapped_currency, threshold)
}

pub fn vault_capacity_at_secure_threshold_based_on_collateral(
vault_id: &DefaultVaultId<T>,
collateral: Amount<T>,
) -> Result<Amount<T>, DispatchError> {
let threshold = Self::secure_collateral_threshold(&vault_id.currencies).ok_or(Error::<T>::ThresholdNotSet)?;
let wrapped_currency = vault_id.wrapped_currency();

Self::calculate_max_wrapped_from_collateral_for_threshold(&collateral, wrapped_currency, threshold)
}

pub fn vault_to_be_backed_tokens(vault_id: &DefaultVaultId<T>) -> Result<Amount<T>, DispatchError> {
let vault = Self::get_active_rich_vault_from_id(vault_id)?;
vault.to_be_backed_tokens()
Expand Down
2 changes: 1 addition & 1 deletion crates/vault-registry/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl<T: Config> CurrencySource<T> {

pub(crate) type BalanceOf<T> = <T as currency::Config>::Balance;

pub(crate) type UnsignedFixedPoint<T> = <T as currency::Config>::UnsignedFixedPoint;
pub type UnsignedFixedPoint<T> = <T as currency::Config>::UnsignedFixedPoint;

pub type CurrencyId<T> = <T as orml_tokens::Config>::CurrencyId;

Expand Down
6 changes: 3 additions & 3 deletions parachain/runtime/runtime-tests/src/parachain/redeem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,10 @@ mod premium_redeem_tests {
assert!(!VaultRegistryPallet::is_vault_below_premium_threshold(&vault_id).unwrap());

let redeem = RedeemPallet::get_open_redeem_request_from_id(&redeem_id).unwrap();
// we should get rewarded only for 150_000 + 3750 tokens (that's when we reach nearer to secure threshold)
// we should get rewarded only for 150_000 + 3840 tokens (that's when we reach nearer to secure threshold)
let expected_premium = FeePallet::get_premium_redeem_fee(
&vault_id
.wrapped(150_000 + 3750) // need to add 0.375 = 153.750
.wrapped(150_000 + 3840) // need to add 0.384 = 153.84
.convert_to(vault_id.collateral_currency())
.unwrap(),
)
Expand All @@ -166,7 +166,7 @@ mod premium_redeem_tests {
execute_redeem(redeem_id);

let compute_collateral = VaultRegistryPallet::compute_collateral(&vault_id).unwrap().amount();
assert_eq!(compute_collateral, 2000000 - 15375); //15.355 COL tokens lost as premium fees
assert_eq!(compute_collateral, 2000000 - 15384); //15.384 COL tokens lost as premium fees

// Setup another redeem request
let redeem_id = setup_redeem(vault_id.wrapped(2_000), USER, &vault_id);
Expand Down

0 comments on commit 3dfeb00

Please sign in to comment.