-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Price improvemenet fee policy autopilot domain #2378
Changes from 2 commits
6be7367
5360135
bf7b8db
c519fb8
3e53f6b
e2adf86
cc74e36
021e3b6
a7352d4
bd72dcd
284e892
0016bb9
cf8c7c3
0bb745f
ed216d5
0cfbc2e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,65 +4,64 @@ | |
//! we define the way to calculate the protocol fee based on the configuration | ||
//! parameters. | ||
|
||
use crate::{ | ||
boundary::{self}, | ||
domain, | ||
use { | ||
crate::{ | ||
boundary::{self}, | ||
domain, | ||
}, | ||
primitive_types::U256, | ||
}; | ||
|
||
/// Constructs fee policies based on the current configuration. | ||
#[derive(Debug)] | ||
pub struct ProtocolFee { | ||
policy: Policy, | ||
policy_builder: PolicyBuilder, | ||
fee_policy_skip_market_orders: bool, | ||
} | ||
|
||
impl ProtocolFee { | ||
pub fn new(policy: Policy, fee_policy_skip_market_orders: bool) -> Self { | ||
pub fn new(policy: PolicyBuilder, fee_policy_skip_market_orders: bool) -> Self { | ||
Self { | ||
policy, | ||
policy_builder: policy, | ||
fee_policy_skip_market_orders, | ||
} | ||
} | ||
|
||
/// Get policies for order. | ||
pub fn get(&self, order: &boundary::Order, quote: Option<&domain::Quote>) -> Vec<Policy> { | ||
match order.metadata.class { | ||
/// Converts an order from the boundary layer to the domain layer, applying | ||
/// protocol fees if necessary. | ||
pub fn to_order(&self, order: boundary::Order, quote: &domain::Quote) -> domain::Order { | ||
squadgazzz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let protocol_fees = match order.metadata.class { | ||
boundary::OrderClass::Market => { | ||
if self.fee_policy_skip_market_orders { | ||
vec![] | ||
} else { | ||
vec![self.policy] | ||
vec![self.policy_builder.build_with(quote)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't the information whether or not to apply a fee for market order be part of the fee policy (builder) and not part of this component? |
||
} | ||
} | ||
boundary::OrderClass::Liquidity => vec![], | ||
boundary::OrderClass::Limit => { | ||
if !self.fee_policy_skip_market_orders { | ||
return vec![self.policy]; | ||
} | ||
|
||
// if the quote is missing, we can't determine if the order is outside the | ||
// market price so we protect the user and not charge a fee | ||
let Some(quote) = quote else { | ||
return vec![]; | ||
}; | ||
|
||
let order_ = boundary::Amounts { | ||
sell: order.data.sell_amount, | ||
buy: order.data.buy_amount, | ||
fee: order.data.fee_amount, | ||
}; | ||
let quote = boundary::Amounts { | ||
sell: quote.sell_amount, | ||
buy: quote.buy_amount, | ||
fee: quote.fee, | ||
}; | ||
if boundary::is_order_outside_market_price(&order_, "e) { | ||
vec![self.policy] | ||
vec![self.policy_builder.build_with(quote)] | ||
} else { | ||
vec![] | ||
let order_ = boundary::Amounts { | ||
sell: order.data.sell_amount, | ||
buy: order.data.buy_amount, | ||
fee: order.data.fee_amount, | ||
}; | ||
let quote_ = boundary::Amounts { | ||
sell: quote.sell_amount, | ||
buy: quote.buy_amount, | ||
fee: quote.fee, | ||
}; | ||
if boundary::is_order_outside_market_price(&order_, "e_) { | ||
vec![self.policy_builder.build_with(quote)] | ||
} else { | ||
vec![] | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
boundary::order::to_domain(order, protocol_fees) | ||
} | ||
} | ||
|
||
|
@@ -83,6 +82,14 @@ pub enum Policy { | |
/// Cap protocol fee with a percentage of the order's volume. | ||
max_volume_factor: f64, | ||
}, | ||
/// A price improvement corresponds to a situation where the order is | ||
/// executed at a better price than the top quote. The protocol fee in such | ||
/// case is calculated from a cut of this price improvement. | ||
PriceImprovement { | ||
factor: f64, | ||
max_volume_factor: f64, | ||
quote: Quote, | ||
}, | ||
/// How much of the order's volume should be taken as a protocol fee. | ||
/// The fee is taken in `sell` token for `sell` orders and in `buy` | ||
/// token for `buy` orders. | ||
|
@@ -92,3 +99,48 @@ pub enum Policy { | |
factor: f64, | ||
}, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum PolicyBuilder { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should make this class private and create it in the main Fee component from the infra config. It might also be nicer to have every variant be there own sub-struct so that we can implement slightly different build behaviors (e.g. surplus and volume don't need the quote). Then the main self.fee_variants.filter_map(|variant| {
match variant {
FeeVariant::Surplus(variant) => variant.apply(order),
FeeVariant::PriceImprovement(variant) => variant.apply(order, quote),
...
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactored |
||
Surplus { factor: f64, max_volume_factor: f64 }, | ||
PriceImprovement { factor: f64, max_volume_factor: f64 }, | ||
Volume { factor: f64 }, | ||
} | ||
|
||
impl PolicyBuilder { | ||
pub fn build_with(&self, quote: &domain::Quote) -> Policy { | ||
match self { | ||
PolicyBuilder::Surplus { | ||
factor, | ||
max_volume_factor, | ||
} => Policy::Surplus { | ||
factor: *factor, | ||
max_volume_factor: *max_volume_factor, | ||
}, | ||
PolicyBuilder::PriceImprovement { | ||
factor, | ||
max_volume_factor, | ||
} => Policy::PriceImprovement { | ||
factor: *factor, | ||
max_volume_factor: *max_volume_factor, | ||
quote: quote.clone().into(), | ||
}, | ||
PolicyBuilder::Volume { factor } => Policy::Volume { factor: *factor }, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Copy, Clone, PartialEq)] | ||
pub struct Quote { | ||
pub sell_amount: U256, | ||
pub buy_amount: U256, | ||
sunce86 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
impl From<domain::Quote> for Quote { | ||
fn from(value: domain::Quote) -> Self { | ||
Self { | ||
sell_amount: value.sell_amount, | ||
buy_amount: value.buy_amount, | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a private type now, if we move it to a
policy
submodule we could even call itPolicy
without having a name collision (the way it turned out now it's not really following the Builder pattern anymore)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved