Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ON HOLD: Calculate score as per "rank by surplus" CIP #2406

Closed
wants to merge 20 commits into from
44 changes: 42 additions & 2 deletions crates/driver/src/boundary/score.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ use {
crate::{
boundary,
domain::{
competition::score::{
competition::{
self,
risk::{ObjectiveValue, SuccessProbability},
score::{
self,
risk::{ObjectiveValue, SuccessProbability},
},
solution::SolverScore,
},
eth,
},
infra::Ethereum,
util::conv::u256::U256Ext,
},
number::conversions::{big_rational_to_u256, u256_to_big_rational},
score::Score,
shared::external_prices::ExternalPrices,
solver::settlement_rater::ScoreCalculator,
};

Expand All @@ -29,3 +36,36 @@ pub fn score(
Err(err) => Err(boundary::Error::from(err).into()),
}
}

/// Converts a solver provided score denominated in surplus tokens, to a
/// competition score denominated in native token.
pub fn to_native_score(
score: SolverScore,
eth: &Ethereum,
auction: &competition::Auction,
) -> Result<Score, score::Error> {
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 native_score = score
.surplus
.iter()
.filter_map(|(token, amount)| {
let native_amount =
prices.try_get_native_amount(token.0 .0, u256_to_big_rational(&amount.0))?;
Some((token.0 .0, big_rational_to_u256(&native_amount).ok()?))
})
.fold(eth::U256::zero(), |acc, (_, amount)| acc + amount);

Ok(Score(native_score.try_into()?))
}
16 changes: 10 additions & 6 deletions crates/driver/src/boundary/settlement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,11 @@ impl Settlement {
);
}

settlement.score = match solution.score().clone() {
competition::SolverScore::Solver(score) => http_solver::model::Score::Solver { score },
competition::SolverScore::RiskAdjusted(success_probability) => {
settlement.score = match solution.old_score().clone() {
competition::OldSolverScore::Solver(score) => {
http_solver::model::Score::Solver { score }
}
competition::OldSolverScore::RiskAdjusted(success_probability) => {
http_solver::model::Score::RiskAdjusted {
success_probability,
gas_amount: None,
Expand Down Expand Up @@ -204,13 +206,15 @@ impl Settlement {
}
}

pub fn score(&self) -> competition::SolverScore {
pub fn score(&self) -> competition::OldSolverScore {
match self.inner.score {
http_solver::model::Score::Solver { score } => competition::SolverScore::Solver(score),
http_solver::model::Score::Solver { score } => {
competition::OldSolverScore::Solver(score)
}
http_solver::model::Score::RiskAdjusted {
success_probability,
..
} => competition::SolverScore::RiskAdjusted(success_probability),
} => competition::OldSolverScore::RiskAdjusted(success_probability),
}
}

Expand Down
7 changes: 4 additions & 3 deletions crates/driver/src/domain/competition/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub use {
risk::{ObjectiveValue, SuccessProbability},
Score,
},
solution::{Solution, SolverScore},
solution::{OldSolverScore, Solution},
};

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

Expand Down
94 changes: 48 additions & 46 deletions crates/driver/src/domain/competition/solution/fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ impl Fulfillment {
factor,
max_volume_factor,
}) => self.calculate_fee(
self.order().sell.amount.0,
self.order().buy.amount.0,
self.order().sell.amount,
self.order().buy.amount,
prices,
*factor,
*max_volume_factor,
Expand All @@ -97,12 +97,12 @@ impl Fulfillment {
quote,
}) => {
let (sell_amount, buy_amount) = adjust_quote_to_order_limits(
self.order().sell.amount.0,
self.order().buy.amount.0,
self.order().sell.amount,
self.order().buy.amount,
self.order().side,
quote.sell.amount.0,
quote.buy.amount.0,
quote.fee.amount.0,
quote.sell.amount,
quote.buy.amount,
quote.fee.amount,
)?;
self.calculate_fee(sell_amount, buy_amount, prices, *factor, *max_volume_factor)
}
Expand All @@ -115,8 +115,8 @@ impl Fulfillment {
/// the order or a quote.
fn calculate_fee(
&self,
limit_sell_amount: eth::U256,
limit_buy_amount: eth::U256,
limit_sell_amount: eth::TokenAmount,
limit_buy_amount: eth::TokenAmount,
prices: ClearingPrices,
factor: f64,
max_volume_factor: f64,
Expand All @@ -132,8 +132,8 @@ impl Fulfillment {

fn fee_from_surplus(
&self,
sell_amount: eth::U256,
buy_amount: eth::U256,
sell_amount: eth::TokenAmount,
buy_amount: eth::TokenAmount,
prices: ClearingPrices,
factor: f64,
) -> Result<eth::U256, Error> {
Expand Down Expand Up @@ -195,35 +195,37 @@ fn apply_factor(amount: eth::U256, factor: f64) -> Result<eth::U256, Error> {
/// - test_adjust_quote_to_in_market_sell_order_limits
/// - test_adjust_quote_to_in_market_buy_order_limits
fn adjust_quote_to_order_limits(
order_sell_amount: eth::U256,
order_buy_amount: eth::U256,
order_sell_amount: eth::TokenAmount,
MartinquaXD marked this conversation as resolved.
Show resolved Hide resolved
order_buy_amount: eth::TokenAmount,
order_side: Side,
quote_sell_amount: eth::U256,
quote_buy_amount: eth::U256,
quote_fee_amount: eth::U256,
) -> Result<(eth::U256, eth::U256), Error> {
quote_sell_amount: eth::TokenAmount,
quote_buy_amount: eth::TokenAmount,
quote_fee_amount: eth::TokenAmount,
) -> Result<(eth::TokenAmount, eth::TokenAmount), Error> {
let quote_sell_amount = quote_sell_amount
.checked_add(quote_fee_amount)
.0
.checked_add(quote_fee_amount.0)
.ok_or(trade::Error::Overflow)?;

match order_side {
Side::Sell => {
let scaled_buy_amount = quote_buy_amount
.checked_mul(order_sell_amount)
.0
.checked_mul(order_sell_amount.0)
.ok_or(trade::Error::Overflow)?
.checked_div(quote_sell_amount)
.ok_or(trade::Error::DivisionByZero)?;
let buy_amount = order_buy_amount.max(scaled_buy_amount);
Ok((order_sell_amount, buy_amount))
let buy_amount = order_buy_amount.0.max(scaled_buy_amount);
Ok((order_sell_amount, buy_amount.into()))
}
Side::Buy => {
let scaled_sell_amount = quote_sell_amount
.checked_mul(order_buy_amount)
.checked_mul(order_buy_amount.0)
.ok_or(trade::Error::Overflow)?
.checked_div(quote_buy_amount)
.checked_div(quote_buy_amount.0)
.ok_or(trade::Error::DivisionByZero)?;
let sell_amount = order_sell_amount.min(scaled_sell_amount);
Ok((sell_amount, order_buy_amount))
let sell_amount = order_sell_amount.0.min(scaled_sell_amount);
Ok((sell_amount.into(), order_buy_amount))
}
}
}
Expand All @@ -243,11 +245,11 @@ mod tests {

#[test]
fn test_adjust_quote_to_out_market_sell_order_limits() {
let order_sell_amount = to_wei(20);
let order_buy_amount = to_wei(19);
let quote_sell_amount = to_wei(21);
let quote_buy_amount = to_wei(18);
let quote_fee_amount = to_wei(1);
let order_sell_amount = to_wei(20).into();
let order_buy_amount = to_wei(19).into();
let quote_sell_amount = to_wei(21).into();
let quote_buy_amount = to_wei(18).into();
let quote_fee_amount = to_wei(1).into();

let (sell_amount, _) = adjust_quote_to_order_limits(
order_sell_amount,
Expand All @@ -267,11 +269,11 @@ mod tests {

#[test]
fn test_adjust_quote_to_out_market_buy_order_limits() {
let order_sell_amount = to_wei(20);
let order_buy_amount = to_wei(19);
let quote_sell_amount = to_wei(21);
let quote_buy_amount = to_wei(18);
let quote_fee_amount = to_wei(1);
let order_sell_amount = to_wei(20).into();
let order_buy_amount = to_wei(19).into();
let quote_sell_amount = to_wei(21).into();
let quote_buy_amount = to_wei(18).into();
let quote_fee_amount = to_wei(1).into();

let (_, buy_amount) = adjust_quote_to_order_limits(
order_sell_amount,
Expand All @@ -291,11 +293,11 @@ mod tests {

#[test]
fn test_adjust_quote_to_in_market_sell_order_limits() {
let order_sell_amount = to_wei(10);
let order_buy_amount = to_wei(20);
let quote_sell_amount = to_wei(9);
let quote_buy_amount = to_wei(25);
let quote_fee_amount = to_wei(1);
let order_sell_amount = to_wei(10).into();
let order_buy_amount = to_wei(20).into();
let quote_sell_amount = to_wei(9).into();
let quote_buy_amount = to_wei(25).into();
let quote_fee_amount = to_wei(1).into();

let (sell_amount, buy_amount) = adjust_quote_to_order_limits(
order_sell_amount,
Expand All @@ -319,11 +321,11 @@ mod tests {

#[test]
fn test_adjust_quote_to_in_market_buy_order_limits() {
let order_sell_amount = to_wei(20);
let order_buy_amount = to_wei(10);
let quote_sell_amount = to_wei(17);
let quote_buy_amount = to_wei(10);
let quote_fee_amount = to_wei(1);
let order_sell_amount = to_wei(20).into();
let order_buy_amount = to_wei(10).into();
let quote_sell_amount = to_wei(17).into();
let quote_buy_amount = to_wei(10).into();
let quote_fee_amount = to_wei(1).into();

let (sell_amount, buy_amount) = adjust_quote_to_order_limits(
order_sell_amount,
Expand All @@ -337,7 +339,7 @@ mod tests {

assert_eq!(
sell_amount,
quote_sell_amount + quote_fee_amount,
(quote_sell_amount.0 + quote_fee_amount.0).into(),
"Sell amount should reflect the improved market condition from the quote for buy \
orders."
);
Expand Down
46 changes: 44 additions & 2 deletions crates/driver/src/domain/competition/solution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub struct Solution {
prices: HashMap<eth::TokenAddress, eth::U256>,
interactions: Vec<Interaction>,
solver: Solver,
old_score: OldSolverScore, // todo CIP38 remove
score: SolverScore,
weth: eth::WethAddress,
}
Expand All @@ -49,15 +50,40 @@ impl Solution {
prices: HashMap<eth::TokenAddress, eth::U256>,
interactions: Vec<Interaction>,
solver: Solver,
score: SolverScore,
old_score: OldSolverScore,
weth: eth::WethAddress,
) -> Result<Self, SolutionError> {
// Score is calculated at the beginning of the solution generation process, so
// that the surplus is based on the raw solution received from the solver,
// before any protocol fees are added.
let score = SolverScore {
surplus: {
let mut surplus = HashMap::new();
for trade in trades.iter() {
match trade.surplus(&prices, weth) {
Ok(eth::Asset { token, amount }) => {
surplus
.entry(token)
.or_insert(eth::TokenAmount(eth::U256::zero()))
.0 += amount.0;
}
Err(_err) => {
// todo CIP38 enable
// return Err(SolutionError::Scoring(err))
}
}
}
surplus
},
};

let solution = Self {
id,
trades,
prices,
interactions,
solver,
old_score,
score,
weth,
};
Expand Down Expand Up @@ -93,6 +119,11 @@ impl Solution {
&self.solver
}

pub fn old_score(&self) -> &OldSolverScore {
&self.old_score
}

/// The score of a solution as per CIP38
pub fn score(&self) -> &SolverScore {
&self.score
}
Expand Down Expand Up @@ -266,17 +297,26 @@ impl std::fmt::Debug for Solution {
.field("prices", &self.prices)
.field("interactions", &self.interactions)
.field("solver", &self.solver.name())
.field("old_score", &self.old_score)
.field("score", &self.score)
.finish()
}
}

/// Carries information how the score should be calculated.
///
/// todo CIP38 remove
#[derive(Debug, Clone)]
pub enum SolverScore {
pub enum OldSolverScore {
Solver(eth::U256),
RiskAdjusted(f64),
}

#[derive(Debug, Clone)]
pub struct SolverScore {
pub surplus: HashMap<eth::TokenAddress, eth::TokenAmount>,
}

/// A unique solution ID. This ID is generated by the solver and only needs to
/// be unique within a single round of competition. This ID is only important in
/// the communication between the driver and the solver, and it is not used by
Expand Down Expand Up @@ -318,6 +358,8 @@ pub enum Error {

#[derive(Debug, thiserror::Error)]
pub enum SolutionError {
#[error("scoring failed: {0:?}")]
Scoring(#[from] crate::domain::competition::solution::trade::Error),
#[error("invalid clearing prices")]
InvalidClearingPrices,
#[error(transparent)]
Expand Down
Loading
Loading