diff --git a/programs/mango-v4/src/instructions/token_register.rs b/programs/mango-v4/src/instructions/token_register.rs index 31a751d1a..6b8ce193d 100644 --- a/programs/mango-v4/src/instructions/token_register.rs +++ b/programs/mango-v4/src/instructions/token_register.rs @@ -137,6 +137,7 @@ pub fn token_register( bank_num: 0, reserved: [0; 256], }; + require_gt!(bank.max_rate, MINIMUM_MAX_RATE); let mut mint_info = ctx.accounts.mint_info.load_init()?; *mint_info = MintInfo { diff --git a/programs/mango-v4/src/instructions/token_register_trustless.rs b/programs/mango-v4/src/instructions/token_register_trustless.rs index e753d8344..b5c9e22d8 100644 --- a/programs/mango-v4/src/instructions/token_register_trustless.rs +++ b/programs/mango-v4/src/instructions/token_register_trustless.rs @@ -109,6 +109,7 @@ pub fn token_register_trustless( bank_num: 0, reserved: [0; 256], }; + require_gt!(bank.max_rate, MINIMUM_MAX_RATE); let mut mint_info = ctx.accounts.mint_info.load_init()?; *mint_info = MintInfo { diff --git a/programs/mango-v4/src/state/bank.rs b/programs/mango-v4/src/state/bank.rs index 73a0f9e33..6277cd9b2 100644 --- a/programs/mango-v4/src/state/bank.rs +++ b/programs/mango-v4/src/state/bank.rs @@ -11,6 +11,7 @@ pub const HOUR: i64 = 3600; pub const DAY: i64 = 86400; pub const DAY_I80F48: I80F48 = I80F48!(86400); pub const YEAR_I80F48: I80F48 = I80F48!(31536000); +pub const MINIMUM_MAX_RATE: I80F48 = I80F48!(0.5); #[account(zero_copy)] pub struct Bank { @@ -497,18 +498,27 @@ impl Bank { // computes new optimal rates and max rate pub fn compute_rates(&self) -> (I80F48, I80F48, I80F48) { - // since we have 3 interest rate legs, consider the middle point of the middle leg as the optimal util - let optimal_util = (self.util0 + self.util1) / 2; + // interest rate legs 2 and 3 are seen as punitive legs, encouraging utilization to move towards optimal utilization + // lets choose util0 as optimal utilization and 0 to utli0 as the leg where we want the utlization to preferably be + let optimal_util = self.util0; // use avg_utilization and not instantaneous_utilization so that rates cannot be manupulated easily let util_diff = self.avg_utilization - optimal_util; // move rates up when utilization is above optimal utilization, and vice versa let adjustment = I80F48::ONE + self.adjustment_factor * util_diff; - // irrespective of which leg current utilization is in, update all rates - ( - cm!(self.rate0 * adjustment), - cm!(self.rate1 * adjustment), - cm!(self.max_rate * adjustment), - ) + + // 1. irrespective of which leg current utilization is in, update all rates + // 2. only update rates as long as new adjusted rates are above MINIMUM_MAX_RATE, + // since we don't want to fall to such low rates that it would take a long time to + // recover to high rates if utilization suddently increases to a high value + if cm!(self.max_rate * adjustment) > MINIMUM_MAX_RATE { + ( + cm!(self.rate0 * adjustment), + cm!(self.rate1 * adjustment), + cm!(self.max_rate * adjustment), + ) + } else { + (self.rate0, self.rate1, self.max_rate) + } } }