From ebf806c38260a84da912875db672272ea36c4dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Duarte?= <15343819+jmg-duarte@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:49:51 +0100 Subject: [PATCH] Stop consuming uniform clearing prices in the autopilot Scoring uses auction-level native prices and settlement verification reads prices from on-chain calldata, so per-solution UCPs are dead weight. Drop them from the autopilot domain `Solution`, the `SolutionError::InvalidPrice` variant, and the `proposed_solutions` price columns (now written as empty arrays). The `/solve` wire format is preserved for rolling-deploy safety: - autopilot tolerates a missing `clearingPrices` (`#[serde(default)]`) - our driver still emits it from existing settlement data `clearingPrices` is marked `deprecated: true` in the driver and orderbook OpenAPI specs. A follow-up PR will remove the field, the deprecation log, and the empty DB columns. --- .../autopilot/src/domain/competition/mod.rs | 17 ++------------- .../domain/competition/winner_selection.rs | 16 ++------------ crates/autopilot/src/infra/persistence/mod.rs | 14 ++----------- .../autopilot/src/infra/solvers/dto/solve.rs | 21 +++++++++++++------ crates/autopilot/src/run_loop.rs | 1 - crates/driver/openapi.yml | 12 ++++++++--- crates/driver/src/domain/competition/mod.rs | 5 +++++ .../domain/competition/solution/settlement.rs | 5 ++++- .../api/routes/solve/dto/solve_response.rs | 3 +++ crates/orderbook/openapi.yml | 7 +++++++ 10 files changed, 49 insertions(+), 52 deletions(-) diff --git a/crates/autopilot/src/domain/competition/mod.rs b/crates/autopilot/src/domain/competition/mod.rs index 2900ddec47..e7bd8e0166 100644 --- a/crates/autopilot/src/domain/competition/mod.rs +++ b/crates/autopilot/src/domain/competition/mod.rs @@ -1,6 +1,6 @@ use { super::auction::order, - crate::domain::{self, auction}, + crate::domain, alloy::primitives::Address, derive_more::Display, eth_domain_types as eth, @@ -21,7 +21,6 @@ pub struct Solution { id: SolutionId, solver: Address, orders: HashMap, - prices: auction::Prices, } impl Solution { @@ -29,14 +28,8 @@ impl Solution { id: SolutionId, solver: Address, orders: HashMap, - prices: auction::Prices, ) -> Self { - Self { - id, - solver, - orders, - prices, - } + Self { id, solver, orders } } } @@ -56,10 +49,6 @@ impl Solution { pub fn orders(&self) -> &HashMap { &self.orders } - - pub fn prices(&self) -> &HashMap { - &self.prices - } } #[derive(Debug, Copy, Clone)] @@ -132,8 +121,6 @@ pub struct ZeroScore; pub enum SolutionError { #[error(transparent)] ZeroScore(#[from] ZeroScore), - #[error(transparent)] - InvalidPrice(#[from] auction::InvalidPrice), #[error("the solver got deny listed")] SolverDenyListed, } diff --git a/crates/autopilot/src/domain/competition/winner_selection.rs b/crates/autopilot/src/domain/competition/winner_selection.rs index d779bd52ee..286d13b773 100644 --- a/crates/autopilot/src/domain/competition/winner_selection.rs +++ b/crates/autopilot/src/domain/competition/winner_selection.rs @@ -1013,7 +1013,7 @@ mod tests { let solution_uid = hash(solution_id); solution_map.insert( solution_id, - create_bid(solution_uid, solver_address, trades, None).await, + create_bid(solution_uid, solver_address, trades).await, ); } @@ -1195,22 +1195,10 @@ mod tests { solution_id: u64, solver_address: Address, trades: Vec<(OrderUid, TradedOrder)>, - prices: Option>, ) -> Bid { - // The prices of the tokens do not affect the result but they keys must exist - // for every token of every trade - let prices = prices.unwrap_or({ - let mut res = HashMap::new(); - for (_, trade) in &trades { - res.insert(trade.buy.token, create_price(eth::U256::ONE)); - res.insert(trade.sell.token, create_price(eth::U256::ONE)); - } - res - }); - let trade_order_map: HashMap = trades.into_iter().collect(); - let solution = Solution::new(solution_id, solver_address, trade_order_map, prices); + let solution = Solution::new(solution_id, solver_address, trade_order_map); let driver = Driver::try_new( url::Url::parse("http://localhost").unwrap(), diff --git a/crates/autopilot/src/infra/persistence/mod.rs b/crates/autopilot/src/infra/persistence/mod.rs index 07266a6182..eb278db20a 100644 --- a/crates/autopilot/src/infra/persistence/mod.rs +++ b/crates/autopilot/src/infra/persistence/mod.rs @@ -248,18 +248,8 @@ impl Persistence { side: order.side.into(), }) .collect(), - price_tokens: bid - .solution() - .prices() - .keys() - .map(|token| ByteArray(token.into_array())) - .collect(), - price_values: bid - .solution() - .prices() - .values() - .map(|price| u256_to_big_decimal(&price.get().0)) - .collect(), + price_tokens: vec![], + price_values: vec![], }; Ok::<_, DatabaseError>(solution) }) diff --git a/crates/autopilot/src/infra/solvers/dto/solve.rs b/crates/autopilot/src/infra/solvers/dto/solve.rs index 8cff19b66c..28b096d7a1 100644 --- a/crates/autopilot/src/infra/solvers/dto/solve.rs +++ b/crates/autopilot/src/infra/solvers/dto/solve.rs @@ -157,6 +157,16 @@ impl Response { pub fn into_domain( self, ) -> Vec> { + for solution in &self.solutions { + if !solution.clearing_prices.is_empty() { + tracing::debug!( + solution_id = solution.solution_id, + submission_address = %solution.submission_address, + num_prices = solution.clearing_prices.len(), + "driver sent deprecated clearingPrices field" + ); + } + } self.solutions .into_iter() .map(Solution::into_domain) @@ -197,12 +207,6 @@ impl Solution { .into_iter() .map(|(o, amounts)| (o.into(), amounts.into_domain())) .collect(), - self.clearing_prices - .into_iter() - .map(|(token, price)| { - domain::auction::Price::try_new(price.into()).map(|price| (token.into(), price)) - }) - .collect::>()?, )) } } @@ -271,6 +275,11 @@ pub struct Solution { /// Address used by the driver to submit the settlement onchain. pub submission_address: Address, pub orders: HashMap, + /// Deprecated: uniform clearing prices are no longer used by the + /// autopilot. Kept here purely so we can detect and log drivers that + /// still send them, in order to chase them down before the field is + /// removed entirely. + #[serde(default)] #[serde_as(as = "HashMap<_, HexOrDecimalU256>")] pub clearing_prices: HashMap, pub gas: Option, diff --git a/crates/autopilot/src/run_loop.rs b/crates/autopilot/src/run_loop.rs index d95fbd7b7f..99f09138b1 100644 --- a/crates/autopilot/src/run_loop.rs +++ b/crates/autopilot/src/run_loop.rs @@ -1015,7 +1015,6 @@ impl Metrics { fn solution_err(driver: &infra::Driver, err: &SolutionError) { let label = match err { SolutionError::ZeroScore(_) => "zero_score", - SolutionError::InvalidPrice(_) => "invalid_price", SolutionError::SolverDenyListed => "solver_deny_listed", }; Self::get() diff --git a/crates/driver/openapi.yml b/crates/driver/openapi.yml index 50ab2176c6..a10c4e073e 100644 --- a/crates/driver/openapi.yml +++ b/crates/driver/openapi.yml @@ -537,11 +537,17 @@ components: type: string description: The effective amount the user received after all fees. clearingPrices: + deprecated: true description: > - Mapping of hex token address to price. + Deprecated. The autopilot no longer consumes uniform clearing + prices from the `/solve` response and ignores this field if + present. It is still emitted by drivers for backward + compatibility with autopilots running the previous code, and + will be removed in a follow-up release. - The prices of tokens for settled user orders as passed to the - settlement contract. + + Mapping of hex token address to price. The prices of tokens + for settled user orders as passed to the settlement contract. type: object additionalProperties: $ref: "#/components/schemas/BigUint" diff --git a/crates/driver/src/domain/competition/mod.rs b/crates/driver/src/domain/competition/mod.rs index 864dbf39dc..776016d664 100644 --- a/crates/driver/src/domain/competition/mod.rs +++ b/crates/driver/src/domain/competition/mod.rs @@ -1011,6 +1011,11 @@ pub struct Solved { pub id: solution::Id, pub score: eth::Ether, pub trades: HashMap, + /// Deprecated: uniform clearing prices are no longer consumed by the + /// autopilot. Still emitted on the `/solve` response so that autopilots + /// running the previous code can deserialise it during a rolling deploy. + /// Remove together with the response field once the new autopilot is + /// fully rolled out. pub prices: HashMap, pub gas: Option, } diff --git a/crates/driver/src/domain/competition/solution/settlement.rs b/crates/driver/src/domain/competition/solution/settlement.rs index c2d62f41fa..4b2f37ba47 100644 --- a/crates/driver/src/domain/competition/solution/settlement.rs +++ b/crates/driver/src/domain/competition/solution/settlement.rs @@ -360,7 +360,10 @@ impl Settlement { acc } - /// The uniform price vector this settlement proposes + /// The uniform price vector this settlement proposes. + /// + /// Deprecated: only emitted on the `/solve` response so that autopilots + /// running the previous code can deserialise it during a rolling deploy. pub fn prices(&self) -> HashMap { self.solution .clearing_prices() 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..0a98073152 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 @@ -76,6 +76,9 @@ pub struct Solution { score: eth::U256, #[serde_as(as = "HashMap")] orders: HashMap, + /// Deprecated: kept only for backward compatibility with autopilots + /// running the previous code during a rolling deploy. Will be removed in + /// a follow-up once the new autopilot is fully rolled out. #[serde_as(as = "HashMap<_, serde_ext::U256>")] clearing_prices: HashMap, } diff --git a/crates/orderbook/openapi.yml b/crates/orderbook/openapi.yml index e53e3097c9..170df2fc81 100644 --- a/crates/orderbook/openapi.yml +++ b/crates/orderbook/openapi.yml @@ -2137,10 +2137,17 @@ components: Transaction in which the solution was executed onchain (if available). nullable: true clearingPrices: + deprecated: true type: object additionalProperties: $ref: "#/components/schemas/BigUint" description: > + Deprecated. The autopilot no longer persists per-solution uniform + clearing prices, so this field will be empty for solutions of + auctions produced by recent autopilots. Solutions stored before + this change keep their original values. + + The prices of tokens for settled user orders as passed to the settlement contract. orders: