diff --git a/crates/driver/src/domain/competition/score.rs b/crates/driver/src/domain/competition/score.rs index fd60022024..e5c8e6b43a 100644 --- a/crates/driver/src/domain/competition/score.rs +++ b/crates/driver/src/domain/competition/score.rs @@ -131,7 +131,7 @@ pub mod risk { eth::NonZeroU256::new(self.0 - other.0 .0).unwrap(), )) } else { - Err(Error::ObjectiveValueNonPositive) + Err(Error::ObjectiveValueNonPositive(self, other)) } } } @@ -147,8 +147,8 @@ pub mod risk { /// and protocol. Score calculator does not make sense for such /// solutions, since score calculator is expected to return /// value (0, ObjectiveValue] - #[error("objective value is non-positive")] - ObjectiveValueNonPositive, + #[error("objective value is non-positive, quality {0:?}, gas cost {1:?}")] + ObjectiveValueNonPositive(Quality, GasCost), #[error(transparent)] Boundary(#[from] boundary::Error), } diff --git a/crates/driver/src/infra/notify/mod.rs b/crates/driver/src/infra/notify/mod.rs index a218903b2e..77b6c18ac4 100644 --- a/crates/driver/src/infra/notify/mod.rs +++ b/crates/driver/src/infra/notify/mod.rs @@ -45,9 +45,12 @@ pub fn scoring_failed( )) => notification::Kind::ScoringFailed( notification::ScoreKind::SuccessProbabilityOutOfRange(*success_probability), ), - score::Error::RiskAdjusted(score::risk::Error::ObjectiveValueNonPositive) => { - notification::Kind::ScoringFailed(notification::ScoreKind::ObjectiveValueNonPositive) - } + score::Error::RiskAdjusted(score::risk::Error::ObjectiveValueNonPositive( + quality, + gas_cost, + )) => notification::Kind::ScoringFailed( + notification::ScoreKind::ObjectiveValueNonPositive(*quality, *gas_cost), + ), score::Error::RiskAdjusted(score::risk::Error::Boundary(_)) => return, score::Error::Boundary(_) => return, }; diff --git a/crates/driver/src/infra/notify/notification.rs b/crates/driver/src/infra/notify/notification.rs index 5b7eab0b5f..d804ef4443 100644 --- a/crates/driver/src/infra/notify/notification.rs +++ b/crates/driver/src/infra/notify/notification.rs @@ -1,7 +1,7 @@ use { crate::domain::{ competition::{auction, score::Quality, solution, Score}, - eth::{self, Ether, TokenAddress}, + eth::{self, Ether, GasCost, TokenAddress}, }, std::collections::BTreeSet, }; @@ -57,11 +57,11 @@ pub enum ScoreKind { /// [0, 1] /// [ONLY APPLICABLE TO SCORES BASED ON SUCCESS PROBABILITY] SuccessProbabilityOutOfRange(f64), - /// Objective value is defined as surplus + fees - gas costs. Protocol - /// doesn't allow solutions that cost more than they bring to the users and - /// protocol. + /// Objective value is defined as quality (surplus + fees) - gas costs. + /// Protocol doesn't allow solutions that cost more than they bring to + /// the users and protocol. /// [ONLY APPLICABLE TO SCORES BASED ON SUCCESS PROBABILITY] - ObjectiveValueNonPositive, + ObjectiveValueNonPositive(Quality, GasCost), } #[derive(Debug)] diff --git a/crates/driver/src/infra/solver/dto/notification.rs b/crates/driver/src/infra/solver/dto/notification.rs index 9ac6edc11f..af4148bed9 100644 --- a/crates/driver/src/infra/solver/dto/notification.rs +++ b/crates/driver/src/infra/solver/dto/notification.rs @@ -47,9 +47,13 @@ impl Notification { )) => Kind::ScoringFailed(ScoreKind::SuccessProbabilityOutOfRange { probability: success_probability, }), - notify::Kind::ScoringFailed(notify::ScoreKind::ObjectiveValueNonPositive) => { - Kind::ScoringFailed(ScoreKind::ObjectiveValueNonPositive) - } + notify::Kind::ScoringFailed(notify::ScoreKind::ObjectiveValueNonPositive( + quality, + gas_cost, + )) => Kind::ScoringFailed(ScoreKind::ObjectiveValueNonPositive { + quality: quality.0, + gas_cost: gas_cost.0 .0, + }), notify::Kind::NonBufferableTokensUsed(tokens) => Kind::NonBufferableTokensUsed { tokens: tokens.into_iter().map(|token| token.0 .0).collect(), }, @@ -129,7 +133,13 @@ pub enum ScoreKind { SuccessProbabilityOutOfRange { probability: f64, }, - ObjectiveValueNonPositive, + #[serde(rename_all = "camelCase")] + ObjectiveValueNonPositive { + #[serde_as(as = "serialize::U256")] + quality: eth::U256, + #[serde_as(as = "serialize::U256")] + gas_cost: eth::U256, + }, } #[serde_as] diff --git a/crates/shared/src/http_solver/model.rs b/crates/shared/src/http_solver/model.rs index 1d78c8a1d1..fb4aa2b322 100644 --- a/crates/shared/src/http_solver/model.rs +++ b/crates/shared/src/http_solver/model.rs @@ -457,7 +457,18 @@ pub enum SolverRejectionReason { NonPositiveScore, /// Objective value is too low. - ObjectiveValueNonPositive, + /// [TODO] Remove once the colocation is finalized. + #[serde(rename = "objectiveValueNonPositive")] + ObjectiveValueNonPositiveLegacy, + + /// Objective value is too low. + #[serde(rename_all = "camelCase")] + ObjectiveValueNonPositive { + #[serde_as(as = "HexOrDecimalU256")] + quality: U256, + #[serde_as(as = "HexOrDecimalU256")] + gas_cost: U256, + }, /// Success probability is out of the allowed range [0, 1] SuccessProbabilityOutOfRange, @@ -1212,4 +1223,71 @@ mod tests { }), ); } + + #[test] + fn serialize_objective_value_non_positive_legacy() { + let auction_result = + AuctionResult::Rejected(SolverRejectionReason::ObjectiveValueNonPositiveLegacy); + + assert_eq!( + serde_json::to_value(auction_result).unwrap(), + json!({ + "rejected": "objectiveValueNonPositive", + }), + ); + } + + #[test] + fn serialize_objective_value_non_positive_colocated() { + let auction_result = + AuctionResult::Rejected(SolverRejectionReason::ObjectiveValueNonPositive { + quality: U256::from(1), + gas_cost: U256::from(2), + }); + + assert_eq!( + serde_json::to_value(auction_result).unwrap(), + json!({ + "rejected": { + "objectiveValueNonPositive": { + "quality": "1", + "gasCost": "2", + }, + } + }), + ); + } + + #[test] + fn serialize_non_positive_score() { + let auction_result = AuctionResult::Rejected(SolverRejectionReason::NonPositiveScore); + + assert_eq!( + serde_json::to_value(auction_result).unwrap(), + json!({ + "rejected": "nonPositiveScore", + }), + ); + } + + #[test] + fn serialize_score_higher_than_quality() { + let auction_result = + AuctionResult::Rejected(SolverRejectionReason::ScoreHigherThanQuality { + score: U256::from(1), + quality: U256::from(2), + }); + + assert_eq!( + serde_json::to_value(auction_result).unwrap(), + json!({ + "rejected": { + "scoreHigherThanQuality": { + "score": "1", + "quality": "2", + }, + } + }), + ); + } } diff --git a/crates/solver/src/settlement_ranker.rs b/crates/solver/src/settlement_ranker.rs index 084dbdd293..9fd4479f83 100644 --- a/crates/solver/src/settlement_ranker.rs +++ b/crates/solver/src/settlement_ranker.rs @@ -220,7 +220,7 @@ impl SettlementRanker { ); let reason = match error { ScoringError::ObjectiveValueNonPositive(_) => { - Some(SolverRejectionReason::ObjectiveValueNonPositive) + Some(SolverRejectionReason::ObjectiveValueNonPositiveLegacy) } ScoringError::SuccessProbabilityOutOfRange(_) => { Some(SolverRejectionReason::SuccessProbabilityOutOfRange) diff --git a/crates/solvers/src/api/routes/notify/dto/notification.rs b/crates/solvers/src/api/routes/notify/dto/notification.rs index 67eb75d11a..ede2b5f228 100644 --- a/crates/solvers/src/api/routes/notify/dto/notification.rs +++ b/crates/solvers/src/api/routes/notify/dto/notification.rs @@ -29,9 +29,12 @@ impl Notification { value: tx.value.into(), access_list: tx.access_list.clone(), }), - Kind::ScoringFailed(ScoreKind::ObjectiveValueNonPositive) => { + Kind::ScoringFailed(ScoreKind::ObjectiveValueNonPositive { quality, gas_cost }) => { notification::Kind::ScoringFailed( - notification::ScoreKind::ObjectiveValueNonPositive, + notification::ScoreKind::ObjectiveValueNonPositive( + (*quality).into(), + (*gas_cost).into(), + ), ) } Kind::ScoringFailed(ScoreKind::ZeroScore) => { @@ -136,7 +139,13 @@ pub enum ScoreKind { SuccessProbabilityOutOfRange { probability: f64, }, - ObjectiveValueNonPositive, + #[serde(rename_all = "camelCase")] + ObjectiveValueNonPositive { + #[serde_as(as = "serialize::U256")] + quality: U256, + #[serde_as(as = "serialize::U256")] + gas_cost: U256, + }, } #[serde_as] diff --git a/crates/solvers/src/boundary/legacy.rs b/crates/solvers/src/boundary/legacy.rs index da3fed4163..a2583880a6 100644 --- a/crates/solvers/src/boundary/legacy.rs +++ b/crates/solvers/src/boundary/legacy.rs @@ -604,8 +604,11 @@ fn to_boundary_auction_result(notification: ¬ification::Notification) -> (i64 }, }), ), - Kind::ScoringFailed(ScoreKind::ObjectiveValueNonPositive) => { - AuctionResult::Rejected(SolverRejectionReason::ObjectiveValueNonPositive) + Kind::ScoringFailed(ScoreKind::ObjectiveValueNonPositive(quality, gas_cost)) => { + AuctionResult::Rejected(SolverRejectionReason::ObjectiveValueNonPositive { + quality: quality.0, + gas_cost: gas_cost.0, + }) } Kind::ScoringFailed(ScoreKind::ZeroScore) => { AuctionResult::Rejected(SolverRejectionReason::NonPositiveScore) diff --git a/crates/solvers/src/domain/notification.rs b/crates/solvers/src/domain/notification.rs index d9404b1108..5d2c75da8e 100644 --- a/crates/solvers/src/domain/notification.rs +++ b/crates/solvers/src/domain/notification.rs @@ -48,7 +48,7 @@ pub enum ScoreKind { ZeroScore, ScoreHigherThanQuality(Score, Quality), SuccessProbabilityOutOfRange(SuccessProbability), - ObjectiveValueNonPositive, + ObjectiveValueNonPositive(Quality, GasCost), } #[derive(Debug, Copy, Clone)] @@ -68,3 +68,12 @@ impl From for Quality { Self(value) } } + +#[derive(Debug, Copy, Clone)] +pub struct GasCost(pub eth::U256); + +impl From for GasCost { + fn from(value: eth::U256) -> Self { + Self(value) + } +}