From bffb3cb39676c6fe01f818e95992d6384f5dd587 Mon Sep 17 00:00:00 2001 From: Felix Leupold Date: Wed, 3 Jan 2024 11:59:26 +0100 Subject: [PATCH] Enforce timeout for DEX solver price estimators --- .../driver/src/boundary/liquidity/zeroex.rs | 2 +- crates/shared/src/price_estimation/factory.rs | 32 ++++++++++++++++--- crates/shared/src/trade_finding.rs | 6 ++++ crates/shared/src/trade_finding/external.rs | 8 +---- crates/shared/src/zeroex_api.rs | 25 +++++++-------- crates/solver/src/run.rs | 2 +- 6 files changed, 49 insertions(+), 26 deletions(-) diff --git a/crates/driver/src/boundary/liquidity/zeroex.rs b/crates/driver/src/boundary/liquidity/zeroex.rs index 8a0eb8c4c6..5273ecc0cf 100644 --- a/crates/driver/src/boundary/liquidity/zeroex.rs +++ b/crates/driver/src/boundary/liquidity/zeroex.rs @@ -18,7 +18,7 @@ pub async fn collector( http_timeout: config.http_timeout, }); let api = Arc::new(DefaultZeroExApi::new( - http_client_factory, + http_client_factory.builder(), config.base_url.clone(), config.api_key.clone(), blocks, diff --git a/crates/shared/src/price_estimation/factory.rs b/crates/shared/src/price_estimation/factory.rs index 4e54b0e491..3456bb941f 100644 --- a/crates/shared/src/price_estimation/factory.rs +++ b/crates/shared/src/price_estimation/factory.rs @@ -38,6 +38,7 @@ use { uniswap_v3::pool_fetching::PoolFetching as UniswapV3PoolFetching, }, token_info::TokenInfoFetching, + trade_finding, zeroex_api::DefaultZeroExApi, }, anyhow::{anyhow, Context as _, Result}, @@ -443,7 +444,13 @@ impl PriceEstimatorCreating for ParaswapPriceEstimator { fn init(factory: &PriceEstimatorFactory, name: &str, solver: Self::Params) -> Result { Ok(ParaswapPriceEstimator::new( Arc::new(DefaultParaswapApi { - client: factory.components.http_factory.create(), + client: factory.components.http_factory.configure(|builder| { + builder.timeout( + trade_finding::time_limit() + .to_std() + .expect("negative time limit"), + ) + }), base_url: factory.shared_args.paraswap_api_url.clone(), partner: factory .shared_args @@ -467,8 +474,13 @@ impl PriceEstimatorCreating for ZeroExPriceEstimator { type Params = H160; fn init(factory: &PriceEstimatorFactory, name: &str, solver: Self::Params) -> Result { + let client_builder = factory.components.http_factory.builder().timeout( + trade_finding::time_limit() + .to_std() + .expect("negative time limit"), + ); let api = DefaultZeroExApi::new( - &factory.components.http_factory, + client_builder, factory .shared_args .zeroex_url @@ -498,7 +510,13 @@ impl PriceEstimatorCreating for OneInchPriceEstimator { fn init(factory: &PriceEstimatorFactory, name: &str, solver: Self::Params) -> Result { let api = OneInchClientImpl::new( factory.shared_args.one_inch_url.clone(), - factory.components.http_factory.create(), + factory.components.http_factory.configure(|builder| { + builder.timeout( + trade_finding::time_limit() + .to_std() + .expect("negative time limit"), + ) + }), factory.network.chain_id, factory.network.block_stream.clone(), ) @@ -524,7 +542,13 @@ impl PriceEstimatorCreating for BalancerSor { fn init(factory: &PriceEstimatorFactory, name: &str, solver: Self::Params) -> Result { Ok(BalancerSor::new( Arc::new(DefaultBalancerSorApi::new( - factory.components.http_factory.create(), + factory.components.http_factory.configure(|builder| { + builder.timeout( + trade_finding::time_limit() + .to_std() + .expect("negative time limit"), + ) + }), factory .args .balancer_sor_url diff --git a/crates/shared/src/trade_finding.rs b/crates/shared/src/trade_finding.rs index 4cc6e17d79..31129d4f4c 100644 --- a/crates/shared/src/trade_finding.rs +++ b/crates/shared/src/trade_finding.rs @@ -16,6 +16,12 @@ use { thiserror::Error, }; +/// Returns the default time limit used for quoting with external co-located +/// solvers. +pub fn time_limit() -> chrono::Duration { + chrono::Duration::seconds(5) +} + /// Find a trade for a token pair. /// /// This is similar to the `PriceEstimating` interface, but it expects calldata diff --git a/crates/shared/src/trade_finding/external.rs b/crates/shared/src/trade_finding/external.rs index d47998bb74..711743b306 100644 --- a/crates/shared/src/trade_finding/external.rs +++ b/crates/shared/src/trade_finding/external.rs @@ -42,7 +42,7 @@ impl ExternalTradeFinder { /// Queries the `/quote` endpoint of the configured driver and deserializes /// the result into a Quote or Trade. async fn shared_query(&self, query: &Query) -> Result { - let deadline = chrono::Utc::now() + Self::time_limit(); + let deadline = chrono::Utc::now() + super::time_limit(); let order = dto::Order { sell_token: query.sell_token, buy_token: query.buy_token, @@ -97,12 +97,6 @@ impl ExternalTradeFinder { .await .map_err(TradeError::from) } - - /// Returns the default time limit used for quoting with external co-located - /// solvers. - fn time_limit() -> chrono::Duration { - chrono::Duration::seconds(5) - } } impl From for Trade { diff --git a/crates/shared/src/zeroex_api.rs b/crates/shared/src/zeroex_api.rs index 66476e0665..12649e953b 100644 --- a/crates/shared/src/zeroex_api.rs +++ b/crates/shared/src/zeroex_api.rs @@ -7,7 +7,6 @@ use { crate::{ debug_bytes, - http_client::HttpClientFactory, interaction::{EncodedInteraction, Interaction}, }, anyhow::{Context, Result}, @@ -19,6 +18,7 @@ use { reqwest::{ header::{HeaderMap, HeaderValue}, Client, + ClientBuilder, IntoUrl, StatusCode, Url, @@ -375,26 +375,25 @@ impl DefaultZeroExApi { /// Create a new 0x HTTP API client with the specified base URL. pub fn new( - http_factory: &HttpClientFactory, + client_builder: ClientBuilder, base_url: impl IntoUrl, api_key: Option, block_stream: CurrentBlockStream, ) -> Result { - let client = match api_key { - Some(api_key) => { - let mut key = HeaderValue::from_str(&api_key)?; - key.set_sensitive(true); + let client_builder = if let Some(api_key) = api_key { + let mut key = HeaderValue::from_str(&api_key)?; + key.set_sensitive(true); - let mut headers = HeaderMap::new(); - headers.insert("0x-api-key", key); + let mut headers = HeaderMap::new(); + headers.insert("0x-api-key", key); - http_factory.configure(|builder| builder.default_headers(headers)) - } - None => http_factory.create(), + client_builder.default_headers(headers) + } else { + client_builder }; Ok(Self { - client, + client: client_builder.build().unwrap(), base_url: base_url.into_url().context("zeroex api url")?, block_stream, }) @@ -408,7 +407,7 @@ impl DefaultZeroExApi { pub fn test() -> Self { let (_, block_stream) = watch::channel(BlockInfo::default()); Self::new( - &HttpClientFactory::default(), + Client::builder(), std::env::var("ZEROEX_URL").unwrap_or_else(|_| Self::DEFAULT_URL.to_string()), std::env::var("ZEROEX_API_KEY").ok(), block_stream, diff --git a/crates/solver/src/run.rs b/crates/solver/src/run.rs index 5af41d7191..e353efcbd0 100644 --- a/crates/solver/src/run.rs +++ b/crates/solver/src/run.rs @@ -290,7 +290,7 @@ pub async fn run(args: Arguments) { let zeroex_api = Arc::new( DefaultZeroExApi::new( - &http_factory, + http_factory.builder(), args.shared .zeroex_url .as_deref()