From 40e8098de13ed29a2165b868381e344151d8b615 Mon Sep 17 00:00:00 2001 From: Mateo Date: Wed, 24 Apr 2024 11:06:51 +0200 Subject: [PATCH 1/5] Store auction with liquidity --- Cargo.lock | 1 + .../src/infra/persistence/dto/order.rs | 1 - crates/driver/Cargo.toml | 1 + crates/driver/src/domain/competition/mod.rs | 2 + crates/driver/src/domain/eth/gas.rs | 4 + crates/driver/src/infra/config/file/load.rs | 1 + crates/driver/src/infra/config/file/mod.rs | 6 +- crates/driver/src/infra/mod.rs | 1 + crates/driver/src/infra/persistence/dto.rs | 395 ++++++++++++++++++ crates/driver/src/infra/persistence/mod.rs | 77 ++++ crates/driver/src/infra/solver/mod.rs | 27 +- crates/driver/src/run.rs | 18 +- crates/s3/src/lib.rs | 2 +- 13 files changed, 524 insertions(+), 12 deletions(-) create mode 100644 crates/driver/src/infra/persistence/dto.rs create mode 100644 crates/driver/src/infra/persistence/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 92c3947b84..2e823f767e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1661,6 +1661,7 @@ dependencies = [ "prometheus-metric-storage", "rand", "reqwest", + "s3", "secp256k1", "serde", "serde_json", diff --git a/crates/autopilot/src/infra/persistence/dto/order.rs b/crates/autopilot/src/infra/persistence/dto/order.rs index 85d5aab2b8..145ebc635b 100644 --- a/crates/autopilot/src/infra/persistence/dto/order.rs +++ b/crates/autopilot/src/infra/persistence/dto/order.rs @@ -258,7 +258,6 @@ impl From for domain::auction::order::EcdsaSignature { } } -#[serde_as] #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum FeePolicy { diff --git a/crates/driver/Cargo.toml b/crates/driver/Cargo.toml index afd9f6f1a1..02cba3f674 100644 --- a/crates/driver/Cargo.toml +++ b/crates/driver/Cargo.toml @@ -17,6 +17,7 @@ path = "src/main.rs" [dependencies] app-data = { path = "../app-data" } bytes-hex = { path = "../bytes-hex" } +s3 = { path = "../s3" } async-trait = { workspace = true } axum = { workspace = true } bigdecimal = { workspace = true } diff --git a/crates/driver/src/domain/competition/mod.rs b/crates/driver/src/domain/competition/mod.rs index e3eb2282d3..d688b025cc 100644 --- a/crates/driver/src/domain/competition/mod.rs +++ b/crates/driver/src/domain/competition/mod.rs @@ -79,6 +79,8 @@ impl Competition { } })?; + self.solver + .store_auction_with_liquidity(auction, &liquidity); observe::postprocessing(&solutions, auction.deadline().driver()); // Discard solutions that don't have unique ID. diff --git a/crates/driver/src/domain/eth/gas.rs b/crates/driver/src/domain/eth/gas.rs index 3c53ed526c..f1fc3a4c21 100644 --- a/crates/driver/src/domain/eth/gas.rs +++ b/crates/driver/src/domain/eth/gas.rs @@ -71,6 +71,10 @@ impl GasPrice { .into() } + pub fn base(&self) -> FeePerGas { + self.base + } + pub fn max(&self) -> FeePerGas { self.max } diff --git a/crates/driver/src/infra/config/file/load.rs b/crates/driver/src/infra/config/file/load.rs index 37e43ed181..a370a94cb6 100644 --- a/crates/driver/src/infra/config/file/load.rs +++ b/crates/driver/src/infra/config/file/load.rs @@ -89,6 +89,7 @@ pub async fn load(chain: eth::ChainId, path: &Path) -> infra::Config { true => SolutionMerging::Allowed, false => SolutionMerging::Forbidden, }, + s3: config.s3, } })) .await, diff --git a/crates/driver/src/infra/config/file/mod.rs b/crates/driver/src/infra/config/file/mod.rs index f87779c701..5c3fd42cbc 100644 --- a/crates/driver/src/infra/config/file/mod.rs +++ b/crates/driver/src/infra/config/file/mod.rs @@ -1,6 +1,6 @@ pub use load::load; use { - crate::{domain::eth, util::serialize}, + crate::{domain::eth, infra::persistence::S3, util::serialize}, reqwest::Url, serde::{Deserialize, Serialize}, serde_with::serde_as, @@ -225,6 +225,10 @@ struct SolverConfig { /// auction together. #[serde(default)] merge_solutions: bool, + + /// S3 path for storing the auctions with liquidity + #[serde(default)] + s3: Option, } #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)] diff --git a/crates/driver/src/infra/mod.rs b/crates/driver/src/infra/mod.rs index 2b809ed7f6..b33526f545 100644 --- a/crates/driver/src/infra/mod.rs +++ b/crates/driver/src/infra/mod.rs @@ -6,6 +6,7 @@ pub mod liquidity; pub mod mempool; pub mod notify; pub mod observe; +pub mod persistence; pub mod simulator; pub mod solver; pub mod time; diff --git a/crates/driver/src/infra/persistence/dto.rs b/crates/driver/src/infra/persistence/dto.rs new file mode 100644 index 0000000000..aae118f068 --- /dev/null +++ b/crates/driver/src/infra/persistence/dto.rs @@ -0,0 +1,395 @@ +use { + crate::domain::{ + competition::{ + self, + auction::{self}, + order::{self, fees}, + Auction, + }, + eth::{self}, + liquidity, + time, + }, + app_data::AppDataHash, + model::order::OrderUid, + number::serialization::HexOrDecimalU256, + primitive_types::{H160, U256}, + serde::Serialize, + serde_with::serde_as, + std::collections::BTreeMap, +}; + +#[serde_as] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AuctionWithLiquidity { + prices: BTreeMap, + gas_price: GasPrice, + deadline: Deadline, + orders: Vec, + liquidity: Vec, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct Deadline { + driver: i64, + solvers: i64, +} + +impl From for Deadline { + fn from(value: time::Deadline) -> Self { + Self { + driver: value.driver().timestamp(), + solvers: value.solvers().timestamp(), + } + } +} + +#[serde_as] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct Token { + decimals: Option, + symbol: Option, + address: H160, + #[serde_as(as = "Option")] + price: Option, + #[serde_as(as = "HexOrDecimalU256")] + available_balance: U256, + trusted: bool, +} + +impl From for Token { + fn from(value: auction::Token) -> Self { + Self { + decimals: value.decimals, + symbol: value.symbol, + address: value.address.into(), + price: value.price.map(Into::into), + available_balance: value.available_balance, + trusted: value.trusted, + } + } +} + +#[serde_as] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct GasPrice { + #[serde_as(as = "HexOrDecimalU256")] + max: U256, + #[serde_as(as = "HexOrDecimalU256")] + tip: U256, + #[serde_as(as = "HexOrDecimalU256")] + base: U256, +} + +impl From for GasPrice { + fn from(value: eth::GasPrice) -> Self { + Self { + max: value.max().into(), + tip: value.tip().into(), + base: value.base().into(), + } + } +} + +impl AuctionWithLiquidity { + pub fn build(auction: &Auction, liquidity: &[liquidity::Liquidity]) -> Self { + Self { + prices: auction + .tokens() + .iter() + .cloned() + .map(|token| (token.address.into(), token.into())) + .collect::>(), + gas_price: auction.gas_price().into(), + deadline: auction.deadline().into(), + orders: auction.orders().iter().cloned().map(Into::into).collect(), + liquidity: liquidity.iter().cloned().map(Into::into).collect(), + } + } +} + +#[serde_as] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct Liquidity { + id: usize, + gas: U256, + kind: String, +} + +impl From for Liquidity { + fn from(value: crate::domain::Liquidity) -> Self { + Self { + id: value.id.into(), + gas: value.gas.into(), + kind: { + let kind: &'static str = (&value.kind).into(); + kind.to_string() + }, + } + } +} + +#[serde_as] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct Order { + uid: OrderUid, + sell_token: H160, + buy_token: H160, + #[serde_as(as = "HexOrDecimalU256")] + sell_amount: U256, + #[serde_as(as = "HexOrDecimalU256")] + buy_amount: U256, + protocol_fees: Vec, + valid_to: u32, + side: Side, + kind: Kind, + receiver: Option, + partial: Partial, + pre_interactions: Vec, + post_interactions: Vec, + sell_token_balance: SellTokenSource, + buy_token_balance: BuyTokenDestination, + app_data: AppDataHash, + #[serde(flatten)] + signature: Signature, +} + +impl From for Order { + fn from(value: competition::Order) -> Self { + Self { + uid: OrderUid(value.uid.0 .0), + sell_token: value.sell.token.into(), + buy_token: value.buy.token.into(), + sell_amount: value.sell.amount.into(), + buy_amount: value.buy.amount.into(), + protocol_fees: value.protocol_fees.into_iter().map(Into::into).collect(), + valid_to: value.valid_to.into(), + kind: value.kind.into(), + side: value.side.into(), + receiver: value.receiver.map(Into::into), + partial: value.partial.into(), + pre_interactions: value.pre_interactions.into_iter().map(Into::into).collect(), + post_interactions: value + .post_interactions + .into_iter() + .map(Into::into) + .collect(), + sell_token_balance: value.sell_token_balance.into(), + buy_token_balance: value.buy_token_balance.into(), + app_data: AppDataHash(value.app_data.0 .0), + signature: Signature { + signing_scheme: value.signature.scheme.into(), + signature: value.signature.data.into(), + }, + } + } +} + +#[derive(Serialize)] +#[serde(rename_all = "snake_case")] +enum Side { + Buy, + Sell, +} + +impl From for Side { + fn from(value: order::Side) -> Self { + match value { + order::Side::Buy => Self::Buy, + order::Side::Sell => Self::Sell, + } + } +} + +#[derive(Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SellTokenSource { + Erc20, + External, + Internal, +} + +impl From for SellTokenSource { + fn from(value: order::SellTokenBalance) -> Self { + match value { + order::SellTokenBalance::Erc20 => Self::Erc20, + order::SellTokenBalance::Internal => Self::Internal, + order::SellTokenBalance::External => Self::External, + } + } +} + +#[derive(Serialize)] +#[serde(rename_all = "snake_case")] +enum BuyTokenDestination { + Erc20, + Internal, +} + +impl From for BuyTokenDestination { + fn from(value: order::BuyTokenBalance) -> Self { + match value { + order::BuyTokenBalance::Erc20 => Self::Erc20, + order::BuyTokenBalance::Internal => Self::Internal, + } + } +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +enum Partial { + Yes { available: U256 }, + No, +} + +impl From for Partial { + fn from(value: order::Partial) -> Self { + match value { + order::Partial::Yes { available } => Self::Yes { + available: available.into(), + }, + order::Partial::No => Self::No, + } + } +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +enum FeePolicy { + #[serde(rename_all = "camelCase")] + Surplus { factor: f64, max_volume_factor: f64 }, + #[serde(rename_all = "camelCase")] + PriceImprovement { + factor: f64, + max_volume_factor: f64, + quote: Quote, + }, + #[serde(rename_all = "camelCase")] + Volume { factor: f64 }, +} + +impl From for FeePolicy { + fn from(value: order::FeePolicy) -> Self { + match value { + order::FeePolicy::Surplus { + factor, + max_volume_factor, + } => Self::Surplus { + factor, + max_volume_factor, + }, + order::FeePolicy::PriceImprovement { + factor, + max_volume_factor, + quote, + } => Self::PriceImprovement { + factor, + max_volume_factor, + quote: quote.into(), + }, + order::FeePolicy::Volume { factor } => Self::Volume { factor }, + } + } +} + +#[serde_as] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct Quote { + #[serde_as(as = "HexOrDecimalU256")] + sell_amount: U256, + sell_token: H160, + #[serde_as(as = "HexOrDecimalU256")] + buy_amount: U256, + buy_token: H160, + #[serde_as(as = "HexOrDecimalU256")] + fee_amount: U256, + fee_token: H160, +} + +impl From for Quote { + fn from(value: fees::Quote) -> Self { + Self { + sell_amount: value.sell.amount.into(), + sell_token: value.sell.token.into(), + buy_amount: value.buy.amount.into(), + buy_token: value.buy.token.into(), + fee_amount: value.fee.amount.into(), + fee_token: value.fee.token.into(), + } + } +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +enum Kind { + Market, + Limit, + Liquidity, +} + +impl From for Kind { + fn from(value: order::Kind) -> Self { + match value { + order::Kind::Market => Self::Market, + order::Kind::Limit => Self::Limit, + order::Kind::Liquidity => Self::Liquidity, + } + } +} + +/// Signature over the order data. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +struct Signature { + signing_scheme: Scheme, + #[serde(with = "bytes_hex")] + signature: Vec, +} + +#[derive(Debug, Clone, Copy, Serialize)] +#[serde(rename_all = "lowercase")] +enum Scheme { + Eip712, + EthSign, + Eip1271, + PreSign, +} + +impl From for Scheme { + fn from(value: order::signature::Scheme) -> Self { + match value { + order::signature::Scheme::Eip712 => Self::Eip1271, + order::signature::Scheme::EthSign => Self::EthSign, + order::signature::Scheme::Eip1271 => Self::Eip712, + order::signature::Scheme::PreSign => Self::PreSign, + } + } +} + +#[serde_as] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct Interaction { + target: H160, + #[serde_as(as = "HexOrDecimalU256")] + value: U256, + #[serde(with = "bytes_hex")] + call_data: Vec, +} + +impl From for Interaction { + fn from(value: eth::Interaction) -> Self { + Self { + target: value.target.into(), + value: value.value.into(), + call_data: value.call_data.into(), + } + } +} diff --git a/crates/driver/src/infra/persistence/mod.rs b/crates/driver/src/infra/persistence/mod.rs new file mode 100644 index 0000000000..f5f9781249 --- /dev/null +++ b/crates/driver/src/infra/persistence/mod.rs @@ -0,0 +1,77 @@ +mod dto; + +use { + crate::{ + domain::{ + competition::{auction::Id, Auction}, + liquidity, + }, + infra::solver::Config, + }, + serde::Deserialize, + std::sync::Arc, + tracing::Instrument, +}; + +#[derive(Clone, Debug, Default, Deserialize)] +#[serde(rename_all = "kebab-case", deny_unknown_fields)] +pub struct S3 { + /// The s3_instance_upload_* arguments configure how auction instances + /// should be uploaded to AWS S3. + /// They must either all be set or all not set. + pub s3_instance_upload_bucket: String, + + /// Prepended to the auction id to form the final instance filename on S3. + /// Something like "staging/mainnet/" + pub s3_instance_upload_filename_prefix: String, +} + +impl From for s3::Config { + fn from(value: S3) -> Self { + Self { + bucket: value.s3_instance_upload_bucket, + filename_prefix: value.s3_instance_upload_filename_prefix, + } + } +} + +#[derive(Clone, Debug)] +pub struct Persistence { + s3: Option>, +} + +impl Persistence { + pub async fn build(config: &Config) -> Self { + if let Some(s3) = &config.s3 { + Self { + s3: Some(Arc::new(s3::Uploader::new(s3.clone().into()).await)), + } + } else { + Self { s3: None } + } + } + + /// Saves the given auction with liquidity + pub fn archive_auction(&self, id: Id, auction: &Auction, liquidity: &[liquidity::Liquidity]) { + let Some(uploader) = self.s3.clone() else { + return; + }; + let auction_with_liquidity = dto::AuctionWithLiquidity::build(auction, liquidity); + tokio::spawn( + async move { + match uploader + .upload(id.to_string(), auction_with_liquidity) + .await + { + Ok(key) => { + tracing::info!(?key, "uploaded auction to s3"); + } + Err(err) => { + tracing::warn!(?err, "failed to upload auction to s3"); + } + } + } + .instrument(tracing::Span::current()), + ); + } +} diff --git a/crates/driver/src/infra/solver/mod.rs b/crates/driver/src/infra/solver/mod.rs index ac13aa60c5..fd7d633b13 100644 --- a/crates/driver/src/infra/solver/mod.rs +++ b/crates/driver/src/infra/solver/mod.rs @@ -10,7 +10,11 @@ use { liquidity, time::Remaining, }, - infra::{blockchain::Ethereum, config::file::FeeHandler}, + infra::{ + blockchain::Ethereum, + config::file::FeeHandler, + persistence::{Persistence, S3}, + }, util, }, anyhow::Result, @@ -85,6 +89,7 @@ pub struct Solver { client: reqwest::Client, config: Config, eth: Ethereum, + persistence: Persistence, } #[derive(Debug, Clone)] @@ -108,10 +113,12 @@ pub struct Config { /// TODO: Remove once all solvers are moved to use limit orders for quoting pub quote_using_limit_orders: bool, pub merge_solutions: SolutionMerging, + /// S3 path for storing the auctions with liquidity + pub s3: Option, } impl Solver { - pub fn new(config: Config, eth: Ethereum) -> Result { + pub async fn new(config: Config, eth: Ethereum) -> Result { let mut headers = reqwest::header::HeaderMap::new(); headers.insert( reqwest::header::CONTENT_TYPE, @@ -124,12 +131,15 @@ impl Solver { headers.insert(header_name, val.parse()?); } + let persistence = Persistence::build(&config).await; + Ok(Self { client: reqwest::ClientBuilder::new() .default_headers(headers) .build()?, config, eth, + persistence, }) } @@ -230,6 +240,19 @@ impl Solver { }; tokio::task::spawn(future.in_current_span()); } + + /// Store the auction with liquidity in a S3 bucket + pub fn store_auction_with_liquidity( + &self, + auction: &Auction, + liquidity: &[liquidity::Liquidity], + ) { + let Some(auction_id) = auction.id() else { + return; + }; + self.persistence + .archive_auction(auction_id, auction, liquidity); + } } /// Controls whether or not the driver is allowed to merge multiple solutions diff --git a/crates/driver/src/run.rs b/crates/driver/src/run.rs index d491118569..5ef03aae15 100644 --- a/crates/driver/src/run.rs +++ b/crates/driver/src/run.rs @@ -13,6 +13,7 @@ use { }, }, clap::Parser, + futures::future::join_all, std::{net::SocketAddr, sync::Arc, time::Duration}, tokio::sync::oneshot, }; @@ -51,7 +52,7 @@ async fn run_with(args: cli::Args, addr_sender: Option Ethereum { Ethereum::new(ethrpc, config.contracts, gas).await } -fn solvers(config: &config::Config, eth: &Ethereum) -> Vec { - config - .solvers - .iter() - .map(|config| Solver::new(config.clone(), eth.clone()).unwrap()) - .collect() +async fn solvers(config: &config::Config, eth: &Ethereum) -> Vec { + join_all( + config + .solvers + .iter() + .map(|config| async move { Solver::new(config.clone(), eth.clone()).await.unwrap() }) + .collect::>(), + ) + .await } async fn liquidity(config: &config::Config, eth: &Ethereum) -> liquidity::Fetcher { diff --git a/crates/s3/src/lib.rs b/crates/s3/src/lib.rs index 197a196e0d..e1e3ea28a7 100644 --- a/crates/s3/src/lib.rs +++ b/crates/s3/src/lib.rs @@ -15,7 +15,7 @@ pub struct Config { pub filename_prefix: String, } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Uploader { bucket: String, filename_prefix: String, From d6e1313f31f884762a7bd1d81898e15447c82856 Mon Sep 17 00:00:00 2001 From: Mateo Date: Fri, 26 Apr 2024 10:35:22 +0200 Subject: [PATCH 2/5] Serialize current liquidity and auction instead of creating specific --- .../driver/src/domain/competition/auction.rs | 22 +- .../src/domain/competition/order/fees.rs | 11 +- .../src/domain/competition/order/mod.rs | 39 +- .../src/domain/competition/order/signature.rs | 8 +- crates/driver/src/domain/eth/gas.rs | 13 +- crates/driver/src/domain/eth/mod.rs | 31 +- crates/driver/src/domain/liquidity/mod.rs | 21 +- crates/driver/src/domain/time.rs | 4 +- crates/driver/src/infra/config/file/mod.rs | 3 +- crates/driver/src/infra/persistence/dto.rs | 395 ------------------ crates/driver/src/infra/persistence/mod.rs | 29 +- crates/driver/src/infra/solver/mod.rs | 3 +- crates/driver/src/util/bytes.rs | 18 + crates/driver/src/util/time.rs | 5 +- 14 files changed, 155 insertions(+), 447 deletions(-) delete mode 100644 crates/driver/src/infra/persistence/dto.rs diff --git a/crates/driver/src/domain/competition/auction.rs b/crates/driver/src/domain/competition/auction.rs index 79b5f015f2..11c558aaad 100644 --- a/crates/driver/src/domain/competition/auction.rs +++ b/crates/driver/src/domain/competition/auction.rs @@ -4,7 +4,7 @@ use { domain::{ competition::{self, auction}, eth, - liquidity, + liquidity::{self}, time, }, infra::{self, blockchain, observe, Ethereum}, @@ -12,6 +12,9 @@ use { }, futures::future::{join_all, BoxFuture, FutureExt, Shared}, itertools::Itertools, + number::serialization::HexOrDecimalU256, + serde::Serialize, + serde_with::serde_as, std::{ collections::{HashMap, HashSet}, sync::{Arc, Mutex}, @@ -22,7 +25,8 @@ use { /// An auction is a set of orders that can be solved. The solvers calculate /// [`super::solution::Solution`]s by picking subsets of these orders and /// solving them. -#[derive(Debug)] +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Auction { /// See the [`Self::id`] method. id: Option, @@ -346,7 +350,8 @@ impl AuctionProcessor { } /// The tokens that are used in an auction. -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Serialize)] +#[serde(transparent)] pub struct Tokens(HashMap); impl Tokens { @@ -366,13 +371,16 @@ impl Tokens { } } -#[derive(Debug, Clone)] +#[serde_as] +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Token { pub decimals: Option, pub symbol: Option, pub address: eth::TokenAddress, pub price: Option, /// The balance of this token available in our settlement contract. + #[serde_as(as = "HexOrDecimalU256")] pub available_balance: eth::U256, /// Is this token well-known and trusted by the protocol? pub trusted: bool, @@ -380,7 +388,8 @@ pub struct Token { /// The price of a token in wei. This represents how much wei is needed to buy /// 10**18 of another token. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize)] +#[serde(transparent)] pub struct Price(eth::Ether); impl Price { @@ -430,7 +439,8 @@ impl From for Price { /// All auction prices pub type Prices = HashMap; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize)] +#[serde(transparent)] pub struct Id(pub i64); impl Id { diff --git a/crates/driver/src/domain/competition/order/fees.rs b/crates/driver/src/domain/competition/order/fees.rs index 9129dd83ee..9a959b6d34 100644 --- a/crates/driver/src/domain/competition/order/fees.rs +++ b/crates/driver/src/domain/competition/order/fees.rs @@ -1,10 +1,12 @@ -use crate::domain::eth; +use {crate::domain::eth, serde_with::serde_derive::Serialize}; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] pub enum FeePolicy { /// If the order receives more than limit price, take the protocol fee as a /// percentage of the difference. The fee is taken in `sell` token for /// `buy` orders and in `buy` token for `sell` orders. + #[serde(rename_all = "camelCase")] Surplus { /// Factor of surplus the protocol charges as a fee. /// Surplus is the difference between executed price and limit price @@ -20,6 +22,7 @@ pub enum FeePolicy { /// A price improvement corresponds to a situation where the order is /// executed at a better price than the top quote. The protocol fee in such /// case is calculated as a percentage of this price improvement. + #[serde(rename_all = "camelCase")] PriceImprovement { /// Price improvement is the difference between executed price and the /// best quote or limit price, whichever is better for the user. @@ -39,6 +42,7 @@ pub enum FeePolicy { /// How much of the order's volume should be taken as a protocol fee. /// The fee is taken in `sell` token for `sell` orders and in `buy` /// token for `buy` orders. + #[serde(rename_all = "camelCase")] Volume { /// Percentage of the order's volume should be taken as a protocol /// fee. @@ -46,7 +50,8 @@ pub enum FeePolicy { }, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Quote { pub sell: eth::Asset, pub buy: eth::Asset, diff --git a/crates/driver/src/domain/competition/order/mod.rs b/crates/driver/src/domain/competition/order/mod.rs index 4852d39abd..08ab79c213 100644 --- a/crates/driver/src/domain/competition/order/mod.rs +++ b/crates/driver/src/domain/competition/order/mod.rs @@ -8,6 +8,9 @@ use { bigdecimal::Zero, model::order::{BuyTokenDestination, SellTokenSource}, num::CheckedDiv, + number::serialization::HexOrDecimalU256, + serde::Serialize, + serde_with::serde_as, }; pub use {fees::FeePolicy, signature::Signature}; @@ -15,7 +18,8 @@ pub mod fees; pub mod signature; /// An order in the auction. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Order { pub uid: Uid, /// The user specified a custom address to receive the output of this order. @@ -70,8 +74,10 @@ impl From for eth::U256 { /// An amount denominated in the sell token for [`Side::Sell`] [`Order`]s, or in /// the buy token for [`Side::Buy`] [`Order`]s. -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct TargetAmount(pub eth::U256); +#[serde_as] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)] +#[serde(transparent)] +pub struct TargetAmount(#[serde_as(as = "HexOrDecimalU256")] pub eth::U256); impl From for TargetAmount { fn from(value: eth::U256) -> Self { @@ -228,11 +234,13 @@ impl Available { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase")] pub enum Partial { /// A partially order doesn't require the full amount to be traded. /// E.g. only 10% of the requested amount may be traded, if this leads /// to the most optimal solution. + #[serde(rename_all = "camelCase")] Yes { /// The available amount that can be used from the order. /// @@ -240,6 +248,7 @@ pub enum Partial { /// executed as well as the trader's balance. available: TargetAmount, }, + #[serde(rename_all = "camelCase")] No, } @@ -247,8 +256,9 @@ pub enum Partial { pub const UID_LEN: usize = 56; /// UID of an order. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Uid(pub Bytes<[u8; UID_LEN]>); +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)] +#[serde(transparent)] +pub struct Uid(#[serde(with = "bytes_hex")] pub Bytes<[u8; UID_LEN]>); impl Default for Uid { fn default() -> Self { @@ -263,7 +273,8 @@ impl PartialEq<[u8; UID_LEN]> for Uid { } // TODO These doc comments are incorrect for limit orders -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase")] pub enum Side { /// Buy an exact amount. The sell amount can vary due to e.g. partial fills /// or slippage. @@ -291,8 +302,9 @@ pub const APP_DATA_LEN: usize = 32; /// This is a hash allowing arbitrary user data to be associated with an order. /// While this type holds the hash, the data itself is uploaded to IPFS. This /// hash is signed along with the order. -#[derive(Debug, Default, Clone, Copy)] -pub struct AppData(pub Bytes<[u8; APP_DATA_LEN]>); +#[derive(Debug, Default, Clone, Copy, Serialize)] +#[serde(transparent)] +pub struct AppData(#[serde(with = "bytes_hex")] pub Bytes<[u8; APP_DATA_LEN]>); impl From<[u8; APP_DATA_LEN]> for AppData { fn from(inner: [u8; APP_DATA_LEN]) -> Self { @@ -306,7 +318,8 @@ impl From for [u8; APP_DATA_LEN] { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase")] pub enum Kind { /// Order intended to be immediately executed. This is the "regular" type of /// order. @@ -327,7 +340,8 @@ pub enum Kind { } /// [Balancer V2](https://docs.balancer.fi/) integration, used for settlement encoding. -#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub enum SellTokenBalance { Erc20, Internal, @@ -357,7 +371,8 @@ impl SellTokenBalance { } /// [Balancer V2](https://docs.balancer.fi/) integration, used for settlement encoding. -#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub enum BuyTokenBalance { Erc20, Internal, diff --git a/crates/driver/src/domain/competition/order/signature.rs b/crates/driver/src/domain/competition/order/signature.rs index b77b8a3edd..367c5d1d22 100644 --- a/crates/driver/src/domain/competition/order/signature.rs +++ b/crates/driver/src/domain/competition/order/signature.rs @@ -1,12 +1,15 @@ use { crate::{domain::eth, util::Bytes}, model::signature::EcdsaSignature, + serde::Serialize, }; /// Signature over the order data. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Signature { pub scheme: Scheme, + #[serde(with = "bytes_hex")] pub data: Bytes>, /// The address used to sign and place this order. pub signer: eth::Address, @@ -34,7 +37,8 @@ impl Signature { /// The scheme used for signing the order. This is used by the solver and /// the protocol, the driver does not care about the details of signature /// verification. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize)] +#[serde(rename_all = "lowercase")] pub enum Scheme { Eip712, EthSign, diff --git a/crates/driver/src/domain/eth/gas.rs b/crates/driver/src/domain/eth/gas.rs index f1fc3a4c21..6541b7d6fb 100644 --- a/crates/driver/src/domain/eth/gas.rs +++ b/crates/driver/src/domain/eth/gas.rs @@ -2,6 +2,9 @@ use { super::{Ether, U256}, bigdecimal::Zero, derive_more::Display, + number::serialization::HexOrDecimalU256, + serde::Serialize, + serde_with::serde_as, std::{ops, ops::Add}, }; @@ -9,8 +12,9 @@ use { /// /// The amount of Ether that is paid in transaction fees is proportional to this /// amount as well as the transaction's [`EffectiveGasPrice`]. -#[derive(Debug, Default, Display, Clone, Copy, Ord, Eq, PartialOrd, PartialEq)] -pub struct Gas(pub U256); +#[serde_as] +#[derive(Debug, Default, Display, Clone, Copy, Ord, Eq, PartialOrd, PartialEq, Serialize)] +pub struct Gas(#[serde_as(as = "HexOrDecimalU256")] pub U256); impl From for Gas { fn from(value: U256) -> Self { @@ -51,7 +55,7 @@ impl Zero for Gas { /// An EIP-1559 gas price estimate. /// /// https://eips.ethereum.org/EIPS/eip-1559#specification -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize)] pub struct GasPrice { /// The maximum total fee that should be charged. max: FeePerGas, @@ -130,7 +134,8 @@ impl From for GasPrice { /// `{max,max_priority,base}_fee_per_gas` as defined by EIP-1559. /// /// https://eips.ethereum.org/EIPS/eip-1559#specification -#[derive(Debug, Clone, Copy, Ord, Eq, PartialEq, PartialOrd)] +#[derive(Debug, Clone, Copy, Ord, Eq, PartialEq, PartialOrd, Serialize)] +#[serde(transparent)] pub struct FeePerGas(pub Ether); impl FeePerGas { diff --git a/crates/driver/src/domain/eth/mod.rs b/crates/driver/src/domain/eth/mod.rs index 47808f5516..b7d969c482 100644 --- a/crates/driver/src/domain/eth/mod.rs +++ b/crates/driver/src/domain/eth/mod.rs @@ -1,6 +1,8 @@ use { crate::util::Bytes, itertools::Itertools, + serde::Serialize, + serde_with::serde_as, std::{ collections::{HashMap, HashSet}, ops::{Div, Mul, Sub}, @@ -11,6 +13,7 @@ pub mod allowance; mod eip712; mod gas; +use number::serialization::HexOrDecimalU256; pub use { allowance::Allowance, eip712::{DomainFields, DomainSeparator}, @@ -118,7 +121,8 @@ impl From for StorageKey { } /// An address. Can be an EOA or a smart contract address. -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +#[serde(transparent)] pub struct Address(pub H160); impl From for Address { @@ -136,7 +140,8 @@ impl From
for H160 { // TODO This type should probably use Ethereum::is_contract to verify during // construction that it does indeed point to a contract /// A smart contract address. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +#[serde(transparent)] pub struct ContractAddress(pub H160); impl From for ContractAddress { @@ -160,7 +165,8 @@ impl From for Address { /// An ERC20 token address. /// /// https://eips.ethereum.org/EIPS/eip-20 -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +#[serde(transparent)] pub struct TokenAddress(pub ContractAddress); impl TokenAddress { @@ -177,8 +183,10 @@ impl TokenAddress { /// An ERC20 token amount. /// /// https://eips.ethereum.org/EIPS/eip-20 -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct TokenAmount(pub U256); +#[serde_as] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +#[serde(transparent)] +pub struct TokenAmount(#[serde_as(as = "HexOrDecimalU256")] pub U256); impl TokenAmount { /// Applies a factor to the token amount. @@ -332,15 +340,18 @@ impl From for ContractAddress { /// An asset on the Ethereum blockchain. Represents a particular amount of a /// particular token. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Asset { pub amount: TokenAmount, pub token: TokenAddress, } /// An amount of native Ether tokens denominated in wei. -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct Ether(pub U256); +#[serde_as] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)] +#[serde(transparent)] +pub struct Ether(#[serde_as(as = "HexOrDecimalU256")] pub U256); impl From for Ether { fn from(value: U256) -> Self { @@ -403,10 +414,12 @@ impl From for BlockNo { } /// An onchain transaction which interacts with a smart contract. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Interaction { pub target: Address, pub value: Ether, + #[serde(with = "bytes_hex")] pub call_data: Bytes>, } diff --git a/crates/driver/src/domain/liquidity/mod.rs b/crates/driver/src/domain/liquidity/mod.rs index 254f39ebca..ad2b8c7966 100644 --- a/crates/driver/src/domain/liquidity/mod.rs +++ b/crates/driver/src/domain/liquidity/mod.rs @@ -1,7 +1,11 @@ // TODO Remove dead_code #![allow(dead_code)] -use {crate::domain::eth, std::cmp::Ordering}; +use { + crate::domain::eth, + serde::{Serialize, Serializer}, + std::cmp::Ordering, +}; pub mod balancer; pub mod swapr; @@ -9,7 +13,8 @@ pub mod uniswap; pub mod zeroex; /// A source of liquidity which can be used by the solver. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Liquidity { pub id: Id, /// Estimation of gas needed to use this liquidity on-chain. @@ -17,7 +22,8 @@ pub struct Liquidity { pub kind: Kind, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] +#[serde(transparent)] pub struct Id(pub usize); impl From for Id { @@ -74,6 +80,15 @@ impl From<&Kind> for &'static str { } } +impl Serialize for Kind { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(self.into()) + } +} + /// An ordered token pair. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct TokenPair(eth::TokenAddress, eth::TokenAddress); diff --git a/crates/driver/src/domain/time.rs b/crates/driver/src/domain/time.rs index 9bd7cc637b..5a1fe338f2 100644 --- a/crates/driver/src/domain/time.rs +++ b/crates/driver/src/domain/time.rs @@ -4,6 +4,7 @@ use { solver::Timeouts, {self}, }, + serde::Serialize, thiserror::Error, }; @@ -12,7 +13,8 @@ use { /// driver deadline. /// The solvers are expected to return the solution to the driver before the /// solvers deadline. -#[derive(Copy, Clone, Debug, Default)] +#[derive(Copy, Clone, Debug, Default, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Deadline { driver: chrono::DateTime, solvers: chrono::DateTime, diff --git a/crates/driver/src/infra/config/file/mod.rs b/crates/driver/src/infra/config/file/mod.rs index 5c3fd42cbc..66aed26f72 100644 --- a/crates/driver/src/infra/config/file/mod.rs +++ b/crates/driver/src/infra/config/file/mod.rs @@ -226,7 +226,8 @@ struct SolverConfig { #[serde(default)] merge_solutions: bool, - /// S3 path for storing the auctions with liquidity + /// S3 configuration for storing the auction in the form they are sent to + /// the solver engine #[serde(default)] s3: Option, } diff --git a/crates/driver/src/infra/persistence/dto.rs b/crates/driver/src/infra/persistence/dto.rs deleted file mode 100644 index aae118f068..0000000000 --- a/crates/driver/src/infra/persistence/dto.rs +++ /dev/null @@ -1,395 +0,0 @@ -use { - crate::domain::{ - competition::{ - self, - auction::{self}, - order::{self, fees}, - Auction, - }, - eth::{self}, - liquidity, - time, - }, - app_data::AppDataHash, - model::order::OrderUid, - number::serialization::HexOrDecimalU256, - primitive_types::{H160, U256}, - serde::Serialize, - serde_with::serde_as, - std::collections::BTreeMap, -}; - -#[serde_as] -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct AuctionWithLiquidity { - prices: BTreeMap, - gas_price: GasPrice, - deadline: Deadline, - orders: Vec, - liquidity: Vec, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct Deadline { - driver: i64, - solvers: i64, -} - -impl From for Deadline { - fn from(value: time::Deadline) -> Self { - Self { - driver: value.driver().timestamp(), - solvers: value.solvers().timestamp(), - } - } -} - -#[serde_as] -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct Token { - decimals: Option, - symbol: Option, - address: H160, - #[serde_as(as = "Option")] - price: Option, - #[serde_as(as = "HexOrDecimalU256")] - available_balance: U256, - trusted: bool, -} - -impl From for Token { - fn from(value: auction::Token) -> Self { - Self { - decimals: value.decimals, - symbol: value.symbol, - address: value.address.into(), - price: value.price.map(Into::into), - available_balance: value.available_balance, - trusted: value.trusted, - } - } -} - -#[serde_as] -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct GasPrice { - #[serde_as(as = "HexOrDecimalU256")] - max: U256, - #[serde_as(as = "HexOrDecimalU256")] - tip: U256, - #[serde_as(as = "HexOrDecimalU256")] - base: U256, -} - -impl From for GasPrice { - fn from(value: eth::GasPrice) -> Self { - Self { - max: value.max().into(), - tip: value.tip().into(), - base: value.base().into(), - } - } -} - -impl AuctionWithLiquidity { - pub fn build(auction: &Auction, liquidity: &[liquidity::Liquidity]) -> Self { - Self { - prices: auction - .tokens() - .iter() - .cloned() - .map(|token| (token.address.into(), token.into())) - .collect::>(), - gas_price: auction.gas_price().into(), - deadline: auction.deadline().into(), - orders: auction.orders().iter().cloned().map(Into::into).collect(), - liquidity: liquidity.iter().cloned().map(Into::into).collect(), - } - } -} - -#[serde_as] -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct Liquidity { - id: usize, - gas: U256, - kind: String, -} - -impl From for Liquidity { - fn from(value: crate::domain::Liquidity) -> Self { - Self { - id: value.id.into(), - gas: value.gas.into(), - kind: { - let kind: &'static str = (&value.kind).into(); - kind.to_string() - }, - } - } -} - -#[serde_as] -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct Order { - uid: OrderUid, - sell_token: H160, - buy_token: H160, - #[serde_as(as = "HexOrDecimalU256")] - sell_amount: U256, - #[serde_as(as = "HexOrDecimalU256")] - buy_amount: U256, - protocol_fees: Vec, - valid_to: u32, - side: Side, - kind: Kind, - receiver: Option, - partial: Partial, - pre_interactions: Vec, - post_interactions: Vec, - sell_token_balance: SellTokenSource, - buy_token_balance: BuyTokenDestination, - app_data: AppDataHash, - #[serde(flatten)] - signature: Signature, -} - -impl From for Order { - fn from(value: competition::Order) -> Self { - Self { - uid: OrderUid(value.uid.0 .0), - sell_token: value.sell.token.into(), - buy_token: value.buy.token.into(), - sell_amount: value.sell.amount.into(), - buy_amount: value.buy.amount.into(), - protocol_fees: value.protocol_fees.into_iter().map(Into::into).collect(), - valid_to: value.valid_to.into(), - kind: value.kind.into(), - side: value.side.into(), - receiver: value.receiver.map(Into::into), - partial: value.partial.into(), - pre_interactions: value.pre_interactions.into_iter().map(Into::into).collect(), - post_interactions: value - .post_interactions - .into_iter() - .map(Into::into) - .collect(), - sell_token_balance: value.sell_token_balance.into(), - buy_token_balance: value.buy_token_balance.into(), - app_data: AppDataHash(value.app_data.0 .0), - signature: Signature { - signing_scheme: value.signature.scheme.into(), - signature: value.signature.data.into(), - }, - } - } -} - -#[derive(Serialize)] -#[serde(rename_all = "snake_case")] -enum Side { - Buy, - Sell, -} - -impl From for Side { - fn from(value: order::Side) -> Self { - match value { - order::Side::Buy => Self::Buy, - order::Side::Sell => Self::Sell, - } - } -} - -#[derive(Serialize)] -#[serde(rename_all = "snake_case")] -pub enum SellTokenSource { - Erc20, - External, - Internal, -} - -impl From for SellTokenSource { - fn from(value: order::SellTokenBalance) -> Self { - match value { - order::SellTokenBalance::Erc20 => Self::Erc20, - order::SellTokenBalance::Internal => Self::Internal, - order::SellTokenBalance::External => Self::External, - } - } -} - -#[derive(Serialize)] -#[serde(rename_all = "snake_case")] -enum BuyTokenDestination { - Erc20, - Internal, -} - -impl From for BuyTokenDestination { - fn from(value: order::BuyTokenBalance) -> Self { - match value { - order::BuyTokenBalance::Erc20 => Self::Erc20, - order::BuyTokenBalance::Internal => Self::Internal, - } - } -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -enum Partial { - Yes { available: U256 }, - No, -} - -impl From for Partial { - fn from(value: order::Partial) -> Self { - match value { - order::Partial::Yes { available } => Self::Yes { - available: available.into(), - }, - order::Partial::No => Self::No, - } - } -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -enum FeePolicy { - #[serde(rename_all = "camelCase")] - Surplus { factor: f64, max_volume_factor: f64 }, - #[serde(rename_all = "camelCase")] - PriceImprovement { - factor: f64, - max_volume_factor: f64, - quote: Quote, - }, - #[serde(rename_all = "camelCase")] - Volume { factor: f64 }, -} - -impl From for FeePolicy { - fn from(value: order::FeePolicy) -> Self { - match value { - order::FeePolicy::Surplus { - factor, - max_volume_factor, - } => Self::Surplus { - factor, - max_volume_factor, - }, - order::FeePolicy::PriceImprovement { - factor, - max_volume_factor, - quote, - } => Self::PriceImprovement { - factor, - max_volume_factor, - quote: quote.into(), - }, - order::FeePolicy::Volume { factor } => Self::Volume { factor }, - } - } -} - -#[serde_as] -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct Quote { - #[serde_as(as = "HexOrDecimalU256")] - sell_amount: U256, - sell_token: H160, - #[serde_as(as = "HexOrDecimalU256")] - buy_amount: U256, - buy_token: H160, - #[serde_as(as = "HexOrDecimalU256")] - fee_amount: U256, - fee_token: H160, -} - -impl From for Quote { - fn from(value: fees::Quote) -> Self { - Self { - sell_amount: value.sell.amount.into(), - sell_token: value.sell.token.into(), - buy_amount: value.buy.amount.into(), - buy_token: value.buy.token.into(), - fee_amount: value.fee.amount.into(), - fee_token: value.fee.token.into(), - } - } -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -enum Kind { - Market, - Limit, - Liquidity, -} - -impl From for Kind { - fn from(value: order::Kind) -> Self { - match value { - order::Kind::Market => Self::Market, - order::Kind::Limit => Self::Limit, - order::Kind::Liquidity => Self::Liquidity, - } - } -} - -/// Signature over the order data. -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct Signature { - signing_scheme: Scheme, - #[serde(with = "bytes_hex")] - signature: Vec, -} - -#[derive(Debug, Clone, Copy, Serialize)] -#[serde(rename_all = "lowercase")] -enum Scheme { - Eip712, - EthSign, - Eip1271, - PreSign, -} - -impl From for Scheme { - fn from(value: order::signature::Scheme) -> Self { - match value { - order::signature::Scheme::Eip712 => Self::Eip1271, - order::signature::Scheme::EthSign => Self::EthSign, - order::signature::Scheme::Eip1271 => Self::Eip712, - order::signature::Scheme::PreSign => Self::PreSign, - } - } -} - -#[serde_as] -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct Interaction { - target: H160, - #[serde_as(as = "HexOrDecimalU256")] - value: U256, - #[serde(with = "bytes_hex")] - call_data: Vec, -} - -impl From for Interaction { - fn from(value: eth::Interaction) -> Self { - Self { - target: value.target.into(), - value: value.value.into(), - call_data: value.call_data.into(), - } - } -} diff --git a/crates/driver/src/infra/persistence/mod.rs b/crates/driver/src/infra/persistence/mod.rs index f5f9781249..1c90eb5d95 100644 --- a/crates/driver/src/infra/persistence/mod.rs +++ b/crates/driver/src/infra/persistence/mod.rs @@ -1,5 +1,3 @@ -mod dto; - use { crate::{ domain::{ @@ -8,29 +6,38 @@ use { }, infra::solver::Config, }, - serde::Deserialize, + serde::{Deserialize, Serialize}, + serde_with::serde_as, std::sync::Arc, tracing::Instrument, }; +#[serde_as] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AuctionWithLiquidity { + pub auction: Auction, + pub liquidity: Vec, +} + #[derive(Clone, Debug, Default, Deserialize)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] pub struct S3 { /// The s3_instance_upload_* arguments configure how auction instances /// should be uploaded to AWS S3. /// They must either all be set or all not set. - pub s3_instance_upload_bucket: String, + pub bucket: String, /// Prepended to the auction id to form the final instance filename on S3. /// Something like "staging/mainnet/" - pub s3_instance_upload_filename_prefix: String, + pub prefix: String, } impl From for s3::Config { fn from(value: S3) -> Self { Self { - bucket: value.s3_instance_upload_bucket, - filename_prefix: value.s3_instance_upload_filename_prefix, + bucket: value.bucket, + filename_prefix: value.prefix, } } } @@ -51,12 +58,16 @@ impl Persistence { } } - /// Saves the given auction with liquidity + /// Saves the given auction with liquidity with fire and forget mentality + /// (non-blocking operation) pub fn archive_auction(&self, id: Id, auction: &Auction, liquidity: &[liquidity::Liquidity]) { let Some(uploader) = self.s3.clone() else { return; }; - let auction_with_liquidity = dto::AuctionWithLiquidity::build(auction, liquidity); + let auction_with_liquidity = AuctionWithLiquidity { + auction: auction.clone(), + liquidity: liquidity.to_vec(), + }; tokio::spawn( async move { match uploader diff --git a/crates/driver/src/infra/solver/mod.rs b/crates/driver/src/infra/solver/mod.rs index fd7d633b13..8be63ea2c1 100644 --- a/crates/driver/src/infra/solver/mod.rs +++ b/crates/driver/src/infra/solver/mod.rs @@ -113,7 +113,8 @@ pub struct Config { /// TODO: Remove once all solvers are moved to use limit orders for quoting pub quote_using_limit_orders: bool, pub merge_solutions: SolutionMerging, - /// S3 path for storing the auctions with liquidity + /// S3 configuration for storing the auction in the form they are sent to + /// the solver engine pub s3: Option, } diff --git a/crates/driver/src/util/bytes.rs b/crates/driver/src/util/bytes.rs index 1ff531c545..7452712627 100644 --- a/crates/driver/src/util/bytes.rs +++ b/crates/driver/src/util/bytes.rs @@ -2,6 +2,24 @@ #[derive(Clone, Copy, PartialEq, Eq, Hash, Default)] pub struct Bytes(pub T); +impl AsRef<[u8]> for Bytes> { + fn as_ref(&self) -> &[u8] { + self.0.as_slice() + } +} + +impl AsRef<[u8]> for Bytes<[u8; 56]> { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsRef<[u8]> for Bytes<[u8; 32]> { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + impl std::fmt::Debug for Bytes where T: AsRef<[u8]>, diff --git a/crates/driver/src/util/time.rs b/crates/driver/src/util/time.rs index e5e53d2fe5..e17cac580f 100644 --- a/crates/driver/src/util/time.rs +++ b/crates/driver/src/util/time.rs @@ -1,7 +1,10 @@ +use serde::Serialize; + /// A Unix timestamp denominated in seconds since epoch. /// /// https://en.wikipedia.org/wiki/Unix_time -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)] +#[serde(transparent)] pub struct Timestamp(pub u32); impl From for Timestamp { From ecbc9b7a219e16d260447ed406c5308659db9a86 Mon Sep 17 00:00:00 2001 From: Mateo Date: Mon, 29 Apr 2024 14:57:32 +0200 Subject: [PATCH 3/5] Rework comments --- .../driver/src/domain/competition/auction.rs | 20 ++---- crates/driver/src/domain/competition/mod.rs | 13 ++-- .../src/domain/competition/order/fees.rs | 11 +-- .../src/domain/competition/order/mod.rs | 39 ++++------- .../src/domain/competition/order/signature.rs | 8 +-- crates/driver/src/domain/eth/gas.rs | 13 ++-- crates/driver/src/domain/eth/mod.rs | 31 +++------ crates/driver/src/domain/liquidity/mod.rs | 21 +----- crates/driver/src/domain/time.rs | 4 +- crates/driver/src/infra/api/mod.rs | 1 + crates/driver/src/infra/api/routes/mod.rs | 1 + .../src/infra/api/routes/solve/dto/auction.rs | 24 +++---- .../driver/src/infra/api/routes/solve/mod.rs | 20 ++++-- crates/driver/src/infra/persistence/mod.rs | 69 +++++++++++++++---- crates/driver/src/infra/solver/mod.rs | 17 ++--- crates/driver/src/util/bytes.rs | 18 ----- crates/driver/src/util/time.rs | 5 +- 17 files changed, 137 insertions(+), 178 deletions(-) diff --git a/crates/driver/src/domain/competition/auction.rs b/crates/driver/src/domain/competition/auction.rs index 11c558aaad..b17bced396 100644 --- a/crates/driver/src/domain/competition/auction.rs +++ b/crates/driver/src/domain/competition/auction.rs @@ -12,9 +12,6 @@ use { }, futures::future::{join_all, BoxFuture, FutureExt, Shared}, itertools::Itertools, - number::serialization::HexOrDecimalU256, - serde::Serialize, - serde_with::serde_as, std::{ collections::{HashMap, HashSet}, sync::{Arc, Mutex}, @@ -25,8 +22,7 @@ use { /// An auction is a set of orders that can be solved. The solvers calculate /// [`super::solution::Solution`]s by picking subsets of these orders and /// solving them. -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug)] pub struct Auction { /// See the [`Self::id`] method. id: Option, @@ -350,8 +346,7 @@ impl AuctionProcessor { } /// The tokens that are used in an auction. -#[derive(Debug, Default, Clone, Serialize)] -#[serde(transparent)] +#[derive(Debug, Default, Clone)] pub struct Tokens(HashMap); impl Tokens { @@ -371,16 +366,13 @@ impl Tokens { } } -#[serde_as] -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone)] pub struct Token { pub decimals: Option, pub symbol: Option, pub address: eth::TokenAddress, pub price: Option, /// The balance of this token available in our settlement contract. - #[serde_as(as = "HexOrDecimalU256")] pub available_balance: eth::U256, /// Is this token well-known and trusted by the protocol? pub trusted: bool, @@ -388,8 +380,7 @@ pub struct Token { /// The price of a token in wei. This represents how much wei is needed to buy /// 10**18 of another token. -#[derive(Debug, Clone, Copy, Serialize)] -#[serde(transparent)] +#[derive(Debug, Clone, Copy)] pub struct Price(eth::Ether); impl Price { @@ -439,8 +430,7 @@ impl From for Price { /// All auction prices pub type Prices = HashMap; -#[derive(Debug, Clone, Copy, Serialize)] -#[serde(transparent)] +#[derive(Debug, Clone, Copy)] pub struct Id(pub i64); impl Id { diff --git a/crates/driver/src/domain/competition/mod.rs b/crates/driver/src/domain/competition/mod.rs index d688b025cc..9c38d31a3b 100644 --- a/crates/driver/src/domain/competition/mod.rs +++ b/crates/driver/src/domain/competition/mod.rs @@ -5,7 +5,7 @@ use { Mempools, }, crate::{ - domain::{competition::solution::Settlement, eth}, + domain::{competition::solution::Settlement, eth, Liquidity}, infra::{ self, blockchain::Ethereum, @@ -55,7 +55,10 @@ pub struct Competition { impl Competition { /// Solve an auction as part of this competition. - pub async fn solve(&self, auction: &Auction) -> Result, Error> { + pub async fn solve( + &self, + auction: &Auction, + ) -> Result<(Option, Vec), Error> { let liquidity = match self.solver.liquidity() { solver::Liquidity::Fetch => { self.liquidity @@ -79,8 +82,6 @@ impl Competition { } })?; - self.solver - .store_auction_with_liquidity(auction, &liquidity); observe::postprocessing(&solutions, auction.deadline().driver()); // Discard solutions that don't have unique ID. @@ -207,7 +208,7 @@ impl Competition { let settlement = match settlement { Some(settlement) => settlement, // Don't wait for the deadline because we can't produce a solution anyway. - None => return Ok(score), + None => return Ok((score, liquidity)), }; // Re-simulate the solution on every new block until the deadline ends to make @@ -239,7 +240,7 @@ impl Competition { let _ = tokio::time::timeout(remaining, simulate_on_new_blocks).await; } - Ok(score) + Ok((score, liquidity)) } pub async fn reveal(&self) -> Result { diff --git a/crates/driver/src/domain/competition/order/fees.rs b/crates/driver/src/domain/competition/order/fees.rs index 9a959b6d34..9129dd83ee 100644 --- a/crates/driver/src/domain/competition/order/fees.rs +++ b/crates/driver/src/domain/competition/order/fees.rs @@ -1,12 +1,10 @@ -use {crate::domain::eth, serde_with::serde_derive::Serialize}; +use crate::domain::eth; -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Clone, Debug)] pub enum FeePolicy { /// If the order receives more than limit price, take the protocol fee as a /// percentage of the difference. The fee is taken in `sell` token for /// `buy` orders and in `buy` token for `sell` orders. - #[serde(rename_all = "camelCase")] Surplus { /// Factor of surplus the protocol charges as a fee. /// Surplus is the difference between executed price and limit price @@ -22,7 +20,6 @@ pub enum FeePolicy { /// A price improvement corresponds to a situation where the order is /// executed at a better price than the top quote. The protocol fee in such /// case is calculated as a percentage of this price improvement. - #[serde(rename_all = "camelCase")] PriceImprovement { /// Price improvement is the difference between executed price and the /// best quote or limit price, whichever is better for the user. @@ -42,7 +39,6 @@ pub enum FeePolicy { /// How much of the order's volume should be taken as a protocol fee. /// The fee is taken in `sell` token for `sell` orders and in `buy` /// token for `buy` orders. - #[serde(rename_all = "camelCase")] Volume { /// Percentage of the order's volume should be taken as a protocol /// fee. @@ -50,8 +46,7 @@ pub enum FeePolicy { }, } -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Clone, Debug)] pub struct Quote { pub sell: eth::Asset, pub buy: eth::Asset, diff --git a/crates/driver/src/domain/competition/order/mod.rs b/crates/driver/src/domain/competition/order/mod.rs index 08ab79c213..4852d39abd 100644 --- a/crates/driver/src/domain/competition/order/mod.rs +++ b/crates/driver/src/domain/competition/order/mod.rs @@ -8,9 +8,6 @@ use { bigdecimal::Zero, model::order::{BuyTokenDestination, SellTokenSource}, num::CheckedDiv, - number::serialization::HexOrDecimalU256, - serde::Serialize, - serde_with::serde_as, }; pub use {fees::FeePolicy, signature::Signature}; @@ -18,8 +15,7 @@ pub mod fees; pub mod signature; /// An order in the auction. -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone)] pub struct Order { pub uid: Uid, /// The user specified a custom address to receive the output of this order. @@ -74,10 +70,8 @@ impl From for eth::U256 { /// An amount denominated in the sell token for [`Side::Sell`] [`Order`]s, or in /// the buy token for [`Side::Buy`] [`Order`]s. -#[serde_as] -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(transparent)] -pub struct TargetAmount(#[serde_as(as = "HexOrDecimalU256")] pub eth::U256); +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct TargetAmount(pub eth::U256); impl From for TargetAmount { fn from(value: eth::U256) -> Self { @@ -234,13 +228,11 @@ impl Available { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Partial { /// A partially order doesn't require the full amount to be traded. /// E.g. only 10% of the requested amount may be traded, if this leads /// to the most optimal solution. - #[serde(rename_all = "camelCase")] Yes { /// The available amount that can be used from the order. /// @@ -248,7 +240,6 @@ pub enum Partial { /// executed as well as the trader's balance. available: TargetAmount, }, - #[serde(rename_all = "camelCase")] No, } @@ -256,9 +247,8 @@ pub enum Partial { pub const UID_LEN: usize = 56; /// UID of an order. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)] -#[serde(transparent)] -pub struct Uid(#[serde(with = "bytes_hex")] pub Bytes<[u8; UID_LEN]>); +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Uid(pub Bytes<[u8; UID_LEN]>); impl Default for Uid { fn default() -> Self { @@ -273,8 +263,7 @@ impl PartialEq<[u8; UID_LEN]> for Uid { } // TODO These doc comments are incorrect for limit orders -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Side { /// Buy an exact amount. The sell amount can vary due to e.g. partial fills /// or slippage. @@ -302,9 +291,8 @@ pub const APP_DATA_LEN: usize = 32; /// This is a hash allowing arbitrary user data to be associated with an order. /// While this type holds the hash, the data itself is uploaded to IPFS. This /// hash is signed along with the order. -#[derive(Debug, Default, Clone, Copy, Serialize)] -#[serde(transparent)] -pub struct AppData(#[serde(with = "bytes_hex")] pub Bytes<[u8; APP_DATA_LEN]>); +#[derive(Debug, Default, Clone, Copy)] +pub struct AppData(pub Bytes<[u8; APP_DATA_LEN]>); impl From<[u8; APP_DATA_LEN]> for AppData { fn from(inner: [u8; APP_DATA_LEN]) -> Self { @@ -318,8 +306,7 @@ impl From for [u8; APP_DATA_LEN] { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Kind { /// Order intended to be immediately executed. This is the "regular" type of /// order. @@ -340,8 +327,7 @@ pub enum Kind { } /// [Balancer V2](https://docs.balancer.fi/) integration, used for settlement encoding. -#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] pub enum SellTokenBalance { Erc20, Internal, @@ -371,8 +357,7 @@ impl SellTokenBalance { } /// [Balancer V2](https://docs.balancer.fi/) integration, used for settlement encoding. -#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] pub enum BuyTokenBalance { Erc20, Internal, diff --git a/crates/driver/src/domain/competition/order/signature.rs b/crates/driver/src/domain/competition/order/signature.rs index 367c5d1d22..b77b8a3edd 100644 --- a/crates/driver/src/domain/competition/order/signature.rs +++ b/crates/driver/src/domain/competition/order/signature.rs @@ -1,15 +1,12 @@ use { crate::{domain::eth, util::Bytes}, model::signature::EcdsaSignature, - serde::Serialize, }; /// Signature over the order data. -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone)] pub struct Signature { pub scheme: Scheme, - #[serde(with = "bytes_hex")] pub data: Bytes>, /// The address used to sign and place this order. pub signer: eth::Address, @@ -37,8 +34,7 @@ impl Signature { /// The scheme used for signing the order. This is used by the solver and /// the protocol, the driver does not care about the details of signature /// verification. -#[derive(Debug, Clone, Copy, Serialize)] -#[serde(rename_all = "lowercase")] +#[derive(Debug, Clone, Copy)] pub enum Scheme { Eip712, EthSign, diff --git a/crates/driver/src/domain/eth/gas.rs b/crates/driver/src/domain/eth/gas.rs index 6541b7d6fb..f1fc3a4c21 100644 --- a/crates/driver/src/domain/eth/gas.rs +++ b/crates/driver/src/domain/eth/gas.rs @@ -2,9 +2,6 @@ use { super::{Ether, U256}, bigdecimal::Zero, derive_more::Display, - number::serialization::HexOrDecimalU256, - serde::Serialize, - serde_with::serde_as, std::{ops, ops::Add}, }; @@ -12,9 +9,8 @@ use { /// /// The amount of Ether that is paid in transaction fees is proportional to this /// amount as well as the transaction's [`EffectiveGasPrice`]. -#[serde_as] -#[derive(Debug, Default, Display, Clone, Copy, Ord, Eq, PartialOrd, PartialEq, Serialize)] -pub struct Gas(#[serde_as(as = "HexOrDecimalU256")] pub U256); +#[derive(Debug, Default, Display, Clone, Copy, Ord, Eq, PartialOrd, PartialEq)] +pub struct Gas(pub U256); impl From for Gas { fn from(value: U256) -> Self { @@ -55,7 +51,7 @@ impl Zero for Gas { /// An EIP-1559 gas price estimate. /// /// https://eips.ethereum.org/EIPS/eip-1559#specification -#[derive(Debug, Clone, Copy, Serialize)] +#[derive(Debug, Clone, Copy)] pub struct GasPrice { /// The maximum total fee that should be charged. max: FeePerGas, @@ -134,8 +130,7 @@ impl From for GasPrice { /// `{max,max_priority,base}_fee_per_gas` as defined by EIP-1559. /// /// https://eips.ethereum.org/EIPS/eip-1559#specification -#[derive(Debug, Clone, Copy, Ord, Eq, PartialEq, PartialOrd, Serialize)] -#[serde(transparent)] +#[derive(Debug, Clone, Copy, Ord, Eq, PartialEq, PartialOrd)] pub struct FeePerGas(pub Ether); impl FeePerGas { diff --git a/crates/driver/src/domain/eth/mod.rs b/crates/driver/src/domain/eth/mod.rs index b7d969c482..47808f5516 100644 --- a/crates/driver/src/domain/eth/mod.rs +++ b/crates/driver/src/domain/eth/mod.rs @@ -1,8 +1,6 @@ use { crate::util::Bytes, itertools::Itertools, - serde::Serialize, - serde_with::serde_as, std::{ collections::{HashMap, HashSet}, ops::{Div, Mul, Sub}, @@ -13,7 +11,6 @@ pub mod allowance; mod eip712; mod gas; -use number::serialization::HexOrDecimalU256; pub use { allowance::Allowance, eip712::{DomainFields, DomainSeparator}, @@ -121,8 +118,7 @@ impl From for StorageKey { } /// An address. Can be an EOA or a smart contract address. -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] -#[serde(transparent)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Address(pub H160); impl From for Address { @@ -140,8 +136,7 @@ impl From
for H160 { // TODO This type should probably use Ethereum::is_contract to verify during // construction that it does indeed point to a contract /// A smart contract address. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] -#[serde(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ContractAddress(pub H160); impl From for ContractAddress { @@ -165,8 +160,7 @@ impl From for Address { /// An ERC20 token address. /// /// https://eips.ethereum.org/EIPS/eip-20 -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] -#[serde(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TokenAddress(pub ContractAddress); impl TokenAddress { @@ -183,10 +177,8 @@ impl TokenAddress { /// An ERC20 token amount. /// /// https://eips.ethereum.org/EIPS/eip-20 -#[serde_as] -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] -#[serde(transparent)] -pub struct TokenAmount(#[serde_as(as = "HexOrDecimalU256")] pub U256); +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TokenAmount(pub U256); impl TokenAmount { /// Applies a factor to the token amount. @@ -340,18 +332,15 @@ impl From for ContractAddress { /// An asset on the Ethereum blockchain. Represents a particular amount of a /// particular token. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct Asset { pub amount: TokenAmount, pub token: TokenAddress, } /// An amount of native Ether tokens denominated in wei. -#[serde_as] -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)] -#[serde(transparent)] -pub struct Ether(#[serde_as(as = "HexOrDecimalU256")] pub U256); +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Ether(pub U256); impl From for Ether { fn from(value: U256) -> Self { @@ -414,12 +403,10 @@ impl From for BlockNo { } /// An onchain transaction which interacts with a smart contract. -#[derive(Debug, Clone, Eq, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Interaction { pub target: Address, pub value: Ether, - #[serde(with = "bytes_hex")] pub call_data: Bytes>, } diff --git a/crates/driver/src/domain/liquidity/mod.rs b/crates/driver/src/domain/liquidity/mod.rs index ad2b8c7966..254f39ebca 100644 --- a/crates/driver/src/domain/liquidity/mod.rs +++ b/crates/driver/src/domain/liquidity/mod.rs @@ -1,11 +1,7 @@ // TODO Remove dead_code #![allow(dead_code)] -use { - crate::domain::eth, - serde::{Serialize, Serializer}, - std::cmp::Ordering, -}; +use {crate::domain::eth, std::cmp::Ordering}; pub mod balancer; pub mod swapr; @@ -13,8 +9,7 @@ pub mod uniswap; pub mod zeroex; /// A source of liquidity which can be used by the solver. -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone)] pub struct Liquidity { pub id: Id, /// Estimation of gas needed to use this liquidity on-chain. @@ -22,8 +17,7 @@ pub struct Liquidity { pub kind: Kind, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] -#[serde(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Id(pub usize); impl From for Id { @@ -80,15 +74,6 @@ impl From<&Kind> for &'static str { } } -impl Serialize for Kind { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(self.into()) - } -} - /// An ordered token pair. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct TokenPair(eth::TokenAddress, eth::TokenAddress); diff --git a/crates/driver/src/domain/time.rs b/crates/driver/src/domain/time.rs index 5a1fe338f2..9bd7cc637b 100644 --- a/crates/driver/src/domain/time.rs +++ b/crates/driver/src/domain/time.rs @@ -4,7 +4,6 @@ use { solver::Timeouts, {self}, }, - serde::Serialize, thiserror::Error, }; @@ -13,8 +12,7 @@ use { /// driver deadline. /// The solvers are expected to return the solution to the driver before the /// solvers deadline. -#[derive(Copy, Clone, Debug, Default, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Copy, Clone, Debug, Default)] pub struct Deadline { driver: chrono::DateTime, solvers: chrono::DateTime, diff --git a/crates/driver/src/infra/api/mod.rs b/crates/driver/src/infra/api/mod.rs index 9eec96536b..556c9e4b1e 100644 --- a/crates/driver/src/infra/api/mod.rs +++ b/crates/driver/src/infra/api/mod.rs @@ -1,3 +1,4 @@ +pub use routes::Auction; use { crate::{ domain::{self, Mempools}, diff --git a/crates/driver/src/infra/api/routes/mod.rs b/crates/driver/src/infra/api/routes/mod.rs index ee1027b0e9..b22b4a61df 100644 --- a/crates/driver/src/infra/api/routes/mod.rs +++ b/crates/driver/src/infra/api/routes/mod.rs @@ -6,6 +6,7 @@ mod reveal; mod settle; mod solve; +pub use solve::Auction; pub(super) use { healthz::healthz, info::info, diff --git a/crates/driver/src/infra/api/routes/solve/dto/auction.rs b/crates/driver/src/infra/api/routes/solve/dto/auction.rs index ee134831c0..d0156e1cbd 100644 --- a/crates/driver/src/infra/api/routes/solve/dto/auction.rs +++ b/crates/driver/src/infra/api/routes/solve/dto/auction.rs @@ -8,7 +8,7 @@ use { infra::{solver::Timeouts, tokens, Ethereum}, util::serialize, }, - serde::Deserialize, + serde::{Deserialize, Serialize}, serde_with::serde_as, }; @@ -188,7 +188,7 @@ impl From for Error { } #[serde_as] -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct Auction { #[serde_as(as = "serde_with::DisplayFromStr")] @@ -205,7 +205,7 @@ impl Auction { } #[serde_as] -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Token { pub address: eth::H160, @@ -215,7 +215,7 @@ struct Token { } #[serde_as] -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Order { #[serde_as(as = "serialize::Hex")] @@ -249,7 +249,7 @@ struct Order { signature: Vec, } -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] enum Kind { Sell, @@ -257,7 +257,7 @@ enum Kind { } #[serde_as] -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Interaction { target: eth::H160, @@ -267,7 +267,7 @@ struct Interaction { call_data: Vec, } -#[derive(Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] enum SellTokenBalance { #[default] @@ -276,7 +276,7 @@ enum SellTokenBalance { External, } -#[derive(Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] enum BuyTokenBalance { #[default] @@ -284,7 +284,7 @@ enum BuyTokenBalance { Internal, } -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "lowercase", deny_unknown_fields)] enum SigningScheme { Eip712, @@ -293,7 +293,7 @@ enum SigningScheme { Eip1271, } -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] enum Class { Market, @@ -301,7 +301,7 @@ enum Class { Liquidity, } -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] enum FeePolicy { #[serde(rename_all = "camelCase")] @@ -317,7 +317,7 @@ enum FeePolicy { } #[serde_as] -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct Quote { #[serde_as(as = "serialize::U256")] diff --git a/crates/driver/src/infra/api/routes/solve/mod.rs b/crates/driver/src/infra/api/routes/solve/mod.rs index 07fc1916fa..49c968a002 100644 --- a/crates/driver/src/infra/api/routes/solve/mod.rs +++ b/crates/driver/src/infra/api/routes/solve/mod.rs @@ -1,6 +1,6 @@ mod dto; -pub use dto::AuctionError; +pub use dto::{Auction, AuctionError}; use { crate::infra::{ api::{Error, State}, @@ -23,17 +23,27 @@ async fn route( let handle_request = async { observe::auction(auction_id); let start = Instant::now(); - let auction = auction - .0 + let auction = auction.0; + let domain_auction = auction + .clone() .into_domain(state.eth(), state.tokens(), state.timeouts()) .await .tap_err(|err| { observe::invalid_dto(err, "auction"); })?; tracing::debug!(elapsed = ?start.elapsed(), "auction task execution time"); - let auction = state.pre_processor().prioritize(auction).await; + let domain_auction = state.pre_processor().prioritize(domain_auction).await; let competition = state.competition(); - let result = competition.solve(&auction).await; + let result = match competition.solve(&domain_auction).await { + Ok((result, liquidity)) => { + state + .solver() + .persistence() + .archive_auction(auction, liquidity); + Ok(result) + } + Err(e) => Err(e), + }; observe::solved(state.solver().name(), &result); Ok(axum::Json(dto::Solved::new(result?, &competition.solver))) }; diff --git a/crates/driver/src/infra/persistence/mod.rs b/crates/driver/src/infra/persistence/mod.rs index 1c90eb5d95..829fe9c726 100644 --- a/crates/driver/src/infra/persistence/mod.rs +++ b/crates/driver/src/infra/persistence/mod.rs @@ -1,11 +1,10 @@ use { crate::{ - domain::{ - competition::{auction::Id, Auction}, - liquidity, - }, - infra::solver::Config, + domain::liquidity::{self}, + infra::{api::Auction, solver::Config}, }, + number::serialization::HexOrDecimalU256, + primitive_types::U256, serde::{Deserialize, Serialize}, serde_with::serde_as, std::sync::Arc, @@ -15,9 +14,54 @@ use { #[serde_as] #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub struct AuctionWithLiquidity { - pub auction: Auction, - pub liquidity: Vec, +struct AuctionWithLiquidity { + auction: Auction, + liquidity: Vec, +} + +#[serde_as] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct Liquidity { + id: usize, + #[serde_as(as = "HexOrDecimalU256")] + gas: U256, + kind: Kind, +} + +impl From for Liquidity { + fn from(value: liquidity::Liquidity) -> Self { + Self { + id: value.id.into(), + gas: value.gas.into(), + kind: value.kind.into(), + } + } +} + +#[serde_as] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +enum Kind { + UniswapV2, + UniswapV3, + BalancerV2Stable, + BalancerV2Weighted, + Swapr, + ZeroEx, +} + +impl From for Kind { + fn from(value: liquidity::Kind) -> Self { + match value { + liquidity::Kind::UniswapV2(_) => Self::UniswapV2, + liquidity::Kind::UniswapV3(_) => Self::UniswapV3, + liquidity::Kind::BalancerV2Stable(_) => Self::BalancerV2Stable, + liquidity::Kind::BalancerV2Weighted(_) => Self::BalancerV2Weighted, + liquidity::Kind::Swapr(_) => Self::Swapr, + liquidity::Kind::ZeroEx(_) => Self::ZeroEx, + } + } } #[derive(Clone, Debug, Default, Deserialize)] @@ -60,18 +104,19 @@ impl Persistence { /// Saves the given auction with liquidity with fire and forget mentality /// (non-blocking operation) - pub fn archive_auction(&self, id: Id, auction: &Auction, liquidity: &[liquidity::Liquidity]) { + pub fn archive_auction(&self, auction: Auction, liquidity: Vec) { let Some(uploader) = self.s3.clone() else { return; }; + let auction_id = auction.id(); let auction_with_liquidity = AuctionWithLiquidity { - auction: auction.clone(), - liquidity: liquidity.to_vec(), + auction, + liquidity: liquidity.into_iter().map(Into::into).collect(), }; tokio::spawn( async move { match uploader - .upload(id.to_string(), auction_with_liquidity) + .upload(auction_id.to_string(), auction_with_liquidity) .await { Ok(key) => { diff --git a/crates/driver/src/infra/solver/mod.rs b/crates/driver/src/infra/solver/mod.rs index 8be63ea2c1..89d60ea096 100644 --- a/crates/driver/src/infra/solver/mod.rs +++ b/crates/driver/src/infra/solver/mod.rs @@ -144,6 +144,10 @@ impl Solver { }) } + pub fn persistence(&self) -> Persistence { + self.persistence.clone() + } + pub fn name(&self) -> &Name { &self.config.name } @@ -241,19 +245,6 @@ impl Solver { }; tokio::task::spawn(future.in_current_span()); } - - /// Store the auction with liquidity in a S3 bucket - pub fn store_auction_with_liquidity( - &self, - auction: &Auction, - liquidity: &[liquidity::Liquidity], - ) { - let Some(auction_id) = auction.id() else { - return; - }; - self.persistence - .archive_auction(auction_id, auction, liquidity); - } } /// Controls whether or not the driver is allowed to merge multiple solutions diff --git a/crates/driver/src/util/bytes.rs b/crates/driver/src/util/bytes.rs index 7452712627..1ff531c545 100644 --- a/crates/driver/src/util/bytes.rs +++ b/crates/driver/src/util/bytes.rs @@ -2,24 +2,6 @@ #[derive(Clone, Copy, PartialEq, Eq, Hash, Default)] pub struct Bytes(pub T); -impl AsRef<[u8]> for Bytes> { - fn as_ref(&self) -> &[u8] { - self.0.as_slice() - } -} - -impl AsRef<[u8]> for Bytes<[u8; 56]> { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsRef<[u8]> for Bytes<[u8; 32]> { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - impl std::fmt::Debug for Bytes where T: AsRef<[u8]>, diff --git a/crates/driver/src/util/time.rs b/crates/driver/src/util/time.rs index e17cac580f..e5e53d2fe5 100644 --- a/crates/driver/src/util/time.rs +++ b/crates/driver/src/util/time.rs @@ -1,10 +1,7 @@ -use serde::Serialize; - /// A Unix timestamp denominated in seconds since epoch. /// /// https://en.wikipedia.org/wiki/Unix_time -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)] -#[serde(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Timestamp(pub u32); impl From for Timestamp { From 70fd46d704623571833097272ffe393e98919fba Mon Sep 17 00:00:00 2001 From: Mateo Date: Mon, 29 Apr 2024 18:29:16 +0200 Subject: [PATCH 4/5] Rework comments --- crates/driver/src/domain/competition/mod.rs | 11 +-- crates/driver/src/domain/eth/gas.rs | 4 - crates/driver/src/infra/api/mod.rs | 1 - crates/driver/src/infra/api/routes/mod.rs | 1 - .../src/infra/api/routes/solve/dto/auction.rs | 24 +++--- .../driver/src/infra/api/routes/solve/mod.rs | 20 ++--- crates/driver/src/infra/persistence/mod.rs | 77 ++----------------- crates/driver/src/infra/solver/mod.rs | 1 + 8 files changed, 29 insertions(+), 110 deletions(-) diff --git a/crates/driver/src/domain/competition/mod.rs b/crates/driver/src/domain/competition/mod.rs index 9c38d31a3b..e3eb2282d3 100644 --- a/crates/driver/src/domain/competition/mod.rs +++ b/crates/driver/src/domain/competition/mod.rs @@ -5,7 +5,7 @@ use { Mempools, }, crate::{ - domain::{competition::solution::Settlement, eth, Liquidity}, + domain::{competition::solution::Settlement, eth}, infra::{ self, blockchain::Ethereum, @@ -55,10 +55,7 @@ pub struct Competition { impl Competition { /// Solve an auction as part of this competition. - pub async fn solve( - &self, - auction: &Auction, - ) -> Result<(Option, Vec), Error> { + pub async fn solve(&self, auction: &Auction) -> Result, Error> { let liquidity = match self.solver.liquidity() { solver::Liquidity::Fetch => { self.liquidity @@ -208,7 +205,7 @@ impl Competition { let settlement = match settlement { Some(settlement) => settlement, // Don't wait for the deadline because we can't produce a solution anyway. - None => return Ok((score, liquidity)), + None => return Ok(score), }; // Re-simulate the solution on every new block until the deadline ends to make @@ -240,7 +237,7 @@ impl Competition { let _ = tokio::time::timeout(remaining, simulate_on_new_blocks).await; } - Ok((score, liquidity)) + Ok(score) } pub async fn reveal(&self) -> Result { diff --git a/crates/driver/src/domain/eth/gas.rs b/crates/driver/src/domain/eth/gas.rs index f1fc3a4c21..3c53ed526c 100644 --- a/crates/driver/src/domain/eth/gas.rs +++ b/crates/driver/src/domain/eth/gas.rs @@ -71,10 +71,6 @@ impl GasPrice { .into() } - pub fn base(&self) -> FeePerGas { - self.base - } - pub fn max(&self) -> FeePerGas { self.max } diff --git a/crates/driver/src/infra/api/mod.rs b/crates/driver/src/infra/api/mod.rs index 556c9e4b1e..9eec96536b 100644 --- a/crates/driver/src/infra/api/mod.rs +++ b/crates/driver/src/infra/api/mod.rs @@ -1,4 +1,3 @@ -pub use routes::Auction; use { crate::{ domain::{self, Mempools}, diff --git a/crates/driver/src/infra/api/routes/mod.rs b/crates/driver/src/infra/api/routes/mod.rs index b22b4a61df..ee1027b0e9 100644 --- a/crates/driver/src/infra/api/routes/mod.rs +++ b/crates/driver/src/infra/api/routes/mod.rs @@ -6,7 +6,6 @@ mod reveal; mod settle; mod solve; -pub use solve::Auction; pub(super) use { healthz::healthz, info::info, diff --git a/crates/driver/src/infra/api/routes/solve/dto/auction.rs b/crates/driver/src/infra/api/routes/solve/dto/auction.rs index d0156e1cbd..ee134831c0 100644 --- a/crates/driver/src/infra/api/routes/solve/dto/auction.rs +++ b/crates/driver/src/infra/api/routes/solve/dto/auction.rs @@ -8,7 +8,7 @@ use { infra::{solver::Timeouts, tokens, Ethereum}, util::serialize, }, - serde::{Deserialize, Serialize}, + serde::Deserialize, serde_with::serde_as, }; @@ -188,7 +188,7 @@ impl From for Error { } #[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct Auction { #[serde_as(as = "serde_with::DisplayFromStr")] @@ -205,7 +205,7 @@ impl Auction { } #[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Token { pub address: eth::H160, @@ -215,7 +215,7 @@ struct Token { } #[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Order { #[serde_as(as = "serialize::Hex")] @@ -249,7 +249,7 @@ struct Order { signature: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] enum Kind { Sell, @@ -257,7 +257,7 @@ enum Kind { } #[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Interaction { target: eth::H160, @@ -267,7 +267,7 @@ struct Interaction { call_data: Vec, } -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Debug, Default, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] enum SellTokenBalance { #[default] @@ -276,7 +276,7 @@ enum SellTokenBalance { External, } -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Debug, Default, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] enum BuyTokenBalance { #[default] @@ -284,7 +284,7 @@ enum BuyTokenBalance { Internal, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "lowercase", deny_unknown_fields)] enum SigningScheme { Eip712, @@ -293,7 +293,7 @@ enum SigningScheme { Eip1271, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] enum Class { Market, @@ -301,7 +301,7 @@ enum Class { Liquidity, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] enum FeePolicy { #[serde(rename_all = "camelCase")] @@ -317,7 +317,7 @@ enum FeePolicy { } #[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct Quote { #[serde_as(as = "serialize::U256")] diff --git a/crates/driver/src/infra/api/routes/solve/mod.rs b/crates/driver/src/infra/api/routes/solve/mod.rs index 49c968a002..07fc1916fa 100644 --- a/crates/driver/src/infra/api/routes/solve/mod.rs +++ b/crates/driver/src/infra/api/routes/solve/mod.rs @@ -1,6 +1,6 @@ mod dto; -pub use dto::{Auction, AuctionError}; +pub use dto::AuctionError; use { crate::infra::{ api::{Error, State}, @@ -23,27 +23,17 @@ async fn route( let handle_request = async { observe::auction(auction_id); let start = Instant::now(); - let auction = auction.0; - let domain_auction = auction - .clone() + let auction = auction + .0 .into_domain(state.eth(), state.tokens(), state.timeouts()) .await .tap_err(|err| { observe::invalid_dto(err, "auction"); })?; tracing::debug!(elapsed = ?start.elapsed(), "auction task execution time"); - let domain_auction = state.pre_processor().prioritize(domain_auction).await; + let auction = state.pre_processor().prioritize(auction).await; let competition = state.competition(); - let result = match competition.solve(&domain_auction).await { - Ok((result, liquidity)) => { - state - .solver() - .persistence() - .archive_auction(auction, liquidity); - Ok(result) - } - Err(e) => Err(e), - }; + let result = competition.solve(&auction).await; observe::solved(state.solver().name(), &result); Ok(axum::Json(dto::Solved::new(result?, &competition.solver))) }; diff --git a/crates/driver/src/infra/persistence/mod.rs b/crates/driver/src/infra/persistence/mod.rs index 829fe9c726..a012f7e794 100644 --- a/crates/driver/src/infra/persistence/mod.rs +++ b/crates/driver/src/infra/persistence/mod.rs @@ -1,69 +1,10 @@ use { - crate::{ - domain::liquidity::{self}, - infra::{api::Auction, solver::Config}, - }, - number::serialization::HexOrDecimalU256, - primitive_types::U256, - serde::{Deserialize, Serialize}, - serde_with::serde_as, + crate::{domain::competition::auction::Id, infra::solver::Config}, + serde::Deserialize, std::sync::Arc, tracing::Instrument, }; -#[serde_as] -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct AuctionWithLiquidity { - auction: Auction, - liquidity: Vec, -} - -#[serde_as] -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct Liquidity { - id: usize, - #[serde_as(as = "HexOrDecimalU256")] - gas: U256, - kind: Kind, -} - -impl From for Liquidity { - fn from(value: liquidity::Liquidity) -> Self { - Self { - id: value.id.into(), - gas: value.gas.into(), - kind: value.kind.into(), - } - } -} - -#[serde_as] -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -enum Kind { - UniswapV2, - UniswapV3, - BalancerV2Stable, - BalancerV2Weighted, - Swapr, - ZeroEx, -} - -impl From for Kind { - fn from(value: liquidity::Kind) -> Self { - match value { - liquidity::Kind::UniswapV2(_) => Self::UniswapV2, - liquidity::Kind::UniswapV3(_) => Self::UniswapV3, - liquidity::Kind::BalancerV2Stable(_) => Self::BalancerV2Stable, - liquidity::Kind::BalancerV2Weighted(_) => Self::BalancerV2Weighted, - liquidity::Kind::Swapr(_) => Self::Swapr, - liquidity::Kind::ZeroEx(_) => Self::ZeroEx, - } - } -} - #[derive(Clone, Debug, Default, Deserialize)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] pub struct S3 { @@ -104,21 +45,17 @@ impl Persistence { /// Saves the given auction with liquidity with fire and forget mentality /// (non-blocking operation) - pub fn archive_auction(&self, auction: Auction, liquidity: Vec) { + pub fn archive_auction(&self, auction_id: Option, body: &str) { let Some(uploader) = self.s3.clone() else { return; }; - let auction_id = auction.id(); - let auction_with_liquidity = AuctionWithLiquidity { - auction, - liquidity: liquidity.into_iter().map(Into::into).collect(), + let Some(id) = auction_id else { + return; }; + let body = body.to_string(); tokio::spawn( async move { - match uploader - .upload(auction_id.to_string(), auction_with_liquidity) - .await - { + match uploader.upload(id.to_string(), body).await { Ok(key) => { tracing::info!(?key, "uploaded auction to s3"); } diff --git a/crates/driver/src/infra/solver/mod.rs b/crates/driver/src/infra/solver/mod.rs index 89d60ea096..f9e11b6302 100644 --- a/crates/driver/src/infra/solver/mod.rs +++ b/crates/driver/src/infra/solver/mod.rs @@ -202,6 +202,7 @@ impl Solver { self.config.fee_handler, )) .unwrap(); + self.persistence.archive_auction(auction.id(), &body); let url = shared::url::join(&self.config.endpoint, "solve"); super::observe::solver_request(&url, &body); let mut req = self From 5a7f087b358d89d6a8463983c8c03aeb26f99c24 Mon Sep 17 00:00:00 2001 From: Mateo Date: Tue, 30 Apr 2024 09:41:47 +0200 Subject: [PATCH 5/5] Rework comments --- crates/driver/src/infra/config/file/load.rs | 2 +- crates/driver/src/infra/config/file/mod.rs | 15 +++++++-- crates/driver/src/infra/persistence/mod.rs | 35 ++++++++++++--------- crates/driver/src/infra/solver/mod.rs | 8 +++-- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/crates/driver/src/infra/config/file/load.rs b/crates/driver/src/infra/config/file/load.rs index a370a94cb6..da8c52c5c1 100644 --- a/crates/driver/src/infra/config/file/load.rs +++ b/crates/driver/src/infra/config/file/load.rs @@ -89,7 +89,7 @@ pub async fn load(chain: eth::ChainId, path: &Path) -> infra::Config { true => SolutionMerging::Allowed, false => SolutionMerging::Forbidden, }, - s3: config.s3, + s3: config.s3.map(Into::into), } })) .await, diff --git a/crates/driver/src/infra/config/file/mod.rs b/crates/driver/src/infra/config/file/mod.rs index 66aed26f72..3c0ab7c113 100644 --- a/crates/driver/src/infra/config/file/mod.rs +++ b/crates/driver/src/infra/config/file/mod.rs @@ -1,6 +1,6 @@ pub use load::load; use { - crate::{domain::eth, infra::persistence::S3, util::serialize}, + crate::{domain::eth, util::serialize}, reqwest::Url, serde::{Deserialize, Serialize}, serde_with::serde_as, @@ -226,7 +226,7 @@ struct SolverConfig { #[serde(default)] merge_solutions: bool, - /// S3 configuration for storing the auction in the form they are sent to + /// S3 configuration for storing the auctions in the form they are sent to /// the solver engine #[serde(default)] s3: Option, @@ -240,6 +240,17 @@ pub enum FeeHandler { Solver, } +#[derive(Clone, Debug, Default, Deserialize)] +#[serde(rename_all = "kebab-case", deny_unknown_fields)] +pub struct S3 { + /// Name of the AWS S3 bucket in which the auctions will be stored + pub bucket: String, + + /// Prepended to the auction id to form the final instance filename on AWS + /// S3 bucket. Something like "staging/mainnet/" + pub prefix: String, +} + #[serde_as] #[derive(Debug, Deserialize)] #[serde(untagged)] diff --git a/crates/driver/src/infra/persistence/mod.rs b/crates/driver/src/infra/persistence/mod.rs index a012f7e794..a62f30c25c 100644 --- a/crates/driver/src/infra/persistence/mod.rs +++ b/crates/driver/src/infra/persistence/mod.rs @@ -1,23 +1,31 @@ use { - crate::{domain::competition::auction::Id, infra::solver::Config}, - serde::Deserialize, + crate::{ + domain::competition::auction::Id, + infra::{config::file, solver::Config}, + }, std::sync::Arc, tracing::Instrument, }; -#[derive(Clone, Debug, Default, Deserialize)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] +#[derive(Clone, Debug, Default)] pub struct S3 { - /// The s3_instance_upload_* arguments configure how auction instances - /// should be uploaded to AWS S3. - /// They must either all be set or all not set. + /// Name of the AWS S3 bucket in which the auctions will be stored pub bucket: String, - /// Prepended to the auction id to form the final instance filename on S3. - /// Something like "staging/mainnet/" + /// Prepended to the auction id to form the final instance filename on AWS + /// S3 bucket. Something like "staging/mainnet/" pub prefix: String, } +impl From for S3 { + fn from(value: file::S3) -> Self { + Self { + bucket: value.bucket, + prefix: value.prefix, + } + } +} + impl From for s3::Config { fn from(value: S3) -> Self { Self { @@ -45,19 +53,16 @@ impl Persistence { /// Saves the given auction with liquidity with fire and forget mentality /// (non-blocking operation) - pub fn archive_auction(&self, auction_id: Option, body: &str) { + pub fn archive_auction(&self, auction_id: Id, body: &str) { let Some(uploader) = self.s3.clone() else { return; }; - let Some(id) = auction_id else { - return; - }; let body = body.to_string(); tokio::spawn( async move { - match uploader.upload(id.to_string(), body).await { + match uploader.upload(auction_id.to_string(), body).await { Ok(key) => { - tracing::info!(?key, "uploaded auction to s3"); + tracing::debug!(?key, "uploaded auction with liquidity to s3"); } Err(err) => { tracing::warn!(?err, "failed to upload auction to s3"); diff --git a/crates/driver/src/infra/solver/mod.rs b/crates/driver/src/infra/solver/mod.rs index f9e11b6302..3d2e95be13 100644 --- a/crates/driver/src/infra/solver/mod.rs +++ b/crates/driver/src/infra/solver/mod.rs @@ -113,7 +113,7 @@ pub struct Config { /// TODO: Remove once all solvers are moved to use limit orders for quoting pub quote_using_limit_orders: bool, pub merge_solutions: SolutionMerging, - /// S3 configuration for storing the auction in the form they are sent to + /// S3 configuration for storing the auctions in the form they are sent to /// the solver engine pub s3: Option, } @@ -202,7 +202,11 @@ impl Solver { self.config.fee_handler, )) .unwrap(); - self.persistence.archive_auction(auction.id(), &body); + // Only auctions with IDs are real auctions (/quote requests don't have an ID, + // and it makes no sense to store them) + if let Some(id) = auction.id() { + self.persistence.archive_auction(id, &body); + }; let url = shared::url::join(&self.config.endpoint, "solve"); super::observe::solver_request(&url, &body); let mut req = self