From d17a47cd55c7cb309c44ef5665d8ec4dd3e2f0a0 Mon Sep 17 00:00:00 2001 From: ashleychandy Date: Fri, 8 May 2026 06:33:41 +0000 Subject: [PATCH 1/4] optimize metrics calculation with single-pass summarize --- crates/autopilot/src/domain/settlement/mod.rs | 85 +++++++++++++++++++ crates/autopilot/src/infra/persistence/mod.rs | 15 ++-- 2 files changed, 94 insertions(+), 6 deletions(-) diff --git a/crates/autopilot/src/domain/settlement/mod.rs b/crates/autopilot/src/domain/settlement/mod.rs index 40fd64de2f..334bf51b0b 100644 --- a/crates/autopilot/src/domain/settlement/mod.rs +++ b/crates/autopilot/src/domain/settlement/mod.rs @@ -13,6 +13,7 @@ use { database::{orders::OrderKind, solver_competition_v2::Solution}, eth_domain_types as eth, futures::TryFutureExt, + num::Zero, number::conversions::big_decimal_to_u256, std::collections::{HashMap, HashSet}, }; @@ -28,6 +29,18 @@ pub use { transaction::Transaction, }; +/// Combined settlement metrics computed in a single pass for efficiency. +/// Collapses multiple iterations over trades into one. +#[derive(Debug)] +pub struct SettlementMetrics<'a> { + pub gas: eth::Gas, + pub gas_price: eth::EffectiveGasPrice, + pub surplus: eth::Ether, + pub fee: eth::Ether, + pub fee_breakdown: HashMap, + pub jit_orders: Vec<&'a trade::Jit>, +} + /// A settled transaction together with the `Auction`, for which it was executed /// on-chain. /// @@ -146,6 +159,78 @@ impl Settlement { .collect() } + /// Efficiently compute all settlement data by collapsing multiple + /// iterations. Instead of calling surplus_in_ether(), fee_in_ether(), + /// fee_breakdown(), and jit_orders() separately (4 iterations over + /// trades), this method performs a single iteration and calls each + /// trade method once per trade. + pub fn summarize(&self) -> SettlementMetrics<'_> { + let mut surplus = eth::Ether::zero(); + let mut fee = eth::Ether::zero(); + let mut fee_breakdown = HashMap::with_capacity(self.trades.len()); + let mut jit_orders = Vec::new(); + + for trade in &self.trades { + // Accumulate surplus (same logic as surplus_in_ether) + let trade_surplus = + trade + .surplus_in_ether(&self.auction.prices) + .unwrap_or_else(|err| { + tracing::warn!( + ?err, + trade = %trade.uid(), + "possible incomplete surplus calculation", + ); + num::zero() + }); + surplus = surplus + trade_surplus; + + // Accumulate fee (same logic as fee_in_ether) + let trade_fee = trade + .fee_in_ether(&self.auction.prices) + .unwrap_or_else(|err| { + tracing::warn!( + ?err, + trade = %trade.uid(), + "possible incomplete fee calculation", + ); + num::zero() + }); + fee = fee + trade_fee; + + // Collect fee breakdown (same logic as fee_breakdown) + let breakdown = trade.fee_breakdown(&self.auction).unwrap_or_else(|err| { + tracing::warn!( + ?err, + trade = %trade.uid(), + "possible incomplete fee breakdown calculation", + ); + trade::FeeBreakdown { + total: eth::Asset { + token: trade.sell_token(), + amount: num::zero(), + }, + protocol: vec![], + } + }); + fee_breakdown.insert(*trade.uid(), breakdown); + + // Collect JIT orders (same logic as jit_orders) + if let Some(jit) = trade.as_jit() { + jit_orders.push(jit); + } + } + + SettlementMetrics { + gas: self.gas, + gas_price: self.gas_price, + surplus, + fee, + fee_breakdown, + jit_orders, + } + } + pub async fn new( settled: Transaction, persistence: &infra::Persistence, diff --git a/crates/autopilot/src/infra/persistence/mod.rs b/crates/autopilot/src/infra/persistence/mod.rs index 07266a6182..5283abf984 100644 --- a/crates/autopilot/src/infra/persistence/mod.rs +++ b/crates/autopilot/src/infra/persistence/mod.rs @@ -795,12 +795,15 @@ impl Persistence { .await?; if let Some(settlement) = settlement { - let gas = settlement.gas(); - let gas_price = settlement.gas_price(); - let surplus = settlement.surplus_in_ether(); - let fee = settlement.fee_in_ether(); - let fee_breakdown = settlement.fee_breakdown(); - let jit_orders = settlement.jit_orders(); + // Use optimized single-pass calculation + let summary = settlement.summarize(); + let gas = summary.gas; + let gas_price = summary.gas_price; + let surplus = summary.surplus; + let fee = summary.fee; + let fee_breakdown = summary.fee_breakdown; + let jit_orders = summary.jit_orders; + let solver: database::Address = ByteArray(settlement.solver().0.0); tracing::debug!( From 666fc8bcfb04ed877d3bd487ca18cc54390e5a1e Mon Sep 17 00:00:00 2001 From: ashleychandy Date: Fri, 8 May 2026 06:41:45 +0000 Subject: [PATCH 2/4] destructure settlement metrics --- crates/autopilot/src/infra/persistence/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/autopilot/src/infra/persistence/mod.rs b/crates/autopilot/src/infra/persistence/mod.rs index 5283abf984..e1d60a9e4c 100644 --- a/crates/autopilot/src/infra/persistence/mod.rs +++ b/crates/autopilot/src/infra/persistence/mod.rs @@ -796,14 +796,14 @@ impl Persistence { if let Some(settlement) = settlement { // Use optimized single-pass calculation - let summary = settlement.summarize(); - let gas = summary.gas; - let gas_price = summary.gas_price; - let surplus = summary.surplus; - let fee = summary.fee; - let fee_breakdown = summary.fee_breakdown; - let jit_orders = summary.jit_orders; - + let domain::settlement::SettlementMetrics { + gas, + gas_price, + surplus, + fee, + fee_breakdown, + jit_orders, + } = settlement.summarize(); let solver: database::Address = ByteArray(settlement.solver().0.0); tracing::debug!( From a14446ce7a9d0285a150d51e404db1a6f7d64b88 Mon Sep 17 00:00:00 2001 From: ashleychandy Date: Fri, 8 May 2026 10:25:42 +0000 Subject: [PATCH 3/4] restrict metrics visibility and consolidate computation --- crates/autopilot/src/domain/settlement/mod.rs | 99 ++----------------- 1 file changed, 10 insertions(+), 89 deletions(-) diff --git a/crates/autopilot/src/domain/settlement/mod.rs b/crates/autopilot/src/domain/settlement/mod.rs index 334bf51b0b..dd87f007e1 100644 --- a/crates/autopilot/src/domain/settlement/mod.rs +++ b/crates/autopilot/src/domain/settlement/mod.rs @@ -30,15 +30,14 @@ pub use { }; /// Combined settlement metrics computed in a single pass for efficiency. -/// Collapses multiple iterations over trades into one. #[derive(Debug)] -pub struct SettlementMetrics<'a> { - pub gas: eth::Gas, - pub gas_price: eth::EffectiveGasPrice, - pub surplus: eth::Ether, - pub fee: eth::Ether, - pub fee_breakdown: HashMap, - pub jit_orders: Vec<&'a trade::Jit>, +pub(crate) struct SettlementMetrics<'a> { + pub(crate) gas: eth::Gas, + pub(crate) gas_price: eth::EffectiveGasPrice, + pub(crate) surplus: eth::Ether, + pub(crate) fee: eth::Ether, + pub(crate) fee_breakdown: HashMap, + pub(crate) jit_orders: Vec<&'a trade::Jit>, } /// A settled transaction together with the `Auction`, for which it was executed @@ -88,90 +87,15 @@ impl Settlement { self.solution_uid } - /// Total surplus for all trades in the settlement. - pub fn surplus_in_ether(&self) -> eth::Ether { - self.trades - .iter() - .map(|trade| { - trade - .surplus_in_ether(&self.auction.prices) - .unwrap_or_else(|err| { - tracing::warn!( - ?err, - trade = %trade.uid(), - "possible incomplete surplus calculation", - ); - num::zero() - }) - }) - .sum() - } - - /// Total fee taken for all the trades in the settlement. - pub fn fee_in_ether(&self) -> eth::Ether { - self.trades - .iter() - .map(|trade| { - trade - .fee_in_ether(&self.auction.prices) - .unwrap_or_else(|err| { - tracing::warn!( - ?err, - trade = %trade.uid(), - "possible incomplete fee calculation", - ); - num::zero() - }) - }) - .sum() - } - - /// Per order fees breakdown. Contains all orders from the settlement - pub fn fee_breakdown(&self) -> HashMap { - self.trades - .iter() - .map(|trade| { - let fee_breakdown = trade.fee_breakdown(&self.auction).unwrap_or_else(|err| { - tracing::warn!( - ?err, - trade = %trade.uid(), - "possible incomplete fee breakdown calculation", - ); - trade::FeeBreakdown { - total: eth::Asset { - // TODO surplus token - token: trade.sell_token(), - amount: num::zero(), - }, - protocol: vec![], - } - }); - (*trade.uid(), fee_breakdown) - }) - .collect() - } - - /// Return all trades that are classified as Just-In-Time (JIT) orders. - pub fn jit_orders(&self) -> Vec<&trade::Jit> { - self.trades - .iter() - .filter_map(|trade| trade.as_jit()) - .collect() - } - - /// Efficiently compute all settlement data by collapsing multiple - /// iterations. Instead of calling surplus_in_ether(), fee_in_ether(), - /// fee_breakdown(), and jit_orders() separately (4 iterations over - /// trades), this method performs a single iteration and calls each - /// trade method once per trade. - pub fn summarize(&self) -> SettlementMetrics<'_> { + /// Summarizes settlement data required by the autopilot, see + /// [`SettlementMetrics`] for details. + pub(crate) fn summarize(&self) -> SettlementMetrics<'_> { let mut surplus = eth::Ether::zero(); let mut fee = eth::Ether::zero(); let mut fee_breakdown = HashMap::with_capacity(self.trades.len()); let mut jit_orders = Vec::new(); for trade in &self.trades { - // Accumulate surplus (same logic as surplus_in_ether) let trade_surplus = trade .surplus_in_ether(&self.auction.prices) @@ -185,7 +109,6 @@ impl Settlement { }); surplus = surplus + trade_surplus; - // Accumulate fee (same logic as fee_in_ether) let trade_fee = trade .fee_in_ether(&self.auction.prices) .unwrap_or_else(|err| { @@ -198,7 +121,6 @@ impl Settlement { }); fee = fee + trade_fee; - // Collect fee breakdown (same logic as fee_breakdown) let breakdown = trade.fee_breakdown(&self.auction).unwrap_or_else(|err| { tracing::warn!( ?err, @@ -215,7 +137,6 @@ impl Settlement { }); fee_breakdown.insert(*trade.uid(), breakdown); - // Collect JIT orders (same logic as jit_orders) if let Some(jit) = trade.as_jit() { jit_orders.push(jit); } From fac3f5441302eb51014a2a82016e10e42caee1fa Mon Sep 17 00:00:00 2001 From: ashleychandy Date: Fri, 8 May 2026 13:52:16 +0000 Subject: [PATCH 4/4] clarify metrics and redundant comment --- crates/autopilot/src/domain/settlement/mod.rs | 3 ++- crates/autopilot/src/infra/persistence/mod.rs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/autopilot/src/domain/settlement/mod.rs b/crates/autopilot/src/domain/settlement/mod.rs index dd87f007e1..bbc2ac5010 100644 --- a/crates/autopilot/src/domain/settlement/mod.rs +++ b/crates/autopilot/src/domain/settlement/mod.rs @@ -29,13 +29,14 @@ pub use { transaction::Transaction, }; -/// Combined settlement metrics computed in a single pass for efficiency. +/// Summary of settlement metrics — used gas, gas price, surplus, fee, etc. #[derive(Debug)] pub(crate) struct SettlementMetrics<'a> { pub(crate) gas: eth::Gas, pub(crate) gas_price: eth::EffectiveGasPrice, pub(crate) surplus: eth::Ether, pub(crate) fee: eth::Ether, + /// Map between order and the respective fees. pub(crate) fee_breakdown: HashMap, pub(crate) jit_orders: Vec<&'a trade::Jit>, } diff --git a/crates/autopilot/src/infra/persistence/mod.rs b/crates/autopilot/src/infra/persistence/mod.rs index e1d60a9e4c..030fa06f13 100644 --- a/crates/autopilot/src/infra/persistence/mod.rs +++ b/crates/autopilot/src/infra/persistence/mod.rs @@ -795,7 +795,6 @@ impl Persistence { .await?; if let Some(settlement) = settlement { - // Use optimized single-pass calculation let domain::settlement::SettlementMetrics { gas, gas_price,