Skip to content

Commit

Permalink
Allow whitelisting addresses for protocol fees discounts
Browse files Browse the repository at this point in the history
Remove the extra logic to determine the kind of discounts

Support only whitelisting of addresses and not struct

Remove commented code

Inline the fee policy whitelisted addresses filter

Remove domain separator
  • Loading branch information
m-lord-renkse committed Apr 4, 2024
1 parent 5e43504 commit 5f88090
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 49 deletions.
11 changes: 11 additions & 0 deletions crates/autopilot/src/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ pub struct Arguments {
#[clap(long, env, default_value = "0.01")]
pub fee_policy_max_partner_fee: FeeFactor,

/// List of addresses which are exempt from the protocol
/// fees
#[clap(long, env, use_value_delimiter = true)]
pub protocol_fee_exempt_addresses: Vec<H160>,

/// Arguments for uploading information to S3.
#[clap(flatten)]
pub s3: infra::persistence::cli::S3,
Expand Down Expand Up @@ -262,6 +267,7 @@ impl std::fmt::Display for Arguments {
auction_update_interval,
max_settlement_transaction_wait,
s3,
protocol_fee_exempt_addresses,
} = self;

write!(f, "{}", shared)?;
Expand Down Expand Up @@ -314,6 +320,11 @@ impl std::fmt::Display for Arguments {
display_option(f, "shadow", shadow)?;
writeln!(f, "solve_deadline: {:?}", solve_deadline)?;
writeln!(f, "fee_policies: {:?}", fee_policies)?;
writeln!(
f,
"protocol_fee_exempt_addresses: {:?}",
protocol_fee_exempt_addresses
)?;
writeln!(
f,
"fee_policy_max_partner_fee: {:?}",
Expand Down
71 changes: 42 additions & 29 deletions crates/autopilot/src/domain/fee/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ mod policy;

use {
crate::{
arguments,
arguments::{self},
boundary::{self},
domain::{self},
},
app_data::Validator,
derive_more::Into,
itertools::Itertools,
primitive_types::U256,
primitive_types::{H160, U256},
prometheus::core::Number,
std::str::FromStr,
std::{collections::HashSet, str::FromStr},
};

enum OrderClass {
Expand Down Expand Up @@ -51,33 +51,39 @@ impl From<arguments::FeePolicy> for ProtocolFee {
}
}

pub type ProtocolFeeExemptAddresses = HashSet<H160>;

pub struct ProtocolFees {
fee_policies: Vec<ProtocolFee>,
max_partner_fee: FeeFactor,
/// List of addresses which are exempt from the protocol
/// fees
protocol_fee_exempt_addresses: ProtocolFeeExemptAddresses,
}

impl ProtocolFees {
pub fn new(
fee_policies: &[arguments::FeePolicy],
fee_policy_max_partner_fee: FeeFactor,
protocol_fee_exempt_addresses: &[H160],
) -> Self {
Self {
fee_policies: fee_policies
.iter()
.cloned()
.map(ProtocolFee::from)
.collect(),
protocol_fee_exempt_addresses: protocol_fee_exempt_addresses
.iter()
.cloned()
.collect::<HashSet<_>>(),
max_partner_fee: fee_policy_max_partner_fee,
}
}

/// Converts an order from the boundary layer to the domain layer, applying
/// protocol fees if necessary.
pub fn apply(
protocol_fees: &ProtocolFees,
order: boundary::Order,
quote: &domain::Quote,
) -> domain::Order {
pub fn apply(&self, order: boundary::Order, quote: &domain::Quote) -> domain::Order {
// If the partner fee is specified, it overwrites the current volume fee policy
if let Some(validated_app_data) = order
.metadata
Expand All @@ -92,7 +98,7 @@ impl ProtocolFees {
let fee_policy = vec![Policy::Volume {
factor: FeeFactor::try_from_capped(
partner_fee.bps.into_f64() / 10_000.0,
protocol_fees.max_partner_fee.into(),
self.max_partner_fee.into(),
)
.unwrap(),
}];
Expand All @@ -110,26 +116,33 @@ impl ProtocolFees {
buy: quote.buy_amount,
fee: quote.fee,
};
let protocol_fees = protocol_fees
.fee_policies
.iter()
// TODO: support multiple fee policies
.find_map(|fee_policy| {
let outside_market_price = boundary::is_order_outside_market_price(&order_, &quote_, order.data.kind);
match (outside_market_price, &fee_policy.order_class) {
(_, OrderClass::Any) => Some(&fee_policy.policy),
(true, OrderClass::Limit) => Some(&fee_policy.policy),
(false, OrderClass::Market) => Some(&fee_policy.policy),
_ => None,
}
})
.and_then(|policy| match policy {
policy::Policy::Surplus(variant) => variant.apply(&order),
policy::Policy::PriceImprovement(variant) => variant.apply(&order, quote),
policy::Policy::Volume(variant) => variant.apply(&order),
})
.into_iter()
.collect_vec();
let protocol_fees = if self
.protocol_fee_exempt_addresses
.contains(&order.metadata.owner)
{
vec![]
} else {
self
.fee_policies
.iter()
// TODO: support multiple fee policies
.find_map(|fee_policy| {
let outside_market_price = boundary::is_order_outside_market_price(&order_, &quote_, order.data.kind);
match (outside_market_price, &fee_policy.order_class) {
(_, OrderClass::Any) => Some(&fee_policy.policy),
(true, OrderClass::Limit) => Some(&fee_policy.policy),
(false, OrderClass::Market) => Some(&fee_policy.policy),
_ => None,
}
})
.and_then(|policy| match policy {
policy::Policy::Surplus(variant) => variant.apply(&order),
policy::Policy::PriceImprovement(variant) => variant.apply(&order, quote),
policy::Policy::Volume(variant) => variant.apply(&order),
})
.into_iter()
.collect_vec()
};
boundary::order::to_domain(order, protocol_fees)
}
}
Expand Down
6 changes: 5 additions & 1 deletion crates/autopilot/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,11 @@ 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::ProtocolFees::new(&args.fee_policies, args.fee_policy_max_partner_fee),
domain::ProtocolFees::new(
&args.fee_policies,
args.fee_policy_max_partner_fee,
args.protocol_fee_exempt_addresses.as_slice(),
),
);

let liveness = Arc::new(Liveness::new(args.max_auction_age));
Expand Down
4 changes: 2 additions & 2 deletions crates/autopilot/src/solvable_orders.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{
domain,
domain::{self},
infra::{self, banned},
},
anyhow::Result,
Expand Down Expand Up @@ -242,7 +242,7 @@ impl SolvableOrdersCache {
.into_iter()
.filter_map(|order| {
if let Some(quote) = db_solvable_orders.quotes.get(&order.metadata.uid.into()) {
Some(domain::ProtocolFees::apply(&self.protocol_fees, order, quote))
Some(self.protocol_fees.apply(order, quote))
} else {
tracing::warn!(order_uid = %order.metadata.uid, "order is skipped, quote is missing");
None
Expand Down
88 changes: 71 additions & 17 deletions crates/e2e/tests/e2e/protocol_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,17 @@ async fn combined_protocol_fees(web3: Web3) {
let mut onchain = OnchainComponents::deploy(web3.clone()).await;

let [solver] = onchain.make_solvers(to_wei(200)).await;
let [trader] = onchain.make_accounts(to_wei(200)).await;
let [limit_order_token, market_order_token, partner_fee_order_token] = onchain
.deploy_tokens_with_weth_uni_v2_pools(to_wei(20), to_wei(20))
.await;
let [trader, trader_exempt] = onchain.make_accounts(to_wei(200)).await;
let [limit_order_token, market_order_token, partner_fee_order_token, fee_exempt_token] =
onchain
.deploy_tokens_with_weth_uni_v2_pools(to_wei(20), to_wei(20))
.await;

for token in &[
&limit_order_token,
&market_order_token,
&partner_fee_order_token,
&fee_exempt_token,
] {
token.mint(solver.address(), to_wei(1000)).await;
tx!(
Expand All @@ -86,6 +88,10 @@ async fn combined_protocol_fees(web3: Web3) {
trader.account(),
token.approve(onchain.contracts().uniswap_v2_router.address(), to_wei(100))
);
tx!(
trader_exempt.account(),
token.approve(onchain.contracts().uniswap_v2_router.address(), to_wei(100))
);
}

tx!(
Expand All @@ -95,11 +101,23 @@ async fn combined_protocol_fees(web3: Web3) {
.weth
.approve(onchain.contracts().allowance, to_wei(100))
);
tx!(
trader_exempt.account(),
onchain
.contracts()
.weth
.approve(onchain.contracts().allowance, to_wei(100))
);
tx_value!(
trader.account(),
to_wei(100),
onchain.contracts().weth.deposit()
);
tx_value!(
trader_exempt.account(),
to_wei(100),
onchain.contracts().weth.deposit()
);
tx!(
solver.account(),
onchain
Expand All @@ -111,6 +129,10 @@ async fn combined_protocol_fees(web3: Web3) {
let autopilot_config = vec![
ProtocolFeesConfig(vec![limit_surplus_policy, market_price_improvement_policy]).to_string(),
"--fee-policy-max-partner-fee=0.02".to_string(),
format!(
"--protocol-fee-exempt-addresses={:?}",
trader_exempt.address()
),
];
let services = Services::new(onchain.contracts()).await;
services
Expand All @@ -126,12 +148,13 @@ async fn combined_protocol_fees(web3: Web3) {
tracing::info!("Acquiring quotes.");
let quote_valid_to = model::time::now_in_epoch_seconds() + 300;
let sell_amount = to_wei(10);
let [limit_quote_before, market_quote_before, partner_fee_quote] =
let [limit_quote_before, market_quote_before, partner_fee_quote, fee_exempt_token_before] =
futures::future::try_join_all(
[
&limit_order_token,
&market_order_token,
&partner_fee_order_token,
&fee_exempt_token,
]
.map(|token| {
get_quote(
Expand Down Expand Up @@ -182,6 +205,16 @@ async fn combined_protocol_fees(web3: Web3) {
&onchain.contracts().domain_separator,
SecretKeyRef::from(&SecretKey::from_slice(trader.private_key()).unwrap()),
);
let fee_exempt_token_order = OrderCreation {
sell_amount,
buy_amount: to_wei(5),
..sell_order_from_quote(&fee_exempt_token_before)
}
.sign(
EcdsaSigningScheme::Eip712,
&onchain.contracts().domain_separator,
SecretKeyRef::from(&SecretKey::from_slice(trader_exempt.private_key()).unwrap()),
);

tracing::info!("Rebalancing AMM pools for market & limit order.");
onchain
Expand All @@ -190,6 +223,9 @@ async fn combined_protocol_fees(web3: Web3) {
onchain
.mint_token_to_weth_uni_v2_pool(&limit_order_token, to_wei(1000))
.await;
onchain
.mint_token_to_weth_uni_v2_pool(&fee_exempt_token, to_wei(1000))
.await;

tracing::info!("Waiting for liquidity state to update");
wait_for_condition(TIMEOUT, || async {
Expand All @@ -210,28 +246,31 @@ async fn combined_protocol_fees(web3: Web3) {
.await
.unwrap();

let [market_quote_after, limit_quote_after] =
futures::future::try_join_all([&market_order_token, &limit_order_token].map(|token| {
get_quote(
&services,
onchain.contracts().weth.address(),
token.address(),
OrderKind::Sell,
sell_amount,
quote_valid_to,
)
}))
let [market_quote_after, limit_quote_after, fee_exempt_token_after] =
futures::future::try_join_all(
[&market_order_token, &limit_order_token, &fee_exempt_token].map(|token| {
get_quote(
&services,
onchain.contracts().weth.address(),
token.address(),
OrderKind::Sell,
sell_amount,
quote_valid_to,
)
}),
)
.await
.unwrap()
.try_into()
.expect("Expected exactly two elements");

let [market_price_improvement_uid, limit_surplus_order_uid, partner_fee_order_uid] =
let [market_price_improvement_uid, limit_surplus_order_uid, partner_fee_order_uid, fee_exempt_token_order_uid] =
futures::future::try_join_all(
[
&market_price_improvement_order,
&limit_surplus_order,
&partner_fee_order,
&fee_exempt_token_order,
]
.map(|order| services.create_order(order)),
)
Expand All @@ -248,6 +287,7 @@ async fn combined_protocol_fees(web3: Web3) {
&market_price_improvement_uid,
&limit_surplus_order_uid,
&partner_fee_order_uid,
&fee_exempt_token_order_uid,
]
.map(|uid| async {
services
Expand Down Expand Up @@ -295,6 +335,13 @@ async fn combined_protocol_fees(web3: Web3) {
// see `limit_surplus_policy.factor`, which is 0.3
assert!(limit_executed_surplus_fee_in_buy_token >= limit_quote_diff * 3 / 10);

let fee_exempt_order = services
.get_order(&fee_exempt_token_order_uid)
.await
.unwrap();
let fee_exempt_surplus_fee_in_buy_token =
surplus_fee_in_buy_token(&fee_exempt_order, &fee_exempt_token_after.quote);

let balance_after = market_order_token
.balance_of(onchain.contracts().gp_settlement.address())
.call()
Expand All @@ -315,6 +362,13 @@ async fn combined_protocol_fees(web3: Web3) {
.await
.unwrap();
assert_approximately_eq!(partner_fee_executed_surplus_fee_in_buy_token, balance_after);

let balance_after = fee_exempt_token
.balance_of(onchain.contracts().gp_settlement.address())
.call()
.await
.unwrap();
assert_approximately_eq!(fee_exempt_surplus_fee_in_buy_token, balance_after);
}

async fn get_quote(
Expand Down

0 comments on commit 5f88090

Please sign in to comment.