pub mod defaults;
use crate::prelude::*;
use self::defaults::ConfigDefaults;
use super::spot_price::{SpotPriceConfig, SpotPriceConfigInit};
#[cw_serde]
pub struct Config {
pub trading_fee_notional_size: Decimal256,
pub trading_fee_counter_collateral: Decimal256,
pub crank_execs: u32,
pub max_leverage: Number,
pub funding_rate_sensitivity: Decimal256,
pub funding_rate_max_annualized: Decimal256,
pub borrow_fee_rate_min_annualized: NumberGtZero,
pub borrow_fee_rate_max_annualized: NumberGtZero,
pub carry_leverage: Decimal256,
pub mute_events: bool,
pub liquifunding_delay_seconds: u32,
pub protocol_tax: Decimal256,
pub unstake_period_seconds: u32,
pub target_utilization: NonZero<Decimal256>,
pub borrow_fee_sensitivity: NumberGtZero,
pub max_xlp_rewards_multiplier: NumberGtZero,
pub min_xlp_rewards_multiplier: NumberGtZero,
pub delta_neutrality_fee_sensitivity: NumberGtZero,
pub delta_neutrality_fee_cap: NumberGtZero,
pub delta_neutrality_fee_tax: Decimal256,
pub crank_fee_charged: Usd,
#[serde(default = "ConfigDefaults::crank_fee_surcharge")]
pub crank_fee_surcharge: Usd,
pub crank_fee_reward: Usd,
pub minimum_deposit_usd: Usd,
#[serde(default = "ConfigDefaults::liquifunding_delay_fuzz_seconds")]
pub liquifunding_delay_fuzz_seconds: u32,
#[serde(default)]
pub max_liquidity: MaxLiquidity,
#[serde(default)]
pub disable_position_nft_exec: bool,
#[serde(default)]
pub liquidity_cooldown_seconds: u32,
#[serde(default = "ConfigDefaults::exposure_margin_ratio")]
pub exposure_margin_ratio: Decimal256,
#[serde(default = "ConfigDefaults::referral_reward_ratio")]
pub referral_reward_ratio: Decimal256,
pub spot_price: SpotPriceConfig,
#[serde(rename = "price_update_too_old_seconds")]
pub _unused1: Option<u32>,
#[serde(rename = "unpend_limit")]
pub _unused2: Option<u32>,
#[serde(rename = "limit_order_fee")]
pub _unused3: Option<Collateral>,
#[serde(rename = "staleness_seconds")]
pub _unused4: Option<u32>,
}
#[cw_serde]
pub enum MaxLiquidity {
Unlimited {},
Usd {
amount: NonZero<Usd>,
},
}
impl Default for MaxLiquidity {
fn default() -> Self {
MaxLiquidity::Unlimited {}
}
}
impl Config {
pub fn new(spot_price: SpotPriceConfig) -> Self {
Self {
trading_fee_notional_size: ConfigDefaults::trading_fee_notional_size(),
trading_fee_counter_collateral: ConfigDefaults::trading_fee_counter_collateral(),
crank_execs: ConfigDefaults::crank_execs(),
max_leverage: ConfigDefaults::max_leverage(),
carry_leverage: ConfigDefaults::carry_leverage(),
funding_rate_max_annualized: ConfigDefaults::funding_rate_max_annualized(),
borrow_fee_rate_min_annualized: ConfigDefaults::borrow_fee_rate_min_annualized(),
borrow_fee_rate_max_annualized: ConfigDefaults::borrow_fee_rate_max_annualized(),
funding_rate_sensitivity: ConfigDefaults::funding_rate_sensitivity(),
mute_events: ConfigDefaults::mute_events(),
liquifunding_delay_seconds: ConfigDefaults::liquifunding_delay_seconds(),
protocol_tax: ConfigDefaults::protocol_tax(),
unstake_period_seconds: ConfigDefaults::unstake_period_seconds(),
target_utilization: ConfigDefaults::target_utilization(),
borrow_fee_sensitivity: ConfigDefaults::borrow_fee_sensitivity(),
max_xlp_rewards_multiplier: ConfigDefaults::max_xlp_rewards_multiplier(),
min_xlp_rewards_multiplier: ConfigDefaults::min_xlp_rewards_multiplier(),
delta_neutrality_fee_sensitivity: ConfigDefaults::delta_neutrality_fee_sensitivity(),
delta_neutrality_fee_cap: ConfigDefaults::delta_neutrality_fee_cap(),
delta_neutrality_fee_tax: ConfigDefaults::delta_neutrality_fee_tax(),
crank_fee_charged: ConfigDefaults::crank_fee_charged(),
crank_fee_surcharge: ConfigDefaults::crank_fee_surcharge(),
crank_fee_reward: ConfigDefaults::crank_fee_reward(),
minimum_deposit_usd: ConfigDefaults::minimum_deposit_usd(),
liquifunding_delay_fuzz_seconds: ConfigDefaults::liquifunding_delay_fuzz_seconds(),
max_liquidity: ConfigDefaults::max_liquidity(),
disable_position_nft_exec: ConfigDefaults::disable_position_nft_exec(),
liquidity_cooldown_seconds: ConfigDefaults::liquidity_cooldown_seconds(),
exposure_margin_ratio: ConfigDefaults::exposure_margin_ratio(),
referral_reward_ratio: ConfigDefaults::referral_reward_ratio(),
spot_price,
_unused1: None,
_unused2: None,
_unused3: None,
_unused4: None,
}
}
pub fn validate(&self) -> Result<()> {
if self.trading_fee_notional_size >= "0.0999".parse().unwrap() {
let error_msg = format!("trading_fee_notional_size must be in the range 0 to 0.0999 inclusive ({} is invalid)", self.trading_fee_notional_size);
bail!(PerpError::market(ErrorId::Config, error_msg))
}
if self.trading_fee_counter_collateral >= "0.0999".parse().unwrap() {
let error_msg = format!("trading_fee_counter_collateral must be in the range 0 to 0.0999 inclusive ({} is invalid)", self.trading_fee_counter_collateral);
bail!(PerpError::market(ErrorId::Config, error_msg))
}
if self.crank_execs == 0 {
bail!(PerpError::market(
ErrorId::Config,
"crank_execs_per_batch must be greater than zero"
))
}
if self.max_leverage <= Number::ONE {
bail!(PerpError::market(
ErrorId::Config,
format!(
"max_leverage must be greater than one ({} is invalid)",
self.max_leverage
)
))
}
if self.carry_leverage <= Decimal256::one() {
bail!(PerpError::market(
ErrorId::Config,
format!(
"carry_leverage must be greater than one ({} is invalid)",
self.carry_leverage
)
))
}
if (self.carry_leverage.into_number() + Number::ONE)? > self.max_leverage {
let msg = format!("carry_leverage must be at least one less than max_leverage ({} is invalid, max_leverage is {})",
self.carry_leverage,
self.max_leverage);
bail!(PerpError::market(ErrorId::Config, msg))
}
if self.borrow_fee_rate_max_annualized < self.borrow_fee_rate_min_annualized {
let msg = format!("borrow_fee_rate_min_annualized ({}) must be less than borrow_fee_rate_max_annualized ({})",
self.borrow_fee_rate_min_annualized,
self.borrow_fee_rate_max_annualized);
bail!(PerpError::market(ErrorId::Config, msg))
}
if self.protocol_tax >= Decimal256::one() {
let msg = format!(
"protocol_tax must be less than or equal to 1 ({} is invalid)",
self.protocol_tax
);
bail!(PerpError::market(ErrorId::Config, msg))
}
if self.unstake_period_seconds == 0 {
let msg = format!(
"unstake period must be greater than 0 ({} is invalid)",
self.unstake_period_seconds
);
bail!(PerpError::market(ErrorId::Config, msg))
}
if Number::from(self.target_utilization) >= Number::ONE {
let msg = format!(
"Target utilization ratio must be between 0 and 1 exclusive ({} is invalid)",
self.target_utilization
);
bail!(PerpError::market(ErrorId::Config, msg))
}
if Number::from(self.min_xlp_rewards_multiplier) < Number::ONE {
let msg = format!(
"Min xLP rewards multiplier must be at least 1 ({} is invalid)",
self.max_xlp_rewards_multiplier
);
bail!(PerpError::market(ErrorId::Config, msg))
}
if self.max_xlp_rewards_multiplier < self.min_xlp_rewards_multiplier {
let msg = format!(
"Max xLP rewards multiplier ({}) must be greater than or equal to the min ({})",
self.max_xlp_rewards_multiplier, self.min_xlp_rewards_multiplier
);
bail!(PerpError::market(ErrorId::Config, msg))
}
if self.crank_fee_charged < self.crank_fee_reward {
let msg = format!(
"Crank fee charged ({}) must be greater than or equal to the crank fee reward ({})",
self.crank_fee_charged, self.crank_fee_reward
);
bail!(PerpError::market(ErrorId::Config, msg))
}
if self.delta_neutrality_fee_tax > Decimal256::one() {
let msg = format!(
"Delta neutrality fee tax ({}) must be less than or equal to 1",
self.delta_neutrality_fee_tax
);
bail!(PerpError::market(ErrorId::Config, msg))
}
if self.liquifunding_delay_fuzz_seconds >= self.liquifunding_delay_seconds {
let msg = format!("Liquifunding delay fuzz ({}) must be less than or equal to the liquifunding delay ({})",
self.liquifunding_delay_fuzz_seconds,
self.liquifunding_delay_seconds);
bail!(PerpError::market(ErrorId::Config, msg))
}
Ok(())
}
}
#[cw_serde]
#[allow(missing_docs)]
#[derive(Default)]
pub struct ConfigUpdate {
pub trading_fee_notional_size: Option<Decimal256>,
pub trading_fee_counter_collateral: Option<Decimal256>,
pub crank_execs: Option<u32>,
pub max_leverage: Option<Number>,
pub carry_leverage: Option<Decimal256>,
pub funding_rate_sensitivity: Option<Decimal256>,
pub funding_rate_max_annualized: Option<Decimal256>,
pub borrow_fee_rate_min_annualized: Option<NumberGtZero>,
pub borrow_fee_rate_max_annualized: Option<NumberGtZero>,
pub mute_events: Option<bool>,
pub liquifunding_delay_seconds: Option<u32>,
pub protocol_tax: Option<Decimal256>,
pub unstake_period_seconds: Option<u32>,
pub target_utilization: Option<NumberGtZero>,
pub borrow_fee_sensitivity: Option<NumberGtZero>,
pub max_xlp_rewards_multiplier: Option<NumberGtZero>,
pub min_xlp_rewards_multiplier: Option<NumberGtZero>,
pub delta_neutrality_fee_sensitivity: Option<NumberGtZero>,
pub delta_neutrality_fee_cap: Option<NumberGtZero>,
pub delta_neutrality_fee_tax: Option<Decimal256>,
pub crank_fee_charged: Option<Usd>,
pub crank_fee_surcharge: Option<Usd>,
pub crank_fee_reward: Option<Usd>,
pub minimum_deposit_usd: Option<Usd>,
pub liquifunding_delay_fuzz_seconds: Option<u32>,
pub max_liquidity: Option<MaxLiquidity>,
pub disable_position_nft_exec: Option<bool>,
pub liquidity_cooldown_seconds: Option<u32>,
pub spot_price: Option<SpotPriceConfigInit>,
pub exposure_margin_ratio: Option<Decimal256>,
pub referral_reward_ratio: Option<Decimal256>,
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for ConfigUpdate {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self {
trading_fee_notional_size: arbitrary_decimal_256_option(u)?,
trading_fee_counter_collateral: arbitrary_decimal_256_option(u)?,
crank_execs: u.arbitrary()?,
max_leverage: u.arbitrary()?,
carry_leverage: arbitrary_decimal_256_option(u)?,
funding_rate_sensitivity: arbitrary_decimal_256_option(u)?,
funding_rate_max_annualized: arbitrary_decimal_256_option(u)?,
borrow_fee_rate_min_annualized: u.arbitrary()?,
borrow_fee_rate_max_annualized: u.arbitrary()?,
mute_events: u.arbitrary()?,
liquifunding_delay_seconds: u.arbitrary()?,
protocol_tax: arbitrary_decimal_256_option(u)?,
unstake_period_seconds: u.arbitrary()?,
target_utilization: u.arbitrary()?,
borrow_fee_sensitivity: u.arbitrary()?,
max_xlp_rewards_multiplier: u.arbitrary()?,
min_xlp_rewards_multiplier: u.arbitrary()?,
delta_neutrality_fee_sensitivity: u.arbitrary()?,
delta_neutrality_fee_cap: u.arbitrary()?,
delta_neutrality_fee_tax: arbitrary_decimal_256_option(u)?,
crank_fee_charged: u.arbitrary()?,
crank_fee_surcharge: u.arbitrary()?,
crank_fee_reward: u.arbitrary()?,
minimum_deposit_usd: u.arbitrary()?,
liquifunding_delay_fuzz_seconds: None,
max_liquidity: None,
disable_position_nft_exec: None,
liquidity_cooldown_seconds: None,
exposure_margin_ratio: arbitrary_decimal_256_option(u)?,
referral_reward_ratio: None,
spot_price: None,
})
}
}
impl From<Config> for ConfigUpdate {
fn from(src: Config) -> Self {
Self {
trading_fee_notional_size: Some(src.trading_fee_notional_size),
trading_fee_counter_collateral: Some(src.trading_fee_counter_collateral),
crank_execs: Some(src.crank_execs),
max_leverage: Some(src.max_leverage),
carry_leverage: Some(src.carry_leverage),
funding_rate_sensitivity: Some(src.funding_rate_sensitivity),
funding_rate_max_annualized: Some(src.funding_rate_max_annualized),
mute_events: Some(src.mute_events),
liquifunding_delay_seconds: Some(src.liquifunding_delay_seconds),
protocol_tax: Some(src.protocol_tax),
unstake_period_seconds: Some(src.unstake_period_seconds),
target_utilization: Some(src.target_utilization),
borrow_fee_sensitivity: Some(src.borrow_fee_sensitivity),
borrow_fee_rate_min_annualized: Some(src.borrow_fee_rate_min_annualized),
borrow_fee_rate_max_annualized: Some(src.borrow_fee_rate_max_annualized),
max_xlp_rewards_multiplier: Some(src.max_xlp_rewards_multiplier),
min_xlp_rewards_multiplier: Some(src.min_xlp_rewards_multiplier),
delta_neutrality_fee_sensitivity: Some(src.delta_neutrality_fee_sensitivity),
delta_neutrality_fee_cap: Some(src.delta_neutrality_fee_cap),
delta_neutrality_fee_tax: Some(src.delta_neutrality_fee_tax),
crank_fee_charged: Some(src.crank_fee_charged),
crank_fee_surcharge: Some(src.crank_fee_surcharge),
crank_fee_reward: Some(src.crank_fee_reward),
minimum_deposit_usd: Some(src.minimum_deposit_usd),
liquifunding_delay_fuzz_seconds: Some(src.liquifunding_delay_fuzz_seconds),
max_liquidity: Some(src.max_liquidity),
disable_position_nft_exec: Some(src.disable_position_nft_exec),
liquidity_cooldown_seconds: Some(src.liquidity_cooldown_seconds),
exposure_margin_ratio: Some(src.exposure_margin_ratio),
referral_reward_ratio: Some(src.referral_reward_ratio),
spot_price: Some(src.spot_price.into()),
}
}
}