Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 1 addition & 187 deletions crates/autopilot/src/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ use {
crate::{database::INSERT_BATCH_SIZE_DEFAULT, infra},
alloy::primitives::Address,
anyhow::Context,
chrono::{DateTime, Utc},
clap::ValueEnum,
shared::{
arguments::{FeeFactor, display_option, display_secret_option},
arguments::{display_option, display_secret_option},
http_client,
price_estimation::{self, NativePriceEstimators},
},
Expand Down Expand Up @@ -172,10 +170,6 @@ pub struct CliArguments {
)]
pub solve_deadline: Duration,

/// Describes how the protocol fees should be calculated.
#[clap(flatten)]
pub fee_policies_config: FeePoliciesConfig,

/// Arguments for uploading information to S3.
#[clap(flatten)]
pub s3: infra::persistence::cli::S3,
Expand Down Expand Up @@ -291,7 +285,6 @@ impl std::fmt::Display for CliArguments {
submission_deadline,
shadow,
solve_deadline,
fee_policies_config,
order_events_cleanup_interval,
order_events_cleanup_threshold,
db_write_url,
Expand Down Expand Up @@ -349,7 +342,6 @@ impl std::fmt::Display for CliArguments {
writeln!(f, "submission_deadline: {submission_deadline}")?;
display_option(f, "shadow", shadow)?;
writeln!(f, "solve_deadline: {solve_deadline:?}")?;
writeln!(f, "fee_policies_config: {fee_policies_config:?}")?;
writeln!(
f,
"order_events_cleanup_interval: {order_events_cleanup_interval:?}"
Expand Down Expand Up @@ -395,153 +387,6 @@ impl std::fmt::Display for CliArguments {
}
}

#[derive(clap::Parser, Debug, Clone)]
pub struct FeePoliciesConfig {
/// Describes how the protocol fees should be calculated.
#[clap(long, env, use_value_delimiter = true)]
pub fee_policies: Vec<FeePolicy>,

/// Maximum partner fee allowed. If the partner fee specified is greater
/// than this maximum, the partner fee will be capped
#[clap(long, env, default_value = "0.01")]
pub fee_policy_max_partner_fee: FeeFactor,

/// Volume fee policies that will become effective at a future timestamp.
#[clap(flatten)]
pub upcoming_fee_policies: UpcomingFeePolicies,
}

/// A fee policy to be used for orders base on it's class.
/// Examples:
/// - Surplus with a high enough cap for limit orders: surplus:0.5:0.9:limit
///
/// - Surplus with cap for market orders: surplus:0.5:0.06:market
///
/// - Price improvement with a high enough cap for any order class:
/// price_improvement:0.5:0.9:any
///
/// - Price improvement with cap for limit orders:
/// price_improvement:0.5:0.06:limit
///
/// - Volume based fee for any order class: volume:0.1:any
#[derive(Debug, Clone)]
pub struct FeePolicy {
pub fee_policy_kind: FeePolicyKind,
pub fee_policy_order_class: FeePolicyOrderClass,
}

/// Fee policies that will become effective at a future timestamp.
#[derive(clap::Parser, Debug, Clone)]
pub struct UpcomingFeePolicies {
#[clap(
id = "upcoming_fee_policies",
long = "upcoming-fee-policies",
env = "UPCOMING_FEE_POLICIES",
use_value_delimiter = true
)]
pub fee_policies: Vec<FeePolicy>,

#[clap(
long = "upcoming-fee-policies-timestamp",
env = "UPCOMING_FEE_POLICIES_TIMESTAMP"
)]
pub effective_from_timestamp: Option<DateTime<Utc>>,
}

#[derive(clap::Parser, Debug, Clone)]
pub enum FeePolicyKind {
/// How much of the order's surplus should be taken as a protocol fee.
Surplus {
factor: FeeFactor,
max_volume_factor: FeeFactor,
},
/// How much of the order's price improvement should be taken as a protocol
/// fee where price improvement is a difference between the executed price
/// and the best quote.
PriceImprovement {
factor: FeeFactor,
max_volume_factor: FeeFactor,
},
/// How much of the order's volume should be taken as a protocol fee.
Volume { factor: FeeFactor },
}

#[derive(clap::Parser, clap::ValueEnum, Clone, Debug)]
pub enum FeePolicyOrderClass {
/// If a fee policy needs to be applied to in-market orders.
Market,
/// If a fee policy needs to be applied to limit orders.
Limit,
/// If a fee policy needs to be applied regardless of the order class.
Any,
}

impl FromStr for FeePolicy {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(':');
let kind = parts.next().context("missing fee policy kind")?;
let fee_policy_kind = match kind {
"surplus" => {
let factor = parts
.next()
.context("missing surplus factor")?
.parse::<f64>()
.map_err(|e| anyhow::anyhow!("invalid surplus factor: {}", e))?;
let max_volume_factor = parts
.next()
.context("missing max volume factor")?
.parse::<f64>()
.map_err(|e| anyhow::anyhow!("invalid max volume factor: {}", e))?;
Ok(FeePolicyKind::Surplus {
factor: factor.try_into()?,
max_volume_factor: max_volume_factor.try_into()?,
})
}
"priceImprovement" => {
let factor = parts
.next()
.context("missing price improvement factor")?
.parse::<f64>()
.map_err(|e| anyhow::anyhow!("invalid price improvement factor: {}", e))?;
let max_volume_factor = parts
.next()
.context("missing price improvement max volume factor")?
.parse::<f64>()
.map_err(|e| {
anyhow::anyhow!("invalid price improvement max volume factor: {}", e)
})?;
Ok(FeePolicyKind::PriceImprovement {
factor: factor.try_into()?,
max_volume_factor: max_volume_factor.try_into()?,
})
}
"volume" => {
let factor = parts
.next()
.context("missing volume factor")?
.parse::<f64>()
.map_err(|e| anyhow::anyhow!("invalid volume factor: {}", e))?;
Ok(FeePolicyKind::Volume {
factor: factor.try_into()?,
})
}
_ => Err(anyhow::anyhow!("invalid fee policy kind: {}", kind)),
}?;
let fee_policy_order_class = FeePolicyOrderClass::from_str(
parts.next().context("missing fee policy order class")?,
true,
)
.map_err(|e| anyhow::anyhow!("invalid fee policy order class: {}", e))?;

Ok(FeePolicy {
fee_policy_kind,
fee_policy_order_class,
})
}
}

#[derive(Debug, Clone)]
pub struct CowAmmConfig {
/// Which contract to index for CoW AMM deployment events.
Expand Down Expand Up @@ -584,34 +429,3 @@ impl FromStr for CowAmmConfig {
})
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_fee_factor_limits() {
let policies = vec![
"volume:1.0:market",
"volume:-1.0:limit",
"surplus:1.0:0.5:any",
"surplus:0.5:1.0:limit",
"surplus:0.5:-1.0:market",
"surplus:-1.0:0.5:limit",
"priceImprovement:1.0:0.5:market",
"priceImprovement:-1.0:0.5:any",
"priceImprovement:0.5:1.0:market",
"priceImprovement:0.5:-1.0:limit",
];

for policy in policies {
assert!(
FeePolicy::from_str(policy)
.err()
.unwrap()
.to_string()
.contains("Factor must be in the range [0, 1)"),
)
}
}
}
Loading
Loading