From 938f21b40dd0a5a290d9cc4d955f350b41bceee8 Mon Sep 17 00:00:00 2001 From: ilya Date: Tue, 12 Mar 2024 12:24:20 +0000 Subject: [PATCH 01/19] Partially fillable orders in driver tests --- .../driver/src/tests/cases/protocol_fees.rs | 68 +++++++++++++++++++ crates/driver/src/tests/setup/blockchain.rs | 30 ++++++++ crates/driver/src/tests/setup/driver.rs | 15 ++-- crates/driver/src/tests/setup/mod.rs | 11 ++- crates/driver/src/tests/setup/solver.rs | 4 +- 5 files changed, 121 insertions(+), 7 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 4fef6935bb..4686861402 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -10,11 +10,13 @@ use crate::{ ab_solution, fee::{Policy, Quote}, ExpectedOrderAmounts, + Partial, Test, }, }, }; +#[derive(Clone)] struct Amounts { sell: eth::U256, buy: eth::U256, @@ -31,6 +33,8 @@ struct Order { sell_amount: eth::U256, buy_amount: eth::U256, side: order::Side, + // For partially fillable orders + partially_executed: Option, } struct TestCase { @@ -56,6 +60,15 @@ async fn protocol_fee_test_case(test_case: TestCase) { sell: test_case.execution.driver.sell, buy: test_case.execution.driver.buy, }; + let executed = + test_case + .order + .partially_executed + .clone() + .map(|executed| match test_case.order.side { + order::Side::Sell => executed.sell, + order::Side::Buy => executed.buy, + }); let order = ab_order() .kind(order::Kind::Limit) @@ -67,6 +80,11 @@ async fn protocol_fee_test_case(test_case: TestCase) { .solver_fee(Some(test_case.execution.driver.sell / 100)) .side(test_case.order.side) .fee_policy(test_case.fee_policy) + .executed(executed) + .partial(match test_case.order.partially_executed { + Some(executed) => Partial::Yes {executed_sell: executed.sell, executed_buy: executed.buy}, + None => Partial::No + }) // Surplus is configured explicitly via executed/quoted amounts .no_surplus() .expected_amounts(expected_amounts); @@ -96,6 +114,7 @@ async fn surplus_protocol_fee_buy_order_not_capped() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, + partially_executed: None, }, execution: Execution { // 20 ETH surplus in sell token (after network fee), half of which is kept by the @@ -114,6 +133,42 @@ async fn surplus_protocol_fee_buy_order_not_capped() { protocol_fee_test_case(test_case).await; } +#[tokio::test] +#[ignore] +async fn surplus_protocol_fee_buy_partial_order() { + let fee_policy = Policy::Surplus { + factor: 0.5, + // high enough so we don't get capped by volume fee + max_volume_factor: 1.0, + }; + let test_case = TestCase { + fee_policy, + order: Order { + sell_amount: 50.ether().into_wei(), + buy_amount: 40.ether().into_wei(), + side: order::Side::Buy, + partially_executed: Some(Amounts { + sell: 25.ether().into_wei(), + buy: 20.ether().into_wei(), + }), + }, + execution: Execution { + // 20 ETH surplus in sell token (after network fee), half of which is kept by the + // protocol + solver: Amounts { + sell: 30.ether().into_wei(), + buy: 40.ether().into_wei(), + }, + driver: Amounts { + sell: 20.ether().into_wei(), + buy: 20.ether().into_wei(), + }, + }, + }; + + protocol_fee_test_case(test_case).await; +} + #[tokio::test] #[ignore] async fn surplus_protocol_fee_sell_order_not_capped() { @@ -128,6 +183,7 @@ async fn surplus_protocol_fee_sell_order_not_capped() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Sell, + partially_executed: None, }, execution: Execution { // 20 ETH surplus, half of which gets captured by the protocol @@ -158,6 +214,7 @@ async fn surplus_protocol_fee_buy_order_capped() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, + partially_executed: None, }, execution: Execution { // Fee is capped at 10% of solver proposed sell volume @@ -189,6 +246,7 @@ async fn surplus_protocol_fee_sell_order_capped() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Sell, + partially_executed: None, }, execution: Execution { // Fee is capped at 10% of solver proposed buy volume @@ -215,6 +273,7 @@ async fn volume_protocol_fee_buy_order() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, + partially_executed: None, }, execution: Execution { // Half of the solver proposed sell volume is kept by the protocol @@ -242,6 +301,7 @@ async fn volume_protocol_fee_sell_order() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Sell, + partially_executed: None, }, execution: Execution { // 10% of the solver proposed buy value is kept by the protocol @@ -278,6 +338,7 @@ async fn price_improvement_fee_buy_in_market_order_not_capped() { sell_amount: 60.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, + partially_executed: None, }, execution: Execution { // Sell 10 ETH less than quoted, half of which is kept by the protocol @@ -315,6 +376,7 @@ async fn price_improvement_fee_sell_in_market_order_not_capped() { // Demanding to receive less than quoted (in-market) buy_amount: 40.ether().into_wei(), side: order::Side::Sell, + partially_executed: None, }, execution: Execution { // Receive 10 ETH more than quoted, half of which gets captured by the protocol @@ -351,6 +413,7 @@ async fn price_improvement_fee_buy_out_of_market_order_not_capped() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, + partially_executed: None, }, execution: Execution { // Sell 10 ETH less than requested, half of which is kept by the protocol @@ -388,6 +451,7 @@ async fn price_improvement_fee_sell_out_of_market_order_not_capped() { // Demanding to receive more than quoted (out-market) buy_amount: 50.ether().into_wei(), side: order::Side::Sell, + partially_executed: None, }, execution: Execution { // Receive 10 ETH more than quoted, half of which gets captured by the protocol @@ -424,6 +488,7 @@ async fn price_improvement_fee_buy_in_market_order_capped() { sell_amount: 60.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, + partially_executed: None, }, execution: Execution { // Fee is capped at 5% of solver proposed sell volume @@ -461,6 +526,7 @@ async fn price_improvement_fee_sell_in_market_order_capped() { // Demanding to receive less than quoted (in-market) buy_amount: 40.ether().into_wei(), side: order::Side::Sell, + partially_executed: None, }, execution: Execution { // Fee is capped at 5% of solver proposed buy volume @@ -497,6 +563,7 @@ async fn price_improvement_fee_buy_out_of_market_order_capped() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, + partially_executed: None, }, execution: Execution { // Fee is capped at 5% of solver proposed sell volume @@ -534,6 +601,7 @@ async fn price_improvement_fee_sell_out_of_market_order_capped() { // Demanding to receive more than quoted (out-market) buy_amount: 50.ether().into_wei(), side: order::Side::Sell, + partially_executed: None, }, execution: Execution { // Fee is capped at 5% of solver proposed buy volume diff --git a/crates/driver/src/tests/setup/blockchain.rs b/crates/driver/src/tests/setup/blockchain.rs index d0f1834468..9f5234853e 100644 --- a/crates/driver/src/tests/setup/blockchain.rs +++ b/crates/driver/src/tests/setup/blockchain.rs @@ -131,6 +131,36 @@ impl QuotedOrder { } } + pub fn driver_buy_amount(&self) -> eth::U256 { + let executed_buy = match self.order.partial { + Partial::Yes { + executed_sell: _, + executed_buy, + } => executed_buy, + Partial::No => eth::U256::zero(), + }; + let buy_amount = match self.order.side { + order::Side::Buy => self.buy, + order::Side::Sell => self.buy / self.order.surplus_factor, + }; + buy_amount.saturating_sub(executed_buy) + } + + pub fn driver_sell_amount(&self) -> eth::U256 { + let executed_sell = match self.order.partial { + Partial::Yes { + executed_sell, + executed_buy: _, + } => executed_sell, + Partial::No => eth::U256::zero(), + }; + let sell_amount = match self.order.side { + order::Side::Buy => self.sell * self.order.surplus_factor, + order::Side::Sell => self.sell, + }; + sell_amount.saturating_sub(executed_sell) + } + /// The UID of the order. pub fn order_uid(&self, blockchain: &Blockchain) -> tests::boundary::OrderUid { self.boundary(blockchain).uid() diff --git a/crates/driver/src/tests/setup/driver.rs b/crates/driver/src/tests/setup/driver.rs index 142335dd7e..47466e94e6 100644 --- a/crates/driver/src/tests/setup/driver.rs +++ b/crates/driver/src/tests/setup/driver.rs @@ -69,6 +69,16 @@ pub fn solve_req(test: &Test) -> serde_json::Value { let mut quotes = test.quoted_orders.clone(); quotes.shuffle(&mut rand::thread_rng()); for quote in quotes.iter() { + let executed = match quote.order.partial { + Partial::Yes { + executed_sell, + executed_buy, + } => match quote.order.side { + order::Side::Sell => executed_sell.to_string(), + order::Side::Buy => executed_buy.to_string(), + }, + Partial::No => "0".to_owned(), + }; orders_json.push(json!({ "uid": quote.order_uid(&test.blockchain), "sellToken": hex_address(test.blockchain.get_token(quote.order.sell_token)), @@ -88,10 +98,7 @@ pub fn solve_req(test: &Test) -> serde_json::Value { }, "owner": hex_address(test.trader_address), "partiallyFillable": matches!(quote.order.partial, Partial::Yes { .. }), - "executed": match quote.order.partial { - Partial::Yes { executed } => executed.to_string(), - Partial::No => "0".to_owned(), - }, + "executed": executed, "preInteractions": [], "postInteractions": [], "class": match quote.order.kind { diff --git a/crates/driver/src/tests/setup/mod.rs b/crates/driver/src/tests/setup/mod.rs index 2133d8d4a7..e245668231 100644 --- a/crates/driver/src/tests/setup/mod.rs +++ b/crates/driver/src/tests/setup/mod.rs @@ -57,7 +57,8 @@ pub enum Partial { #[default] No, Yes { - executed: eth::U256, + executed_sell: eth::U256, + executed_buy: eth::U256, }, } @@ -253,6 +254,14 @@ impl Order { } } + pub fn partial(self, partial: Partial) -> Self { + Self { partial, ..self } + } + + pub fn executed(self, executed: Option) -> Self { + Self { executed, ..self } + } + fn surplus_fee(&self) -> eth::U256 { match self.kind { order::Kind::Limit => self.solver_fee.unwrap_or_default(), diff --git a/crates/driver/src/tests/setup/solver.rs b/crates/driver/src/tests/setup/solver.rs index d21877fb3c..96dc6b75f5 100644 --- a/crates/driver/src/tests/setup/solver.rs +++ b/crates/driver/src/tests/setup/solver.rs @@ -59,11 +59,11 @@ impl Solver { "buyToken": hex_address(config.blockchain.get_token(buy_token)), "sellAmount": match quote.order.side { order::Side::Buy if config.quote => "22300745198530623141535718272648361505980416".to_owned(), - _ => quote.sell_amount().to_string(), + _ => quote.driver_sell_amount().to_string(), }, "buyAmount": match quote.order.side { order::Side::Sell if config.quote => "1".to_owned(), - _ => quote.buy_amount().to_string(), + _ => quote.driver_buy_amount().to_string(), }, "feeAmount": quote.order.user_fee.to_string(), "kind": match quote.order.side { From c1fa5b55eac094d5b9a7fef8ca3b08536af4d4ba Mon Sep 17 00:00:00 2001 From: ilya Date: Tue, 12 Mar 2024 17:08:56 +0000 Subject: [PATCH 02/19] Use partially fillable values --- crates/driver/src/tests/cases/protocol_fees.rs | 10 +++++----- crates/driver/src/tests/setup/blockchain.rs | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 4686861402..f2e86eb2ae 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -60,7 +60,7 @@ async fn protocol_fee_test_case(test_case: TestCase) { sell: test_case.execution.driver.sell, buy: test_case.execution.driver.buy, }; - let executed = + let executed_target = test_case .order .partially_executed @@ -80,7 +80,7 @@ async fn protocol_fee_test_case(test_case: TestCase) { .solver_fee(Some(test_case.execution.driver.sell / 100)) .side(test_case.order.side) .fee_policy(test_case.fee_policy) - .executed(executed) + .executed(executed_target) .partial(match test_case.order.partially_executed { Some(executed) => Partial::Yes {executed_sell: executed.sell, executed_buy: executed.buy}, None => Partial::No @@ -153,11 +153,11 @@ async fn surplus_protocol_fee_buy_partial_order() { }), }, execution: Execution { - // 20 ETH surplus in sell token (after network fee), half of which is kept by the + // 10 ETH surplus in sell token (after network fee), half of which is kept by the // protocol solver: Amounts { - sell: 30.ether().into_wei(), - buy: 40.ether().into_wei(), + sell: 15.ether().into_wei(), + buy: 20.ether().into_wei(), }, driver: Amounts { sell: 20.ether().into_wei(), diff --git a/crates/driver/src/tests/setup/blockchain.rs b/crates/driver/src/tests/setup/blockchain.rs index 9f5234853e..4981e38a25 100644 --- a/crates/driver/src/tests/setup/blockchain.rs +++ b/crates/driver/src/tests/setup/blockchain.rs @@ -590,8 +590,18 @@ impl Blockchain { /// Compute the execution of an order given the available liquidity pub fn execution(&self, order: &Order) -> Execution { + let (oder_sell_amount, order_buy_amount) = match order.partial { + Partial::Yes { + executed_sell, + executed_buy, + } => ( + order.sell_amount.saturating_sub(executed_sell), + order.buy_amount.map(|buy| buy.saturating_sub(executed_buy)), + ), + Partial::No => (order.sell_amount, order.buy_amount), + }; let pair = self.find_pair(order); - let (sell, buy) = match (order.side, order.buy_amount) { + let (sell, buy) = match (order.side, order_buy_amount) { // For buy order with explicitly specified amounts, use the buy amount (order::Side::Buy, Some(buy_amount)) => ( pair.pool.in_given_out(Asset { @@ -602,9 +612,9 @@ impl Blockchain { ), // Otherwise assume the full sell amount to compute the execution (_, _) => ( - order.sell_amount, + oder_sell_amount, pair.pool.out_given_in(Asset { - amount: order.sell_amount, + amount: oder_sell_amount, token: order.sell_token, }), ), From 0ff04bfbcc76880f186d2add04d15ef202c0c182 Mon Sep 17 00:00:00 2001 From: ilya Date: Tue, 12 Mar 2024 17:29:44 +0000 Subject: [PATCH 03/19] Better values --- crates/driver/src/tests/cases/protocol_fees.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index f2e86eb2ae..d10fe698b0 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -148,20 +148,20 @@ async fn surplus_protocol_fee_buy_partial_order() { buy_amount: 40.ether().into_wei(), side: order::Side::Buy, partially_executed: Some(Amounts { - sell: 25.ether().into_wei(), - buy: 20.ether().into_wei(), + sell: 15.ether().into_wei(), + buy: 12.ether().into_wei(), }), }, execution: Execution { - // 10 ETH surplus in sell token (after network fee), half of which is kept by the + // 6 ETH surplus in sell token (after network fee), half of which is kept by the // protocol solver: Amounts { - sell: 15.ether().into_wei(), - buy: 20.ether().into_wei(), + sell: 41.ether().into_wei(), + buy: 28.ether().into_wei(), }, driver: Amounts { - sell: 20.ether().into_wei(), - buy: 20.ether().into_wei(), + sell: 38.ether().into_wei(), + buy: 28.ether().into_wei(), }, }, }; From 578ab425eb68d30fb49feef655ca43dfa8a66e53 Mon Sep 17 00:00:00 2001 From: ilya Date: Tue, 12 Mar 2024 17:35:43 +0000 Subject: [PATCH 04/19] tmp --- crates/driver/src/tests/cases/protocol_fees.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index d10fe698b0..9ccd23d34f 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -60,7 +60,7 @@ async fn protocol_fee_test_case(test_case: TestCase) { sell: test_case.execution.driver.sell, buy: test_case.execution.driver.buy, }; - let executed_target = + let _executed_target = test_case .order .partially_executed @@ -80,7 +80,7 @@ async fn protocol_fee_test_case(test_case: TestCase) { .solver_fee(Some(test_case.execution.driver.sell / 100)) .side(test_case.order.side) .fee_policy(test_case.fee_policy) - .executed(executed_target) + // .executed(executed_target) .partial(match test_case.order.partially_executed { Some(executed) => Partial::Yes {executed_sell: executed.sell, executed_buy: executed.buy}, None => Partial::No From f176d45b660481b52cd743ea1bff6214bceaa4ba Mon Sep 17 00:00:00 2001 From: ilya Date: Tue, 12 Mar 2024 20:59:06 +0000 Subject: [PATCH 05/19] Remove explicit partially fillable values --- .../driver/src/tests/cases/protocol_fees.rs | 60 +++++++--------- crates/driver/src/tests/setup/blockchain.rs | 71 ++++++++++--------- crates/driver/src/tests/setup/driver.rs | 15 ++-- crates/driver/src/tests/setup/mod.rs | 3 +- 4 files changed, 66 insertions(+), 83 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 9ccd23d34f..28662270df 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -33,8 +33,6 @@ struct Order { sell_amount: eth::U256, buy_amount: eth::U256, side: order::Side, - // For partially fillable orders - partially_executed: Option, } struct TestCase { @@ -54,22 +52,35 @@ async fn protocol_fee_test_case(test_case: TestCase) { .sell_amount(test_case.execution.solver.sell) .buy_amount(test_case.execution.solver.buy); let pool = ab_adjusted_pool(quote); + let partially_executed = match test_case.order.side { + order::Side::Sell => test_case + .order + .sell_amount + .saturating_sub(test_case.execution.solver.sell), + order::Side::Buy => test_case + .order + .buy_amount + .saturating_sub(test_case.execution.solver.buy), + }; + let partially_executed: Option = + (!partially_executed.is_zero()).then_some(partially_executed); + let partial = match partially_executed { + Some(executed) => Partial::Yes { executed }, + None => Partial::No, + }; + let executed = match partial { + Partial::Yes { .. } => match test_case.order.side { + order::Side::Buy => Some(test_case.execution.solver.buy), + order::Side::Sell => Some(test_case.execution.solver.sell), + }, + Partial::No => None, + }; // Amounts expected to be returned by the driver after fee processing let expected_amounts = ExpectedOrderAmounts { sell: test_case.execution.driver.sell, buy: test_case.execution.driver.buy, }; - let _executed_target = - test_case - .order - .partially_executed - .clone() - .map(|executed| match test_case.order.side { - order::Side::Sell => executed.sell, - order::Side::Buy => executed.buy, - }); - let order = ab_order() .kind(order::Kind::Limit) .sell_amount(test_case.order.sell_amount) @@ -80,11 +91,8 @@ async fn protocol_fee_test_case(test_case: TestCase) { .solver_fee(Some(test_case.execution.driver.sell / 100)) .side(test_case.order.side) .fee_policy(test_case.fee_policy) - // .executed(executed_target) - .partial(match test_case.order.partially_executed { - Some(executed) => Partial::Yes {executed_sell: executed.sell, executed_buy: executed.buy}, - None => Partial::No - }) + .executed(executed) + .partial(partial) // Surplus is configured explicitly via executed/quoted amounts .no_surplus() .expected_amounts(expected_amounts); @@ -114,7 +122,6 @@ async fn surplus_protocol_fee_buy_order_not_capped() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, - partially_executed: None, }, execution: Execution { // 20 ETH surplus in sell token (after network fee), half of which is kept by the @@ -147,10 +154,6 @@ async fn surplus_protocol_fee_buy_partial_order() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, - partially_executed: Some(Amounts { - sell: 15.ether().into_wei(), - buy: 12.ether().into_wei(), - }), }, execution: Execution { // 6 ETH surplus in sell token (after network fee), half of which is kept by the @@ -183,7 +186,6 @@ async fn surplus_protocol_fee_sell_order_not_capped() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Sell, - partially_executed: None, }, execution: Execution { // 20 ETH surplus, half of which gets captured by the protocol @@ -214,7 +216,6 @@ async fn surplus_protocol_fee_buy_order_capped() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, - partially_executed: None, }, execution: Execution { // Fee is capped at 10% of solver proposed sell volume @@ -246,7 +247,6 @@ async fn surplus_protocol_fee_sell_order_capped() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Sell, - partially_executed: None, }, execution: Execution { // Fee is capped at 10% of solver proposed buy volume @@ -273,7 +273,6 @@ async fn volume_protocol_fee_buy_order() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, - partially_executed: None, }, execution: Execution { // Half of the solver proposed sell volume is kept by the protocol @@ -301,7 +300,6 @@ async fn volume_protocol_fee_sell_order() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Sell, - partially_executed: None, }, execution: Execution { // 10% of the solver proposed buy value is kept by the protocol @@ -338,7 +336,6 @@ async fn price_improvement_fee_buy_in_market_order_not_capped() { sell_amount: 60.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, - partially_executed: None, }, execution: Execution { // Sell 10 ETH less than quoted, half of which is kept by the protocol @@ -376,7 +373,6 @@ async fn price_improvement_fee_sell_in_market_order_not_capped() { // Demanding to receive less than quoted (in-market) buy_amount: 40.ether().into_wei(), side: order::Side::Sell, - partially_executed: None, }, execution: Execution { // Receive 10 ETH more than quoted, half of which gets captured by the protocol @@ -413,7 +409,6 @@ async fn price_improvement_fee_buy_out_of_market_order_not_capped() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, - partially_executed: None, }, execution: Execution { // Sell 10 ETH less than requested, half of which is kept by the protocol @@ -451,7 +446,6 @@ async fn price_improvement_fee_sell_out_of_market_order_not_capped() { // Demanding to receive more than quoted (out-market) buy_amount: 50.ether().into_wei(), side: order::Side::Sell, - partially_executed: None, }, execution: Execution { // Receive 10 ETH more than quoted, half of which gets captured by the protocol @@ -488,7 +482,6 @@ async fn price_improvement_fee_buy_in_market_order_capped() { sell_amount: 60.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, - partially_executed: None, }, execution: Execution { // Fee is capped at 5% of solver proposed sell volume @@ -526,7 +519,6 @@ async fn price_improvement_fee_sell_in_market_order_capped() { // Demanding to receive less than quoted (in-market) buy_amount: 40.ether().into_wei(), side: order::Side::Sell, - partially_executed: None, }, execution: Execution { // Fee is capped at 5% of solver proposed buy volume @@ -563,7 +555,6 @@ async fn price_improvement_fee_buy_out_of_market_order_capped() { sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, - partially_executed: None, }, execution: Execution { // Fee is capped at 5% of solver proposed sell volume @@ -601,7 +592,6 @@ async fn price_improvement_fee_sell_out_of_market_order_capped() { // Demanding to receive more than quoted (out-market) buy_amount: 50.ether().into_wei(), side: order::Side::Sell, - partially_executed: None, }, execution: Execution { // Fee is capped at 5% of solver proposed buy volume diff --git a/crates/driver/src/tests/setup/blockchain.rs b/crates/driver/src/tests/setup/blockchain.rs index 4981e38a25..911db2fe05 100644 --- a/crates/driver/src/tests/setup/blockchain.rs +++ b/crates/driver/src/tests/setup/blockchain.rs @@ -132,33 +132,33 @@ impl QuotedOrder { } pub fn driver_buy_amount(&self) -> eth::U256 { - let executed_buy = match self.order.partial { - Partial::Yes { - executed_sell: _, - executed_buy, - } => executed_buy, - Partial::No => eth::U256::zero(), + let executed_buy = match self.order.side { + order::Side::Buy => self.order.executed.unwrap_or(self.buy), + order::Side::Sell => self + .order + .executed + .map(|executed_sell| self.buy * executed_sell / self.sell) + .unwrap_or(self.buy), }; - let buy_amount = match self.order.side { - order::Side::Buy => self.buy, - order::Side::Sell => self.buy / self.order.surplus_factor, - }; - buy_amount.saturating_sub(executed_buy) + match self.order.side { + order::Side::Buy => executed_buy, + order::Side::Sell => executed_buy / self.order.surplus_factor, + } } pub fn driver_sell_amount(&self) -> eth::U256 { - let executed_sell = match self.order.partial { - Partial::Yes { - executed_sell, - executed_buy: _, - } => executed_sell, - Partial::No => eth::U256::zero(), - }; - let sell_amount = match self.order.side { - order::Side::Buy => self.sell * self.order.surplus_factor, - order::Side::Sell => self.sell, + let executed_sell = match self.order.side { + order::Side::Sell => self.order.executed.unwrap_or(self.sell), + order::Side::Buy => self + .order + .executed + .map(|executed_buy| self.sell * executed_buy / self.buy) + .unwrap_or(self.sell), }; - sell_amount.saturating_sub(executed_sell) + match self.order.side { + order::Side::Buy => executed_sell * self.order.surplus_factor, + order::Side::Sell => executed_sell, + } } /// The UID of the order. @@ -590,18 +590,8 @@ impl Blockchain { /// Compute the execution of an order given the available liquidity pub fn execution(&self, order: &Order) -> Execution { - let (oder_sell_amount, order_buy_amount) = match order.partial { - Partial::Yes { - executed_sell, - executed_buy, - } => ( - order.sell_amount.saturating_sub(executed_sell), - order.buy_amount.map(|buy| buy.saturating_sub(executed_buy)), - ), - Partial::No => (order.sell_amount, order.buy_amount), - }; let pair = self.find_pair(order); - let (sell, buy) = match (order.side, order_buy_amount) { + let (sell, buy) = match (order.side, order.executed.or(order.buy_amount)) { // For buy order with explicitly specified amounts, use the buy amount (order::Side::Buy, Some(buy_amount)) => ( pair.pool.in_given_out(Asset { @@ -610,11 +600,22 @@ impl Blockchain { }), buy_amount, ), + // todo: simplify it + (order::Side::Sell, _) => { + let sell_amount = order.executed.unwrap_or(order.sell_amount); + ( + sell_amount, + pair.pool.out_given_in(Asset { + amount: sell_amount, + token: order.sell_token, + }), + ) + } // Otherwise assume the full sell amount to compute the execution (_, _) => ( - oder_sell_amount, + order.sell_amount, pair.pool.out_given_in(Asset { - amount: oder_sell_amount, + amount: order.sell_amount, token: order.sell_token, }), ), diff --git a/crates/driver/src/tests/setup/driver.rs b/crates/driver/src/tests/setup/driver.rs index 47466e94e6..142335dd7e 100644 --- a/crates/driver/src/tests/setup/driver.rs +++ b/crates/driver/src/tests/setup/driver.rs @@ -69,16 +69,6 @@ pub fn solve_req(test: &Test) -> serde_json::Value { let mut quotes = test.quoted_orders.clone(); quotes.shuffle(&mut rand::thread_rng()); for quote in quotes.iter() { - let executed = match quote.order.partial { - Partial::Yes { - executed_sell, - executed_buy, - } => match quote.order.side { - order::Side::Sell => executed_sell.to_string(), - order::Side::Buy => executed_buy.to_string(), - }, - Partial::No => "0".to_owned(), - }; orders_json.push(json!({ "uid": quote.order_uid(&test.blockchain), "sellToken": hex_address(test.blockchain.get_token(quote.order.sell_token)), @@ -98,7 +88,10 @@ pub fn solve_req(test: &Test) -> serde_json::Value { }, "owner": hex_address(test.trader_address), "partiallyFillable": matches!(quote.order.partial, Partial::Yes { .. }), - "executed": executed, + "executed": match quote.order.partial { + Partial::Yes { executed } => executed.to_string(), + Partial::No => "0".to_owned(), + }, "preInteractions": [], "postInteractions": [], "class": match quote.order.kind { diff --git a/crates/driver/src/tests/setup/mod.rs b/crates/driver/src/tests/setup/mod.rs index e245668231..20e7c22a45 100644 --- a/crates/driver/src/tests/setup/mod.rs +++ b/crates/driver/src/tests/setup/mod.rs @@ -57,8 +57,7 @@ pub enum Partial { #[default] No, Yes { - executed_sell: eth::U256, - executed_buy: eth::U256, + executed: eth::U256, }, } From 507504760269427f2966ce5f5d24f3b8b1362dd0 Mon Sep 17 00:00:00 2001 From: ilya Date: Wed, 13 Mar 2024 10:06:38 +0000 Subject: [PATCH 06/19] Comments --- crates/driver/src/tests/cases/protocol_fees.rs | 5 +++++ crates/driver/src/tests/setup/blockchain.rs | 4 ++++ crates/driver/src/tests/setup/mod.rs | 5 +++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 28662270df..0dd8dc35e1 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -52,6 +52,8 @@ async fn protocol_fee_test_case(test_case: TestCase) { .sell_amount(test_case.execution.solver.sell) .buy_amount(test_case.execution.solver.buy); let pool = ab_adjusted_pool(quote); + // Check if the order is expected to be partially filled by calculating a + // difference between solver's and order's target amounts let partially_executed = match test_case.order.side { order::Side::Sell => test_case .order @@ -62,12 +64,15 @@ async fn protocol_fee_test_case(test_case: TestCase) { .buy_amount .saturating_sub(test_case.execution.solver.buy), }; + // If there is a difference, the order is considered to be partially fillable let partially_executed: Option = (!partially_executed.is_zero()).then_some(partially_executed); let partial = match partially_executed { Some(executed) => Partial::Yes { executed }, None => Partial::No, }; + // Target amount to be executed by the solver in case of partially fillable + // order let executed = match partial { Partial::Yes { .. } => match test_case.order.side { order::Side::Buy => Some(test_case.execution.solver.buy), diff --git a/crates/driver/src/tests/setup/blockchain.rs b/crates/driver/src/tests/setup/blockchain.rs index 911db2fe05..5ca45d1193 100644 --- a/crates/driver/src/tests/setup/blockchain.rs +++ b/crates/driver/src/tests/setup/blockchain.rs @@ -131,12 +131,14 @@ impl QuotedOrder { } } + /// Calculates buy amount that should be received from a driver pub fn driver_buy_amount(&self) -> eth::U256 { let executed_buy = match self.order.side { order::Side::Buy => self.order.executed.unwrap_or(self.buy), order::Side::Sell => self .order .executed + // Since `executed` is a target amount .map(|executed_sell| self.buy * executed_sell / self.sell) .unwrap_or(self.buy), }; @@ -146,12 +148,14 @@ impl QuotedOrder { } } + /// Calculates sell amount that should be received from a driver pub fn driver_sell_amount(&self) -> eth::U256 { let executed_sell = match self.order.side { order::Side::Sell => self.order.executed.unwrap_or(self.sell), order::Side::Buy => self .order .executed + // Since `executed` is a target amount .map(|executed_buy| self.sell * executed_buy / self.buy) .unwrap_or(self.sell), }; diff --git a/crates/driver/src/tests/setup/mod.rs b/crates/driver/src/tests/setup/mod.rs index 20e7c22a45..abb0787472 100644 --- a/crates/driver/src/tests/setup/mod.rs +++ b/crates/driver/src/tests/setup/mod.rs @@ -128,8 +128,9 @@ pub struct Order { /// buy amount is divided depends on the order side. This is necessary to /// keep the solution scores positive. pub surplus_factor: eth::U256, - /// Override the executed amount of the order. Useful for testing liquidity - /// orders. Otherwise [`execution_diff`] is probably more suitable. + /// Override the executed target amount of the order. Useful for testing + /// liquidity orders. Otherwise [`execution_diff`] is probably more + /// suitable. pub executed: Option, /// Provides explicit expected order executed amounts. pub expected_amounts: Option, From 257d023f88bc211d5a65ece75a5feb61b2b13a29 Mon Sep 17 00:00:00 2001 From: ilya Date: Wed, 13 Mar 2024 13:45:40 +0000 Subject: [PATCH 07/19] Fixed value --- crates/driver/src/tests/cases/protocol_fees.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 0dd8dc35e1..d2f06c43f6 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -164,11 +164,11 @@ async fn surplus_protocol_fee_buy_partial_order() { // 6 ETH surplus in sell token (after network fee), half of which is kept by the // protocol solver: Amounts { - sell: 41.ether().into_wei(), + sell: 29.ether().into_wei(), buy: 28.ether().into_wei(), }, driver: Amounts { - sell: 38.ether().into_wei(), + sell: 32.ether().into_wei(), buy: 28.ether().into_wei(), }, }, From 9c9bf73c4e6477b9328dcd2cbfbd996993d0b3a3 Mon Sep 17 00:00:00 2001 From: ilya Date: Wed, 13 Mar 2024 15:51:47 +0000 Subject: [PATCH 08/19] Sell order adjustments --- .../driver/src/tests/cases/protocol_fees.rs | 49 +++++++++++++++---- crates/driver/src/tests/setup/blockchain.rs | 13 +++-- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index d2f06c43f6..5322508f50 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -71,12 +71,13 @@ async fn protocol_fee_test_case(test_case: TestCase) { Some(executed) => Partial::Yes { executed }, None => Partial::No, }; + let solver_fee = test_case.execution.driver.sell / 100; // Target amount to be executed by the solver in case of partially fillable // order let executed = match partial { Partial::Yes { .. } => match test_case.order.side { order::Side::Buy => Some(test_case.execution.solver.buy), - order::Side::Sell => Some(test_case.execution.solver.sell), + order::Side::Sell => Some(test_case.execution.solver.sell - solver_fee), }, Partial::No => None, }; @@ -93,7 +94,7 @@ async fn protocol_fee_test_case(test_case: TestCase) { // Expected amounts already account for network fee, so it doesn't matter for the math. // However, it cannot be zero, otherwise the order would be perceived as a StaticFee orders (which cannot have Protocol Fees) // todo: can be cleaned up after https://github.com/cowprotocol/services/issues/2507 - .solver_fee(Some(test_case.execution.driver.sell / 100)) + .solver_fee(Some(solver_fee)) .side(test_case.order.side) .fee_policy(test_case.fee_policy) .executed(executed) @@ -147,7 +148,37 @@ async fn surplus_protocol_fee_buy_order_not_capped() { #[tokio::test] #[ignore] -async fn surplus_protocol_fee_buy_partial_order() { +async fn surplus_protocol_fee_sell_order_not_capped() { + let fee_policy = Policy::Surplus { + factor: 0.5, + // high enough so we don't get capped by volume fee + max_volume_factor: 1.0, + }; + let test_case = TestCase { + fee_policy, + order: Order { + sell_amount: 50.ether().into_wei(), + buy_amount: 40.ether().into_wei(), + side: order::Side::Sell, + }, + execution: Execution { + // 20 ETH surplus, half of which gets captured by the protocol + solver: Amounts { + sell: 50.ether().into_wei(), + buy: 60.ether().into_wei(), + }, + driver: Amounts { + sell: 50.ether().into_wei(), + buy: 50.ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} + +#[tokio::test] +#[ignore] +async fn surplus_protocol_fee_partial_buy_order_not_capped() { let fee_policy = Policy::Surplus { factor: 0.5, // high enough so we don't get capped by volume fee @@ -179,7 +210,7 @@ async fn surplus_protocol_fee_buy_partial_order() { #[tokio::test] #[ignore] -async fn surplus_protocol_fee_sell_order_not_capped() { +async fn surplus_protocol_fee_partial_sell_order_not_capped() { let fee_policy = Policy::Surplus { factor: 0.5, // high enough so we don't get capped by volume fee @@ -193,14 +224,14 @@ async fn surplus_protocol_fee_sell_order_not_capped() { side: order::Side::Sell, }, execution: Execution { - // 20 ETH surplus, half of which gets captured by the protocol + // 6 ETH surplus, half of which gets captured by the protocol solver: Amounts { - sell: 50.ether().into_wei(), - buy: 60.ether().into_wei(), + sell: 25.ether().into_wei(), + buy: 26.ether().into_wei(), }, driver: Amounts { - sell: 50.ether().into_wei(), - buy: 50.ether().into_wei(), + sell: 25.ether().into_wei(), + buy: 23.ether().into_wei(), }, }, }; diff --git a/crates/driver/src/tests/setup/blockchain.rs b/crates/driver/src/tests/setup/blockchain.rs index 5ca45d1193..746398a723 100644 --- a/crates/driver/src/tests/setup/blockchain.rs +++ b/crates/driver/src/tests/setup/blockchain.rs @@ -139,7 +139,7 @@ impl QuotedOrder { .order .executed // Since `executed` is a target amount - .map(|executed_sell| self.buy * executed_sell / self.sell) + .map(|executed_sell| self.buy * (executed_sell + self.order.solver_fee.unwrap_or_default()) / self.sell) .unwrap_or(self.buy), }; match self.order.side { @@ -151,7 +151,11 @@ impl QuotedOrder { /// Calculates sell amount that should be received from a driver pub fn driver_sell_amount(&self) -> eth::U256 { let executed_sell = match self.order.side { - order::Side::Sell => self.order.executed.unwrap_or(self.sell), + order::Side::Sell => self + .order + .executed + .map(|sell| sell + self.order.solver_fee.unwrap_or_default()) + .unwrap_or(self.sell), order::Side::Buy => self .order .executed @@ -606,7 +610,10 @@ impl Blockchain { ), // todo: simplify it (order::Side::Sell, _) => { - let sell_amount = order.executed.unwrap_or(order.sell_amount); + let sell_amount = order + .executed + .map(|sell| sell + order.solver_fee.unwrap_or_default()) + .unwrap_or(order.sell_amount); ( sell_amount, pair.pool.out_given_in(Asset { From 2f844ef7132e069dc251170f9c0ed49d420723a1 Mon Sep 17 00:00:00 2001 From: ilya Date: Wed, 13 Mar 2024 17:23:37 +0000 Subject: [PATCH 09/19] Capped surplus partially fillable tests --- .../driver/src/tests/cases/protocol_fees.rs | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 5322508f50..b46daa1f9d 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -265,7 +265,6 @@ async fn surplus_protocol_fee_buy_order_capped() { }, }, }; - protocol_fee_test_case(test_case).await; } @@ -299,6 +298,66 @@ async fn surplus_protocol_fee_sell_order_capped() { protocol_fee_test_case(test_case).await; } +#[tokio::test] +#[ignore] +async fn surplus_protocol_fee_partial_buy_order_capped() { + let fee_policy = Policy::Surplus { + factor: 0.5, + // low enough so we get capped by volume fee + max_volume_factor: 0.2, + }; + let test_case = TestCase { + fee_policy, + order: Order { + sell_amount: 50.ether().into_wei(), + buy_amount: 40.ether().into_wei(), + side: order::Side::Buy, + }, + execution: Execution { + // Fee is capped at 20% of solver proposed sell volume + solver: Amounts { + sell: 25.ether().into_wei(), + buy: 28.ether().into_wei(), + }, + driver: Amounts { + sell: 30.ether().into_wei(), + buy: 28.ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} + +#[tokio::test] +#[ignore] +async fn surplus_protocol_fee_partial_sell_order_capped() { + let fee_policy = Policy::Surplus { + factor: 0.5, + // log enough so we get capped by volume fee + max_volume_factor: 0.1, + }; + let test_case = TestCase { + fee_policy, + order: Order { + sell_amount: 50.ether().into_wei(), + buy_amount: 40.ether().into_wei(), + side: order::Side::Sell, + }, + execution: Execution { + // Fee is capped at 10% of solver proposed buy volume + solver: Amounts { + sell: 25.ether().into_wei(), + buy: 26.ether().into_wei(), + }, + driver: Amounts { + sell: 25.ether().into_wei(), + buy: "23.4".ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} + #[tokio::test] #[ignore] async fn volume_protocol_fee_buy_order() { From 72ff73a81cba8b3c60888aa7e528a828fff2b941 Mon Sep 17 00:00:00 2001 From: ilya Date: Wed, 13 Mar 2024 19:32:46 +0000 Subject: [PATCH 10/19] Volume fee partially fillable orders tests --- .../driver/src/tests/cases/protocol_fees.rs | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index b46daa1f9d..22fdaa845b 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -381,7 +381,6 @@ async fn volume_protocol_fee_buy_order() { }, }, }; - protocol_fee_test_case(test_case).await; } @@ -411,6 +410,58 @@ async fn volume_protocol_fee_sell_order() { protocol_fee_test_case(test_case).await; } +#[tokio::test] +#[ignore] +async fn volume_protocol_fee_partial_buy_order() { + let fee_policy = Policy::Volume { factor: 0.5 }; + let test_case = TestCase { + fee_policy, + order: Order { + sell_amount: 50.ether().into_wei(), + buy_amount: 40.ether().into_wei(), + side: order::Side::Buy, + }, + execution: Execution { + // Half of the solver proposed sell volume is kept by the protocol + solver: Amounts { + sell: 20.ether().into_wei(), + buy: 28.ether().into_wei(), + }, + driver: Amounts { + sell: 30.ether().into_wei(), + buy: 28.ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} + +#[tokio::test] +#[ignore] +async fn volume_protocol_fee_partial_sell_order() { + let fee_policy = Policy::Volume { factor: 0.1 }; + let test_case = TestCase { + fee_policy, + order: Order { + sell_amount: 50.ether().into_wei(), + buy_amount: 40.ether().into_wei(), + side: order::Side::Sell, + }, + execution: Execution { + // 10% of the solver proposed buy value is kept by the protocol + solver: Amounts { + sell: 25.ether().into_wei(), + buy: 30.ether().into_wei(), + }, + driver: Amounts { + sell: 25.ether().into_wei(), + buy: 27.ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} + #[tokio::test] #[ignore] async fn price_improvement_fee_buy_in_market_order_not_capped() { From 227db4e3413a294ff778dda64a3b61bd136316ba Mon Sep 17 00:00:00 2001 From: ilya Date: Wed, 13 Mar 2024 19:54:51 +0000 Subject: [PATCH 11/19] Price Improvement fee partially fillable orders tests --- .../driver/src/tests/cases/protocol_fees.rs | 292 +++++++++++++++++- 1 file changed, 288 insertions(+), 4 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 22fdaa845b..1756eec384 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -495,7 +495,6 @@ async fn price_improvement_fee_buy_in_market_order_not_capped() { }, }, }; - protocol_fee_test_case(test_case).await; } @@ -568,7 +567,6 @@ async fn price_improvement_fee_buy_out_of_market_order_not_capped() { }, }, }; - protocol_fee_test_case(test_case).await; } @@ -641,7 +639,6 @@ async fn price_improvement_fee_buy_in_market_order_capped() { }, }, }; - protocol_fee_test_case(test_case).await; } @@ -714,7 +711,6 @@ async fn price_improvement_fee_buy_out_of_market_order_capped() { }, }, }; - protocol_fee_test_case(test_case).await; } @@ -753,3 +749,291 @@ async fn price_improvement_fee_sell_out_of_market_order_capped() { }; protocol_fee_test_case(test_case).await; } + +#[tokio::test] +#[ignore] +async fn price_improvement_fee_partial_buy_in_market_order_not_capped() { + let fee_policy = Policy::PriceImprovement { + factor: 0.5, + // high enough so we don't get capped by volume fee + max_volume_factor: 1.0, + quote: Quote { + sell: 49.ether().into_wei(), + buy: 40.ether().into_wei(), + network_fee: 1.ether().into_wei(), + }, + }; + let test_case = TestCase { + fee_policy, + order: Order { + // Demanding to sell more than quoted (in-market) + sell_amount: 60.ether().into_wei(), + buy_amount: 40.ether().into_wei(), + side: order::Side::Buy, + }, + execution: Execution { + // Sell 5 ETH less than quoted, half of which is kept by the protocol + solver: Amounts { + sell: 20.ether().into_wei(), + buy: 20.ether().into_wei(), + }, + driver: Amounts { + sell: "22.5".ether().into_wei(), + buy: 20.ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} + +#[tokio::test] +#[ignore] +async fn price_improvement_fee_partial_sell_in_market_order_not_capped() { + let fee_policy = Policy::PriceImprovement { + factor: 0.5, + // high enough so we don't get capped by volume fee + max_volume_factor: 1.0, + quote: Quote { + sell: 49.ether().into_wei(), + buy: 50.ether().into_wei(), + network_fee: 1.ether().into_wei(), + }, + }; + let test_case = TestCase { + fee_policy, + order: Order { + sell_amount: 50.ether().into_wei(), + // Demanding to receive less than quoted (in-market) + buy_amount: 40.ether().into_wei(), + side: order::Side::Sell, + }, + execution: Execution { + // Receive 5 ETH more than quoted, half of which gets captured by the protocol + solver: Amounts { + sell: 25.ether().into_wei(), + buy: 30.ether().into_wei(), + }, + driver: Amounts { + sell: 25.ether().into_wei(), + buy: "27.5".ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} + +#[tokio::test] +#[ignore] +async fn price_improvement_fee_partial_buy_out_of_market_order_not_capped() { + let fee_policy = Policy::PriceImprovement { + factor: 0.5, + // high enough so we don't get capped by volume fee + max_volume_factor: 1.0, + quote: Quote { + sell: 59.ether().into_wei(), + buy: 40.ether().into_wei(), + network_fee: 1.ether().into_wei(), + }, + }; + let test_case = TestCase { + fee_policy, + order: Order { + // Demanding to sell less than quoted (out-market) + sell_amount: 50.ether().into_wei(), + buy_amount: 40.ether().into_wei(), + side: order::Side::Buy, + }, + execution: Execution { + // Sell 5 ETH less than requested, half of which is kept by the protocol + solver: Amounts { + sell: 20.ether().into_wei(), + buy: 20.ether().into_wei(), + }, + driver: Amounts { + sell: "22.5".ether().into_wei(), + buy: 20.ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} + +#[tokio::test] +#[ignore] +async fn price_improvement_fee_partial_sell_out_of_market_order_not_capped() { + let fee_policy = Policy::PriceImprovement { + factor: 0.5, + // high enough so we don't get capped by volume fee + max_volume_factor: 1.0, + quote: Quote { + sell: 49.ether().into_wei(), + buy: 40.ether().into_wei(), + network_fee: 1.ether().into_wei(), + }, + }; + let test_case = TestCase { + fee_policy, + order: Order { + sell_amount: 50.ether().into_wei(), + // Demanding to receive more than quoted (out-market) + buy_amount: 50.ether().into_wei(), + side: order::Side::Sell, + }, + execution: Execution { + // Receive 5 ETH more than quoted, half of which gets captured by the protocol + solver: Amounts { + sell: 25.ether().into_wei(), + buy: 30.ether().into_wei(), + }, + driver: Amounts { + sell: 25.ether().into_wei(), + buy: "27.5".ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} + +#[tokio::test] +#[ignore] +async fn price_improvement_fee_partial_buy_in_market_order_capped() { + let fee_policy = Policy::PriceImprovement { + factor: 0.5, + // low enough so we get capped by volume fee + max_volume_factor: 0.05, + quote: Quote { + sell: 49.ether().into_wei(), + buy: 40.ether().into_wei(), + network_fee: 1.ether().into_wei(), + }, + }; + let test_case = TestCase { + fee_policy, + order: Order { + // Demanding to sell more than quoted (in-market) + sell_amount: 60.ether().into_wei(), + buy_amount: 40.ether().into_wei(), + side: order::Side::Buy, + }, + execution: Execution { + // Fee is capped at 5% of solver proposed sell volume + solver: Amounts { + sell: 20.ether().into_wei(), + buy: 20.ether().into_wei(), + }, + driver: Amounts { + sell: 21.ether().into_wei(), + buy: 20.ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} + +#[tokio::test] +#[ignore] +async fn price_improvement_fee_partial_sell_in_market_order_capped() { + let fee_policy = Policy::PriceImprovement { + factor: 0.5, + // low enough so we get capped by volume fee + max_volume_factor: 0.05, + quote: Quote { + sell: 49.ether().into_wei(), + buy: 50.ether().into_wei(), + network_fee: 1.ether().into_wei(), + }, + }; + let test_case = TestCase { + fee_policy, + order: Order { + sell_amount: 50.ether().into_wei(), + // Demanding to receive less than quoted (in-market) + buy_amount: 40.ether().into_wei(), + side: order::Side::Sell, + }, + execution: Execution { + // Fee is capped at 5% of solver proposed buy volume + solver: Amounts { + sell: 25.ether().into_wei(), + buy: 30.ether().into_wei(), + }, + driver: Amounts { + sell: 25.ether().into_wei(), + buy: "28.5".ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} + +#[tokio::test] +#[ignore] +async fn price_improvement_fee_partial_buy_out_of_market_order_capped() { + let fee_policy = Policy::PriceImprovement { + factor: 0.5, + // low enough so we get capped by volume fee + max_volume_factor: 0.05, + quote: Quote { + sell: 59.ether().into_wei(), + buy: 40.ether().into_wei(), + network_fee: 1.ether().into_wei(), + }, + }; + let test_case = TestCase { + fee_policy, + order: Order { + // Demanding to sell less than quoted (out-market) + sell_amount: 50.ether().into_wei(), + buy_amount: 40.ether().into_wei(), + side: order::Side::Buy, + }, + execution: Execution { + // Fee is capped at 5% of solver proposed sell volume + solver: Amounts { + sell: 20.ether().into_wei(), + buy: 20.ether().into_wei(), + }, + driver: Amounts { + sell: 21.ether().into_wei(), + buy: 20.ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} + +#[tokio::test] +#[ignore] +async fn price_improvement_fee_partial_sell_out_of_market_order_capped() { + let fee_policy = Policy::PriceImprovement { + factor: 0.5, + // low enough so we get capped by volume fee + max_volume_factor: 0.05, + quote: Quote { + sell: 49.ether().into_wei(), + buy: 40.ether().into_wei(), + network_fee: 1.ether().into_wei(), + }, + }; + let test_case = TestCase { + fee_policy, + order: Order { + sell_amount: 50.ether().into_wei(), + // Demanding to receive more than quoted (out-market) + buy_amount: 50.ether().into_wei(), + side: order::Side::Sell, + }, + execution: Execution { + // Fee is capped at 5% of solver proposed buy volume + solver: Amounts { + sell: 25.ether().into_wei(), + buy: 30.ether().into_wei(), + }, + driver: Amounts { + sell: 25.ether().into_wei(), + buy: "28.5".ether().into_wei(), + }, + }, + }; + protocol_fee_test_case(test_case).await; +} From 8ebca7aefea5567847c1b8a9a11c87fe22d6a080 Mon Sep 17 00:00:00 2001 From: ilya Date: Wed, 13 Mar 2024 20:52:21 +0000 Subject: [PATCH 12/19] Minor refactoring --- crates/driver/src/tests/cases/protocol_fees.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 1756eec384..6d3f8bc701 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -65,11 +65,12 @@ async fn protocol_fee_test_case(test_case: TestCase) { .saturating_sub(test_case.execution.solver.buy), }; // If there is a difference, the order is considered to be partially fillable - let partially_executed: Option = - (!partially_executed.is_zero()).then_some(partially_executed); - let partial = match partially_executed { - Some(executed) => Partial::Yes { executed }, - None => Partial::No, + let partial = if partially_executed <= eth::U256::zero() { + Partial::No + } else { + Partial::Yes { + executed: partially_executed, + } }; let solver_fee = test_case.execution.driver.sell / 100; // Target amount to be executed by the solver in case of partially fillable From 8c212ecaff1ccfbf29db15252fa238906f7d0760 Mon Sep 17 00:00:00 2001 From: ilya Date: Thu, 14 Mar 2024 18:25:58 +0000 Subject: [PATCH 13/19] Zero partially executed --- .../driver/src/tests/cases/protocol_fees.rs | 2 +- crates/driver/src/tests/setup/blockchain.rs | 38 ------------------- crates/driver/src/tests/setup/solver.rs | 4 +- 3 files changed, 3 insertions(+), 41 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 6d3f8bc701..8efb6544f7 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -69,7 +69,7 @@ async fn protocol_fee_test_case(test_case: TestCase) { Partial::No } else { Partial::Yes { - executed: partially_executed, + executed: eth::U256::zero(), } }; let solver_fee = test_case.execution.driver.sell / 100; diff --git a/crates/driver/src/tests/setup/blockchain.rs b/crates/driver/src/tests/setup/blockchain.rs index 746398a723..dab8b2b9d2 100644 --- a/crates/driver/src/tests/setup/blockchain.rs +++ b/crates/driver/src/tests/setup/blockchain.rs @@ -131,44 +131,6 @@ impl QuotedOrder { } } - /// Calculates buy amount that should be received from a driver - pub fn driver_buy_amount(&self) -> eth::U256 { - let executed_buy = match self.order.side { - order::Side::Buy => self.order.executed.unwrap_or(self.buy), - order::Side::Sell => self - .order - .executed - // Since `executed` is a target amount - .map(|executed_sell| self.buy * (executed_sell + self.order.solver_fee.unwrap_or_default()) / self.sell) - .unwrap_or(self.buy), - }; - match self.order.side { - order::Side::Buy => executed_buy, - order::Side::Sell => executed_buy / self.order.surplus_factor, - } - } - - /// Calculates sell amount that should be received from a driver - pub fn driver_sell_amount(&self) -> eth::U256 { - let executed_sell = match self.order.side { - order::Side::Sell => self - .order - .executed - .map(|sell| sell + self.order.solver_fee.unwrap_or_default()) - .unwrap_or(self.sell), - order::Side::Buy => self - .order - .executed - // Since `executed` is a target amount - .map(|executed_buy| self.sell * executed_buy / self.buy) - .unwrap_or(self.sell), - }; - match self.order.side { - order::Side::Buy => executed_sell * self.order.surplus_factor, - order::Side::Sell => executed_sell, - } - } - /// The UID of the order. pub fn order_uid(&self, blockchain: &Blockchain) -> tests::boundary::OrderUid { self.boundary(blockchain).uid() diff --git a/crates/driver/src/tests/setup/solver.rs b/crates/driver/src/tests/setup/solver.rs index 96dc6b75f5..d21877fb3c 100644 --- a/crates/driver/src/tests/setup/solver.rs +++ b/crates/driver/src/tests/setup/solver.rs @@ -59,11 +59,11 @@ impl Solver { "buyToken": hex_address(config.blockchain.get_token(buy_token)), "sellAmount": match quote.order.side { order::Side::Buy if config.quote => "22300745198530623141535718272648361505980416".to_owned(), - _ => quote.driver_sell_amount().to_string(), + _ => quote.sell_amount().to_string(), }, "buyAmount": match quote.order.side { order::Side::Sell if config.quote => "1".to_owned(), - _ => quote.driver_buy_amount().to_string(), + _ => quote.buy_amount().to_string(), }, "feeAmount": quote.order.user_fee.to_string(), "kind": match quote.order.side { From 2ac00e79749f67a2717207181a95bdb18d528873 Mon Sep 17 00:00:00 2001 From: ilya Date: Thu, 14 Mar 2024 18:47:23 +0000 Subject: [PATCH 14/19] Applied suggestion --- .../driver/src/tests/cases/protocol_fees.rs | 47 +++++---------- crates/driver/src/tests/setup/blockchain.rs | 59 ++++++++++--------- crates/driver/src/tests/setup/mod.rs | 9 ++- 3 files changed, 54 insertions(+), 61 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 8efb6544f7..4814f4ad11 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -10,7 +10,6 @@ use crate::{ ab_solution, fee::{Policy, Quote}, ExpectedOrderAmounts, - Partial, Test, }, }, @@ -52,37 +51,23 @@ async fn protocol_fee_test_case(test_case: TestCase) { .sell_amount(test_case.execution.solver.sell) .buy_amount(test_case.execution.solver.buy); let pool = ab_adjusted_pool(quote); - // Check if the order is expected to be partially filled by calculating a - // difference between solver's and order's target amounts - let partially_executed = match test_case.order.side { - order::Side::Sell => test_case - .order - .sell_amount - .saturating_sub(test_case.execution.solver.sell), - order::Side::Buy => test_case - .order - .buy_amount - .saturating_sub(test_case.execution.solver.buy), - }; - // If there is a difference, the order is considered to be partially fillable - let partial = if partially_executed <= eth::U256::zero() { - Partial::No - } else { - Partial::Yes { - executed: eth::U256::zero(), - } - }; let solver_fee = test_case.execution.driver.sell / 100; - // Target amount to be executed by the solver in case of partially fillable - // order - let executed = match partial { - Partial::Yes { .. } => match test_case.order.side { - order::Side::Buy => Some(test_case.execution.solver.buy), - order::Side::Sell => Some(test_case.execution.solver.sell - solver_fee), - }, - Partial::No => None, + let executed = match test_case.order.side { + order::Side::Buy => { + if test_case.order.buy_amount > test_case.execution.solver.buy { + Some(test_case.execution.solver.buy) + } else { + None + } + } + order::Side::Sell => { + if test_case.order.sell_amount > test_case.execution.solver.sell { + Some(test_case.execution.solver.sell - solver_fee) + } else { + None + } + } }; - // Amounts expected to be returned by the driver after fee processing let expected_amounts = ExpectedOrderAmounts { sell: test_case.execution.driver.sell, @@ -99,7 +84,7 @@ async fn protocol_fee_test_case(test_case: TestCase) { .side(test_case.order.side) .fee_policy(test_case.fee_policy) .executed(executed) - .partial(partial) + .partial(0.into()) // Surplus is configured explicitly via executed/quoted amounts .no_surplus() .expected_amounts(expected_amounts); diff --git a/crates/driver/src/tests/setup/blockchain.rs b/crates/driver/src/tests/setup/blockchain.rs index dab8b2b9d2..1fa49d7f60 100644 --- a/crates/driver/src/tests/setup/blockchain.rs +++ b/crates/driver/src/tests/setup/blockchain.rs @@ -561,39 +561,42 @@ impl Blockchain { /// Compute the execution of an order given the available liquidity pub fn execution(&self, order: &Order) -> Execution { let pair = self.find_pair(order); - let (sell, buy) = match (order.side, order.executed.or(order.buy_amount)) { - // For buy order with explicitly specified amounts, use the buy amount - (order::Side::Buy, Some(buy_amount)) => ( - pair.pool.in_given_out(Asset { - amount: buy_amount, - token: order.buy_token, - }), - buy_amount, - ), - // todo: simplify it - (order::Side::Sell, _) => { - let sell_amount = order + match order.side { + order::Side::Buy => { + // For buy order with explicitly specified amounts, use the buy amount, + // otherwise assume the full sell amount to compute the execution + let executed = order.executed.or(order.buy_amount); + match executed { + Some(executed) => Execution { + buy: executed, + sell: pair.pool.in_given_out(Asset { + amount: executed, + token: order.buy_token, + }), + }, + None => Execution { + buy: pair.pool.out_given_in(Asset { + amount: order.sell_amount, + token: order.sell_token, + }), + sell: order.sell_amount, + }, + } + } + order::Side::Sell => { + let executed = order .executed - .map(|sell| sell + order.solver_fee.unwrap_or_default()) + .map(|amount| amount + order.solver_fee.unwrap_or_default()) .unwrap_or(order.sell_amount); - ( - sell_amount, - pair.pool.out_given_in(Asset { - amount: sell_amount, + Execution { + buy: pair.pool.out_given_in(Asset { + amount: executed, token: order.sell_token, }), - ) + sell: executed, + } } - // Otherwise assume the full sell amount to compute the execution - (_, _) => ( - order.sell_amount, - pair.pool.out_given_in(Asset { - amount: order.sell_amount, - token: order.sell_token, - }), - ), - }; - Execution { sell, buy } + } } /// Set up the blockchain context and return the interactions needed to diff --git a/crates/driver/src/tests/setup/mod.rs b/crates/driver/src/tests/setup/mod.rs index abb0787472..5bbb6c9a0a 100644 --- a/crates/driver/src/tests/setup/mod.rs +++ b/crates/driver/src/tests/setup/mod.rs @@ -254,8 +254,13 @@ impl Order { } } - pub fn partial(self, partial: Partial) -> Self { - Self { partial, ..self } + pub fn partial(self, already_executed: eth::U256) -> Self { + Self { + partial: Partial::Yes { + executed: already_executed, + }, + ..self + } } pub fn executed(self, executed: Option) -> Self { From d685908b088644b97d3e3ba9fd94633b9bee6646 Mon Sep 17 00:00:00 2001 From: ilya Date: Thu, 14 Mar 2024 19:02:01 +0000 Subject: [PATCH 15/19] Redundant change --- crates/driver/src/tests/cases/protocol_fees.rs | 1 - crates/driver/src/tests/setup/blockchain.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 4814f4ad11..9b9b0a9866 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -15,7 +15,6 @@ use crate::{ }, }; -#[derive(Clone)] struct Amounts { sell: eth::U256, buy: eth::U256, diff --git a/crates/driver/src/tests/setup/blockchain.rs b/crates/driver/src/tests/setup/blockchain.rs index 1fa49d7f60..c805e85e0f 100644 --- a/crates/driver/src/tests/setup/blockchain.rs +++ b/crates/driver/src/tests/setup/blockchain.rs @@ -586,7 +586,7 @@ impl Blockchain { order::Side::Sell => { let executed = order .executed - .map(|amount| amount + order.solver_fee.unwrap_or_default()) + .map(|amount| amount + order.surplus_fee()) .unwrap_or(order.sell_amount); Execution { buy: pair.pool.out_given_in(Asset { From 38469a39578918657c847208af1822a90b15170b Mon Sep 17 00:00:00 2001 From: ilya Date: Thu, 14 Mar 2024 19:33:31 +0000 Subject: [PATCH 16/19] Better tests values --- .../driver/src/tests/cases/protocol_fees.rs | 140 +++++++++--------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 9b9b0a9866..1d126e4cee 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -173,19 +173,19 @@ async fn surplus_protocol_fee_partial_buy_order_not_capped() { fee_policy, order: Order { sell_amount: 50.ether().into_wei(), - buy_amount: 40.ether().into_wei(), + buy_amount: 50.ether().into_wei(), side: order::Side::Buy, }, execution: Execution { - // 6 ETH surplus in sell token (after network fee), half of which is kept by the + // 10 ETH surplus in sell token (after network fee), half of which is kept by the // protocol solver: Amounts { - sell: 29.ether().into_wei(), - buy: 28.ether().into_wei(), + sell: 10.ether().into_wei(), + buy: 20.ether().into_wei(), }, driver: Amounts { - sell: 32.ether().into_wei(), - buy: 28.ether().into_wei(), + sell: 15.ether().into_wei(), + buy: 20.ether().into_wei(), }, }, }; @@ -205,18 +205,18 @@ async fn surplus_protocol_fee_partial_sell_order_not_capped() { fee_policy, order: Order { sell_amount: 50.ether().into_wei(), - buy_amount: 40.ether().into_wei(), + buy_amount: 50.ether().into_wei(), side: order::Side::Sell, }, execution: Execution { - // 6 ETH surplus, half of which gets captured by the protocol + // 10 ETH surplus, half of which gets captured by the protocol solver: Amounts { - sell: 25.ether().into_wei(), - buy: 26.ether().into_wei(), + sell: 20.ether().into_wei(), + buy: 30.ether().into_wei(), }, driver: Amounts { - sell: 25.ether().into_wei(), - buy: 23.ether().into_wei(), + sell: 20.ether().into_wei(), + buy: 25.ether().into_wei(), }, }, }; @@ -295,18 +295,18 @@ async fn surplus_protocol_fee_partial_buy_order_capped() { fee_policy, order: Order { sell_amount: 50.ether().into_wei(), - buy_amount: 40.ether().into_wei(), + buy_amount: 50.ether().into_wei(), side: order::Side::Buy, }, execution: Execution { // Fee is capped at 20% of solver proposed sell volume solver: Amounts { - sell: 25.ether().into_wei(), - buy: 28.ether().into_wei(), + sell: 10.ether().into_wei(), + buy: 20.ether().into_wei(), }, driver: Amounts { - sell: 30.ether().into_wei(), - buy: 28.ether().into_wei(), + sell: 12.ether().into_wei(), + buy: 20.ether().into_wei(), }, }, }; @@ -325,18 +325,18 @@ async fn surplus_protocol_fee_partial_sell_order_capped() { fee_policy, order: Order { sell_amount: 50.ether().into_wei(), - buy_amount: 40.ether().into_wei(), + buy_amount: 50.ether().into_wei(), side: order::Side::Sell, }, execution: Execution { // Fee is capped at 10% of solver proposed buy volume solver: Amounts { - sell: 25.ether().into_wei(), - buy: 26.ether().into_wei(), + sell: 20.ether().into_wei(), + buy: 30.ether().into_wei(), }, driver: Amounts { - sell: 25.ether().into_wei(), - buy: "23.4".ether().into_wei(), + sell: 20.ether().into_wei(), + buy: 27.ether().into_wei(), }, }, }; @@ -403,18 +403,18 @@ async fn volume_protocol_fee_partial_buy_order() { fee_policy, order: Order { sell_amount: 50.ether().into_wei(), - buy_amount: 40.ether().into_wei(), + buy_amount: 50.ether().into_wei(), side: order::Side::Buy, }, execution: Execution { // Half of the solver proposed sell volume is kept by the protocol solver: Amounts { - sell: 20.ether().into_wei(), - buy: 28.ether().into_wei(), + sell: 10.ether().into_wei(), + buy: 20.ether().into_wei(), }, driver: Amounts { - sell: 30.ether().into_wei(), - buy: 28.ether().into_wei(), + sell: 15.ether().into_wei(), + buy: 20.ether().into_wei(), }, }, }; @@ -429,17 +429,17 @@ async fn volume_protocol_fee_partial_sell_order() { fee_policy, order: Order { sell_amount: 50.ether().into_wei(), - buy_amount: 40.ether().into_wei(), + buy_amount: 50.ether().into_wei(), side: order::Side::Sell, }, execution: Execution { // 10% of the solver proposed buy value is kept by the protocol solver: Amounts { - sell: 25.ether().into_wei(), + sell: 20.ether().into_wei(), buy: 30.ether().into_wei(), }, driver: Amounts { - sell: 25.ether().into_wei(), + sell: 20.ether().into_wei(), buy: 27.ether().into_wei(), }, }, @@ -743,7 +743,7 @@ async fn price_improvement_fee_partial_buy_in_market_order_not_capped() { // high enough so we don't get capped by volume fee max_volume_factor: 1.0, quote: Quote { - sell: 49.ether().into_wei(), + sell: 39.ether().into_wei(), buy: 40.ether().into_wei(), network_fee: 1.ether().into_wei(), }, @@ -752,18 +752,18 @@ async fn price_improvement_fee_partial_buy_in_market_order_not_capped() { fee_policy, order: Order { // Demanding to sell more than quoted (in-market) - sell_amount: 60.ether().into_wei(), + sell_amount: 50.ether().into_wei(), buy_amount: 40.ether().into_wei(), side: order::Side::Buy, }, execution: Execution { - // Sell 5 ETH less than quoted, half of which is kept by the protocol + // Sell 10 ETH less than quoted, half of which is kept by the protocol solver: Amounts { - sell: 20.ether().into_wei(), + sell: 10.ether().into_wei(), buy: 20.ether().into_wei(), }, driver: Amounts { - sell: "22.5".ether().into_wei(), + sell: 15.ether().into_wei(), buy: 20.ether().into_wei(), }, }, @@ -793,14 +793,14 @@ async fn price_improvement_fee_partial_sell_in_market_order_not_capped() { side: order::Side::Sell, }, execution: Execution { - // Receive 5 ETH more than quoted, half of which gets captured by the protocol + // Receive 10 ETH more than quoted, half of which gets captured by the protocol solver: Amounts { - sell: 25.ether().into_wei(), + sell: 20.ether().into_wei(), buy: 30.ether().into_wei(), }, driver: Amounts { - sell: 25.ether().into_wei(), - buy: "27.5".ether().into_wei(), + sell: 20.ether().into_wei(), + buy: 25.ether().into_wei(), }, }, }; @@ -816,7 +816,7 @@ async fn price_improvement_fee_partial_buy_out_of_market_order_not_capped() { max_volume_factor: 1.0, quote: Quote { sell: 59.ether().into_wei(), - buy: 40.ether().into_wei(), + buy: 50.ether().into_wei(), network_fee: 1.ether().into_wei(), }, }; @@ -825,17 +825,17 @@ async fn price_improvement_fee_partial_buy_out_of_market_order_not_capped() { order: Order { // Demanding to sell less than quoted (out-market) sell_amount: 50.ether().into_wei(), - buy_amount: 40.ether().into_wei(), + buy_amount: 50.ether().into_wei(), side: order::Side::Buy, }, execution: Execution { - // Sell 5 ETH less than requested, half of which is kept by the protocol + // Sell 10 ETH less than requested, half of which is kept by the protocol solver: Amounts { - sell: 20.ether().into_wei(), + sell: 10.ether().into_wei(), buy: 20.ether().into_wei(), }, driver: Amounts { - sell: "22.5".ether().into_wei(), + sell: 15.ether().into_wei(), buy: 20.ether().into_wei(), }, }, @@ -865,14 +865,14 @@ async fn price_improvement_fee_partial_sell_out_of_market_order_not_capped() { side: order::Side::Sell, }, execution: Execution { - // Receive 5 ETH more than quoted, half of which gets captured by the protocol + // Receive 10 ETH more than quoted, half of which gets captured by the protocol solver: Amounts { - sell: 25.ether().into_wei(), + sell: 20.ether().into_wei(), buy: 30.ether().into_wei(), }, driver: Amounts { - sell: 25.ether().into_wei(), - buy: "27.5".ether().into_wei(), + sell: 20.ether().into_wei(), + buy: 25.ether().into_wei(), }, }, }; @@ -885,10 +885,10 @@ async fn price_improvement_fee_partial_buy_in_market_order_capped() { let fee_policy = Policy::PriceImprovement { factor: 0.5, // low enough so we get capped by volume fee - max_volume_factor: 0.05, + max_volume_factor: 0.1, quote: Quote { sell: 49.ether().into_wei(), - buy: 40.ether().into_wei(), + buy: 50.ether().into_wei(), network_fee: 1.ether().into_wei(), }, }; @@ -897,17 +897,17 @@ async fn price_improvement_fee_partial_buy_in_market_order_capped() { order: Order { // Demanding to sell more than quoted (in-market) sell_amount: 60.ether().into_wei(), - buy_amount: 40.ether().into_wei(), + buy_amount: 50.ether().into_wei(), side: order::Side::Buy, }, execution: Execution { - // Fee is capped at 5% of solver proposed sell volume + // Fee is capped at 10% of solver proposed sell volume solver: Amounts { - sell: 20.ether().into_wei(), + sell: 10.ether().into_wei(), buy: 20.ether().into_wei(), }, driver: Amounts { - sell: 21.ether().into_wei(), + sell: 11.ether().into_wei(), buy: 20.ether().into_wei(), }, }, @@ -921,7 +921,7 @@ async fn price_improvement_fee_partial_sell_in_market_order_capped() { let fee_policy = Policy::PriceImprovement { factor: 0.5, // low enough so we get capped by volume fee - max_volume_factor: 0.05, + max_volume_factor: 0.1, quote: Quote { sell: 49.ether().into_wei(), buy: 50.ether().into_wei(), @@ -937,14 +937,14 @@ async fn price_improvement_fee_partial_sell_in_market_order_capped() { side: order::Side::Sell, }, execution: Execution { - // Fee is capped at 5% of solver proposed buy volume + // Fee is capped at 10% of solver proposed buy volume solver: Amounts { - sell: 25.ether().into_wei(), + sell: 20.ether().into_wei(), buy: 30.ether().into_wei(), }, driver: Amounts { - sell: 25.ether().into_wei(), - buy: "28.5".ether().into_wei(), + sell: 20.ether().into_wei(), + buy: 27.ether().into_wei(), }, }, }; @@ -957,10 +957,10 @@ async fn price_improvement_fee_partial_buy_out_of_market_order_capped() { let fee_policy = Policy::PriceImprovement { factor: 0.5, // low enough so we get capped by volume fee - max_volume_factor: 0.05, + max_volume_factor: 0.1, quote: Quote { sell: 59.ether().into_wei(), - buy: 40.ether().into_wei(), + buy: 50.ether().into_wei(), network_fee: 1.ether().into_wei(), }, }; @@ -969,17 +969,17 @@ async fn price_improvement_fee_partial_buy_out_of_market_order_capped() { order: Order { // Demanding to sell less than quoted (out-market) sell_amount: 50.ether().into_wei(), - buy_amount: 40.ether().into_wei(), + buy_amount: 50.ether().into_wei(), side: order::Side::Buy, }, execution: Execution { - // Fee is capped at 5% of solver proposed sell volume + // Fee is capped at 10% of solver proposed sell volume solver: Amounts { - sell: 20.ether().into_wei(), + sell: 10.ether().into_wei(), buy: 20.ether().into_wei(), }, driver: Amounts { - sell: 21.ether().into_wei(), + sell: 11.ether().into_wei(), buy: 20.ether().into_wei(), }, }, @@ -993,7 +993,7 @@ async fn price_improvement_fee_partial_sell_out_of_market_order_capped() { let fee_policy = Policy::PriceImprovement { factor: 0.5, // low enough so we get capped by volume fee - max_volume_factor: 0.05, + max_volume_factor: 0.1, quote: Quote { sell: 49.ether().into_wei(), buy: 40.ether().into_wei(), @@ -1009,14 +1009,14 @@ async fn price_improvement_fee_partial_sell_out_of_market_order_capped() { side: order::Side::Sell, }, execution: Execution { - // Fee is capped at 5% of solver proposed buy volume + // Fee is capped at 10% of solver proposed buy volume solver: Amounts { - sell: 25.ether().into_wei(), + sell: 20.ether().into_wei(), buy: 30.ether().into_wei(), }, driver: Amounts { - sell: 25.ether().into_wei(), - buy: "28.5".ether().into_wei(), + sell: 20.ether().into_wei(), + buy: 27.ether().into_wei(), }, }, }; From e955a1e56b60c0f3b02dbebf3e6d6ccddae901ed Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 15 Mar 2024 15:01:04 +0000 Subject: [PATCH 17/19] Update crates/driver/src/tests/cases/protocol_fees.rs Co-authored-by: Felix Leupold --- crates/driver/src/tests/cases/protocol_fees.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 1d126e4cee..e7cd5d0360 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -53,11 +53,7 @@ async fn protocol_fee_test_case(test_case: TestCase) { let solver_fee = test_case.execution.driver.sell / 100; let executed = match test_case.order.side { order::Side::Buy => { - if test_case.order.buy_amount > test_case.execution.solver.buy { - Some(test_case.execution.solver.buy) - } else { - None - } + (test_case.order.buy_amount > test_case.execution.solver.buy).then_some(test_case.execution.solver.buy) } order::Side::Sell => { if test_case.order.sell_amount > test_case.execution.solver.sell { From 792900084423aae2a1842ec10eb50b749d75999b Mon Sep 17 00:00:00 2001 From: ilya Date: Sat, 16 Mar 2024 12:23:28 +0000 Subject: [PATCH 18/19] Formatting --- crates/driver/src/tests/cases/protocol_fees.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index e7cd5d0360..cadc6f10c3 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -52,9 +52,8 @@ async fn protocol_fee_test_case(test_case: TestCase) { let pool = ab_adjusted_pool(quote); let solver_fee = test_case.execution.driver.sell / 100; let executed = match test_case.order.side { - order::Side::Buy => { - (test_case.order.buy_amount > test_case.execution.solver.buy).then_some(test_case.execution.solver.buy) - } + order::Side::Buy => (test_case.order.buy_amount > test_case.execution.solver.buy) + .then_some(test_case.execution.solver.buy), order::Side::Sell => { if test_case.order.sell_amount > test_case.execution.solver.sell { Some(test_case.execution.solver.sell - solver_fee) From 20cfba2e5162295c3321c51c8b26da798b534d54 Mon Sep 17 00:00:00 2001 From: ilya Date: Mon, 18 Mar 2024 11:59:12 +0000 Subject: [PATCH 19/19] Minor fix --- crates/driver/src/tests/cases/protocol_fees.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index cadc6f10c3..67cf7c6de1 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -54,13 +54,8 @@ async fn protocol_fee_test_case(test_case: TestCase) { let executed = match test_case.order.side { order::Side::Buy => (test_case.order.buy_amount > test_case.execution.solver.buy) .then_some(test_case.execution.solver.buy), - order::Side::Sell => { - if test_case.order.sell_amount > test_case.execution.solver.sell { - Some(test_case.execution.solver.sell - solver_fee) - } else { - None - } - } + order::Side::Sell => (test_case.order.sell_amount > test_case.execution.solver.sell) + .then_some(test_case.execution.solver.sell - solver_fee), }; // Amounts expected to be returned by the driver after fee processing let expected_amounts = ExpectedOrderAmounts {