diff --git a/src/infra/dex/oneinch/mod.rs b/src/infra/dex/oneinch/mod.rs index b5ed1a6..86b4129 100644 --- a/src/infra/dex/oneinch/mod.rs +++ b/src/infra/dex/oneinch/mod.rs @@ -5,7 +5,10 @@ use { }, ethereum_types::H160, ethrpc::current_block::CurrentBlockStream, - std::sync::atomic::{self, AtomicU64}, + std::{ + sync::atomic::{self, AtomicU64}, + time::{Duration, Instant}, + }, tracing::Instrument, }; @@ -19,6 +22,7 @@ pub struct OneInch { spender: eth::ContractAddress, } +#[derive(Debug, Clone)] pub struct Config { /// The base URL for the 1Inch swap API. pub endpoint: Option, @@ -45,6 +49,7 @@ pub struct Config { pub block_stream: Option, } +#[derive(Debug, Clone)] pub enum Liquidity { Any, Only(Vec), @@ -54,7 +59,31 @@ pub enum Liquidity { pub const DEFAULT_URL: &str = "https://api.1inch.io/v5.0/1/"; impl OneInch { - pub async fn new(config: Config) -> Result { + /// Initializes a new solver instance. Panics if it doesn't succeed after a + /// short period of time. + pub async fn new(config: Config) -> Self { + /// How long we try to initialize the solver before panicking. + const INIT_TIMEOUT: Duration = Duration::from_secs(10); + /// How long to wait before trying to initialize the solver again. + const RETRY_DELAY: Duration = Duration::from_secs(1); + + let start = Instant::now(); + loop { + let error = match Self::try_new(config.clone()).await { + Ok(solver) => return solver, + Err(err) => err, + }; + + if start.elapsed() > INIT_TIMEOUT { + panic!("could not initialize oneinch solver in time"); + } else { + tracing::warn!(?error, "failed to initialize oneinch solver; trying again"); + tokio::time::sleep(RETRY_DELAY).await; + } + } + } + + async fn try_new(config: Config) -> Result { let client = super::Client::new(Default::default(), config.block_stream); let endpoint = config .endpoint diff --git a/src/infra/dex/paraswap/mod.rs b/src/infra/dex/paraswap/mod.rs index 98c3e13..8673898 100644 --- a/src/infra/dex/paraswap/mod.rs +++ b/src/infra/dex/paraswap/mod.rs @@ -43,8 +43,9 @@ pub struct Config { } impl ParaSwap { - pub fn new(config: Config) -> anyhow::Result { - let mut key = reqwest::header::HeaderValue::from_str(&config.api_key)?; + /// Tries to initialize a new solver instance. Panics if it fails. + pub fn new(config: Config) -> Self { + let mut key = reqwest::header::HeaderValue::from_str(&config.api_key).unwrap(); key.set_sensitive(true); let mut headers = reqwest::header::HeaderMap::new(); @@ -52,12 +53,13 @@ impl ParaSwap { let client = reqwest::Client::builder() .default_headers(headers) - .build()?; + .build() + .unwrap(); - Ok(Self { + Self { client: super::Client::new(client, config.block_stream.clone()), config, - }) + } } pub async fn swap( diff --git a/src/run.rs b/src/run.rs index 0432707..3faf71b 100644 --- a/src/run.rs +++ b/src/run.rs @@ -48,14 +48,14 @@ async fn run_with(args: cli::Args, bind: Option>) { cli::Command::OneInch { config } => { let config = config::dex::oneinch::file::load(&config).await; Solver::Dex(solver::Dex::new( - dex::Dex::OneInch(dex::oneinch::OneInch::new(config.oneinch).await.unwrap()), + dex::Dex::OneInch(dex::oneinch::OneInch::new(config.oneinch).await), config.base.clone(), )) } cli::Command::ParaSwap { config } => { let config = config::dex::paraswap::file::load(&config).await; Solver::Dex(solver::Dex::new( - dex::Dex::ParaSwap(dex::paraswap::ParaSwap::new(config.paraswap).unwrap()), + dex::Dex::ParaSwap(dex::paraswap::ParaSwap::new(config.paraswap)), config.base.clone(), )) }