1pub mod defaults;
4use crate::prelude::*;
5
6use self::defaults::ConfigDefaults;
7
8use super::spot_price::{SpotPriceConfig, SpotPriceConfigInit};
9
10#[cw_serde]
17pub struct Config {
18 pub trading_fee_notional_size: Decimal256,
20 pub trading_fee_counter_collateral: Decimal256,
22 pub crank_execs: u32,
24 pub max_leverage: Number,
26 pub funding_rate_sensitivity: Decimal256,
28 pub funding_rate_max_annualized: Decimal256,
30 pub borrow_fee_rate_min_annualized: NumberGtZero,
32 pub borrow_fee_rate_max_annualized: NumberGtZero,
34 pub carry_leverage: Decimal256,
38 pub mute_events: bool,
40 pub liquifunding_delay_seconds: u32,
42 pub protocol_tax: Decimal256,
44 pub unstake_period_seconds: u32,
46 pub target_utilization: NonZero<Decimal256>,
48 pub borrow_fee_sensitivity: NumberGtZero,
52 pub max_xlp_rewards_multiplier: NumberGtZero,
58 pub min_xlp_rewards_multiplier: NumberGtZero,
60 pub delta_neutrality_fee_sensitivity: NumberGtZero,
67 pub delta_neutrality_fee_cap: NumberGtZero,
69 pub delta_neutrality_fee_tax: Decimal256,
71 pub crank_fee_charged: Usd,
73 #[serde(default = "ConfigDefaults::crank_fee_surcharge")]
83 pub crank_fee_surcharge: Usd,
84 pub crank_fee_reward: Usd,
86 pub minimum_deposit_usd: Usd,
88 #[serde(default = "ConfigDefaults::liquifunding_delay_fuzz_seconds")]
94 pub liquifunding_delay_fuzz_seconds: u32,
95 #[serde(default)]
97 pub max_liquidity: MaxLiquidity,
98 #[serde(default)]
101 pub disable_position_nft_exec: bool,
102 #[serde(default)]
117 pub liquidity_cooldown_seconds: u32,
118
119 #[serde(default = "ConfigDefaults::exposure_margin_ratio")]
121 pub exposure_margin_ratio: Decimal256,
122
123 #[serde(default = "ConfigDefaults::referral_reward_ratio")]
125 pub referral_reward_ratio: Decimal256,
126
127 pub spot_price: SpotPriceConfig,
129
130 #[serde(rename = "price_update_too_old_seconds")]
133 pub _unused1: Option<u32>,
134 #[serde(rename = "unpend_limit")]
136 pub _unused2: Option<u32>,
137 #[serde(rename = "limit_order_fee")]
139 pub _unused3: Option<Collateral>,
140 #[serde(rename = "staleness_seconds")]
142 pub _unused4: Option<u32>,
143}
144
145#[cw_serde]
150pub enum MaxLiquidity {
151 Unlimited {},
153 Usd {
157 amount: NonZero<Usd>,
159 },
160}
161
162impl Default for MaxLiquidity {
163 fn default() -> Self {
164 MaxLiquidity::Unlimited {}
165 }
166}
167
168impl Config {
169 pub fn new(spot_price: SpotPriceConfig) -> Self {
171 Self {
173 trading_fee_notional_size: ConfigDefaults::trading_fee_notional_size(),
174 trading_fee_counter_collateral: ConfigDefaults::trading_fee_counter_collateral(),
175 crank_execs: ConfigDefaults::crank_execs(),
176 max_leverage: ConfigDefaults::max_leverage(),
177 carry_leverage: ConfigDefaults::carry_leverage(),
178 funding_rate_max_annualized: ConfigDefaults::funding_rate_max_annualized(),
179 borrow_fee_rate_min_annualized: ConfigDefaults::borrow_fee_rate_min_annualized(),
180 borrow_fee_rate_max_annualized: ConfigDefaults::borrow_fee_rate_max_annualized(),
181 funding_rate_sensitivity: ConfigDefaults::funding_rate_sensitivity(),
182 mute_events: ConfigDefaults::mute_events(),
183 liquifunding_delay_seconds: ConfigDefaults::liquifunding_delay_seconds(),
184 protocol_tax: ConfigDefaults::protocol_tax(),
185 unstake_period_seconds: ConfigDefaults::unstake_period_seconds(),
186 target_utilization: ConfigDefaults::target_utilization(),
187 borrow_fee_sensitivity: ConfigDefaults::borrow_fee_sensitivity(),
188 max_xlp_rewards_multiplier: ConfigDefaults::max_xlp_rewards_multiplier(),
189 min_xlp_rewards_multiplier: ConfigDefaults::min_xlp_rewards_multiplier(),
190 delta_neutrality_fee_sensitivity: ConfigDefaults::delta_neutrality_fee_sensitivity(),
191 delta_neutrality_fee_cap: ConfigDefaults::delta_neutrality_fee_cap(),
192 delta_neutrality_fee_tax: ConfigDefaults::delta_neutrality_fee_tax(),
193 crank_fee_charged: ConfigDefaults::crank_fee_charged(),
194 crank_fee_surcharge: ConfigDefaults::crank_fee_surcharge(),
195 crank_fee_reward: ConfigDefaults::crank_fee_reward(),
196 minimum_deposit_usd: ConfigDefaults::minimum_deposit_usd(),
197 liquifunding_delay_fuzz_seconds: ConfigDefaults::liquifunding_delay_fuzz_seconds(),
198 max_liquidity: ConfigDefaults::max_liquidity(),
199 disable_position_nft_exec: ConfigDefaults::disable_position_nft_exec(),
200 liquidity_cooldown_seconds: ConfigDefaults::liquidity_cooldown_seconds(),
201 exposure_margin_ratio: ConfigDefaults::exposure_margin_ratio(),
202 referral_reward_ratio: ConfigDefaults::referral_reward_ratio(),
203 spot_price,
204 _unused1: None,
205 _unused2: None,
206 _unused3: None,
207 _unused4: None,
208 }
209 }
210
211 pub fn validate(&self) -> Result<()> {
213 if self.trading_fee_notional_size >= "0.0999".parse().unwrap() {
216 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);
217 bail!(PerpError::market(ErrorId::Config, error_msg))
218 }
219
220 if self.trading_fee_counter_collateral >= "0.0999".parse().unwrap() {
221 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);
222 bail!(PerpError::market(ErrorId::Config, error_msg))
223 }
224
225 if self.crank_execs == 0 {
226 bail!(PerpError::market(
227 ErrorId::Config,
228 "crank_execs_per_batch must be greater than zero"
229 ))
230 }
231
232 if self.max_leverage <= Number::ONE {
233 bail!(PerpError::market(
234 ErrorId::Config,
235 format!(
236 "max_leverage must be greater than one ({} is invalid)",
237 self.max_leverage
238 )
239 ))
240 }
241
242 if self.carry_leverage <= Decimal256::one() {
243 bail!(PerpError::market(
244 ErrorId::Config,
245 format!(
246 "carry_leverage must be greater than one ({} is invalid)",
247 self.carry_leverage
248 )
249 ))
250 }
251
252 if (self.carry_leverage.into_number() + Number::ONE)? > self.max_leverage {
253 let msg = format!("carry_leverage must be at least one less than max_leverage ({} is invalid, max_leverage is {})",
254 self.carry_leverage,
255 self.max_leverage);
256 bail!(PerpError::market(ErrorId::Config, msg))
257 }
258
259 if self.borrow_fee_rate_max_annualized < self.borrow_fee_rate_min_annualized {
260 let msg = format!("borrow_fee_rate_min_annualized ({}) must be less than borrow_fee_rate_max_annualized ({})",
261 self.borrow_fee_rate_min_annualized,
262 self.borrow_fee_rate_max_annualized);
263 bail!(PerpError::market(ErrorId::Config, msg))
264 }
265
266 if self.protocol_tax >= Decimal256::one() {
267 let msg = format!(
268 "protocol_tax must be less than or equal to 1 ({} is invalid)",
269 self.protocol_tax
270 );
271 bail!(PerpError::market(ErrorId::Config, msg))
272 }
273
274 if self.unstake_period_seconds == 0 {
275 let msg = format!(
276 "unstake period must be greater than 0 ({} is invalid)",
277 self.unstake_period_seconds
278 );
279 bail!(PerpError::market(ErrorId::Config, msg))
280 }
281
282 if Number::from(self.target_utilization) >= Number::ONE {
283 let msg = format!(
284 "Target utilization ratio must be between 0 and 1 exclusive ({} is invalid)",
285 self.target_utilization
286 );
287 bail!(PerpError::market(ErrorId::Config, msg))
288 }
289
290 if Number::from(self.min_xlp_rewards_multiplier) < Number::ONE {
291 let msg = format!(
292 "Min xLP rewards multiplier must be at least 1 ({} is invalid)",
293 self.max_xlp_rewards_multiplier
294 );
295 bail!(PerpError::market(ErrorId::Config, msg))
296 }
297
298 if self.max_xlp_rewards_multiplier < self.min_xlp_rewards_multiplier {
299 let msg = format!(
300 "Max xLP rewards multiplier ({}) must be greater than or equal to the min ({})",
301 self.max_xlp_rewards_multiplier, self.min_xlp_rewards_multiplier
302 );
303 bail!(PerpError::market(ErrorId::Config, msg))
304 }
305
306 if self.crank_fee_charged < self.crank_fee_reward {
307 let msg = format!(
308 "Crank fee charged ({}) must be greater than or equal to the crank fee reward ({})",
309 self.crank_fee_charged, self.crank_fee_reward
310 );
311 bail!(PerpError::market(ErrorId::Config, msg))
312 }
313
314 if self.delta_neutrality_fee_tax > Decimal256::one() {
315 let msg = format!(
316 "Delta neutrality fee tax ({}) must be less than or equal to 1",
317 self.delta_neutrality_fee_tax
318 );
319 bail!(PerpError::market(ErrorId::Config, msg))
320 }
321
322 if self.liquifunding_delay_fuzz_seconds >= self.liquifunding_delay_seconds {
323 let msg = format!("Liquifunding delay fuzz ({}) must be less than or equal to the liquifunding delay ({})",
324 self.liquifunding_delay_fuzz_seconds,
325 self.liquifunding_delay_seconds);
326 bail!(PerpError::market(ErrorId::Config, msg))
327 }
328
329 if self.exposure_margin_ratio >= Decimal256::one() {
330 let msg = format!(
331 "Exposure margin ratio ({}) must be less than 1",
332 self.exposure_margin_ratio
333 );
334 bail!(PerpError::market(ErrorId::Config, msg))
335 }
336
337 if self.referral_reward_ratio >= Decimal256::one() {
338 let msg = format!(
339 "Referral reward ratio ({}) must be less than 1",
340 self.referral_reward_ratio
341 );
342 bail!(PerpError::market(ErrorId::Config, msg))
343 }
344
345 Ok(())
346 }
347}
348
349#[cw_serde]
354#[allow(missing_docs)]
355#[derive(Default)]
356pub struct ConfigUpdate {
357 pub trading_fee_notional_size: Option<Decimal256>,
358 pub trading_fee_counter_collateral: Option<Decimal256>,
359 pub crank_execs: Option<u32>,
360 pub max_leverage: Option<Number>,
361 pub carry_leverage: Option<Decimal256>,
362 pub funding_rate_sensitivity: Option<Decimal256>,
363 pub funding_rate_max_annualized: Option<Decimal256>,
364 pub borrow_fee_rate_min_annualized: Option<NumberGtZero>,
365 pub borrow_fee_rate_max_annualized: Option<NumberGtZero>,
366 pub mute_events: Option<bool>,
367 pub liquifunding_delay_seconds: Option<u32>,
368 pub protocol_tax: Option<Decimal256>,
369 pub unstake_period_seconds: Option<u32>,
370 pub target_utilization: Option<NumberGtZero>,
371 pub borrow_fee_sensitivity: Option<NumberGtZero>,
372 pub max_xlp_rewards_multiplier: Option<NumberGtZero>,
373 pub min_xlp_rewards_multiplier: Option<NumberGtZero>,
374 pub delta_neutrality_fee_sensitivity: Option<NumberGtZero>,
375 pub delta_neutrality_fee_cap: Option<NumberGtZero>,
376 pub delta_neutrality_fee_tax: Option<Decimal256>,
377 pub crank_fee_charged: Option<Usd>,
378 pub crank_fee_surcharge: Option<Usd>,
379 pub crank_fee_reward: Option<Usd>,
380 pub minimum_deposit_usd: Option<Usd>,
381 pub liquifunding_delay_fuzz_seconds: Option<u32>,
382 pub max_liquidity: Option<MaxLiquidity>,
383 pub disable_position_nft_exec: Option<bool>,
384 pub liquidity_cooldown_seconds: Option<u32>,
385 pub spot_price: Option<SpotPriceConfigInit>,
386 pub exposure_margin_ratio: Option<Decimal256>,
387 pub referral_reward_ratio: Option<Decimal256>,
388}
389#[cfg(feature = "arbitrary")]
390impl<'a> arbitrary::Arbitrary<'a> for ConfigUpdate {
391 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
392 Ok(Self {
393 trading_fee_notional_size: arbitrary_decimal_256_option(u)?,
394 trading_fee_counter_collateral: arbitrary_decimal_256_option(u)?,
395 crank_execs: u.arbitrary()?,
396 max_leverage: u.arbitrary()?,
397 carry_leverage: arbitrary_decimal_256_option(u)?,
398 funding_rate_sensitivity: arbitrary_decimal_256_option(u)?,
399 funding_rate_max_annualized: arbitrary_decimal_256_option(u)?,
400 borrow_fee_rate_min_annualized: u.arbitrary()?,
401 borrow_fee_rate_max_annualized: u.arbitrary()?,
402 mute_events: u.arbitrary()?,
403 liquifunding_delay_seconds: u.arbitrary()?,
404 protocol_tax: arbitrary_decimal_256_option(u)?,
405 unstake_period_seconds: u.arbitrary()?,
406 target_utilization: u.arbitrary()?,
407 borrow_fee_sensitivity: u.arbitrary()?,
408 max_xlp_rewards_multiplier: u.arbitrary()?,
409 min_xlp_rewards_multiplier: u.arbitrary()?,
410 delta_neutrality_fee_sensitivity: u.arbitrary()?,
411 delta_neutrality_fee_cap: u.arbitrary()?,
412 delta_neutrality_fee_tax: arbitrary_decimal_256_option(u)?,
413 crank_fee_charged: u.arbitrary()?,
414 crank_fee_surcharge: u.arbitrary()?,
415 crank_fee_reward: u.arbitrary()?,
416 minimum_deposit_usd: u.arbitrary()?,
417 liquifunding_delay_fuzz_seconds: None,
418 max_liquidity: None,
419 disable_position_nft_exec: None,
420 liquidity_cooldown_seconds: None,
421 exposure_margin_ratio: arbitrary_decimal_256_option(u)?,
422 referral_reward_ratio: None,
423 spot_price: None,
424 })
425 }
426}
427
428impl From<Config> for ConfigUpdate {
429 fn from(src: Config) -> Self {
430 Self {
431 trading_fee_notional_size: Some(src.trading_fee_notional_size),
432 trading_fee_counter_collateral: Some(src.trading_fee_counter_collateral),
433 crank_execs: Some(src.crank_execs),
434 max_leverage: Some(src.max_leverage),
435 carry_leverage: Some(src.carry_leverage),
436 funding_rate_sensitivity: Some(src.funding_rate_sensitivity),
437 funding_rate_max_annualized: Some(src.funding_rate_max_annualized),
438 mute_events: Some(src.mute_events),
439 liquifunding_delay_seconds: Some(src.liquifunding_delay_seconds),
440 protocol_tax: Some(src.protocol_tax),
441 unstake_period_seconds: Some(src.unstake_period_seconds),
442 target_utilization: Some(src.target_utilization),
443 borrow_fee_sensitivity: Some(src.borrow_fee_sensitivity),
444 borrow_fee_rate_min_annualized: Some(src.borrow_fee_rate_min_annualized),
445 borrow_fee_rate_max_annualized: Some(src.borrow_fee_rate_max_annualized),
446 max_xlp_rewards_multiplier: Some(src.max_xlp_rewards_multiplier),
447 min_xlp_rewards_multiplier: Some(src.min_xlp_rewards_multiplier),
448 delta_neutrality_fee_sensitivity: Some(src.delta_neutrality_fee_sensitivity),
449 delta_neutrality_fee_cap: Some(src.delta_neutrality_fee_cap),
450 delta_neutrality_fee_tax: Some(src.delta_neutrality_fee_tax),
451 crank_fee_charged: Some(src.crank_fee_charged),
452 crank_fee_surcharge: Some(src.crank_fee_surcharge),
453 crank_fee_reward: Some(src.crank_fee_reward),
454 minimum_deposit_usd: Some(src.minimum_deposit_usd),
455 liquifunding_delay_fuzz_seconds: Some(src.liquifunding_delay_fuzz_seconds),
456 max_liquidity: Some(src.max_liquidity),
457 disable_position_nft_exec: Some(src.disable_position_nft_exec),
458 liquidity_cooldown_seconds: Some(src.liquidity_cooldown_seconds),
459 exposure_margin_ratio: Some(src.exposure_margin_ratio),
460 referral_reward_ratio: Some(src.referral_reward_ratio),
461 spot_price: Some(src.spot_price.into()),
462 }
463 }
464}