Skip to content

Commit

Permalink
Ignore nonsensical native prices (#3106)
Browse files Browse the repository at this point in the history
# Description
Currently some solvers are reporting nonsensical results for native
prices.

# Changes
Changes the native price competition to ignore subnormal values (e.g.
infinities, and undefined byte patterns) and non-positive values. This
still leaves plenty of room for incorrect values but at least the
obviously wrong values get eliminated by that.

## How to test
added a unit test
  • Loading branch information
MartinquaXD authored and fleupold committed Nov 5, 2024
1 parent 483e371 commit bf9a96c
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 4 deletions.
29 changes: 26 additions & 3 deletions crates/shared/src/price_estimation/competition/native.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -14,12 +15,22 @@ impl NativePriceEstimating for CompetitionEstimator<Arc<dyn NativePriceEstimatin
fn estimate_native_price(&self, token: H160) -> 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()
Expand Down Expand Up @@ -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());
}
}
}
12 changes: 11 additions & 1 deletion crates/shared/src/price_estimation/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit bf9a96c

Please sign in to comment.