Skip to content

Commit

Permalink
Configuration To Skip Liquidity Fetching for Solver (#1925)
Browse files Browse the repository at this point in the history
# Description

This PR makes a **hacky** (but IMO pragmatic) change proposal to the
driver. In particular, in our current setup we share a `driver` instance
across solvers that both use and don't use liquidity. This is an issue
because solvers that don't need liquidity will end up waiting for
liquidity that they don't use, increasing solving and quoting latency
for those solvers.

The way this was imagined to work was to set up separate `driver`
instances for solvers with different liquidity requirements. However,
the downside to this is that those multiple `driver` instances don't
share other valuable caches (balances, token infos, etc.). In addition,
this adds additional complexity in the infrastructure setup (multiple
drivers to deploy, multiple drivers as sources for logs, etc.).

The solution is simple, add an "all or nothing" flag to each solver
configured in the `driver` to control whether or not to invoke the
liquidity fetcher for that solver. This allows us to use a single
`driver` instance, while not increasing latency for `/solve`-ing and
`/quote`-ing with solvers that don't care at all about liquidity.

# Changes

1. Adds a new per-solver configuration option 
2. Skips fetching liquidity if this configuration option is enabled for
a particular solver when quoting and solving.

## How to test

Rust compiler. Logic was simple enough that I did not an driver test for
it. That being said, we should probably have one, but the current driver
test setup is very much geared towards actually computing solutions
(which we don't care about here) and would need a slightly different
setup to make a test for this possible.
  • Loading branch information
Nicholas Rodrigues Lordello authored Oct 9, 2023
1 parent 4dbeeb8 commit 9c21e7e
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 18 deletions.
17 changes: 16 additions & 1 deletion crates/driver/src/domain/competition/auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ use {
domain::{
competition::{self, solution},
eth,
liquidity,
},
infra::{blockchain, observe, Ethereum},
util,
},
futures::future::join_all,
itertools::Itertools,
std::collections::HashMap,
std::collections::{HashMap, HashSet},
thiserror::Error,
};

Expand Down Expand Up @@ -227,6 +228,20 @@ impl Auction {
&self.tokens
}

/// Returns a collection of liquidity token pairs that are relevant to this
/// auction.
pub fn liquidity_pairs(&self) -> HashSet<liquidity::TokenPair> {
self.orders
.iter()
.filter_map(|order| match order.kind {
order::Kind::Market | order::Kind::Limit { .. } => {
liquidity::TokenPair::new(order.sell.token, order.buy.token).ok()
}
order::Kind::Liquidity => None,
})
.collect()
}

pub fn gas_price(&self) -> eth::GasPrice {
self.gas_price
}
Expand Down
21 changes: 5 additions & 16 deletions crates/driver/src/domain/competition/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use {
self::solution::settlement,
super::{eth, Mempools},
crate::{
domain::{competition::solution::Settlement, liquidity},
domain::competition::solution::Settlement,
infra::{
self,
blockchain::Ethereum,
Expand Down Expand Up @@ -47,21 +47,10 @@ pub struct Competition {
impl Competition {
/// Solve an auction as part of this competition.
pub async fn solve(&self, auction: &Auction) -> Result<Solved, Error> {
let liquidity = self
.liquidity
.fetch(
&auction
.orders()
.iter()
.filter_map(|order| match order.kind {
order::Kind::Market | order::Kind::Limit { .. } => {
liquidity::TokenPair::new(order.sell.token, order.buy.token).ok()
}
order::Kind::Liquidity => None,
})
.collect(),
)
.await;
let liquidity = match self.solver.liquidity() {
solver::Liquidity::Fetch => self.liquidity.fetch(&auction.liquidity_pairs()).await,
solver::Liquidity::Skip => Default::default(),
};

// Fetch the solutions from the solver.
let solutions = self
Expand Down
5 changes: 4 additions & 1 deletion crates/driver/src/domain/quote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ impl Order {
liquidity: &infra::liquidity::Fetcher,
tokens: &infra::tokens::Fetcher,
) -> Result<Quote, Error> {
let liquidity = liquidity.fetch(&self.liquidity_pairs()).await;
let liquidity = match solver.liquidity() {
solver::Liquidity::Fetch => liquidity.fetch(&self.liquidity_pairs()).await,
solver::Liquidity::Skip => Default::default(),
};
let timeout = self.deadline.timeout()?;
let solutions = solver
.solve(&self.fake_auction(eth, tokens).await?, &liquidity, timeout)
Expand Down
5 changes: 5 additions & 0 deletions crates/driver/src/infra/config/file/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ pub async fn load(network: &blockchain::Network, path: &Path) -> infra::Config {
relative: config.relative_slippage,
absolute: config.absolute_slippage.map(Into::into),
},
liquidity: if config.skip_liquidity {
solver::Liquidity::Skip
} else {
solver::Liquidity::Fetch
},
account,
}
}))
Expand Down
4 changes: 4 additions & 0 deletions crates/driver/src/infra/config/file/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ struct SolverConfig {
#[serde_as(as = "Option<serialize::U256>")]
absolute_slippage: Option<eth::U256>,

/// Whether or not to skip fetching liquidity for this solver.
#[serde(default)]
skip_liquidity: bool,

/// The account which should be used to sign settlements for this solver.
account: Account,
}
Expand Down
17 changes: 17 additions & 0 deletions crates/driver/src/infra/solver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ pub struct Slippage {
pub absolute: Option<eth::Ether>,
}

#[derive(Clone, Copy, Debug)]
pub enum Liquidity {
/// Liquidity should be fetched and included in the auction sent to this
/// solver.
Fetch,
/// The solver does not need liquidity, so fetching can be skipped for this
/// solver.
Skip,
}

/// Solvers are controlled by the driver. Their job is to search for solutions
/// to auctions. They do this in various ways, often by analyzing different AMMs
/// on the Ethereum blockchain.
Expand All @@ -65,6 +75,8 @@ pub struct Config {
pub name: Name,
/// The acceptable slippage for this solver.
pub slippage: Slippage,
/// Whether or not liquidity is used by this solver.
pub liquidity: Liquidity,
/// The private key of this solver, used for settlement submission.
pub account: ethcontract::Account,
}
Expand Down Expand Up @@ -101,6 +113,11 @@ impl Solver {
&self.config.slippage
}

/// The liquidity configuration of this solver
pub fn liquidity(&self) -> Liquidity {
self.config.liquidity
}

/// The blockchain address of this solver.
pub fn address(&self) -> eth::Address {
self.config.account.address().into()
Expand Down

0 comments on commit 9c21e7e

Please sign in to comment.