From 57c8374ab23031f0e5b3b39fb101fa2d7759dc58 Mon Sep 17 00:00:00 2001 From: ilya Date: Tue, 9 Apr 2024 09:20:15 +0100 Subject: [PATCH] Use `services/solver-dto` (#11) --- Cargo.lock | 62 +++- Cargo.toml | 1 + src/api/routes/solve/dto/auction.rs | 452 ++++++++------------------- src/api/routes/solve/dto/mod.rs | 6 +- src/api/routes/solve/dto/solution.rs | 414 +++++++----------------- src/api/routes/solve/mod.rs | 6 +- src/domain/notification.rs | 31 +- 7 files changed, 301 insertions(+), 671 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3049b1f..711b49b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1260,7 +1260,7 @@ dependencies = [ "ethcontract", "hex", "mockall", - "predicates 3.0.4", + "predicates 3.1.0", "rlp", ] @@ -1588,7 +1588,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.1.0", + "indexmap 2.2.6", "slab", "tokio", "tokio-util 0.7.10", @@ -1942,9 +1942,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -2213,7 +2213,7 @@ dependencies = [ "lazy_static", "maplit", "num", - "number", + "number 0.1.0 (git+https://github.com/cowprotocol/services.git?tag=v2.237.0)", "primitive-types", "secp256k1", "serde", @@ -2394,6 +2394,19 @@ dependencies = [ "serde_with", ] +[[package]] +name = "number" +version = "0.1.0" +source = "git+https://github.com/cowprotocol/services.git?tag=v2.253.0#2e6fbac515927e88dc40b720f1bb135cf8425075" +dependencies = [ + "anyhow", + "bigdecimal", + "num", + "primitive-types", + "serde", + "serde_with", +] + [[package]] name = "object" version = "0.32.1" @@ -2645,14 +2658,13 @@ dependencies = [ [[package]] name = "predicates" -version = "3.0.4" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" dependencies = [ "anstyle", "difflib", "float-cmp", - "itertools 0.11.0", "normalize-line-endings", "predicates-core", "regex", @@ -3184,16 +3196,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.4.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" dependencies = [ "base64 0.21.5", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", + "serde_derive", "serde_json", "serde_with_macros", "time", @@ -3201,9 +3214,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.4.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" dependencies = [ "darling", "proc-macro2", @@ -3296,7 +3309,7 @@ dependencies = [ "mockall", "model", "num", - "number", + "number 0.1.0 (git+https://github.com/cowprotocol/services.git?tag=v2.237.0)", "observe", "primitive-types", "prometheus", @@ -3415,6 +3428,7 @@ dependencies = [ "serde_json", "serde_with", "shared", + "solvers-dto", "tempfile", "thiserror", "tokio", @@ -3425,6 +3439,20 @@ dependencies = [ "web3", ] +[[package]] +name = "solvers-dto" +version = "0.1.0" +source = "git+https://github.com/cowprotocol/services.git?tag=v2.253.0#2e6fbac515927e88dc40b720f1bb135cf8425075" +dependencies = [ + "bigdecimal", + "chrono", + "hex", + "number 0.1.0 (git+https://github.com/cowprotocol/services.git?tag=v2.253.0)", + "serde", + "serde_with", + "web3", +] + [[package]] name = "spin" version = "0.5.2" @@ -3498,7 +3526,7 @@ dependencies = [ "futures-util", "hashlink", "hex", - "indexmap 2.1.0", + "indexmap 2.2.6", "log", "memchr", "native-tls", @@ -3995,7 +4023,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", @@ -4008,7 +4036,7 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "toml_datetime", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 30e039a..4debb8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ contracts = { git = "https://github.com/cowprotocol/services.git", tag = "v2.237 ethrpc = { git = "https://github.com/cowprotocol/services.git", tag = "v2.237.0", package = "ethrpc" } observe = { git = "https://github.com/cowprotocol/services.git", tag = "v2.237.0", package = "observe" } shared = { git = "https://github.com/cowprotocol/services.git", tag = "v2.237.0", package = "shared" } +dto = { git = "https://github.com/cowprotocol/services.git", tag = "v2.253.0", package = "solvers-dto" } [dev-dependencies] glob = "0.3" diff --git a/src/api/routes/solve/dto/auction.rs b/src/api/routes/solve/dto/auction.rs index e761b87..8f4bbf9 100644 --- a/src/api/routes/solve/dto/auction.rs +++ b/src/api/routes/solve/dto/auction.rs @@ -2,213 +2,94 @@ use { crate::{ api::routes::Error, domain::{auction, eth, liquidity, order}, - util::{conv, serialize}, + util::conv, }, - bigdecimal::BigDecimal, - ethereum_types::{H160, H256, U256}, - itertools::Itertools as _, - serde::Deserialize, - serde_with::{serde_as, DisplayFromStr}, - std::collections::HashMap, + dto::auction::*, + itertools::Itertools, }; -impl Auction { - /// Converts a data transfer object into its domain object representation. - pub fn to_domain(&self) -> Result { - Ok(auction::Auction { - id: match self.id { - Some(id) => auction::Id::Solve(id), - None => auction::Id::Quote, - }, - tokens: auction::Tokens( - self.tokens - .iter() - .map(|(address, token)| { - ( - eth::TokenAddress(*address), - auction::Token { - decimals: token.decimals, - symbol: token.symbol.clone(), - reference_price: token - .reference_price - .map(eth::Ether) - .map(auction::Price), - available_balance: token.available_balance, - trusted: token.trusted, - }, - ) - }) - .collect(), - ), - orders: self - .orders +/// Converts a data transfer object into its domain object representation. +pub fn to_domain(auction: &Auction) -> Result { + Ok(auction::Auction { + id: match auction.id { + Some(id) => auction::Id::Solve(id), + None => auction::Id::Quote, + }, + tokens: auction::Tokens( + auction + .tokens .iter() - .map(|order| order::Order { - uid: order::Uid(order.uid), - sell: eth::Asset { - token: eth::TokenAddress(order.sell_token), - amount: order.sell_amount, - }, - buy: eth::Asset { - token: eth::TokenAddress(order.buy_token), - amount: order.buy_amount, - }, - side: match order.kind { - Kind::Buy => order::Side::Buy, - Kind::Sell => order::Side::Sell, - }, - class: match order.class { - Class::Market => order::Class::Market, - Class::Limit => order::Class::Limit, - Class::Liquidity => order::Class::Liquidity, - }, - fee: order::Fee(order.fee_amount), - partially_fillable: order.partially_fillable, + .map(|(address, token)| { + ( + eth::TokenAddress(*address), + auction::Token { + decimals: token.decimals, + symbol: token.symbol.clone(), + reference_price: token + .reference_price + .map(eth::Ether) + .map(auction::Price), + available_balance: token.available_balance, + trusted: token.trusted, + }, + ) }) .collect(), - liquidity: self - .liquidity - .iter() - .map(|liquidity| match liquidity { - Liquidity::ConstantProduct(liquidity) => liquidity.to_domain(), - Liquidity::WeightedProduct(liquidity) => liquidity.to_domain(), - Liquidity::Stable(liquidity) => liquidity.to_domain(), - Liquidity::ConcentratedLiquidity(liquidity) => liquidity.to_domain(), - Liquidity::LimitOrder(liquidity) => Ok(liquidity.to_domain()), - }) - .try_collect()?, - gas_price: auction::GasPrice(eth::Ether(self.effective_gas_price)), - deadline: auction::Deadline(self.deadline), - }) - } -} - -#[serde_as] -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct Auction { - #[serde_as(as = "Option")] - id: Option, - tokens: HashMap, - orders: Vec, - liquidity: Vec, - #[serde_as(as = "serialize::U256")] - effective_gas_price: U256, - deadline: chrono::DateTime, -} - -#[serde_as] -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct Order { - #[serde_as(as = "serialize::Hex")] - uid: [u8; 56], - sell_token: H160, - buy_token: H160, - #[serde_as(as = "serialize::U256")] - sell_amount: U256, - #[serde_as(as = "serialize::U256")] - buy_amount: U256, - #[serde_as(as = "serialize::U256")] - fee_amount: U256, - kind: Kind, - partially_fillable: bool, - class: Class, - fee_policies: Option>, -} - -#[serde_as] -#[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub 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 }, -} - -#[serde_as] -#[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Quote { - #[serde_as(as = "serialize::U256")] - pub sell_amount: eth::U256, - #[serde_as(as = "serialize::U256")] - pub buy_amount: eth::U256, - #[serde_as(as = "serialize::U256")] - pub fee: eth::U256, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -enum Kind { - Sell, - Buy, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -enum Class { - Market, - Limit, - Liquidity, -} - -#[serde_as] -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct Token { - decimals: Option, - symbol: Option, - #[serde_as(as = "Option")] - reference_price: Option, - #[serde_as(as = "serialize::U256")] - available_balance: U256, - trusted: bool, -} - -#[allow(clippy::enum_variant_names)] -#[derive(Debug, Deserialize)] -#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)] -enum Liquidity { - ConstantProduct(ConstantProductPool), - WeightedProduct(WeightedProductPool), - Stable(StablePool), - ConcentratedLiquidity(ConcentratedLiquidityPool), - LimitOrder(ForeignLimitOrder), -} - -#[serde_as] -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct ConstantProductPool { - id: String, - address: H160, - router: H160, - #[serde_as(as = "serialize::U256")] - gas_estimate: U256, - tokens: HashMap, - fee: BigDecimal, + ), + orders: auction + .orders + .iter() + .map(|order| order::Order { + uid: order::Uid(order.uid), + sell: eth::Asset { + token: eth::TokenAddress(order.sell_token), + amount: order.sell_amount, + }, + buy: eth::Asset { + token: eth::TokenAddress(order.buy_token), + amount: order.buy_amount, + }, + side: match order.kind { + Kind::Buy => order::Side::Buy, + Kind::Sell => order::Side::Sell, + }, + class: match order.class { + Class::Market => order::Class::Market, + Class::Limit => order::Class::Limit, + Class::Liquidity => order::Class::Liquidity, + }, + fee: order::Fee(order.fee_amount), + partially_fillable: order.partially_fillable, + }) + .collect(), + liquidity: auction + .liquidity + .iter() + .map(|liquidity| match liquidity { + Liquidity::ConstantProduct(liquidity) => { + constant_product_pool::to_domain(liquidity) + } + Liquidity::WeightedProduct(liquidity) => { + weighted_product_pool::to_domain(liquidity) + } + Liquidity::Stable(liquidity) => stable_pool::to_domain(liquidity), + Liquidity::ConcentratedLiquidity(liquidity) => { + concentrated_liquidity_pool::to_domain(liquidity) + } + Liquidity::LimitOrder(liquidity) => Ok(foreign_limit_order::to_domain(liquidity)), + }) + .try_collect()?, + gas_price: auction::GasPrice(eth::Ether(auction.effective_gas_price)), + deadline: auction::Deadline(auction.deadline), + }) } -#[serde_as] -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct ConstantProductReserve { - #[serde_as(as = "serialize::U256")] - balance: U256, -} +mod constant_product_pool { + use {super::*, itertools::Itertools}; -impl ConstantProductPool { - fn to_domain(&self) -> Result { + pub fn to_domain(pool: &ConstantProductPool) -> Result { let reserves = { - let (a, b) = self + let (a, b) = pool .tokens .iter() .map(|(token, reserve)| eth::Asset { @@ -222,52 +103,22 @@ impl ConstantProductPool { }; Ok(liquidity::Liquidity { - id: liquidity::Id(self.id.clone()), - address: self.address, - gas: eth::Gas(self.gas_estimate), + id: liquidity::Id(pool.id.clone()), + address: pool.address, + gas: eth::Gas(pool.gas_estimate), state: liquidity::State::ConstantProduct(liquidity::constant_product::Pool { reserves, - fee: conv::decimal_to_rational(&self.fee).ok_or("invalid constant product fee")?, + fee: conv::decimal_to_rational(&pool.fee).ok_or("invalid constant product fee")?, }), }) } } -#[serde_as] -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct WeightedProductPool { - id: String, - address: H160, - balancer_pool_id: H256, - #[serde_as(as = "serialize::U256")] - gas_estimate: U256, - tokens: HashMap, - fee: BigDecimal, - version: WeightedProductVersion, -} - -#[serde_as] -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct WeightedProductReserve { - #[serde_as(as = "serialize::U256")] - balance: U256, - scaling_factor: BigDecimal, - weight: BigDecimal, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -enum WeightedProductVersion { - V0, - V3Plus, -} - -impl WeightedProductPool { - fn to_domain(&self) -> Result { +mod weighted_product_pool { + use super::*; + pub fn to_domain(pool: &WeightedProductPool) -> Result { let reserves = { - let entries = self + let entries = pool .tokens .iter() .map(|(address, token)| { @@ -289,13 +140,13 @@ impl WeightedProductPool { }; Ok(liquidity::Liquidity { - id: liquidity::Id(self.id.clone()), - address: self.address, - gas: eth::Gas(self.gas_estimate), + id: liquidity::Id(pool.id.clone()), + address: pool.address, + gas: eth::Gas(pool.gas_estimate), state: liquidity::State::WeightedProduct(liquidity::weighted_product::Pool { reserves, - fee: conv::decimal_to_rational(&self.fee).ok_or("invalid weighted product fee")?, - version: match self.version { + fee: conv::decimal_to_rational(&pool.fee).ok_or("invalid weighted product fee")?, + version: match pool.version { WeightedProductVersion::V0 => liquidity::weighted_product::Version::V0, WeightedProductVersion::V3Plus => liquidity::weighted_product::Version::V3Plus, }, @@ -304,33 +155,12 @@ impl WeightedProductPool { } } -#[serde_as] -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct StablePool { - id: String, - address: H160, - balancer_pool_id: H256, - #[serde_as(as = "serialize::U256")] - gas_estimate: U256, - tokens: HashMap, - amplification_parameter: BigDecimal, - fee: BigDecimal, -} - -#[serde_as] -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct StableReserve { - #[serde_as(as = "serialize::U256")] - balance: U256, - scaling_factor: BigDecimal, -} +mod stable_pool { + use super::*; -impl StablePool { - fn to_domain(&self) -> Result { + pub fn to_domain(pool: &StablePool) -> Result { let reserves = { - let entries = self + let entries = pool .tokens .iter() .map(|(address, token)| { @@ -349,43 +179,25 @@ impl StablePool { }; Ok(liquidity::Liquidity { - id: liquidity::Id(self.id.clone()), - address: self.address, - gas: eth::Gas(self.gas_estimate), + id: liquidity::Id(pool.id.clone()), + address: pool.address, + gas: eth::Gas(pool.gas_estimate), state: liquidity::State::Stable(liquidity::stable::Pool { reserves, - amplification_parameter: conv::decimal_to_rational(&self.amplification_parameter) + amplification_parameter: conv::decimal_to_rational(&pool.amplification_parameter) .ok_or("invalid amplification parameter")?, - fee: conv::decimal_to_rational(&self.fee).ok_or("invalid stable pool fee")?, + fee: conv::decimal_to_rational(&pool.fee).ok_or("invalid stable pool fee")?, }), }) } } -#[serde_as] -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct ConcentratedLiquidityPool { - id: String, - address: H160, - router: H160, - #[serde_as(as = "serialize::U256")] - gas_estimate: U256, - tokens: Vec, - #[serde_as(as = "serialize::U256")] - sqrt_price: U256, - #[serde_as(as = "DisplayFromStr")] - liquidity: u128, - tick: i32, - #[serde_as(as = "HashMap")] - liquidity_net: HashMap, - fee: BigDecimal, -} +mod concentrated_liquidity_pool { + use {super::*, itertools::Itertools}; -impl ConcentratedLiquidityPool { - fn to_domain(&self) -> Result { + pub fn to_domain(pool: &ConcentratedLiquidityPool) -> Result { let tokens = { - let (a, b) = self + let (a, b) = pool .tokens .iter() .copied() @@ -397,15 +209,15 @@ impl ConcentratedLiquidityPool { }; Ok(liquidity::Liquidity { - id: liquidity::Id(self.id.clone()), - address: self.address, - gas: eth::Gas(self.gas_estimate), + id: liquidity::Id(pool.id.clone()), + address: pool.address, + gas: eth::Gas(pool.gas_estimate), state: liquidity::State::Concentrated(liquidity::concentrated::Pool { tokens, - sqrt_price: liquidity::concentrated::SqrtPrice(self.sqrt_price), - liquidity: liquidity::concentrated::Amount(self.liquidity), - tick: liquidity::concentrated::Tick(self.tick), - liquidity_net: self + sqrt_price: liquidity::concentrated::SqrtPrice(pool.sqrt_price), + liquidity: liquidity::concentrated::Amount(pool.liquidity), + tick: liquidity::concentrated::Tick(pool.tick), + liquidity_net: pool .liquidity_net .iter() .map(|(tick, liquidity)| { @@ -416,7 +228,7 @@ impl ConcentratedLiquidityPool { }) .collect(), fee: liquidity::concentrated::Fee( - conv::decimal_to_rational(&self.fee) + conv::decimal_to_rational(&pool.fee) .ok_or("invalid concentrated liquidity pool fee")?, ), }), @@ -424,42 +236,24 @@ impl ConcentratedLiquidityPool { } } -#[serde_as] -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -struct ForeignLimitOrder { - id: String, - address: H160, - #[serde_as(as = "serialize::U256")] - gas_estimate: U256, - #[serde_as(as = "serialize::Hex")] - hash: [u8; 32], - maker_token: H160, - taker_token: H160, - #[serde_as(as = "serialize::U256")] - maker_amount: U256, - #[serde_as(as = "serialize::U256")] - taker_amount: U256, - #[serde_as(as = "serialize::U256")] - taker_token_fee_amount: U256, -} +mod foreign_limit_order { + use super::*; -impl ForeignLimitOrder { - fn to_domain(&self) -> liquidity::Liquidity { + pub fn to_domain(order: &ForeignLimitOrder) -> liquidity::Liquidity { liquidity::Liquidity { - id: liquidity::Id(self.id.clone()), - address: self.address, - gas: eth::Gas(self.gas_estimate), + id: liquidity::Id(order.id.clone()), + address: order.address, + gas: eth::Gas(order.gas_estimate), state: liquidity::State::LimitOrder(liquidity::limit_order::LimitOrder { maker: eth::Asset { - token: eth::TokenAddress(self.maker_token), - amount: self.maker_amount, + token: eth::TokenAddress(order.maker_token), + amount: order.maker_amount, }, taker: eth::Asset { - token: eth::TokenAddress(self.taker_token), - amount: self.taker_amount, + token: eth::TokenAddress(order.taker_token), + amount: order.taker_amount, }, - fee: liquidity::limit_order::TakerAmount(self.taker_token_fee_amount), + fee: liquidity::limit_order::TakerAmount(order.taker_token_fee_amount), }), } } diff --git a/src/api/routes/solve/dto/mod.rs b/src/api/routes/solve/dto/mod.rs index 68e2866..67e611b 100644 --- a/src/api/routes/solve/dto/mod.rs +++ b/src/api/routes/solve/dto/mod.rs @@ -1,4 +1,4 @@ -mod auction; -mod solution; +pub mod auction; +pub mod solution; -pub use self::{auction::Auction, solution::Solutions}; +pub use dto::{auction::Auction, solution::Solutions}; diff --git a/src/api/routes/solve/dto/solution.rs b/src/api/routes/solve/dto/solution.rs index 46cb6b1..7c1bf2f 100644 --- a/src/api/routes/solve/dto/solution.rs +++ b/src/api/routes/solve/dto/solution.rs @@ -1,306 +1,120 @@ use { - crate::{ - domain::{order, solution}, - util::serialize, - }, - ethereum_types::{H160, U256}, - serde::Serialize, - serde_with::serde_as, - std::collections::HashMap, + crate::domain::{order, solution}, + dto::solution::*, }; -impl Solutions { - /// Creates a new solution DTO from its domain object. - pub fn from_domain(solutions: &[solution::Solution]) -> Self { - Self { - solutions: solutions - .iter() - .map(|solution| Solution { - id: solution.id.0, - prices: solution - .prices - .0 - .iter() - .map(|(token, price)| (token.0, *price)) - .collect(), - trades: solution - .trades - .iter() - .map(|trade| match trade { - solution::Trade::Fulfillment(trade) => { - Trade::Fulfillment(Fulfillment { - order: trade.order().uid.0, - executed_amount: trade.executed().amount, - fee: trade.surplus_fee().map(|fee| fee.amount), - }) - } - solution::Trade::Jit(trade) => { - let (signing_scheme, signature) = match &trade.order.signature { - order::Signature::Eip712(signature) => { - (SigningScheme::Eip712, signature.to_bytes().to_vec()) - } - order::Signature::EthSign(signature) => { - (SigningScheme::EthSign, signature.to_bytes().to_vec()) - } - order::Signature::Eip1271(bytes) => { - (SigningScheme::Eip1271, bytes.clone()) - } - order::Signature::PreSign => (SigningScheme::PreSign, vec![]), - }; - - Trade::Jit(JitTrade { - order: JitOrder { - sell_token: trade.order.sell.token.0, - sell_amount: trade.order.sell.amount, - buy_token: trade.order.buy.token.0, - buy_amount: trade.order.buy.amount, - receiver: trade.order.receiver, - valid_to: trade.order.valid_to, - app_data: trade.order.app_data.0, - fee_amount: trade.order.fee.0, - kind: match trade.order.side { - crate::domain::order::Side::Buy => Kind::Buy, - crate::domain::order::Side::Sell => Kind::Sell, - }, - partially_fillable: trade.order.partially_fillable, - sell_token_balance: SellTokenBalance::Erc20, - buy_token_balance: BuyTokenBalance::Erc20, - signing_scheme, - signature, +/// Creates a new solution DTO from its domain object. +pub fn from_domain(solutions: &[solution::Solution]) -> super::Solutions { + super::Solutions { + solutions: solutions + .iter() + .map(|solution| Solution { + id: solution.id.0, + prices: solution + .prices + .0 + .iter() + .map(|(token, price)| (token.0, *price)) + .collect(), + trades: solution + .trades + .iter() + .map(|trade| match trade { + solution::Trade::Fulfillment(trade) => Trade::Fulfillment(Fulfillment { + order: trade.order().uid.0, + executed_amount: trade.executed().amount, + fee: trade.surplus_fee().map(|fee| fee.amount), + }), + solution::Trade::Jit(trade) => { + let (signing_scheme, signature) = match &trade.order.signature { + order::Signature::Eip712(signature) => { + (SigningScheme::Eip712, signature.to_bytes().to_vec()) + } + order::Signature::EthSign(signature) => { + (SigningScheme::EthSign, signature.to_bytes().to_vec()) + } + order::Signature::Eip1271(bytes) => { + (SigningScheme::Eip1271, bytes.clone()) + } + order::Signature::PreSign => (SigningScheme::PreSign, vec![]), + }; + + Trade::Jit(JitTrade { + order: JitOrder { + sell_token: trade.order.sell.token.0, + sell_amount: trade.order.sell.amount, + buy_token: trade.order.buy.token.0, + buy_amount: trade.order.buy.amount, + receiver: trade.order.receiver, + valid_to: trade.order.valid_to, + app_data: trade.order.app_data.0, + fee_amount: trade.order.fee.0, + kind: match trade.order.side { + crate::domain::order::Side::Buy => Kind::Buy, + crate::domain::order::Side::Sell => Kind::Sell, }, - executed_amount: trade.executed, - }) - } - }) - .collect(), - interactions: solution - .interactions - .iter() - .map(|interaction| match interaction { - solution::Interaction::Liquidity(interaction) => { - Interaction::Liquidity(LiquidityInteraction { - id: interaction.liquidity.id.0.clone(), - input_token: interaction.input.token.0, - input_amount: interaction.input.amount, - output_token: interaction.output.token.0, - output_amount: interaction.output.amount, - internalize: interaction.internalize, - }) - } - solution::Interaction::Custom(interaction) => { - Interaction::Custom(CustomInteraction { - target: interaction.target, - value: interaction.value.0, - calldata: interaction.calldata.clone(), - internalize: interaction.internalize, - allowances: interaction - .allowances - .iter() - .map(|allowance| Allowance { - token: allowance.asset.token.0, - amount: allowance.asset.amount, - spender: allowance.spender, - }) - .collect(), - inputs: interaction - .inputs - .iter() - .map(|i| Asset { - token: i.token.0, - amount: i.amount, - }) - .collect(), - outputs: interaction - .outputs - .iter() - .map(|o| Asset { - token: o.token.0, - amount: o.amount, - }) - .collect(), - }) - } - }) - .collect(), - gas: solution.gas.map(|gas| gas.0.as_u64()), - }) - .collect(), - } + partially_fillable: trade.order.partially_fillable, + sell_token_balance: SellTokenBalance::Erc20, + buy_token_balance: BuyTokenBalance::Erc20, + signing_scheme, + signature, + }, + executed_amount: trade.executed, + }) + } + }) + .collect(), + interactions: solution + .interactions + .iter() + .map(|interaction| match interaction { + solution::Interaction::Liquidity(interaction) => { + Interaction::Liquidity(LiquidityInteraction { + id: interaction.liquidity.id.0.clone(), + input_token: interaction.input.token.0, + input_amount: interaction.input.amount, + output_token: interaction.output.token.0, + output_amount: interaction.output.amount, + internalize: interaction.internalize, + }) + } + solution::Interaction::Custom(interaction) => { + Interaction::Custom(CustomInteraction { + target: interaction.target, + value: interaction.value.0, + calldata: interaction.calldata.clone(), + internalize: interaction.internalize, + allowances: interaction + .allowances + .iter() + .map(|allowance| Allowance { + token: allowance.asset.token.0, + amount: allowance.asset.amount, + spender: allowance.spender, + }) + .collect(), + inputs: interaction + .inputs + .iter() + .map(|i| Asset { + token: i.token.0, + amount: i.amount, + }) + .collect(), + outputs: interaction + .outputs + .iter() + .map(|o| Asset { + token: o.token.0, + amount: o.amount, + }) + .collect(), + }) + } + }) + .collect(), + gas: solution.gas.map(|gas| gas.0.as_u64()), + }) + .collect(), } } - -#[derive(Debug, Serialize, Default)] -#[serde(rename_all = "camelCase")] -pub struct Solutions { - solutions: Vec, -} - -#[serde_as] -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct Solution { - id: u64, - #[serde_as(as = "HashMap<_, serialize::U256>")] - prices: HashMap, - trades: Vec, - interactions: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - gas: Option, -} - -#[derive(Debug, Serialize)] -#[serde(tag = "kind", rename_all = "camelCase")] -enum Trade { - Fulfillment(Fulfillment), - Jit(JitTrade), -} - -#[serde_as] -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct Fulfillment { - #[serde_as(as = "serialize::Hex")] - order: [u8; 56], - #[serde_as(as = "serialize::U256")] - executed_amount: U256, - #[serde(skip_serializing_if = "Option::is_none")] - #[serde_as(as = "Option")] - fee: Option, -} - -#[serde_as] -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct JitTrade { - order: JitOrder, - #[serde_as(as = "serialize::U256")] - executed_amount: U256, -} - -#[serde_as] -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct JitOrder { - sell_token: H160, - buy_token: H160, - receiver: H160, - #[serde_as(as = "serialize::U256")] - sell_amount: U256, - #[serde_as(as = "serialize::U256")] - buy_amount: U256, - valid_to: u32, - #[serde_as(as = "serialize::Hex")] - app_data: [u8; 32], - #[serde_as(as = "serialize::U256")] - fee_amount: U256, - kind: Kind, - partially_fillable: bool, - sell_token_balance: SellTokenBalance, - buy_token_balance: BuyTokenBalance, - signing_scheme: SigningScheme, - #[serde_as(as = "serialize::Hex")] - signature: Vec, -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -enum Kind { - Sell, - Buy, -} - -#[derive(Debug, Serialize)] -#[serde(tag = "kind", rename_all = "camelCase")] -enum Interaction { - Liquidity(LiquidityInteraction), - Custom(CustomInteraction), -} - -#[serde_as] -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct LiquidityInteraction { - internalize: bool, - id: String, - input_token: H160, - output_token: H160, - #[serde_as(as = "serialize::U256")] - input_amount: U256, - #[serde_as(as = "serialize::U256")] - output_amount: U256, -} - -#[serde_as] -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct CustomInteraction { - internalize: bool, - target: H160, - #[serde_as(as = "serialize::U256")] - value: U256, - #[serde(rename = "callData")] - #[serde_as(as = "serialize::Hex")] - calldata: Vec, - allowances: Vec, - inputs: Vec, - outputs: Vec, -} - -/// An interaction that can be executed as part of an order's pre- or -/// post-interactions. -#[serde_as] -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct OrderInteraction { - target: H160, - #[serde_as(as = "serialize::U256")] - value: U256, - #[serde(rename = "callData")] - #[serde_as(as = "serialize::Hex")] - calldata: Vec, -} - -#[serde_as] -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct Asset { - token: H160, - #[serde_as(as = "serialize::U256")] - amount: U256, -} - -#[serde_as] -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct Allowance { - token: H160, - spender: H160, - #[serde_as(as = "serialize::U256")] - amount: U256, -} - -#[derive(Debug, Default, Serialize)] -#[serde(rename_all = "camelCase")] -enum SellTokenBalance { - #[default] - Erc20, - Internal, - External, -} - -#[derive(Debug, Default, Serialize)] -#[serde(rename_all = "camelCase")] -enum BuyTokenBalance { - #[default] - Erc20, - Internal, -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -enum SigningScheme { - Eip712, - EthSign, - PreSign, - Eip1271, -} diff --git a/src/api/routes/solve/mod.rs b/src/api/routes/solve/mod.rs index 786800e..3fd9d88 100644 --- a/src/api/routes/solve/mod.rs +++ b/src/api/routes/solve/mod.rs @@ -12,7 +12,7 @@ pub async fn solve( axum::response::Json>, ) { let handle_request = async { - let auction = match auction.to_domain() { + let auction = match dto::auction::to_domain(&auction) { Ok(value) => value, Err(err) => { tracing::warn!(?err, "invalid auction"); @@ -23,8 +23,6 @@ pub async fn solve( } }; - tracing::trace!(?auction); - let auction_id = auction.id; let solutions = state .solve(auction) @@ -33,7 +31,7 @@ pub async fn solve( tracing::trace!(?auction_id, ?solutions); - let solutions = dto::Solutions::from_domain(&solutions); + let solutions = dto::solution::from_domain(&solutions); ( axum::http::StatusCode::OK, axum::response::Json(Response::Ok(solutions)), diff --git a/src/domain/notification.rs b/src/domain/notification.rs index d8fbc14..a04c554 100644 --- a/src/domain/notification.rs +++ b/src/domain/notification.rs @@ -2,7 +2,7 @@ use { super::{ auction, eth::{self, Ether, TokenAddress}, - solution, + solution::{self}, }, std::collections::BTreeSet, }; @@ -19,10 +19,16 @@ pub type SimulationSucceededAtLeastOnce = bool; #[derive(Debug)] pub struct Notification { pub auction_id: auction::Id, - pub solution_id: Option, + pub solution_id: Option, pub kind: Kind, } +#[derive(Debug, Clone)] +pub enum Id { + Single(solution::Id), + Merged(Vec), +} + /// All types of notifications solvers can be informed about. #[derive(Debug)] pub enum Kind { @@ -30,9 +36,11 @@ pub enum Kind { EmptySolution, DuplicatedSolutionId, SimulationFailed(BlockNo, Transaction, SimulationSucceededAtLeastOnce), + ScoringFailed(ScoreKind), NonBufferableTokensUsed(TokensUsed), SolverAccountInsufficientBalance(RequiredEther), Settled(Settlement), + DriverError(String), PostprocessingTimedOut, } @@ -45,20 +53,7 @@ pub enum Settlement { Fail, } -#[derive(Debug, Copy, Clone)] -pub struct Quality(pub eth::U256); - -impl From for Quality { - fn from(value: eth::U256) -> Self { - Self(value) - } -} - -#[derive(Debug, Copy, Clone)] -pub struct GasCost(pub eth::U256); - -impl From for GasCost { - fn from(value: eth::U256) -> Self { - Self(value) - } +#[derive(Debug)] +pub enum ScoreKind { + InvalidClearingPrices, }