Skip to content

Commit

Permalink
Remove CollateralCurrencyIds from module-cdp-engine (#2096)
Browse files Browse the repository at this point in the history
* Removed global interest rate in honzon
Updated tests and configs accordingly

* Changed the method signature of get_interest_rate_per_sec
Updated tests accordingly.

* Fixed broken test

* Replaced the CollateralInterestRateNotSet error with InvalidCollateralType
Updated code and tests accordingly

* WIP removed CollateralCurrencyIds
Now uses CollateralParams to determine valid collateral currencies.

* Fixed test, runtimes and benchmarking tests

* Fixed test and minor improvements

* minor improvement

* Addressed some PR review comments

Co-authored-by: Roy Yang <roy@laminar.one>
  • Loading branch information
syan095 and Roy Yang authored May 10, 2022
1 parent 45c4120 commit 11d9541
Show file tree
Hide file tree
Showing 18 changed files with 223 additions and 139 deletions.
95 changes: 52 additions & 43 deletions modules/cdp-engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,6 @@ pub mod module {
/// always do this.
type UpdateOrigin: EnsureOrigin<Self::Origin>;

/// The list of valid collateral currency types
#[pallet::constant]
type CollateralCurrencyIds: Get<Vec<CurrencyId>>;

/// The default liquidation ratio for all collateral types of CDP
#[pallet::constant]
type DefaultLiquidationRatio: Get<Ratio>;
Expand Down Expand Up @@ -293,12 +289,12 @@ pub mod module {
#[pallet::getter(fn debit_exchange_rate)]
pub type DebitExchangeRate<T: Config> = StorageMap<_, Twox64Concat, CurrencyId, ExchangeRate, OptionQuery>;

/// Mapping from collateral type to its risk management params
/// Mapping from valid collateral type to its risk management params
///
/// CollateralParams: CurrencyId => RiskManagementParams
/// CollateralParams: CurrencyId => Option<RiskManagementParams>
#[pallet::storage]
#[pallet::getter(fn collateral_params)]
pub type CollateralParams<T: Config> = StorageMap<_, Twox64Concat, CurrencyId, RiskManagementParams, ValueQuery>;
pub type CollateralParams<T: Config> = StorageMap<_, Twox64Concat, CurrencyId, RiskManagementParams, OptionQuery>;

/// Timestamp in seconds of the last interest accumulation
///
Expand Down Expand Up @@ -438,8 +434,7 @@ pub mod module {
/// The dispatch origin of this call must be `UpdateOrigin`.
///
/// - `currency_id`: collateral type.
/// - `interest_rate_per_sec`: extra interest rate per sec, `None` means do not update,
/// `Some(None)` means update it to `None`.
/// - `interest_rate_per_sec`: Interest rate per sec, `None` means do not update,
/// - `liquidation_ratio`: liquidation ratio, `None` means do not update, `Some(None)` means
/// update it to `None`.
/// - `liquidation_penalty`: liquidation penalty, `None` means do not update, `Some(None)`
Expand All @@ -459,12 +454,8 @@ pub mod module {
maximum_total_debit_value: ChangeBalance,
) -> DispatchResult {
T::UpdateOrigin::ensure_origin(origin)?;
ensure!(
T::CollateralCurrencyIds::get().contains(&currency_id),
Error::<T>::InvalidCollateralType,
);

let mut collateral_params = Self::collateral_params(currency_id);
let mut collateral_params = Self::collateral_params(currency_id).unwrap_or_default();
if let Change::NewValue(update) = interest_rate_per_sec {
collateral_params.interest_rate_per_sec = update;
Self::deposit_event(Event::InterestRatePerSecUpdated {
Expand Down Expand Up @@ -556,7 +547,7 @@ impl<T: Config> Pallet<T> {
if !T::EmergencyShutdown::is_shutdown() && !now_secs.is_zero() {
let interval_secs = now_secs.saturating_sub(last_accumulation_secs);

for currency_id in T::CollateralCurrencyIds::get() {
for currency_id in Self::get_collateral_currency_ids() {
if let Ok(interest_rate) = Self::get_interest_rate_per_sec(currency_id) {
let rate_to_accumulate = Self::compound_interest_rate(interest_rate, interval_secs);
let total_debits = <LoansOf<T>>::total_positions(currency_id).debit;
Expand Down Expand Up @@ -626,7 +617,7 @@ impl<T: Config> Pallet<T> {
}

fn _offchain_worker() -> Result<(), OffchainErr> {
let collateral_currency_ids = T::CollateralCurrencyIds::get();
let collateral_currency_ids = Self::get_collateral_currency_ids();
if collateral_currency_ids.len().is_zero() {
return Ok(());
}
Expand Down Expand Up @@ -733,26 +724,34 @@ impl<T: Config> Pallet<T> {
if let Some(feed_price) = T::PriceSource::get_relative_price(currency_id, stable_currency_id) {
let collateral_ratio =
Self::calculate_collateral_ratio(currency_id, collateral_amount, debit_amount, feed_price);
if collateral_ratio < Self::get_liquidation_ratio(currency_id) {
CDPStatus::Unsafe
} else {
CDPStatus::Safe
match Self::get_liquidation_ratio(currency_id) {
Ok(liquidation_ratio) => {
if collateral_ratio < liquidation_ratio {
CDPStatus::Unsafe
} else {
CDPStatus::Safe
}
}
Err(e) => CDPStatus::ChecksFailed(e),
}
} else {
CDPStatus::ChecksFailed(Error::<T>::InvalidFeedPrice.into())
}
}

pub fn maximum_total_debit_value(currency_id: CurrencyId) -> Balance {
Self::collateral_params(currency_id).maximum_total_debit_value
pub fn maximum_total_debit_value(currency_id: CurrencyId) -> Result<Balance, DispatchError> {
let params = Self::collateral_params(currency_id).ok_or(Error::<T>::InvalidCollateralType)?;
Ok(params.maximum_total_debit_value)
}

pub fn required_collateral_ratio(currency_id: CurrencyId) -> Option<Ratio> {
Self::collateral_params(currency_id).required_collateral_ratio
pub fn required_collateral_ratio(currency_id: CurrencyId) -> Result<Option<Ratio>, DispatchError> {
let params = Self::collateral_params(currency_id).ok_or(Error::<T>::InvalidCollateralType)?;
Ok(params.required_collateral_ratio)
}

pub fn get_interest_rate_per_sec(currency_id: CurrencyId) -> Result<Rate, DispatchError> {
Self::collateral_params(currency_id)
let params = Self::collateral_params(currency_id).ok_or(Error::<T>::InvalidCollateralType)?;
params
.interest_rate_per_sec
.ok_or_else(|| Error::<T>::InvalidCollateralType.into())
}
Expand All @@ -764,16 +763,16 @@ impl<T: Config> Pallet<T> {
.saturating_sub(Rate::one())
}

pub fn get_liquidation_ratio(currency_id: CurrencyId) -> Ratio {
Self::collateral_params(currency_id)
.liquidation_ratio
.unwrap_or_else(T::DefaultLiquidationRatio::get)
pub fn get_liquidation_ratio(currency_id: CurrencyId) -> Result<Ratio, DispatchError> {
let params = Self::collateral_params(currency_id).ok_or(Error::<T>::InvalidCollateralType)?;
Ok(params.liquidation_ratio.unwrap_or_else(T::DefaultLiquidationRatio::get))
}

pub fn get_liquidation_penalty(currency_id: CurrencyId) -> Rate {
Self::collateral_params(currency_id)
pub fn get_liquidation_penalty(currency_id: CurrencyId) -> Result<Rate, DispatchError> {
let params = Self::collateral_params(currency_id).ok_or(Error::<T>::InvalidCollateralType)?;
Ok(params
.liquidation_penalty
.unwrap_or_else(T::DefaultLiquidationPenalty::get)
.unwrap_or_else(T::DefaultLiquidationPenalty::get))
}

pub fn get_debit_exchange_rate(currency_id: CurrencyId) -> ExchangeRate {
Expand Down Expand Up @@ -809,7 +808,7 @@ impl<T: Config> Pallet<T> {
debit_adjustment: Amount,
) -> DispatchResult {
ensure!(
T::CollateralCurrencyIds::get().contains(&currency_id),
CollateralParams::<T>::contains_key(&currency_id),
Error::<T>::InvalidCollateralType,
);
<LoansOf<T>>::adjust_position(who, currency_id, collateral_adjustment, debit_adjustment)?;
Expand All @@ -829,7 +828,7 @@ impl<T: Config> Pallet<T> {
min_increase_collateral: Balance,
) -> DispatchResult {
ensure!(
T::CollateralCurrencyIds::get().contains(&currency_id),
CollateralParams::<T>::contains_key(&currency_id),
Error::<T>::InvalidCollateralType,
);
let loans_module_account = <LoansOf<T>>::account_id();
Expand Down Expand Up @@ -938,7 +937,7 @@ impl<T: Config> Pallet<T> {
min_decrease_debit_value: Balance,
) -> DispatchResult {
ensure!(
T::CollateralCurrencyIds::get().contains(&currency_id),
CollateralParams::<T>::contains_key(&currency_id),
Error::<T>::InvalidCollateralType,
);

Expand Down Expand Up @@ -1133,7 +1132,8 @@ impl<T: Config> Pallet<T> {
<LoansOf<T>>::confiscate_collateral_and_debit(&who, currency_id, collateral, debit)?;

let bad_debt_value = Self::get_debit_value(currency_id, debit);
let target_stable_amount = Self::get_liquidation_penalty(currency_id).saturating_mul_acc_int(bad_debt_value);
let liquidation_penalty = Self::get_liquidation_penalty(currency_id)?;
let target_stable_amount = liquidation_penalty.saturating_mul_acc_int(bad_debt_value);

match currency_id {
CurrencyId::DexShare(dex_share_0, dex_share_1) => {
Expand Down Expand Up @@ -1253,6 +1253,9 @@ impl<T: Config> Pallet<T> {

Ok(())
}
pub fn get_collateral_currency_ids() -> Vec<CurrencyId> {
CollateralParams::<T>::iter_keys().collect()
}
}

impl<T: Config> RiskManager<T::AccountId, CurrencyId, Balance, Balance> for Pallet<T> {
Expand All @@ -1275,7 +1278,7 @@ impl<T: Config> RiskManager<T::AccountId, CurrencyId, Balance, Balance> for Pall

// check the required collateral ratio
if check_required_ratio {
if let Some(required_collateral_ratio) = Self::required_collateral_ratio(currency_id) {
if let Some(required_collateral_ratio) = Self::required_collateral_ratio(currency_id)? {
ensure!(
collateral_ratio >= required_collateral_ratio,
Error::<T>::BelowRequiredCollateralRatio
Expand All @@ -1284,10 +1287,8 @@ impl<T: Config> RiskManager<T::AccountId, CurrencyId, Balance, Balance> for Pall
}

// check the liquidation ratio
ensure!(
collateral_ratio >= Self::get_liquidation_ratio(currency_id),
Error::<T>::BelowLiquidationRatio
);
let liquidation_ratio = Self::get_liquidation_ratio(currency_id)?;
ensure!(collateral_ratio >= liquidation_ratio, Error::<T>::BelowLiquidationRatio);

// check the minimum_debit_value
ensure!(
Expand All @@ -1306,15 +1307,23 @@ impl<T: Config> RiskManager<T::AccountId, CurrencyId, Balance, Balance> for Pall
}

fn check_debit_cap(currency_id: CurrencyId, total_debit_balance: Balance) -> DispatchResult {
let hard_cap = Self::maximum_total_debit_value(currency_id);
let hard_cap = Self::maximum_total_debit_value(currency_id)?;
let total_debit_value = Self::get_debit_value(currency_id, total_debit_balance);

ensure!(total_debit_value <= hard_cap, Error::<T>::ExceedDebitValueHardCap,);
ensure!(total_debit_value <= hard_cap, Error::<T>::ExceedDebitValueHardCap);

Ok(())
}
}

pub struct CollateralCurrencyIds<T>(PhantomData<T>);
// Returns a list of currently supported/configured collateral currency
impl<T: Config> Get<Vec<CurrencyId>> for CollateralCurrencyIds<T> {
fn get() -> Vec<CurrencyId> {
Pallet::<T>::get_collateral_currency_ids()
}
}

/// Pick a new PRN, in the range [0, `max`) (exclusive).
fn pick_u32<R: RngCore>(rng: &mut R, max: u32) -> u32 {
rng.next_u32() % max
Expand Down
2 changes: 0 additions & 2 deletions modules/cdp-engine/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,13 +302,11 @@ parameter_types! {
pub DefaultDebitExchangeRate: ExchangeRate = ExchangeRate::saturating_from_rational(1, 10);
pub DefaultLiquidationPenalty: Rate = Rate::saturating_from_rational(10, 100);
pub MaxSwapSlippageCompareToOracle: Ratio = Ratio::saturating_from_rational(50, 100);
pub CollateralCurrencyIds: Vec<CurrencyId> = vec![BTC, DOT, LP_AUSD_DOT];
}

impl Config for Runtime {
type Event = Event;
type PriceSource = MockPriceSource;
type CollateralCurrencyIds = CollateralCurrencyIds;
type DefaultLiquidationRatio = DefaultLiquidationRatio;
type DefaultDebitExchangeRate = DefaultDebitExchangeRate;
type DefaultLiquidationPenalty = DefaultLiquidationPenalty;
Expand Down
Loading

0 comments on commit 11d9541

Please sign in to comment.