use crate::mip3::params::{OracleSource, PerpDeployBuilder};
#[must_use]
pub fn btc_standard() -> PerpDeployBuilder {
PerpDeployBuilder::new(
"BTC",
"BTC",
8,
OracleSource::all().to_vec(),
50, 45, -10, 10_000, 0, )
.expect("static preset always valid")
}
#[must_use]
pub fn eth_standard() -> PerpDeployBuilder {
PerpDeployBuilder::new(
"ETH",
"ETH",
8,
OracleSource::all().to_vec(),
50,
45,
-10,
10_000,
0,
)
.expect("static preset always valid")
}
#[must_use]
pub fn long_tail_default() -> PerpDeployBuilder {
let sources: Vec<OracleSource> = OracleSource::all()
.iter()
.copied()
.filter(|s| !matches!(s, OracleSource::Coinbase | OracleSource::Kraken))
.collect();
PerpDeployBuilder::new(
"ALT-PERP", "ALT", 8, sources, 10, 80, 0, 10_000, 5, )
.expect("static preset always valid")
}
#[must_use]
pub fn mm_friendly() -> PerpDeployBuilder {
PerpDeployBuilder::new(
"MM-PERP",
"MM",
8,
OracleSource::all().to_vec(),
20,
30, -20, 1_000,
0,
)
.expect("static preset always valid")
}
pub const PRESET_NAMES: [&str; 4] = [
"btc_standard",
"eth_standard",
"long_tail_default",
"mm_friendly",
];
#[must_use]
pub fn preset_by_name(name: &str) -> Option<PerpDeployBuilder> {
match name {
"btc_standard" => Some(btc_standard()),
"eth_standard" => Some(eth_standard()),
"long_tail_default" => Some(long_tail_default()),
"mm_friendly" => Some(mm_friendly()),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mip3::params::Action;
#[test]
fn btc_preset_has_full_oracle_set() {
let b = btc_standard();
assert_eq!(b.oracle_sources.len(), 10);
assert_eq!(b.max_leverage, 50);
assert_eq!(b.taker_fee_bps, 45);
assert_eq!(b.maker_fee_bps, -10);
}
#[test]
fn eth_preset_has_full_oracle_set() {
let b = eth_standard();
assert_eq!(b.asset_name, "ETH");
assert_eq!(b.asset_symbol, "ETH");
assert_eq!(b.oracle_sources.len(), 10);
}
#[test]
fn long_tail_preset_excludes_coinbase_kraken() {
let b = long_tail_default();
assert_eq!(b.oracle_sources.len(), 8);
assert!(!b.oracle_sources.contains(&OracleSource::Coinbase));
assert!(!b.oracle_sources.contains(&OracleSource::Kraken));
assert_eq!(b.max_leverage, 10);
}
#[test]
fn mm_friendly_preset_has_negative_maker_fee() {
let b = mm_friendly();
assert!(b.maker_fee_bps < 0, "rebate expected");
assert_eq!(b.max_leverage, 20);
assert_eq!(b.min_order_size, 1_000);
}
#[test]
fn preset_names_has_four_entries() {
assert_eq!(PRESET_NAMES.len(), 4);
let mut sorted = PRESET_NAMES.to_vec();
sorted.sort_unstable();
sorted.dedup();
assert_eq!(sorted.len(), 4, "preset names must be unique");
}
#[test]
fn every_preset_produces_eight_actions() {
for name in PRESET_NAMES {
let b = preset_by_name(name).expect("preset_by_name");
let seq = b.deploy_sequence();
assert_eq!(seq.len(), 8, "preset {name} must yield 8 actions");
assert_eq!(seq[0].type_id(), "perp_register_asset");
assert_eq!(seq[7].type_id(), "perp_activate_market");
}
}
#[test]
fn preset_by_name_returns_none_for_unknown() {
assert!(preset_by_name("zz_unknown").is_none());
}
#[test]
fn preset_chained_with_modifiers_still_validates() {
let b = btc_standard().with_asset_name("BTC-PERP-2026");
b.validate().expect("post-chain validation");
let seq = b.deploy_sequence();
for action in &seq {
if let Action::PerpRegisterAsset { asset_name, .. } = action {
assert_eq!(asset_name, "BTC-PERP-2026");
}
}
}
}