Skip to content

Commit

Permalink
Optimal bidding colocation (#1856)
Browse files Browse the repository at this point in the history
Tackles risk estimation and score calculation for colocated driver.

Colocated Driver expects to receive from `solvers` either:
1. `Score` as absolute value
2. `success_probability` value that is used to calculate (1) using
`ScoreSolve`

RiskParameters loading is moved to `solvers` but not used for
calculating the `success_probability` yet, instead, value `1.0` is
returned for Naive, Baseline and Dex solvers. For legacy solvers,
whatever they sent to us is forwarded to `driver`.


Release notes: Currently missing the default values for risk
parameters... I'm fine with defining them explicitly in the
infrastructure repo.
  • Loading branch information
sunce86 authored Sep 29, 2023
1 parent 2ffc504 commit 986613c
Show file tree
Hide file tree
Showing 56 changed files with 508 additions and 250 deletions.
7 changes: 6 additions & 1 deletion crates/autopilot/src/arguments.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use {
primitive_types::H160,
primitive_types::{H160, U256},
shared::{
arguments::{display_list, display_option},
bad_token::token_owner_finder,
Expand Down Expand Up @@ -169,6 +169,10 @@ pub struct Arguments {
#[clap(long, env, default_value = "5")]
pub additional_deadline_for_rewards: usize,

/// Cap used for CIP20 score calculation. Defaults to 0.01 ETH.
#[clap(long, env, default_value = "10000000000000000")]
pub score_cap: U256,

/// Run the autopilot in a shadow mode by specifying an upstream CoW
/// protocol deployment to pull auctions from. This will cause the autopilot
/// to start a run loop where it performs solver competition on driver,
Expand Down Expand Up @@ -236,6 +240,7 @@ impl std::fmt::Display for Arguments {
"additional_deadline_for_rewards: {}",
self.additional_deadline_for_rewards
)?;
writeln!(f, "score_cap: {}", self.score_cap)?;
display_option(f, "shadow", &self.shadow)?;
Ok(())
}
Expand Down
2 changes: 2 additions & 0 deletions crates/autopilot/src/driver_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ pub mod solve {
pub tokens: Vec<Token>,
pub orders: Vec<Order>,
pub deadline: DateTime<Utc>,
#[serde_as(as = "DecimalU256")]
pub score_cap: U256,
}

#[serde_as]
Expand Down
3 changes: 2 additions & 1 deletion crates/autopilot/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ pub async fn run(args: Arguments) {
market_makable_token_list,
submission_deadline: args.submission_deadline as u64,
additional_deadline_for_rewards: args.additional_deadline_for_rewards as u64,
score_cap: args.score_cap,
};
run.run_forever().await;
unreachable!("run loop exited");
Expand Down Expand Up @@ -649,7 +650,7 @@ async fn shadow_mode(args: Arguments) -> ! {
.await
};

let shadow = shadow::RunLoop::new(orderbook, drivers, trusted_tokens);
let shadow = shadow::RunLoop::new(orderbook, drivers, trusted_tokens, args.score_cap);
shadow.run_forever().await;

unreachable!("shadow run loop exited");
Expand Down
12 changes: 10 additions & 2 deletions crates/autopilot/src/run_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use {
SolverSettlement,
},
},
primitive_types::{H160, H256},
primitive_types::{H160, H256, U256},
rand::seq::SliceRandom,
shared::{
event_handling::MAX_REORG_BLOCK_COUNT,
Expand Down Expand Up @@ -56,6 +56,7 @@ pub struct RunLoop {
pub market_makable_token_list: AutoUpdatingTokenList,
pub submission_deadline: u64,
pub additional_deadline_for_rewards: u64,
pub score_cap: U256,
}

impl RunLoop {
Expand Down Expand Up @@ -269,7 +270,12 @@ impl RunLoop {
return Default::default();
}

let request = solve_request(id, auction, &self.market_makable_token_list.all());
let request = solve_request(
id,
auction,
&self.market_makable_token_list.all(),
self.score_cap,
);
let request = &request;

self.database
Expand Down Expand Up @@ -435,6 +441,7 @@ pub fn solve_request(
id: AuctionId,
auction: &Auction,
trusted_tokens: &HashSet<H160>,
score_cap: U256,
) -> solve::Request {
solve::Request {
id,
Expand Down Expand Up @@ -499,5 +506,6 @@ pub fn solve_request(
.unique_by(|token| token.address)
.collect(),
deadline: Utc::now() + chrono::Duration::from_std(SOLVE_TIME_LIMIT).unwrap(),
score_cap,
}
}
6 changes: 5 additions & 1 deletion crates/autopilot/src/shadow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,23 @@ pub struct RunLoop {
trusted_tokens: AutoUpdatingTokenList,
auction: AuctionId,
block: u64,
score_cap: U256,
}

impl RunLoop {
pub fn new(
orderbook: protocol::Orderbook,
drivers: Vec<Driver>,
trusted_tokens: AutoUpdatingTokenList,
score_cap: U256,
) -> Self {
Self {
orderbook,
drivers,
trusted_tokens,
auction: 0,
block: 0,
score_cap,
}
}

Expand Down Expand Up @@ -170,7 +173,8 @@ impl RunLoop {

/// Runs the solver competition, making all configured drivers participate.
async fn competition(&self, id: AuctionId, auction: &Auction) -> Vec<Participant<'_>> {
let request = run_loop::solve_request(id, auction, &self.trusted_tokens.all());
let request =
run_loop::solve_request(id, auction, &self.trusted_tokens.all(), self.score_cap);
let request = &request;

futures::future::join_all(self.drivers.iter().map(|driver| async move {
Expand Down
4 changes: 4 additions & 0 deletions crates/driver/src/boundary/mempool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ impl Mempool {
.await?;
Ok(receipt.transaction_hash.into())
}

pub fn config(&self) -> &Config {
&self.config
}
}

struct AccessListEstimator(eth::AccessList);
Expand Down
92 changes: 59 additions & 33 deletions crates/driver/src/boundary/settlement.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
use {
crate::{
domain,
domain::{
competition::{
self,
auction,
order,
solution::{
self,
settlement::{self, Internalization},
},
solution::settlement::{self, Internalization},
},
eth,
liquidity,
},
infra::Ethereum,
util::conv::u256::U256Ext,
},
anyhow::{anyhow, ensure, Context, Result},
bigdecimal::Signed,
anyhow::{anyhow, Context, Result},
model::{
app_data::AppDataHash,
interaction::InteractionData,
Expand All @@ -37,7 +34,10 @@ use {
},
shared::{
external_prices::ExternalPrices,
http_solver::model::{InternalizationStrategy, TokenAmount},
http_solver::{
self,
model::{InternalizationStrategy, TokenAmount},
},
},
solver::{
interactions::Erc20ApproveInteraction,
Expand All @@ -56,7 +56,6 @@ use {
pub struct Settlement {
pub(super) inner: solver::settlement::Settlement,
pub solver: eth::Address,
risk: solution::Risk,
}

impl Settlement {
Expand Down Expand Up @@ -158,10 +157,19 @@ impl Settlement {
);
}

settlement.score = match solution.score().clone() {
competition::SolverScore::Solver(score) => http_solver::model::Score::Solver { score },
competition::SolverScore::RiskAdjusted(success_probability) => {
http_solver::model::Score::RiskAdjusted {
success_probability,
gas_amount: None,
}
}
};

Ok(Self {
inner: settlement,
solver: solution.solver().address(),
risk: solution.risk(),
})
}

Expand Down Expand Up @@ -199,36 +207,54 @@ impl Settlement {
eth: &Ethereum,
auction: &competition::Auction,
gas: eth::Gas,
) -> Result<competition::solution::Score> {
let prices = ExternalPrices::try_from_auction_prices(
eth.contracts().weth().address(),
auction
.tokens()
.iter()
.filter_map(|token| {
token
.price
.map(|price| (token.address.into(), price.into()))
})
.collect(),
)?;
let gas_price = eth::U256::from(auction.gas_price().effective()).to_big_rational();
let inputs = solver::objective_value::Inputs::from_settlement(
&self.inner,
&prices,
gas_price,
&gas.into(),
);
ensure!(!inputs.objective_value().is_negative(), "negative score");
let objective_value = eth::U256::from_big_rational(&inputs.objective_value())?;
Ok((objective_value - self.risk.0).into())
revert_protection: &domain::RevertProtection,
) -> Result<competition::Score> {
let score = match self.inner.score {
http_solver::model::Score::Solver { score } => score,
http_solver::model::Score::Discount { .. } => {
unreachable!("discounted score no longer supported")
}
http_solver::model::Score::RiskAdjusted {
success_probability,
gas_amount,
} => {
let prices = ExternalPrices::try_from_auction_prices(
eth.contracts().weth().address(),
auction
.tokens()
.iter()
.filter_map(|token| {
token
.price
.map(|price| (token.address.into(), price.into()))
})
.collect(),
)?;
let gas_price = eth::U256::from(auction.gas_price().effective()).to_big_rational();
let inputs = solver::objective_value::Inputs::from_settlement(
&self.inner,
&prices,
gas_price.clone(),
&gas_amount.unwrap_or(gas.into()),
);
solver::settlement_rater::ScoreCalculator::new(
auction.score_cap().to_big_rational(),
matches!(revert_protection, domain::RevertProtection::Disabled),
)
.compute_score(
&inputs.objective_value(),
&inputs.gas_cost(),
success_probability,
)?
}
};
Ok(score.into())
}

pub fn merge(self, other: Self) -> Result<Self> {
self.inner.merge(other.inner).map(|inner| Self {
inner,
solver: self.solver,
risk: self.risk.merge(other.risk),
})
}
}
Expand Down
7 changes: 7 additions & 0 deletions crates/driver/src/domain/competition/auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub struct Auction {
tokens: Tokens,
gas_price: eth::GasPrice,
deadline: Deadline,
score_cap: eth::U256,
}

impl Auction {
Expand All @@ -36,6 +37,7 @@ impl Auction {
tokens: impl Iterator<Item = Token>,
deadline: Deadline,
eth: &Ethereum,
score_cap: eth::U256,
) -> Result<Self, Error> {
let tokens = Tokens(tokens.map(|token| (token.address, token)).collect());

Expand All @@ -59,6 +61,7 @@ impl Auction {
tokens,
gas_price: eth.gas_price().await?,
deadline,
score_cap,
})
}

Expand Down Expand Up @@ -231,6 +234,10 @@ impl Auction {
pub fn deadline(&self) -> Deadline {
self.deadline
}

pub fn score_cap(&self) -> eth::U256 {
self.score_cap
}
}

/// The tokens that are used in an auction.
Expand Down
26 changes: 23 additions & 3 deletions crates/driver/src/domain/competition/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
self::solution::settlement,
super::Mempools,
super::{eth, Mempools},
crate::{
domain::{competition::solution::Settlement, liquidity},
infra::{
Expand All @@ -26,7 +26,7 @@ pub mod solution;
pub use {
auction::Auction,
order::Order,
solution::{Score, Solution, SolverTimeout},
solution::{Solution, SolverScore, SolverTimeout},
};

/// An ongoing competition. There is one competition going on per solver at any
Expand Down Expand Up @@ -142,7 +142,10 @@ impl Competition {
.into_iter()
.map(|settlement| {
observe::scoring(&settlement);
(settlement.score(&self.eth, auction), settlement)
(
settlement.score(&self.eth, auction, &self.mempools.revert_protection()),
settlement,
)
})
.collect_vec();

Expand Down Expand Up @@ -234,6 +237,23 @@ impl Competition {
}
}

/// Represents a single value suitable for comparing/ranking solutions.
/// This is a final score that is observed by the autopilot.
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct Score(pub eth::U256);

impl From<Score> for eth::U256 {
fn from(value: Score) -> Self {
value.0
}
}

impl From<eth::U256> for Score {
fn from(value: eth::U256) -> Self {
Self(value)
}
}

/// Solution information sent to the protocol by the driver before the solution
/// ranking happens.
#[derive(Debug)]
Expand Down
Loading

0 comments on commit 986613c

Please sign in to comment.