From 3dfeb00772b2bcee5d2fba104743f422c552c840 Mon Sep 17 00:00:00 2001 From: nakul1010 Date: Mon, 4 Dec 2023 11:52:17 +0530 Subject: [PATCH] fix: change premium calculation formula. --- crates/fee/src/lib.rs | 15 +++++++ crates/fee/src/types.rs | 2 +- crates/redeem/src/ext.rs | 22 ++++++---- crates/redeem/src/lib.rs | 43 +++++++++---------- crates/vault-registry/src/lib.rs | 24 ++++++----- crates/vault-registry/src/types.rs | 2 +- .../runtime-tests/src/parachain/redeem.rs | 6 +-- 7 files changed, 68 insertions(+), 46 deletions(-) diff --git a/crates/fee/src/lib.rs b/crates/fee/src/lib.rs index 321efd28d4..96523f0149 100644 --- a/crates/fee/src/lib.rs +++ b/crates/fee/src/lib.rs @@ -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] @@ -427,6 +429,19 @@ impl Pallet { amount.checked_rounded_mul(&>::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, + ) -> Result, DispatchError> { + Ok(amount + .checked_sub(&>::get()) + .ok_or(Error::::PremiumRedeemSubtractionFailed)?) + } + /// Calculate punishment fee for a Vault that fails to execute a redeem /// request before the expiry. /// diff --git a/crates/fee/src/types.rs b/crates/fee/src/types.rs index f7bdfccd87..e9e71ce3fb 100644 --- a/crates/fee/src/types.rs +++ b/crates/fee/src/types.rs @@ -5,7 +5,7 @@ use scale_info::TypeInfo; pub(crate) type BalanceOf = ::Balance; -pub(crate) type UnsignedFixedPoint = ::UnsignedFixedPoint; +pub type UnsignedFixedPoint = ::UnsignedFixedPoint; pub(crate) type DefaultVaultId = VaultId<::AccountId, CurrencyId>; diff --git a/crates/redeem/src/ext.rs b/crates/redeem/src/ext.rs index bc63b0228b..9d9cc1f7ba 100644 --- a/crates/redeem/src/ext.rs +++ b/crates/redeem/src/ext.rs @@ -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(vault_id: &DefaultVaultId) -> Result, DispatchError> { >::get_backing_collateral(vault_id) @@ -92,13 +92,6 @@ pub(crate) mod vault_registry { >::vault_capacity_at_secure_threshold(vault_id) } - pub fn vault_capacity_at_secure_threshold_based_on_collateral( - vault_id: &DefaultVaultId, - collateral: Amount, - ) -> Result, DispatchError> { - >::vault_capacity_at_secure_threshold_based_on_collateral(vault_id, collateral) - } - pub fn try_increase_to_be_redeemed_tokens( vault_id: &DefaultVaultId, amount: &Amount, @@ -190,6 +183,12 @@ pub(crate) mod vault_registry { ) -> Result<(Amount, Amount), DispatchError> { >::decrease_to_be_replaced_tokens(vault_id, tokens) } + + pub fn get_secure_threshold( + vault_id: &DefaultVaultId, + ) -> Result, DispatchError> { + >::get_secure_threshold(vault_id) + } } #[cfg_attr(test, mockable)] @@ -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::AccountId { @@ -251,4 +251,10 @@ pub(crate) mod fee { pub fn get_premium_redeem_fee(amount: &Amount) -> Result, DispatchError> { >::get_premium_redeem_fee(amount) } + + pub fn apply_premium_redeem_discount( + amount: &UnsignedFixedPoint, + ) -> Result, DispatchError> { + >::apply_premium_redeem_discount(amount) + } } diff --git a/crates/redeem/src/lib.rs b/crates/redeem/src/lib.rs index f051fd4ed0..f0e2c50396 100644 --- a/crates/redeem/src/lib.rs +++ b/crates/redeem/src/lib.rs @@ -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, @@ -456,6 +456,7 @@ mod self_redeem { Ok(()) } } + // "Internal" functions, callable by code. #[cfg_attr(test, mockable)] impl Pallet { @@ -507,35 +508,31 @@ impl Pallet { 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::( - &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::(&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::(&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::(&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::( + &token_exchange_value.saturating_sub(&backing_collateral)?, // (oldCol - oldTokens * EXCH * SECURE) + )? // FEE * (oldTokens * EXCH * SECURE - oldCol)) + .checked_div(&ext::fee::apply_premium_redeem_discount::(&secure_threshold)?)? // (SECURE - FEE) + } } else { Amount::zero(currency_id) }; diff --git a/crates/vault-registry/src/lib.rs b/crates/vault-registry/src/lib.rs index 7e44e38886..e04e53f91c 100644 --- a/crates/vault-registry/src/lib.rs +++ b/crates/vault-registry/src/lib.rs @@ -1089,6 +1089,20 @@ impl Pallet { 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) -> Result, 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 @@ -1897,16 +1911,6 @@ impl Pallet { 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, - collateral: Amount, - ) -> Result, DispatchError> { - let threshold = Self::secure_collateral_threshold(&vault_id.currencies).ok_or(Error::::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) -> Result, DispatchError> { let vault = Self::get_active_rich_vault_from_id(vault_id)?; vault.to_be_backed_tokens() diff --git a/crates/vault-registry/src/types.rs b/crates/vault-registry/src/types.rs index 3b3ba860f9..cb57a18cf6 100644 --- a/crates/vault-registry/src/types.rs +++ b/crates/vault-registry/src/types.rs @@ -102,7 +102,7 @@ impl CurrencySource { pub(crate) type BalanceOf = ::Balance; -pub(crate) type UnsignedFixedPoint = ::UnsignedFixedPoint; +pub type UnsignedFixedPoint = ::UnsignedFixedPoint; pub type CurrencyId = ::CurrencyId; diff --git a/parachain/runtime/runtime-tests/src/parachain/redeem.rs b/parachain/runtime/runtime-tests/src/parachain/redeem.rs index ecc382fe0b..8ac17cc4bc 100644 --- a/parachain/runtime/runtime-tests/src/parachain/redeem.rs +++ b/parachain/runtime/runtime-tests/src/parachain/redeem.rs @@ -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(), ) @@ -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);