Skip to content

Commit

Permalink
Make the partner fee max cap configurable in autopilot
Browse files Browse the repository at this point in the history
  • Loading branch information
m-lord-renkse committed Mar 22, 2024
1 parent e09bded commit aff70ba
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 58 deletions.
5 changes: 5 additions & 0 deletions crates/autopilot/src/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,11 @@ pub struct FeePolicy {
/// filled.
#[clap(long, env, action = clap::ArgAction::Set, default_value = "true")]
pub fee_policy_skip_market_orders: bool,

/// Maximum partner fee allow. 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,
}

#[derive(clap::Parser, Debug, Clone)]
Expand Down
47 changes: 38 additions & 9 deletions crates/autopilot/src/domain/fee/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use {
crate::{
arguments,
boundary::{self},
domain,
domain::{
self,
fee::policy::{PriceImprovement, Surplus, Volume},
},
},
app_data::Validator,
derive_more::Into,
Expand All @@ -23,15 +26,39 @@ use {
/// Constructs fee policies based on the current configuration.
pub struct ProtocolFee {
policy: policy::Policy,
max_partner_fee: FeeFactor,
}

impl ProtocolFee {
pub fn new(fee_policy_args: arguments::FeePolicy) -> Self {
impl From<arguments::FeePolicy> for ProtocolFee {
fn from(policy_arg: arguments::FeePolicy) -> Self {
let policy = match policy_arg.fee_policy_kind {
arguments::FeePolicyKind::Surplus {
factor,
max_volume_factor,
} => policy::Policy::Surplus(Surplus {
factor,
max_volume_factor,
skip_market_orders: policy_arg.fee_policy_skip_market_orders,
}),
arguments::FeePolicyKind::PriceImprovement {
factor,
max_volume_factor,
} => policy::Policy::PriceImprovement(PriceImprovement {
factor,
max_volume_factor,
}),
arguments::FeePolicyKind::Volume { factor } => {
policy::Policy::Volume(Volume { factor })
}
};
Self {
policy: fee_policy_args.into(),
policy,
max_partner_fee: policy_arg.fee_policy_max_partner_fee,
}
}
}

impl ProtocolFee {
/// Converts an order from the boundary layer to the domain layer, applying
/// protocol fees if necessary.
pub fn apply(&self, order: boundary::Order, quote: &domain::Quote) -> domain::Order {
Expand All @@ -47,9 +74,11 @@ impl ProtocolFee {
{
if let Some(partner_fee) = validated_app_data.protocol.partner_fee {
let fee_policy = vec![Policy::Volume {
factor: FeeFactor::partner_fee_capped_from(
factor: FeeFactor::try_from_capped(
partner_fee.bps.into_f64() / 10_000.0,
),
self.max_partner_fee.into(),
)
.unwrap(),
}];
return boundary::order::to_domain(order, fee_policy);
}
Expand Down Expand Up @@ -105,9 +134,9 @@ pub enum Policy {
pub struct FeeFactor(f64);

impl FeeFactor {
/// Convert a partner fee into a `Factor` capping its value
pub fn partner_fee_capped_from(value: f64) -> Self {
Self(value.max(0.0).min(0.01))
/// Convert a fee into a `FeeFactor` capping its value
pub fn try_from_capped(value: f64, cap: f64) -> anyhow::Result<Self> {
value.max(0.0).min(cap).try_into()
}
}

Expand Down
36 changes: 6 additions & 30 deletions crates/autopilot/src/domain/fee/policy.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::{
arguments,
boundary,
domain::{self, fee::FeeFactor},
};
Expand All @@ -11,41 +10,18 @@ pub enum Policy {
}

pub struct Surplus {
factor: FeeFactor,
max_volume_factor: FeeFactor,
skip_market_orders: bool,
pub(crate) factor: FeeFactor,
pub(crate) max_volume_factor: FeeFactor,
pub(crate) skip_market_orders: bool,
}

pub struct PriceImprovement {
factor: FeeFactor,
max_volume_factor: FeeFactor,
pub(crate) factor: FeeFactor,
pub(crate) max_volume_factor: FeeFactor,
}

pub struct Volume {
factor: FeeFactor,
}

impl From<arguments::FeePolicy> for Policy {
fn from(policy_arg: arguments::FeePolicy) -> Self {
match policy_arg.fee_policy_kind {
arguments::FeePolicyKind::Surplus {
factor,
max_volume_factor,
} => Policy::Surplus(Surplus {
factor,
max_volume_factor,
skip_market_orders: policy_arg.fee_policy_skip_market_orders,
}),
arguments::FeePolicyKind::PriceImprovement {
factor,
max_volume_factor,
} => Policy::PriceImprovement(PriceImprovement {
factor,
max_volume_factor,
}),
arguments::FeePolicyKind::Volume { factor } => Policy::Volume(Volume { factor }),
}
}
pub(crate) factor: FeeFactor,
}

impl Surplus {
Expand Down
2 changes: 1 addition & 1 deletion crates/autopilot/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ pub async fn run(args: Arguments) {
args.limit_order_price_factor
.try_into()
.expect("limit order price factor can't be converted to BigDecimal"),
domain::ProtocolFee::new(args.fee_policy.clone()),
domain::ProtocolFee::from(args.fee_policy.clone()),
);
solvable_orders_cache
.update(block)
Expand Down
85 changes: 67 additions & 18 deletions crates/e2e/tests/e2e/protocol_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ async fn local_node_partner_fee_sell_order() {
run_test(partner_fee_sell_order_test).await;
}

#[tokio::test]
#[ignore]
async fn local_node_partner_fee_with_not_default_cap_sell_order_test() {
run_test(partner_fee_with_not_default_cap_sell_order_test).await;
}

#[tokio::test]
#[ignore]
async fn local_node_surplus_fee_buy_order() {
Expand Down Expand Up @@ -87,7 +93,7 @@ async fn surplus_fee_sell_order_test(web3: Web3) {
// 1480603400674076736) = 1461589542731026166 DAI
execute_test(
web3.clone(),
fee_policy,
vec![fee_policy.to_string()],
OrderKind::Sell,
None,
1480603400674076736u128.into(),
Expand Down Expand Up @@ -117,7 +123,7 @@ async fn surplus_fee_sell_order_capped_test(web3: Web3) {
// 1000150353094783059) = 987306456662572858 DAI
execute_test(
web3.clone(),
fee_policy,
vec![fee_policy.to_string()],
OrderKind::Sell,
None,
1000150353094783059u128.into(),
Expand All @@ -144,7 +150,7 @@ async fn volume_fee_sell_order_test(web3: Web3) {
// 1000150353094783059) = 987306456662572858 DAI
execute_test(
web3.clone(),
fee_policy,
vec![fee_policy.to_string()],
OrderKind::Sell,
None,
1000150353094783059u128.into(),
Expand Down Expand Up @@ -175,7 +181,7 @@ async fn partner_fee_sell_order_test(web3: Web3) {
// 100165388404261365) = 98879067931768848 DAI
execute_test(
web3.clone(),
fee_policy,
vec![fee_policy.to_string()],
OrderKind::Sell,
Some(OrderCreationAppData::Full {
full: json!({
Expand All @@ -195,6 +201,51 @@ async fn partner_fee_sell_order_test(web3: Web3) {
.await;
}

async fn partner_fee_with_not_default_cap_sell_order_test(web3: Web3) {
// Fee policy to be overwritten by the partner fee + capped to 0.02
let fee_policy = FeePolicyKind::PriceImprovement {
factor: 0.5,
max_volume_factor: 0.9,
};
// Without protocol fee:
// Expected execution is 10000000000000000000 GNO for
// 9871415430342266811 DAI, with executed_surplus_fee = 167058994203399 GNO
//
// With protocol fee:
// Expected executed_surplus_fee is 167058994203399 +
// 0.02*(10000000000000000000 - 167058994203399) = 200163717814319331
//
// Final execution is 10000000000000000000 GNO for 9772701276038844388 DAI, with
// executed_surplus_fee = 200163717814319331 GNO
//
// Settlement contract balance after execution = 200163717814319331 GNO =
// 200163717814319331 GNO * 9673987121735421787 / (10000000000000000000 -
// 200163717814319331) = 197593222235191520 DAI
execute_test(
web3.clone(),
vec![
fee_policy.to_string(),
"--fee-policy-max-partner-fee=0.02".to_string(),
],
OrderKind::Sell,
Some(OrderCreationAppData::Full {
full: json!({
"version": "1.1.0",
"metadata": {
"partnerFee": {
"bps":1000,
"recipient": "0xb6BAd41ae76A11D10f7b0E664C5007b908bC77C9",
}
}
})
.to_string(),
}),
200163717814319331u128.into(),
197593222235191520u128.into(),
)
.await;
}

async fn surplus_fee_buy_order_test(web3: Web3) {
let fee_policy = FeePolicyKind::Surplus {
factor: 0.3,
Expand All @@ -216,7 +267,7 @@ async fn surplus_fee_buy_order_test(web3: Web3) {
// Settlement contract balance after execution = executed_surplus_fee GNO
execute_test(
web3.clone(),
fee_policy,
vec![fee_policy.to_string()],
OrderKind::Buy,
None,
1488043031123213136u128.into(),
Expand All @@ -241,7 +292,7 @@ async fn surplus_fee_buy_order_capped_test(web3: Web3) {
// Settlement contract balance after execution = executed_surplus_fee GNO
execute_test(
web3.clone(),
fee_policy,
vec![fee_policy.to_string()],
OrderKind::Buy,
None,
504208401617866820u128.into(),
Expand All @@ -263,7 +314,7 @@ async fn volume_fee_buy_order_test(web3: Web3) {
// Settlement contract balance after execution = executed_surplus_fee GNO
execute_test(
web3.clone(),
fee_policy,
vec![fee_policy.to_string()],
OrderKind::Buy,
None,
504208401617866820u128.into(),
Expand Down Expand Up @@ -301,7 +352,7 @@ async fn price_improvement_fee_sell_order_test(web3: Web3) {
// 205312824093583) = 202676203868731 DAI
execute_test(
web3.clone(),
fee_policy,
vec![fee_policy.to_string()],
OrderKind::Sell,
None,
205312824093583u128.into(),
Expand All @@ -320,7 +371,7 @@ fn is_approximately_equal(executed_value: U256, expected_value: U256) -> bool {

async fn execute_test(
web3: Web3,
fee_policy: FeePolicyKind,
autopilot_config: Vec<String>,
order_kind: OrderKind,
app_data: Option<OrderCreationAppData>,
expected_surplus_fee: U256,
Expand Down Expand Up @@ -393,15 +444,13 @@ async fn execute_test(
endpoint: solver_endpoint,
}],
);
services.start_autopilot(
None,
vec![
"--drivers=test_solver|http://localhost:11088/test_solver".to_string(),
"--price-estimation-drivers=test_quoter|http://localhost:11088/test_solver".to_string(),
"--fee-policy-skip-market-orders=false".to_string(),
fee_policy.to_string(),
],
);
let mut config = vec![
"--drivers=test_solver|http://localhost:11088/test_solver".to_string(),
"--price-estimation-drivers=test_quoter|http://localhost:11088/test_solver".to_string(),
"--fee-policy-skip-market-orders=false".to_string(),
];
config.extend(autopilot_config);
services.start_autopilot(None, config);
services
.start_api(vec![
"--price-estimation-drivers=test_quoter|http://localhost:11088/test_solver".to_string(),
Expand Down

0 comments on commit aff70ba

Please sign in to comment.