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] Build domain::Settlement based on /solve response #2730

Closed
wants to merge 16 commits into from
2 changes: 1 addition & 1 deletion crates/autopilot/src/domain/competition/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub struct TradedAmounts {
pub buy: eth::TokenAmount,
}

#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Score(eth::Ether);

impl Score {
Expand Down
75 changes: 74 additions & 1 deletion crates/autopilot/src/domain/settlement/solution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use {
self::trade::Trade,
crate::domain::{
auction::{self},
auction::{self, order},
competition,
eth,
fee,
Expand Down Expand Up @@ -125,6 +125,71 @@ impl Solution {

Ok(Self { trades, auction_id })
}

/// Build a `settlement::Solution` from a `competition::Solution`.
///
/// Since `competition::Solution` does not contain JIT orders, the resulting
/// `settlement::Solution` might be a subset of it's onchain observable
/// twin.
pub fn from_competition_solution(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
pub fn from_competition_solution(
pub fn from_competition(

solution: &competition::Solution,
auction: &auction::Auction,
auction_id: auction::Id,
) -> Result<Self, error::Solution> {
let auction_orders = auction
.orders
.iter()
.map(|o| (o.uid, o))
.collect::<HashMap<_, _>>();

let mut trades = Vec::with_capacity(solution.orders().len());
for (order_uid, traded) in solution.orders().iter() {
let order = auction_orders
.get(order_uid)
.ok_or(error::Solution::MissingOrder(*order_uid))?;
trades.push(trade::Trade::new(
order.uid,
order.sell,
order.buy,
order.side,
match order.side {
order::Side::Sell => traded.sell.0.into(),
order::Side::Buy => traded.buy.0.into(),
},
trade::Prices {
uniform: trade::ClearingPrices {
sell: solution
.prices()
.get(&order.sell.token)
.ok_or(error::Solution::MissingClearingPrice(order.sell.token))?
.get()
.into(),
buy: solution
.prices()
.get(&order.buy.token)
.ok_or(error::Solution::MissingClearingPrice(order.buy.token))?
.get()
.into(),
},
custom: trade::ClearingPrices {
// settlement contract uses this formula to convert executed amounts:
// SELL order: executedBuyAmount = executed * sellPrice / buyPrice;
// BUY order: executedSellAmount = executed * buyPrice / sellPrice;
//
// With an example of converting 1 GNO for 100 DAI:
// SELL order: executedBuyAmount = 1 * 100 / 1 = 100 =>
// (sellPrice = 100, buyPrice = 1)
// BUY order: executedSellAmount = 100 * 1 / 100 = 1 =>
// (sellPrice = 100, buyPrice = 1)
Comment on lines +175 to +183
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nanonit: if it was for me you could remove this comment (I think it's quite clear that the exchange rate can be represented with the effective buy/sell amounts)

sell: traded.buy.into(),
buy: traded.sell.into(),
},
},
));
}

Ok(Self { trades, auction_id })
}
}

pub mod error {
Expand Down Expand Up @@ -159,6 +224,14 @@ pub mod error {
}
}
}

#[derive(Debug, thiserror::Error)]
pub enum Solution {
#[error("order not found in the auction for uid {0:?}")]
MissingOrder(OrderUid),
#[error("referenced clearing price missing for token {0:?}")]
MissingClearingPrice(eth::TokenAddress),
}
}

#[cfg(test)]
Expand Down
27 changes: 27 additions & 0 deletions crates/autopilot/src/run_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,33 @@ impl RunLoop {
}
};

{
// Also calculate the score from the solution itself
Copy link
Contributor Author

@sunce86 sunce86 May 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a temporary code that will be replaced when we switch to using autopilot calculated score. So did not spend too much time polishing it. It's used to detect differences in driver/autopilot score calculations.

// For now, don't actually use it for ranking, instead log differences to solver
// provided score for debugging purposes.
let policies = auction
.orders
.iter()
.map(|order| (order.uid, order.protocol_fees.clone()))
.collect();
let settlement_solution = domain::settlement::Solution::from_competition_solution(
solution, &auction, auction_id,
)
.map(|s| s.score(&auction.prices, &policies));

if let Ok(Ok(score)) = settlement_solution {
if score != solution.score() {
tracing::warn!(
"score mismatch: solver provided score: {:?}, calculated score: {:?}",
solution.score(),
score
);
}
} else {
tracing::warn!(?settlement_solution, "failed to calculate score");
}
}

let order_uids = solution.order_ids().copied().collect();
self.persistence
.store_order_events(order_uids, OrderEventLabel::Considered);
Expand Down
Loading