Bank: store interest curve scale and target util separately (#755)

Allows for better configurability independent of the previous MAX_RATE
concept
This commit is contained in:
Christian Kamm 2023-10-17 13:14:07 +02:00 committed by GitHub
parent 8110dd1566
commit 3b28856692
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 421 additions and 59 deletions

View File

@ -596,6 +596,14 @@
{ {
"name": "flashLoanSwapFeeRate", "name": "flashLoanSwapFeeRate",
"type": "f32" "type": "f32"
},
{
"name": "interestCurveScaling",
"type": "f32"
},
{
"name": "interestTargetUtilization",
"type": "f32"
} }
] ]
}, },
@ -930,6 +938,18 @@
"type": { "type": {
"option": "f32" "option": "f32"
} }
},
{
"name": "interestCurveScalingOpt",
"type": {
"option": "f32"
}
},
{
"name": "interestTargetUtilizationOpt",
"type": {
"option": "f32"
}
} }
] ]
}, },
@ -7033,12 +7053,29 @@
"name": "flashLoanSwapFeeRate", "name": "flashLoanSwapFeeRate",
"type": "f32" "type": "f32"
}, },
{
"name": "interestTargetUtilization",
"docs": [
"Target utilization: If actual utilization is higher, scale up interest.",
"If it's lower, scale down interest (if possible)"
],
"type": "f32"
},
{
"name": "interestCurveScaling",
"docs": [
"Current interest curve scaling, always >= 1.0",
"",
"Except when first migrating to having this field, then 0.0"
],
"type": "f64"
},
{ {
"name": "reserved", "name": "reserved",
"type": { "type": {
"array": [ "array": [
"u8", "u8",
2092 2080
] ]
} }
} }
@ -11335,6 +11372,56 @@
} }
] ]
}, },
{
"name": "UpdateRateLogV2",
"fields": [
{
"name": "mangoGroup",
"type": "publicKey",
"index": false
},
{
"name": "tokenIndex",
"type": "u16",
"index": false
},
{
"name": "rate0",
"type": "i128",
"index": false
},
{
"name": "util0",
"type": "i128",
"index": false
},
{
"name": "rate1",
"type": "i128",
"index": false
},
{
"name": "util1",
"type": "i128",
"index": false
},
{
"name": "maxRate",
"type": "i128",
"index": false
},
{
"name": "curveScaling",
"type": "f64",
"index": false
},
{
"name": "targetUtilization",
"type": "f32",
"index": false
}
]
},
{ {
"name": "TokenLiqWithTokenLog", "name": "TokenLiqWithTokenLog",
"fields": [ "fields": [

View File

@ -42,6 +42,8 @@ pub fn token_edit(
token_conditional_swap_taker_fee_rate_opt: Option<f32>, token_conditional_swap_taker_fee_rate_opt: Option<f32>,
token_conditional_swap_maker_fee_rate_opt: Option<f32>, token_conditional_swap_maker_fee_rate_opt: Option<f32>,
flash_loan_swap_fee_rate_opt: Option<f32>, flash_loan_swap_fee_rate_opt: Option<f32>,
interest_curve_scaling_opt: Option<f32>,
interest_target_utilization_opt: Option<f32>,
) -> Result<()> { ) -> Result<()> {
let group = ctx.accounts.group.load()?; let group = ctx.accounts.group.load()?;
@ -339,6 +341,27 @@ pub fn token_edit(
bank.flash_loan_swap_fee_rate = fee_rate; bank.flash_loan_swap_fee_rate = fee_rate;
require_group_admin = true; require_group_admin = true;
} }
if let Some(interest_curve_scaling) = interest_curve_scaling_opt {
msg!(
"Interest curve scaling old {:?}, new {:?}",
bank.interest_curve_scaling,
interest_curve_scaling
);
require_gte!(interest_curve_scaling, 1.0);
bank.interest_curve_scaling = interest_curve_scaling.into();
require_group_admin = true;
}
if let Some(interest_target_utilization) = interest_target_utilization_opt {
msg!(
"Interest target utilization old {:?}, new {:?}",
bank.interest_target_utilization,
interest_target_utilization
);
require_gte!(interest_target_utilization, 0.0);
bank.interest_target_utilization = interest_target_utilization;
require_group_admin = true;
}
} }
// account constraint #1 // account constraint #1

View File

@ -38,6 +38,8 @@ pub fn token_register(
token_conditional_swap_taker_fee_rate: f32, token_conditional_swap_taker_fee_rate: f32,
token_conditional_swap_maker_fee_rate: f32, token_conditional_swap_maker_fee_rate: f32,
flash_loan_swap_fee_rate: f32, flash_loan_swap_fee_rate: f32,
interest_curve_scaling: f32,
interest_target_utilization: f32,
) -> Result<()> { ) -> Result<()> {
// Require token 0 to be in the insurance token // Require token 0 to be in the insurance token
if token_index == INSURANCE_TOKEN_INDEX { if token_index == INSURANCE_TOKEN_INDEX {
@ -108,7 +110,9 @@ pub fn token_register(
token_conditional_swap_taker_fee_rate, token_conditional_swap_taker_fee_rate,
token_conditional_swap_maker_fee_rate, token_conditional_swap_maker_fee_rate,
flash_loan_swap_fee_rate: flash_loan_swap_fee_rate, flash_loan_swap_fee_rate: flash_loan_swap_fee_rate,
reserved: [0; 2092], interest_target_utilization,
interest_curve_scaling: interest_curve_scaling.into(),
reserved: [0; 2080],
}; };
if let Ok(oracle_price) = if let Ok(oracle_price) =

View File

@ -46,10 +46,10 @@ pub fn token_register_trustless(
// 10% daily adjustment at 0% or 100% utilization // 10% daily adjustment at 0% or 100% utilization
adjustment_factor: I80F48::from_num(0.004), adjustment_factor: I80F48::from_num(0.004),
util0: I80F48::from_num(0.5), util0: I80F48::from_num(0.5),
rate0: I80F48::from_num(0.072), rate0: I80F48::from_num(0.018),
util1: I80F48::from_num(0.8), util1: I80F48::from_num(0.8),
rate1: I80F48::from_num(0.2), rate1: I80F48::from_num(0.05),
max_rate: I80F48::from_num(2.0), max_rate: I80F48::from_num(0.5),
collected_fees_native: I80F48::ZERO, collected_fees_native: I80F48::ZERO,
loan_origination_fee_rate: I80F48::from_num(0.0005), loan_origination_fee_rate: I80F48::from_num(0.0005),
loan_fee_rate: I80F48::from_num(0.005), loan_fee_rate: I80F48::from_num(0.005),
@ -80,9 +80,10 @@ pub fn token_register_trustless(
token_conditional_swap_taker_fee_rate: 0.0005, token_conditional_swap_taker_fee_rate: 0.0005,
token_conditional_swap_maker_fee_rate: 0.0005, token_conditional_swap_maker_fee_rate: 0.0005,
flash_loan_swap_fee_rate: 0.0005, flash_loan_swap_fee_rate: 0.0005,
reserved: [0; 2092], interest_target_utilization: 0.5,
interest_curve_scaling: 4.0,
reserved: [0; 2080],
}; };
require_gt!(bank.max_rate, MINIMUM_MAX_RATE);
if let Ok(oracle_price) = if let Ok(oracle_price) =
bank.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?, None) bank.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?, None)

View File

@ -2,7 +2,7 @@ use anchor_lang::prelude::*;
use crate::accounts_ix::*; use crate::accounts_ix::*;
use crate::error::MangoError; use crate::error::MangoError;
use crate::logs::{UpdateIndexLog, UpdateRateLog}; use crate::logs::{UpdateIndexLog, UpdateRateLogV2};
use crate::state::HOUR; use crate::state::HOUR;
use crate::{ use crate::{
accounts_zerocopy::{AccountInfoRef, LoadMutZeroCopyRef, LoadZeroCopyRef}, accounts_zerocopy::{AccountInfoRef, LoadMutZeroCopyRef, LoadZeroCopyRef},
@ -140,32 +140,54 @@ pub fn token_update_index_and_rate(ctx: Context<TokenUpdateIndexAndRate>) -> Res
// compute optimal rates, and max rate and set them on the bank // compute optimal rates, and max rate and set them on the bank
{ {
let some_bank = ctx.remaining_accounts[0].load::<Bank>()?; let mut some_bank = ctx.remaining_accounts[0].load_mut::<Bank>()?;
let diff_ts = I80F48::from_num(now_ts - some_bank.bank_rate_last_updated); let diff_ts = I80F48::from_num(now_ts - some_bank.bank_rate_last_updated);
// update each hour // update each hour
if diff_ts > HOUR { if diff_ts > HOUR {
let (rate0, rate1, max_rate) = some_bank.compute_rates(); // First setup when new parameters are introduced
if some_bank.interest_curve_scaling == 0.0 {
let old_max_rate = 0.5;
some_bank.interest_curve_scaling =
some_bank.max_rate.to_num::<f64>() / old_max_rate;
some_bank.interest_target_utilization = some_bank.util0.to_num();
emit!(UpdateRateLog { let descale_factor = I80F48::from_num(1.0 / some_bank.interest_curve_scaling);
some_bank.rate0 *= descale_factor;
some_bank.rate1 *= descale_factor;
some_bank.max_rate *= descale_factor;
}
some_bank.update_interest_rate_scaling();
let rate0 = some_bank.rate0;
let rate1 = some_bank.rate1;
let max_rate = some_bank.max_rate;
let scaling = some_bank.interest_curve_scaling;
let target_util = some_bank.interest_target_utilization;
emit!(UpdateRateLogV2 {
mango_group: mint_info.group.key(), mango_group: mint_info.group.key(),
token_index: mint_info.token_index, token_index: mint_info.token_index,
rate0: rate0.to_bits(), rate0: rate0.to_bits(),
util0: some_bank.util0.to_bits(),
rate1: rate1.to_bits(), rate1: rate1.to_bits(),
util1: some_bank.util1.to_bits(),
max_rate: max_rate.to_bits(), max_rate: max_rate.to_bits(),
curve_scaling: some_bank.interest_curve_scaling,
target_utilization: some_bank.interest_target_utilization,
}); });
drop(some_bank); drop(some_bank);
msg!("rate0 {}", rate0); // Apply the new parameters to all banks
msg!("rate1 {}", rate1);
msg!("max_rate {}", max_rate);
for ai in ctx.remaining_accounts.iter() { for ai in ctx.remaining_accounts.iter() {
let mut bank = ai.load_mut::<Bank>()?; let mut bank = ai.load_mut::<Bank>()?;
bank.bank_rate_last_updated = now_ts; bank.bank_rate_last_updated = now_ts;
bank.interest_curve_scaling = scaling;
bank.interest_target_utilization = target_util;
bank.rate0 = rate0; bank.rate0 = rate0;
bank.rate1 = rate1; bank.rate1 = rate1;
bank.max_rate = max_rate; bank.max_rate = max_rate;

View File

@ -148,6 +148,8 @@ pub mod mango_v4 {
token_conditional_swap_taker_fee_rate: f32, token_conditional_swap_taker_fee_rate: f32,
token_conditional_swap_maker_fee_rate: f32, token_conditional_swap_maker_fee_rate: f32,
flash_loan_swap_fee_rate: f32, flash_loan_swap_fee_rate: f32,
interest_curve_scaling: f32,
interest_target_utilization: f32,
) -> Result<()> { ) -> Result<()> {
#[cfg(feature = "enable-gpl")] #[cfg(feature = "enable-gpl")]
instructions::token_register( instructions::token_register(
@ -175,6 +177,8 @@ pub mod mango_v4 {
token_conditional_swap_taker_fee_rate, token_conditional_swap_taker_fee_rate,
token_conditional_swap_maker_fee_rate, token_conditional_swap_maker_fee_rate,
flash_loan_swap_fee_rate, flash_loan_swap_fee_rate,
interest_curve_scaling,
interest_target_utilization,
)?; )?;
Ok(()) Ok(())
} }
@ -219,6 +223,8 @@ pub mod mango_v4 {
token_conditional_swap_taker_fee_rate_opt: Option<f32>, token_conditional_swap_taker_fee_rate_opt: Option<f32>,
token_conditional_swap_maker_fee_rate_opt: Option<f32>, token_conditional_swap_maker_fee_rate_opt: Option<f32>,
flash_loan_swap_fee_rate_opt: Option<f32>, flash_loan_swap_fee_rate_opt: Option<f32>,
interest_curve_scaling_opt: Option<f32>,
interest_target_utilization_opt: Option<f32>,
) -> Result<()> { ) -> Result<()> {
#[cfg(feature = "enable-gpl")] #[cfg(feature = "enable-gpl")]
instructions::token_edit( instructions::token_edit(
@ -250,6 +256,8 @@ pub mod mango_v4 {
token_conditional_swap_taker_fee_rate_opt, token_conditional_swap_taker_fee_rate_opt,
token_conditional_swap_maker_fee_rate_opt, token_conditional_swap_maker_fee_rate_opt,
flash_loan_swap_fee_rate_opt, flash_loan_swap_fee_rate_opt,
interest_curve_scaling_opt,
interest_target_utilization_opt,
)?; )?;
Ok(()) Ok(())
} }

View File

@ -300,6 +300,20 @@ pub struct UpdateRateLog {
pub max_rate: i128, // I80F48 pub max_rate: i128, // I80F48
} }
#[event]
pub struct UpdateRateLogV2 {
pub mango_group: Pubkey,
pub token_index: u16,
// contrary to v1 these do not have curve_scaling factored in!
pub rate0: i128, // I80F48
pub util0: i128, // I80F48
pub rate1: i128, // I80F48
pub util1: i128, // I80F48
pub max_rate: i128, // I80F48
pub curve_scaling: f64,
pub target_utilization: f32,
}
#[event] #[event]
pub struct TokenLiqWithTokenLog { pub struct TokenLiqWithTokenLog {
pub mango_group: Pubkey, pub mango_group: Pubkey,

View File

@ -18,7 +18,6 @@ pub const DAY: i64 = 86400;
pub const DAY_I80F48: I80F48 = I80F48::from_bits(86_400 * I80F48::ONE.to_bits()); pub const DAY_I80F48: I80F48 = I80F48::from_bits(86_400 * I80F48::ONE.to_bits());
pub const ONE_BPS: I80F48 = I80F48::from_bits(28147497671); pub const ONE_BPS: I80F48 = I80F48::from_bits(28147497671);
pub const YEAR_I80F48: I80F48 = I80F48::from_bits(31_536_000 * I80F48::ONE.to_bits()); pub const YEAR_I80F48: I80F48 = I80F48::from_bits(31_536_000 * I80F48::ONE.to_bits());
pub const MINIMUM_MAX_RATE: I80F48 = I80F48::from_bits(I80F48::ONE.to_bits() / 2);
#[derive(Derivative)] #[derive(Derivative)]
#[derivative(Debug)] #[derivative(Debug)]
@ -148,8 +147,17 @@ pub struct Bank {
pub flash_loan_swap_fee_rate: f32, pub flash_loan_swap_fee_rate: f32,
/// Target utilization: If actual utilization is higher, scale up interest.
/// If it's lower, scale down interest (if possible)
pub interest_target_utilization: f32,
/// Current interest curve scaling, always >= 1.0
///
/// Except when first migrating to having this field, then 0.0
pub interest_curve_scaling: f64,
#[derivative(Debug = "ignore")] #[derivative(Debug = "ignore")]
pub reserved: [u8; 2092], pub reserved: [u8; 2080],
} }
const_assert_eq!( const_assert_eq!(
size_of::<Bank>(), size_of::<Bank>(),
@ -180,8 +188,9 @@ const_assert_eq!(
+ 1 + 1
+ 6 + 6
+ 8 + 8
+ 3 * 4 + 4 * 4
+ 2092 + 8
+ 2080
); );
const_assert_eq!(size_of::<Bank>(), 3064); const_assert_eq!(size_of::<Bank>(), 3064);
const_assert_eq!(size_of::<Bank>() % 8, 0); const_assert_eq!(size_of::<Bank>() % 8, 0);
@ -211,9 +220,11 @@ impl Bank {
indexed_deposits: I80F48::ZERO, indexed_deposits: I80F48::ZERO,
indexed_borrows: I80F48::ZERO, indexed_borrows: I80F48::ZERO,
collected_fees_native: I80F48::ZERO, collected_fees_native: I80F48::ZERO,
fees_withdrawn: 0,
dust: I80F48::ZERO, dust: I80F48::ZERO,
flash_loan_approved_amount: 0, flash_loan_approved_amount: 0,
flash_loan_token_account_initial: u64::MAX, flash_loan_token_account_initial: u64::MAX,
net_borrows_in_window: 0,
bump, bump,
bank_num, bank_num,
@ -245,22 +256,24 @@ impl Bank {
token_index: existing_bank.token_index, token_index: existing_bank.token_index,
mint_decimals: existing_bank.mint_decimals, mint_decimals: existing_bank.mint_decimals,
oracle_config: existing_bank.oracle_config, oracle_config: existing_bank.oracle_config,
stable_price_model: StablePriceModel::default(), stable_price_model: existing_bank.stable_price_model,
min_vault_to_deposits_ratio: existing_bank.min_vault_to_deposits_ratio, min_vault_to_deposits_ratio: existing_bank.min_vault_to_deposits_ratio,
net_borrow_limit_per_window_quote: existing_bank.net_borrow_limit_per_window_quote, net_borrow_limit_per_window_quote: existing_bank.net_borrow_limit_per_window_quote,
net_borrow_limit_window_size_ts: existing_bank.net_borrow_limit_window_size_ts, net_borrow_limit_window_size_ts: existing_bank.net_borrow_limit_window_size_ts,
last_net_borrows_window_start_ts: existing_bank.last_net_borrows_window_start_ts, last_net_borrows_window_start_ts: existing_bank.last_net_borrows_window_start_ts,
net_borrows_in_window: 0, borrow_weight_scale_start_quote: existing_bank.borrow_weight_scale_start_quote,
borrow_weight_scale_start_quote: f64::MAX, deposit_weight_scale_start_quote: existing_bank.deposit_weight_scale_start_quote,
deposit_weight_scale_start_quote: f64::MAX, reduce_only: existing_bank.reduce_only,
reduce_only: 0, force_close: existing_bank.force_close,
force_close: 0,
padding: [0; 6], padding: [0; 6],
fees_withdrawn: 0, token_conditional_swap_taker_fee_rate: existing_bank
token_conditional_swap_taker_fee_rate: 0.0, .token_conditional_swap_taker_fee_rate,
token_conditional_swap_maker_fee_rate: 0.0, token_conditional_swap_maker_fee_rate: existing_bank
flash_loan_swap_fee_rate: 0.0, .token_conditional_swap_maker_fee_rate,
reserved: [0; 2092], flash_loan_swap_fee_rate: existing_bank.flash_loan_swap_fee_rate,
interest_target_utilization: existing_bank.interest_target_utilization,
interest_curve_scaling: existing_bank.interest_curve_scaling,
reserved: [0; 2080],
} }
} }
@ -270,7 +283,7 @@ impl Bank {
require_gte!(self.rate0, I80F48::ZERO); require_gte!(self.rate0, I80F48::ZERO);
require_gte!(self.util1, I80F48::ZERO); require_gte!(self.util1, I80F48::ZERO);
require_gte!(self.rate1, I80F48::ZERO); require_gte!(self.rate1, I80F48::ZERO);
require_gte!(self.max_rate, MINIMUM_MAX_RATE); require_gte!(self.max_rate, I80F48::ZERO);
require_gte!(self.loan_fee_rate, 0.0); require_gte!(self.loan_fee_rate, 0.0);
require_gte!(self.loan_origination_fee_rate, 0.0); require_gte!(self.loan_origination_fee_rate, 0.0);
require_gte!(self.maint_asset_weight, 0.0); require_gte!(self.maint_asset_weight, 0.0);
@ -286,6 +299,8 @@ impl Bank {
require_gte!(self.token_conditional_swap_taker_fee_rate, 0.0); require_gte!(self.token_conditional_swap_taker_fee_rate, 0.0);
require_gte!(self.token_conditional_swap_maker_fee_rate, 0.0); require_gte!(self.token_conditional_swap_maker_fee_rate, 0.0);
require_gte!(self.flash_loan_swap_fee_rate, 0.0); require_gte!(self.flash_loan_swap_fee_rate, 0.0);
require_gte!(self.interest_curve_scaling, 1.0);
require_gte!(self.interest_target_utilization, 0.0);
Ok(()) Ok(())
} }
@ -795,6 +810,7 @@ impl Bank {
self.util1, self.util1,
self.rate1, self.rate1,
self.max_rate, self.max_rate,
self.interest_curve_scaling,
) )
} }
@ -808,8 +824,9 @@ impl Bank {
util1: I80F48, util1: I80F48,
rate1: I80F48, rate1: I80F48,
max_rate: I80F48, max_rate: I80F48,
scaling: f64,
) -> I80F48 { ) -> I80F48 {
if utilization <= util0 { let v = if utilization <= util0 {
let slope = rate0 / util0; let slope = rate0 / util0;
slope * utilization slope * utilization
} else if utilization <= util1 { } else if utilization <= util1 {
@ -820,6 +837,13 @@ impl Bank {
let extra_util = utilization - util1; let extra_util = utilization - util1;
let slope = (max_rate - rate1) / (I80F48::ONE - util1); let slope = (max_rate - rate1) / (I80F48::ONE - util1);
rate1 + slope * extra_util rate1 + slope * extra_util
};
// scaling will be 0 when it's introduced
if scaling == 0.0 {
v
} else {
v * I80F48::from_num(scaling)
} }
} }
@ -855,34 +879,23 @@ impl Bank {
} }
// computes new optimal rates and max rate // computes new optimal rates and max rate
pub fn compute_rates(&self) -> (I80F48, I80F48, I80F48) { pub fn update_interest_rate_scaling(&mut self) {
// interest rate legs 2 and 3 are seen as punitive legs, encouraging utilization to move towards optimal utilization // Interest increases above target_util, decreases below
// lets choose util0 as optimal utilization and 0 to utli0 as the leg where we want the utlization to preferably be let target_util = self.interest_target_utilization as f64;
let optimal_util = self.util0;
// use avg_utilization and not instantaneous_utilization so that rates cannot be manipulated easily // use avg_utilization and not instantaneous_utilization so that rates cannot be manipulated easily
let avg_util = self.avg_utilization; let avg_util = self.avg_utilization.to_num::<f64>();
// move rates up when utilization is above optimal utilization, and vice versa // move rates up when utilization is above optimal utilization, and vice versa
// util factor is between -1 (avg util = 0) and +1 (avg util = 100%) // util factor is between -1 (avg util = 0) and +1 (avg util = 100%)
let util_factor = if avg_util > optimal_util { let util_factor = if avg_util > target_util {
(avg_util - optimal_util) / (I80F48::ONE - optimal_util) (avg_util - target_util) / (1.0 - target_util)
} else { } else {
(avg_util - optimal_util) / optimal_util (avg_util - target_util) / target_util
}; };
let adjustment = I80F48::ONE + self.adjustment_factor * util_factor; let adjustment = 1.0 + self.adjustment_factor.to_num::<f64>() * util_factor;
// 1. irrespective of which leg current utilization is in, update all rates self.interest_curve_scaling = (self.interest_curve_scaling * adjustment).max(1.0)
// 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 (self.max_rate * adjustment) > MINIMUM_MAX_RATE {
(
(self.rate0 * adjustment),
(self.rate1 * adjustment),
(self.max_rate * adjustment),
)
} else {
(self.rate0, self.rate1, self.max_rate)
}
} }
pub fn oracle_price( pub fn oracle_price(

View File

@ -997,6 +997,8 @@ impl ClientInstruction for TokenRegisterInstruction {
token_conditional_swap_taker_fee_rate: 0.0, token_conditional_swap_taker_fee_rate: 0.0,
token_conditional_swap_maker_fee_rate: 0.0, token_conditional_swap_maker_fee_rate: 0.0,
flash_loan_swap_fee_rate: 0.0, flash_loan_swap_fee_rate: 0.0,
interest_curve_scaling: 1.0,
interest_target_utilization: 0.5,
}; };
let bank = Pubkey::find_program_address( let bank = Pubkey::find_program_address(
@ -1241,6 +1243,8 @@ pub fn token_edit_instruction_default() -> mango_v4::instruction::TokenEdit {
token_conditional_swap_taker_fee_rate_opt: None, token_conditional_swap_taker_fee_rate_opt: None,
token_conditional_swap_maker_fee_rate_opt: None, token_conditional_swap_maker_fee_rate_opt: None,
flash_loan_swap_fee_rate_opt: None, flash_loan_swap_fee_rate_opt: None,
interest_curve_scaling_opt: None,
interest_target_utilization_opt: None,
} }
} }

View File

@ -410,6 +410,8 @@ export class MangoClient {
params.tokenConditionalSwapTakerFeeRate, params.tokenConditionalSwapTakerFeeRate,
params.tokenConditionalSwapMakerFeeRate, params.tokenConditionalSwapMakerFeeRate,
params.flashLoanSwapFeeRate, params.flashLoanSwapFeeRate,
params.interestCurveScaling,
params.interestTargetUtilization,
) )
.accounts({ .accounts({
group: group.publicKey, group: group.publicKey,
@ -485,6 +487,8 @@ export class MangoClient {
params.tokenConditionalSwapTakerFeeRate, params.tokenConditionalSwapTakerFeeRate,
params.tokenConditionalSwapMakerFeeRate, params.tokenConditionalSwapMakerFeeRate,
params.flashLoanSwapFeeRate, params.flashLoanSwapFeeRate,
params.interestCurveScaling,
params.interestTargetUtilization,
) )
.accounts({ .accounts({
group: group.publicKey, group: group.publicKey,

View File

@ -25,6 +25,8 @@ export interface TokenRegisterParams {
tokenConditionalSwapTakerFeeRate: number; tokenConditionalSwapTakerFeeRate: number;
tokenConditionalSwapMakerFeeRate: number; tokenConditionalSwapMakerFeeRate: number;
flashLoanSwapFeeRate: number; flashLoanSwapFeeRate: number;
interestCurveScaling: number;
interestTargetUtilization: number;
} }
export const DefaultTokenRegisterParams: TokenRegisterParams = { export const DefaultTokenRegisterParams: TokenRegisterParams = {
@ -35,10 +37,10 @@ export const DefaultTokenRegisterParams: TokenRegisterParams = {
groupInsuranceFund: false, groupInsuranceFund: false,
interestRateParams: { interestRateParams: {
util0: 0.5, util0: 0.5,
rate0: 0.072, rate0: 0.018,
util1: 0.8, util1: 0.8,
rate1: 0.2, rate1: 0.05,
maxRate: 2, maxRate: 0.5,
adjustmentFactor: 0.004, adjustmentFactor: 0.004,
}, },
loanFeeRate: 0.0005, loanFeeRate: 0.0005,
@ -60,6 +62,8 @@ export const DefaultTokenRegisterParams: TokenRegisterParams = {
tokenConditionalSwapTakerFeeRate: 0.0005, tokenConditionalSwapTakerFeeRate: 0.0005,
tokenConditionalSwapMakerFeeRate: 0.0005, tokenConditionalSwapMakerFeeRate: 0.0005,
flashLoanSwapFeeRate: 0.0005, flashLoanSwapFeeRate: 0.0005,
interestCurveScaling: 4.0,
interestTargetUtilization: 0.5,
}; };
export interface TokenEditParams { export interface TokenEditParams {
@ -90,6 +94,8 @@ export interface TokenEditParams {
tokenConditionalSwapTakerFeeRate: number | null; tokenConditionalSwapTakerFeeRate: number | null;
tokenConditionalSwapMakerFeeRate: number | null; tokenConditionalSwapMakerFeeRate: number | null;
flashLoanSwapFeeRate: number | null; flashLoanSwapFeeRate: number | null;
interestCurveScaling: number | null;
interestTargetUtilization: number | null;
} }
export const NullTokenEditParams: TokenEditParams = { export const NullTokenEditParams: TokenEditParams = {
@ -120,6 +126,8 @@ export const NullTokenEditParams: TokenEditParams = {
tokenConditionalSwapTakerFeeRate: null, tokenConditionalSwapTakerFeeRate: null,
tokenConditionalSwapMakerFeeRate: null, tokenConditionalSwapMakerFeeRate: null,
flashLoanSwapFeeRate: null, flashLoanSwapFeeRate: null,
interestCurveScaling: null,
interestTargetUtilization: null,
}; };
export interface PerpEditParams { export interface PerpEditParams {

View File

@ -596,6 +596,14 @@ export type MangoV4 = {
{ {
"name": "flashLoanSwapFeeRate", "name": "flashLoanSwapFeeRate",
"type": "f32" "type": "f32"
},
{
"name": "interestCurveScaling",
"type": "f32"
},
{
"name": "interestTargetUtilization",
"type": "f32"
} }
] ]
}, },
@ -930,6 +938,18 @@ export type MangoV4 = {
"type": { "type": {
"option": "f32" "option": "f32"
} }
},
{
"name": "interestCurveScalingOpt",
"type": {
"option": "f32"
}
},
{
"name": "interestTargetUtilizationOpt",
"type": {
"option": "f32"
}
} }
] ]
}, },
@ -7033,12 +7053,29 @@ export type MangoV4 = {
"name": "flashLoanSwapFeeRate", "name": "flashLoanSwapFeeRate",
"type": "f32" "type": "f32"
}, },
{
"name": "interestTargetUtilization",
"docs": [
"Target utilization: If actual utilization is higher, scale up interest.",
"If it's lower, scale down interest (if possible)"
],
"type": "f32"
},
{
"name": "interestCurveScaling",
"docs": [
"Current interest curve scaling, always >= 1.0",
"",
"Except when first migrating to having this field, then 0.0"
],
"type": "f64"
},
{ {
"name": "reserved", "name": "reserved",
"type": { "type": {
"array": [ "array": [
"u8", "u8",
2092 2080
] ]
} }
} }
@ -11335,6 +11372,56 @@ export type MangoV4 = {
} }
] ]
}, },
{
"name": "UpdateRateLogV2",
"fields": [
{
"name": "mangoGroup",
"type": "publicKey",
"index": false
},
{
"name": "tokenIndex",
"type": "u16",
"index": false
},
{
"name": "rate0",
"type": "i128",
"index": false
},
{
"name": "util0",
"type": "i128",
"index": false
},
{
"name": "rate1",
"type": "i128",
"index": false
},
{
"name": "util1",
"type": "i128",
"index": false
},
{
"name": "maxRate",
"type": "i128",
"index": false
},
{
"name": "curveScaling",
"type": "f64",
"index": false
},
{
"name": "targetUtilization",
"type": "f32",
"index": false
}
]
},
{ {
"name": "TokenLiqWithTokenLog", "name": "TokenLiqWithTokenLog",
"fields": [ "fields": [
@ -13699,6 +13786,14 @@ export const IDL: MangoV4 = {
{ {
"name": "flashLoanSwapFeeRate", "name": "flashLoanSwapFeeRate",
"type": "f32" "type": "f32"
},
{
"name": "interestCurveScaling",
"type": "f32"
},
{
"name": "interestTargetUtilization",
"type": "f32"
} }
] ]
}, },
@ -14033,6 +14128,18 @@ export const IDL: MangoV4 = {
"type": { "type": {
"option": "f32" "option": "f32"
} }
},
{
"name": "interestCurveScalingOpt",
"type": {
"option": "f32"
}
},
{
"name": "interestTargetUtilizationOpt",
"type": {
"option": "f32"
}
} }
] ]
}, },
@ -20136,12 +20243,29 @@ export const IDL: MangoV4 = {
"name": "flashLoanSwapFeeRate", "name": "flashLoanSwapFeeRate",
"type": "f32" "type": "f32"
}, },
{
"name": "interestTargetUtilization",
"docs": [
"Target utilization: If actual utilization is higher, scale up interest.",
"If it's lower, scale down interest (if possible)"
],
"type": "f32"
},
{
"name": "interestCurveScaling",
"docs": [
"Current interest curve scaling, always >= 1.0",
"",
"Except when first migrating to having this field, then 0.0"
],
"type": "f64"
},
{ {
"name": "reserved", "name": "reserved",
"type": { "type": {
"array": [ "array": [
"u8", "u8",
2092 2080
] ]
} }
} }
@ -24438,6 +24562,56 @@ export const IDL: MangoV4 = {
} }
] ]
}, },
{
"name": "UpdateRateLogV2",
"fields": [
{
"name": "mangoGroup",
"type": "publicKey",
"index": false
},
{
"name": "tokenIndex",
"type": "u16",
"index": false
},
{
"name": "rate0",
"type": "i128",
"index": false
},
{
"name": "util0",
"type": "i128",
"index": false
},
{
"name": "rate1",
"type": "i128",
"index": false
},
{
"name": "util1",
"type": "i128",
"index": false
},
{
"name": "maxRate",
"type": "i128",
"index": false
},
{
"name": "curveScaling",
"type": "f64",
"index": false
},
{
"name": "targetUtilization",
"type": "f32",
"index": false
}
]
},
{ {
"name": "TokenLiqWithTokenLog", "name": "TokenLiqWithTokenLog",
"fields": [ "fields": [