diff --git a/crates/shared/src/price_estimation/competition/native.rs b/crates/shared/src/price_estimation/competition/native.rs index 0600500fa1..53c4babab3 100644 --- a/crates/shared/src/price_estimation/competition/native.rs +++ b/crates/shared/src/price_estimation/competition/native.rs @@ -1,9 +1,10 @@ use { super::{compare_error, CompetitionEstimator}, crate::price_estimation::{ - native::{NativePriceEstimateResult, NativePriceEstimating}, + native::{is_price_malformed, NativePriceEstimateResult, NativePriceEstimating}, PriceEstimationError, }, + anyhow::Context, futures::{future::BoxFuture, FutureExt}, model::order::OrderKind, primitive_types::H160, @@ -14,12 +15,22 @@ impl NativePriceEstimating for CompetitionEstimator BoxFuture<'_, NativePriceEstimateResult> { async move { let results = self - .produce_results(token, Result::is_ok, |e, q| e.estimate_native_price(q)) + .produce_results(token, Result::is_ok, |e, q| { + async move { + let res = e.estimate_native_price(q).await; + if res.as_ref().is_ok_and(|price| is_price_malformed(*price)) { + let err = anyhow::anyhow!("estimator returned malformed price"); + return Err(PriceEstimationError::EstimatorInternal(err)); + } + res + } + .boxed() + }) .await; let winner = results .into_iter() .max_by(|a, b| compare_native_result(&a.1, &b.1)) - .expect("we get passed at least 1 result and did not filter out any of them"); + .context("could not get any native price")?; self.report_winner(&token, OrderKind::Buy, winner) } .boxed() @@ -123,4 +134,16 @@ mod tests { .await; assert_eq!(best, native_price(1.)); } + + /// Nonsensical prices like infinities, and non-positive values get ignored. + #[tokio::test] + async fn ignore_nonsensical_prices() { + let subnormal = f64::from_bits(1); + assert!(!subnormal.is_normal()); + + for price in [f64::NEG_INFINITY, -1., 0., f64::INFINITY, subnormal] { + let best = best_response(PriceRanking::MaxOutAmount, vec![native_price(price)]).await; + assert!(best.is_err()); + } + } } diff --git a/crates/shared/src/price_estimation/native.rs b/crates/shared/src/price_estimation/native.rs index dba3c8886b..e0928ddb8d 100644 --- a/crates/shared/src/price_estimation/native.rs +++ b/crates/shared/src/price_estimation/native.rs @@ -93,12 +93,22 @@ impl NativePriceEstimating for NativePriceEstimator { async move { let query = Arc::new(self.query(&token)); let estimate = self.inner.estimate(query.clone()).await?; - Ok(estimate.price_in_buy_token_f64(&query)) + let price = estimate.price_in_buy_token_f64(&query); + if is_price_malformed(price) { + let err = anyhow::anyhow!("estimator returned malformed price: {price}"); + Err(PriceEstimationError::EstimatorInternal(err)) + } else { + Ok(price) + } } .boxed() } } +pub(crate) fn is_price_malformed(price: f64) -> bool { + !price.is_normal() || price <= 0. +} + #[cfg(test)] mod tests { use {