diff --git a/crates/autopilot/src/run_loop.rs b/crates/autopilot/src/run_loop.rs index d95fbd7b7f..1ac8f5fbdc 100644 --- a/crates/autopilot/src/run_loop.rs +++ b/crates/autopilot/src/run_loop.rs @@ -480,7 +480,7 @@ impl RunLoop { .map(|(index, bid)| SolverSettlement { solver: bid.driver().name.clone(), solver_address: bid.solution().solver(), - score: Some(Score::Solver(bid.score().get().0)), + score: Some(Score::Protocol(bid.score().get().0)), ranking: index + 1, orders: bid .solution() diff --git a/crates/driver/src/domain/competition/mod.rs b/crates/driver/src/domain/competition/mod.rs index 864dbf39dc..4742c62a39 100644 --- a/crates/driver/src/domain/competition/mod.rs +++ b/crates/driver/src/domain/competition/mod.rs @@ -530,10 +530,9 @@ impl Competition { let scored: Vec<(Solved, Settlement)> = scores .into_iter() .sorted_by_key(|(score, _)| Reverse(*score)) - .map(|(score, settlement)| { + .map(|(_, settlement)| { let solved = Solved { id: settlement.solution().clone(), - score, trades: settlement.orders(), prices: settlement.prices(), gas: Some(settlement.gas.estimate), @@ -1009,7 +1008,6 @@ fn merge( #[derive(Debug)] pub struct Solved { pub id: solution::Id, - pub score: eth::Ether, pub trades: HashMap, pub prices: HashMap, pub gas: Option, diff --git a/crates/driver/src/infra/api/routes/solve/dto/solve_response.rs b/crates/driver/src/infra/api/routes/solve/dto/solve_response.rs index 3db573f820..1eaab52144 100644 --- a/crates/driver/src/infra/api/routes/solve/dto/solve_response.rs +++ b/crates/driver/src/infra/api/routes/solve/dto/solve_response.rs @@ -30,7 +30,6 @@ impl Solution { pub fn new(solution_id: u64, solved: competition::Solved, solver: &Solver) -> Self { Self { solution_id, - score: solved.score.0, submission_address: solver.address(), orders: solved .trades @@ -72,8 +71,6 @@ pub struct Solution { /// in subsequent requests (reveal, settle). solution_id: u64, submission_address: eth::Address, - #[serde_as(as = "serde_ext::U256")] - score: eth::U256, #[serde_as(as = "HashMap")] orders: HashMap, #[serde_as(as = "HashMap<_, serde_ext::U256>")] diff --git a/crates/driver/src/tests/cases/haircut.rs b/crates/driver/src/tests/cases/haircut.rs index 6485ff408b..641c7a7d45 100644 --- a/crates/driver/src/tests/cases/haircut.rs +++ b/crates/driver/src/tests/cases/haircut.rs @@ -52,7 +52,6 @@ async fn order_haircut_reduces_score() { .await; let solve_no_haircut = test_no_haircut.solve().await.ok(); - let score_no_haircut = solve_no_haircut.score(); // Now test with 500 bps (5%) haircut let test_with_haircut = tests::setup() @@ -71,22 +70,6 @@ async fn order_haircut_reduces_score() { .await; let solve_with_haircut = test_with_haircut.solve().await.ok(); - let score_with_haircut = solve_with_haircut.score(); - - // With 500 bps (5%) haircut, the score should be reduced by approximately 5%. - // Compute the actual percentage: (score_with_haircut * 100) / score_no_haircut - // Should be approximately 95 (allowing 94-96 range for tolerance). - let percentage: u64 = ((score_with_haircut * eth::U256::from(100)) / score_no_haircut) - .try_into() - .unwrap(); - - assert!( - (94..=96).contains(&percentage), - "Haircut score {} should be ~95% of baseline {}, but was {}%", - score_with_haircut, - score_no_haircut, - percentage - ); // Extract executedBuy from baseline (no haircut) let solution_no_haircut = solve_no_haircut.solution(); @@ -192,7 +175,6 @@ async fn buy_order_haircut() { .await; let solve_no_haircut = test_no_haircut.solve().await.ok(); - let score_no_haircut = solve_no_haircut.score(); let test_with_haircut = tests::setup() .name("Buy order haircut - with 500 bps") @@ -211,28 +193,6 @@ async fn buy_order_haircut() { .await; let solve_with_haircut = test_with_haircut.solve().await.ok(); - let score_with_haircut = solve_with_haircut.score(); - - // For buy orders, the haircut is applied to the executed buy amount and then - // converted to sell token. The impact on score depends on the price ratio. - // With 500 bps (5%) haircut on a 2 ETH buy amount, the haircut is 0.1 ETH in - // buy token. When converted to sell token at the pool's price ratio, this - // results in a smaller percentage impact on the score compared to sell - // orders. Expected: score reduction of ~1% (percentage ~99%) rather than - // 5%. - let percentage: u64 = ((score_with_haircut * eth::U256::from(100)) / score_no_haircut) - .try_into() - .unwrap(); - - // For buy orders with this setup, expect ~99% (1% reduction) due to price - // conversion - assert!( - (98..=100).contains(&percentage) && score_with_haircut < score_no_haircut, - "Haircut score {} should be ~99% of baseline {} (reduced by ~1%), but was {}%", - score_with_haircut, - score_no_haircut, - percentage - ); // Extract executedSell from baseline (no haircut) let solution_no_haircut = solve_no_haircut.solution(); diff --git a/crates/driver/src/tests/cases/jit_orders.rs b/crates/driver/src/tests/cases/jit_orders.rs index bfa8d4e46d..70d5d85ff3 100644 --- a/crates/driver/src/tests/cases/jit_orders.rs +++ b/crates/driver/src/tests/cases/jit_orders.rs @@ -43,7 +43,6 @@ struct JitOrder { struct Solution { jit_order: JitOrder, - expected_score: eth::U256, } struct TestCase { @@ -55,8 +54,6 @@ struct TestCase { #[cfg(test)] async fn protocol_fee_test_case(test_case: TestCase) { - use number::testing::ApproxEq; - let test_name = format!("JIT Order: {:?}", test_case.solution.jit_order.order.side); // Adjust liquidity pools so that the order is executable at the amounts // expected from the solver. @@ -116,11 +113,6 @@ async fn protocol_fee_test_case(test_case: TestCase) { .await; let result = test.solve().await.ok(); - assert!( - result - .score() - .is_approx_eq(&test_case.solution.expected_score, None), - ); result.jit_orders(&[jit_order]); } @@ -156,7 +148,6 @@ async fn surplus_protocol_fee_jit_order_from_surplus_capturing_owner_not_capped( }, // Surplus is 40 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (50 / 60 = 80%) this leaves us with a score of 32 ETH. - expected_score: 32.ether().into_wei(), }, }; @@ -195,7 +186,6 @@ async fn surplus_protocol_fee_jit_order_not_capped() { }, // Surplus is 20 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 50 = 80%) this leaves us with a score of 16 ETH. - expected_score: 16.ether().into_wei(), }, }; diff --git a/crates/driver/src/tests/cases/protocol_fees.rs b/crates/driver/src/tests/cases/protocol_fees.rs index 2612528223..cef88c049d 100644 --- a/crates/driver/src/tests/cases/protocol_fees.rs +++ b/crates/driver/src/tests/cases/protocol_fees.rs @@ -42,14 +42,11 @@ struct TestCase { order: Order, fee_policy: Vec, execution: Execution, - expected_score: eth::U256, fee_handler: FeeHandler, } #[cfg(test)] async fn protocol_fee_test_case(test_case: TestCase) { - use number::testing::ApproxEq; - let test_name = format!( "Protocol Fee: {:?} {:?}", test_case.order.side, test_case.fee_policy @@ -99,7 +96,6 @@ async fn protocol_fee_test_case(test_case: TestCase) { .await; let result = test.solve().await.ok(); - assert!(result.score().is_approx_eq(&test_case.expected_score, None),); result.orders(&[order]); } @@ -147,7 +143,6 @@ async fn triple_surplus_protocol_fee_buy_order_not_capped() { }, // Surplus is 20 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (50 / 60 = 80%) this leaves us with a score of 16 ETH. - expected_score: 16.ether().into_wei(), fee_handler: FeeHandler::Driver, }; @@ -191,7 +186,6 @@ async fn triple_surplus_protocol_fee_sell_order_not_capped() { buy: 42.5.ether().into_wei(), }, }, - expected_score: 20.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -234,7 +228,6 @@ async fn surplus_and_volume_protocol_fee_buy_order_not_capped() { }, // Surplus is 20 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 60 = 66.6%) this leaves us with a score of 13.3 ETH. - expected_score: 13.3333333333333.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -288,7 +281,6 @@ async fn surplus_and_price_improvement_protocol_fee_sell_order_not_capped() { buy: 17.ether().into_wei(), }, }, - expected_score: 20.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -340,7 +332,6 @@ async fn surplus_and_price_improvement_fee_buy_in_market_order_not_capped() { }, // Surplus is 20 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 60 = 66.6%) this leaves us with a score of 13.3 ETH. - expected_score: 13.33333333333333.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -375,7 +366,6 @@ async fn surplus_protocol_fee_buy_order_not_capped() { }, // Surplus is 20 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 50 = 80%) this leaves us with a score of 16 ETH. - expected_score: 16.ether().into_wei(), fee_handler: FeeHandler::Driver, }; @@ -408,7 +398,6 @@ async fn protocol_fee_calculated_on_the_solver_side() { }, // Surplus is 35 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 50 = 80%) this leaves us with a score of 28 ETH. - expected_score: 28.ether().into_wei(), fee_handler: FeeHandler::Solver, }; @@ -441,7 +430,6 @@ async fn surplus_protocol_fee_sell_order_not_capped() { buy: 50.ether().into_wei(), }, }, - expected_score: 20.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -474,7 +462,6 @@ async fn surplus_protocol_fee_partial_buy_order_not_capped() { buy: 20.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; @@ -507,7 +494,6 @@ async fn surplus_protocol_fee_partial_sell_order_not_capped() { buy: 25.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -541,7 +527,6 @@ async fn surplus_protocol_fee_buy_order_capped() { }, // Surplus is 20 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 50 = 80%) this leaves us with a score of 16 ETH. - expected_score: 16.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -573,7 +558,6 @@ async fn surplus_protocol_fee_sell_order_capped() { buy: 54.ether().into_wei(), }, }, - expected_score: 20.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -605,7 +589,6 @@ async fn surplus_protocol_fee_partial_buy_order_capped() { buy: 20.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -637,7 +620,6 @@ async fn surplus_protocol_fee_partial_sell_order_capped() { buy: 27.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -667,7 +649,6 @@ async fn volume_protocol_fee_buy_order() { }, // Surplus is 20 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 50 = 80%) this leaves us with a score of 16 ETH. - expected_score: 16.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -699,7 +680,6 @@ async fn volume_protocol_fee_buy_order_at_limit_price() { }, // Surplus is 10 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 50 = 80%) this leaves us with a score of 8 ETH. - expected_score: 8.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -727,7 +707,6 @@ async fn volume_protocol_fee_sell_order() { buy: 45.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -757,7 +736,6 @@ async fn volume_protocol_fee_sell_order_at_limit_price() { buy: 40.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -785,7 +763,6 @@ async fn volume_protocol_fee_partial_buy_order() { buy: 20.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -816,7 +793,6 @@ async fn volume_protocol_fee_partial_buy_order_at_limit_price() { buy: 20.ether().into_wei(), }, }, - expected_score: 4.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -844,7 +820,6 @@ async fn volume_protocol_fee_partial_sell_order() { buy: 27.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -875,7 +850,6 @@ async fn volume_protocol_fee_partial_sell_order_at_limit_price() { buy: 20.ether().into_wei(), }, }, - expected_score: 5.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -916,7 +890,6 @@ async fn price_improvement_fee_buy_in_market_order_not_capped() { }, // Surplus is 20 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 60 = 66.6%) this leaves us with a score of 13.3 ETH. - expected_score: 13.33333333333333.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -955,7 +928,6 @@ async fn price_improvement_fee_sell_in_market_order_not_capped() { buy: 53.ether().into_wei(), }, }, - expected_score: 20.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -996,7 +968,6 @@ async fn price_improvement_fee_buy_out_of_market_order_not_capped() { }, // Surplus is 10 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 50 = 80%) this leaves us with a score of 8 ETH. - expected_score: 8.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1035,7 +1006,6 @@ async fn price_improvement_fee_sell_out_of_market_order_not_capped() { buy: 55.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1076,7 +1046,6 @@ async fn price_improvement_fee_buy_in_market_order_capped() { }, // Surplus is 20 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 60 = 66.6%) this leaves us with a score of 13.3 ETH. - expected_score: 13.33333333333333.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1115,7 +1084,6 @@ async fn price_improvement_fee_sell_in_market_order_capped() { buy: 57.ether().into_wei(), }, }, - expected_score: 20.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1156,7 +1124,6 @@ async fn price_improvement_fee_buy_out_of_market_order_capped() { }, // Surplus is 10 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 50 = 80%) this leaves us with a score of 8 ETH. - expected_score: 8.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1195,7 +1162,6 @@ async fn price_improvement_fee_sell_out_of_market_order_capped() { buy: 57.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1236,7 +1202,6 @@ async fn price_improvement_fee_partial_buy_in_market_order_not_capped() { }, // Surplus is 15 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (40 / 50 = 80%) this leaves us with a score of 12 ETH. - expected_score: 12.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1277,7 +1242,6 @@ async fn price_improvement_fee_partial_sell_in_market_order_not_capped() { buy: 24.ether().into_wei(), }, }, - expected_score: 20.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1316,7 +1280,6 @@ async fn price_improvement_fee_partial_buy_out_of_market_order_not_capped() { buy: 20.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1355,7 +1318,6 @@ async fn price_improvement_fee_partial_sell_out_of_market_order_not_capped() { buy: 25.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1396,7 +1358,6 @@ async fn price_improvement_fee_partial_buy_in_market_order_capped() { }, // Surplus is 20 ETH worth of sell tokens, converted to buy tokens using the order's // limit price (50 / 75 = 66.6%) this leaves us with a score of 13.3 ETH. - expected_score: 13.333333333333333.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1435,7 +1396,6 @@ async fn price_improvement_fee_partial_sell_in_market_order_capped() { buy: 27.ether().into_wei(), }, }, - expected_score: 20.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1474,7 +1434,6 @@ async fn price_improvement_fee_partial_buy_out_of_market_order_capped() { buy: 20.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1513,7 +1472,6 @@ async fn price_improvement_fee_partial_sell_out_of_market_order_capped() { buy: 27.ether().into_wei(), }, }, - expected_score: 10.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; @@ -1552,7 +1510,6 @@ async fn price_improvement_fee_sell_no_improvement() { buy: 45.ether().into_wei(), }, }, - expected_score: 5.ether().into_wei(), fee_handler: FeeHandler::Driver, }; protocol_fee_test_case(test_case).await; diff --git a/crates/driver/src/tests/setup/mod.rs b/crates/driver/src/tests/setup/mod.rs index 9eada0359a..3093123d0c 100644 --- a/crates/driver/src/tests/setup/mod.rs +++ b/crates/driver/src/tests/setup/mod.rs @@ -1309,19 +1309,10 @@ impl SolveOk<'_> { let solution = solutions[0].clone(); assert!(solution.is_object()); // response contains 1 optional field - assert!((5..=6).contains(&solution.as_object().unwrap().len())); + assert!((4..=5).contains(&solution.as_object().unwrap().len())); solution } - /// Extracts the score from the response. Since response can contain - /// multiple solutions, it takes the score from the first solution. - pub fn score(&self) -> eth::U256 { - let solution = self.solution(); - assert!(solution.get("score").is_some()); - let score = solution.get("score").unwrap().as_str().unwrap(); - eth::U256::from_str_radix(score, 10).unwrap() - } - /// Ensures that `/solve` returns no solutions. pub fn empty(self) { assert!(self.solutions().is_empty()); diff --git a/crates/model/src/solver_competition.rs b/crates/model/src/solver_competition.rs index 20b9dd934b..60333c5e61 100644 --- a/crates/model/src/solver_competition.rs +++ b/crates/model/src/solver_competition.rs @@ -61,15 +61,14 @@ pub struct SolverSettlement { #[serde_as] #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum Score { - /// The score is provided by the solver. + /// [DEPRECATED] Kept to not break the solver competition API. #[serde(rename = "score")] Solver(#[serde_as(as = "HexOrDecimalU256")] U256), /// The score is calculated by the protocol (and equal to the objective /// function). #[serde(rename = "scoreProtocol")] Protocol(#[serde_as(as = "HexOrDecimalU256")] U256), - /// The score is calculated by the protocol and success_probability provided - /// by solver is taken into account + /// [DEPRECATED] Kept to not break the solver competition API. #[serde(rename = "scoreProtocolWithSolverRisk")] ProtocolWithSolverRisk(#[serde_as(as = "HexOrDecimalU256")] U256), /// The score is calculated by the protocol, by applying a discount to the @@ -151,7 +150,7 @@ mod tests { { "solver": "2", "solverAddress": "0x2222222222222222222222222222222222222222", - "score": "1", + "scoreProtocol": "1", "ranking": 1, "clearingPrices": { "0x2222222222222222222222222222222222222222": "8", @@ -196,7 +195,7 @@ mod tests { solutions: vec![SolverSettlement { solver: "2".to_string(), solver_address: Address::repeat_byte(0x22), - score: Some(Score::Solver(U256::ONE)), + score: Some(Score::Protocol(U256::ONE)), ranking: 1, clearing_prices: btreemap! { Address::repeat_byte(0x22) => U256::from(8), @@ -378,4 +377,20 @@ mod tests { }); assert!(serde_json::from_value::(competition).is_ok()) } + + #[test] + fn score_protocol_round_trip() { + let score = Score::Protocol(U256::from(42)); + let json = serde_json::to_value(&score).unwrap(); + assert_eq!(json, serde_json::json!({"scoreProtocol": "42"})); + let deserialized: Score = serde_json::from_value(json).unwrap(); + assert_eq!(deserialized, score); + } + + #[test] + fn score_solver_legacy_deserialize() { + let json = serde_json::json!({"score": "100"}); + let score: Score = serde_json::from_value(json).unwrap(); + assert_eq!(score, Score::Solver(U256::from(100))); + } }