From 5b156c55a2564bdc4a6f71e3a3b5c1a8221b5938 Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 26 Apr 2024 18:17:11 +0100 Subject: [PATCH 01/19] Utoipa for the solvers crate --- Cargo.lock | 113 +++ crates/openapi-generator/Cargo.toml | 11 + crates/openapi-generator/build.rs | 13 + crates/openapi-generator/src/lib.rs | 0 crates/solvers-dto/Cargo.toml | 2 + crates/solvers-dto/src/auction.rs | 749 ++++++++++++++++- crates/solvers-dto/src/notification.rs | 69 ++ crates/solvers-dto/src/solution.rs | 378 ++++++++- crates/solvers/Cargo.toml | 3 + crates/solvers/openapi_gen.yml | 779 ++++++++++++++++++ crates/solvers/src/api/mod.rs | 68 ++ crates/solvers/src/api/routes/mod.rs | 4 +- crates/solvers/src/api/routes/notify/mod.rs | 9 + .../src/api/routes/solve/dto/auction.rs | 145 +++- .../src/api/routes/solve/dto/solution.rs | 24 +- crates/solvers/src/api/routes/solve/mod.rs | 12 + crates/solvers/src/lib.rs | 2 + 17 files changed, 2282 insertions(+), 99 deletions(-) create mode 100644 crates/openapi-generator/Cargo.toml create mode 100644 crates/openapi-generator/build.rs create mode 100644 crates/openapi-generator/src/lib.rs create mode 100644 crates/solvers/openapi_gen.yml diff --git a/Cargo.lock b/Cargo.lock index 41e3b3fac9..f9d19062e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3111,6 +3111,13 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openapi-generator" +version = "0.1.0" +dependencies = [ + "solvers", +] + [[package]] name = "openssl" version = "0.10.64" @@ -3431,6 +3438,30 @@ dependencies = [ "toml_edit 0.19.14", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.81" @@ -4049,6 +4080,19 @@ dependencies = [ "syn 2.0.60", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.2.5", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha1" version = "0.10.5" @@ -4274,6 +4318,7 @@ dependencies = [ "serde", "serde_json", "serde_with", + "serde_yaml", "shared", "solver", "solvers-dto", @@ -4284,6 +4329,8 @@ dependencies = [ "tower", "tower-http", "tracing", + "utoipa", + "utoipauto", "web3", ] @@ -4296,7 +4343,9 @@ dependencies = [ "hex", "number", "serde", + "serde_json", "serde_with", + "utoipa", "web3", ] @@ -5090,6 +5139,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.7.1" @@ -5126,6 +5181,64 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "utoipa" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "272ebdfbc99111033031d2f10e018836056e4d2c8e2acda76450ec7974269fa7" +dependencies = [ + "indexmap 2.2.5", + "serde", + "serde_json", + "serde_yaml", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3c9f4d08338c1bfa70dde39412a040a884c6f318b3d09aaaf3437a1e52027fc" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "syn 2.0.60", +] + +[[package]] +name = "utoipauto" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8b0206d46c52c964fbbecbf0f4015941000ce1122a3cc6103a64496bc9997e" +dependencies = [ + "utoipauto-macro", +] + +[[package]] +name = "utoipauto-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f188b14783527b8694a62ac420f945049a8ad02ae403c59cd6d6d46ce70e7d00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "utoipauto-macro" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8441b6e9e8e22128550e718d08b9da55182e85920e3d307894300cb9fcd4791b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", + "utoipauto-core", +] + [[package]] name = "uuid" version = "1.4.1" diff --git a/crates/openapi-generator/Cargo.toml b/crates/openapi-generator/Cargo.toml new file mode 100644 index 0000000000..eb70a0fdcd --- /dev/null +++ b/crates/openapi-generator/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "openapi-generator" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[build-dependencies] +solvers = { path = "../solvers" } diff --git a/crates/openapi-generator/build.rs b/crates/openapi-generator/build.rs new file mode 100644 index 0000000000..30da62499e --- /dev/null +++ b/crates/openapi-generator/build.rs @@ -0,0 +1,13 @@ +// Trick to generate the OpenAPI spec on build time. +// See: https://github.com/juhaku/utoipa/issues/214#issuecomment-1179589373 + +use std::fs; + +const SOLVERS_OPENAPI_PATH: &str = "../solvers/openapi_gen.yml"; + +fn main() { + let openapi_yaml = + solvers::generate_openapi_yaml().expect("Error generating the OpenAPI documentation"); + fs::write(SOLVERS_OPENAPI_PATH, openapi_yaml) + .expect("Error writing the solvers OpenAPI documentation"); +} diff --git a/crates/openapi-generator/src/lib.rs b/crates/openapi-generator/src/lib.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/solvers-dto/Cargo.toml b/crates/solvers-dto/Cargo.toml index 931f536290..fcbb54f869 100644 --- a/crates/solvers-dto/Cargo.toml +++ b/crates/solvers-dto/Cargo.toml @@ -11,5 +11,7 @@ chrono = { workspace = true } hex = { workspace = true } number = { path = "../number"} serde = { workspace = true } +serde_json = { workspace = true } serde_with = { workspace = true } +utoipa = { version = "4.2.0" } web3 = { workspace = true } diff --git a/crates/solvers-dto/src/auction.rs b/crates/solvers-dto/src/auction.rs index dcd9b47e38..2630030e37 100644 --- a/crates/solvers-dto/src/auction.rs +++ b/crates/solvers-dto/src/auction.rs @@ -3,66 +3,120 @@ use { bigdecimal::BigDecimal, number::serialization::HexOrDecimalU256, serde::Deserialize, + serde_json::{Number, Value}, serde_with::{serde_as, DisplayFromStr}, std::collections::HashMap, + utoipa::{ + openapi::{ + AllOfBuilder, + ArrayBuilder, + ObjectBuilder, + OneOfBuilder, + Ref, + RefOr, + Schema, + SchemaType::{self}, + }, + ToSchema, + }, web3::types::{H160, H256, U256}, }; +/// The abstract auction to be solved by the searcher. #[serde_as] -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct Auction { + /// An opaque identifier for the auction. Will be set to `null` for requests + /// that are not part of an auction (when quoting token prices for example). #[serde_as(as = "Option")] + #[schema(value_type = String)] pub id: Option, - pub tokens: HashMap, + /// A map of token addresses to token information. + pub tokens: HashMap, + /// The solvable orders included in the auction. pub orders: Vec, + /// On-chain liquidity that can be used by the solution. pub liquidity: Vec, + /// The current estimated gas price that will be paid when executing a + /// settlement. Additionally, this is the gas price that is multiplied with + /// a settlement's gas estimate for solution scoring. #[serde_as(as = "HexOrDecimalU256")] + #[schema(value_type = TokenAmount)] pub effective_gas_price: U256, + /// The deadline by which a solution to the auction is required. Requests + /// that go beyond this deadline are expected to be cancelled by the caller. + #[schema(value_type = DateTime)] pub deadline: chrono::DateTime, } +/// CoW Protocol order information relevant to execution. #[serde_as] -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct Order { + #[schema(value_type = OrderUid)] #[serde_as(as = "serialize::Hex")] pub uid: [u8; 56], + #[schema(value_type = Token)] pub sell_token: H160, + #[schema(value_type = Token)] pub buy_token: H160, + #[schema(value_type = TokenAmount)] #[serde_as(as = "HexOrDecimalU256")] pub sell_amount: U256, + #[schema(value_type = TokenAmount)] #[serde_as(as = "HexOrDecimalU256")] pub buy_amount: U256, - pub kind: Kind, + pub kind: OrderKind, + /// Whether or not this order can be partially filled. If this is false, + /// then the order is a "fill-or-kill" order, meaning it needs to be + /// completely filled or not at all. pub partially_fillable: bool, - pub class: Class, + pub class: OrderClass, + /// Any protocol fee policies that apply to the order. pub fee_policies: Option>, } -#[derive(Debug, Deserialize)] +/// How the CoW Protocol order was classified. +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub enum Kind { +pub enum OrderKind { Sell, Buy, } -#[derive(Debug, Deserialize)] +/// How the CoW Protocol order was classified. +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub enum Class { +pub enum OrderClass { Market, Limit, Liquidity, } +/// A fee policy that applies to an order. #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub enum FeePolicy { + /// If the order receives more than limit price, pay the protocol a factor + /// of the difference. #[serde(rename_all = "camelCase")] - Surplus { factor: f64, max_volume_factor: f64 }, + Surplus { + /// The factor of the user surplus that the protocol will request from + /// the solver after settling the order + factor: f64, + /// Never charge more than that percentage of the order volume. + max_volume_factor: f64, + }, + /// A cut from the price improvement over the best quote is taken as a + /// protocol fee. #[serde(rename_all = "camelCase")] PriceImprovement { + /// The factor of the user surplus that the protocol will request from + /// the solver after settling the order. factor: f64, + /// Never charge more than that percentage of the order volume. max_volume_factor: f64, quote: Quote, }, @@ -70,35 +124,211 @@ pub enum FeePolicy { Volume { factor: f64 }, } +impl ToSchema<'static> for FeePolicy { + fn schema() -> (&'static str, RefOr) { + ( + "FeePolicy", + Schema::OneOf( + OneOfBuilder::new() + .description(Some("A fee policy that applies to an order")) + .item(Ref::from_schema_name("SurplusFee")) + .item(Ref::from_schema_name("PriceImprovement")) + .item(Ref::from_schema_name("VolumeFee")) + .build(), + ) + .into(), + ) + } +} + #[serde_as] -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct Quote { #[serde_as(as = "HexOrDecimalU256")] + #[schema(value_type = TokenAmount)] pub sell_amount: U256, #[serde_as(as = "HexOrDecimalU256")] + #[schema(value_type = TokenAmount)] pub buy_amount: U256, #[serde_as(as = "HexOrDecimalU256")] + #[schema(value_type = TokenAmount)] pub fee: U256, } +/// Information about a token relevant to the auction. #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct Token { +pub struct TokenInfo { + /// The ERC20.decimals value for this token. This may be missing for ERC20 + /// tokens that don't implement the optional metadata extension. pub decimals: Option, + /// The ERC20.symbol value for this token. This may be missing for ERC20 + /// tokens that don't implement the optional metadata extension. pub symbol: Option, + /// The reference price of this token for the auction used for scoring. This + /// price is only included for tokens for which there are CoW Protocol + /// orders. #[serde_as(as = "Option")] pub reference_price: Option, + /// The balance held by the Settlement contract that is available during a + /// settlement. #[serde_as(as = "HexOrDecimalU256")] pub available_balance: U256, + /// A flag which indicates that solvers are allowed to perform gas cost + /// optimizations for this token by not routing the trades via an AMM, and + /// instead use its available balances, as specified by CIP-2. pub trusted: bool, } +impl ToSchema<'static> for TokenInfo { + fn schema() -> (&'static str, RefOr) { + ( + "TokenInfo", + Schema::Object( + ObjectBuilder::new() + .description(Some("Information about an ERC20 token.")) + .required("availableBalance") + .required("trusted") + .property( + "decimals", + ObjectBuilder::new() + .schema_type(SchemaType::Integer) + .description(Some( + "The ERC20.decimals value for this token. This may be missing for \ + ERC20 tokens that don't implement the optional metadata \ + extension.", + )), + ) + .property( + "symbol", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .description(Some( + "The ERC20.symbol value for this token. This may be missing for \ + ERC20 tokens that don't implement the optional metadata \ + extension.", + )), + ) + .property( + "referencePrice", + AllOfBuilder::new() + .description(Some( + "The reference price of this token for the auction used for \ + scoring. This price is only included for tokens for which there \ + are CoW Protocol orders.", + )) + .item(Ref::from_schema_name("NativePrice")), + ) + .property( + "availableBalance", + AllOfBuilder::new() + .description(Some( + "The balance held by the Settlement contract that is available \ + during a settlement.", + )) + .item(Ref::from_schema_name("TokenAmount")), + ) + .property( + "trusted", + ObjectBuilder::new() + .schema_type(SchemaType::Boolean) + .description(Some( + "A flag which indicates that solvers are allowed to perform gas \ + cost optimizations for this token by not routing the trades via \ + an AMM, and instead use its available balances, as specified by \ + CIP-2.", + )), + ) + .build(), + ) + .into(), + ) + } +} + +/// On-chain liquidity that can be used in a solution. This liquidity is +/// provided to facilitate onboarding new solvers. Additional liquidity that is +/// not included in this set may still be used in solutions. +#[allow(clippy::enum_variant_names)] +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct Liquidity { + /// An opaque ID used for uniquely identifying the liquidity within a single + /// auction (note that they are **not** guaranteed to be unique across + /// auctions). This ID is used in the solution for matching interactions + /// with the executed liquidity. + pub id: String, + /// The Ethereum public address of the liquidity. The actual address that is + /// specified is dependent on the kind of liquidity. + pub address: H160, + /// A rough approximation of gas units required to use this liquidity + /// on-chain. + pub gas_estimate: U256, + pub parameter: LiquidityParameters, +} + +impl ToSchema<'static> for Liquidity { + fn schema() -> (&'static str, RefOr) { + ( + "Liquidity", + Schema::AllOf( + AllOfBuilder::new() + .description(Some( + "On-chain liquidity that can be used in a solution. This liquidity is \ + provided to facilitate onboarding new solvers. Additional liquidity that \ + is not included in this set may still be used in solutions.", + )) + .item(Ref::from_schema_name("LiquidityParameters")) + .item(Schema::Object( + ObjectBuilder::new() + .property( + "id", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .description(Some( + "An opaque ID used for uniquely identifying the liquidity \ + within a single auction (note that they are **not** \ + guaranteed to be unique across auctions). This ID is \ + used in the solution for matching interactions with the \ + executed liquidity.", + )), + ) + .property( + "address", + AllOfBuilder::new() + .description(Some( + "A rough approximation of gas units required to use this \ + liquidity on-chain.", + )) + .item(Ref::from_schema_name("Address")), + ) + .property( + "gasEstimate", + AllOfBuilder::new() + .description(Some( + "A rough approximation of gas units required to use this \ + liquidity on-chain.", + )) + .item(Ref::from_schema_name("BigInt")), + ) + .required("id") + .required("address") + .required("gasEstimate") + .build(), + )) + .build(), + ) + .into(), + ) + } +} + #[allow(clippy::enum_variant_names)] #[derive(Debug, Deserialize)] #[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)] -pub enum Liquidity { +pub enum LiquidityParameters { ConstantProduct(ConstantProductPool), WeightedProduct(WeightedProductPool), Stable(StablePool), @@ -106,41 +336,145 @@ pub enum Liquidity { LimitOrder(ForeignLimitOrder), } +impl ToSchema<'static> for LiquidityParameters { + fn schema() -> (&'static str, RefOr) { + ( + "LiquidityParameters", + Schema::OneOf( + OneOfBuilder::new() + .item(Ref::from_schema_name("ConstantProductPool")) + .item(Ref::from_schema_name("WeightedProductPool")) + .item(Ref::from_schema_name("StablePool")) + .item(Ref::from_schema_name("ConcentratedLiquidityPool")) + .item(Ref::from_schema_name("ForeignLimitOrder")) + .build(), + ) + .into(), + ) + } +} + +/// A UniswapV2-like constant product liquidity pool for a token pair. #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ConstantProductPool { - pub id: String, - pub address: H160, pub router: H160, - #[serde_as(as = "HexOrDecimalU256")] - pub gas_estimate: U256, pub tokens: HashMap, pub fee: BigDecimal, } +impl ToSchema<'static> for ConstantProductPool { + fn schema() -> (&'static str, RefOr) { + ( + "ConstantProductPool", + Schema::Object( + ObjectBuilder::new() + .description(Some( + "A UniswapV2-like constant product liquidity pool for a token pair.", + )) + .required("kind") + .required("router") + .required("tokens") + .required("fee") + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["constantProduct"])), + ) + .property("router", Ref::from_schema_name("Address")) + .property( + "tokens", + ObjectBuilder::new() + .description(Some("A mapping of token address to its reserve amounts.")) + .additional_properties(Some(Ref::from_schema_name("TokenReserve"))), + ) + .property("fee", Ref::from_schema_name("Decimal")) + .build(), + ) + .into(), + ) + } +} + +/// A reserve of tokens in an on-chain liquidity pool. #[serde_as] -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] +#[schema(title = "TokenReserve")] pub struct ConstantProductReserve { #[serde_as(as = "HexOrDecimalU256")] pub balance: U256, } +/// A Balancer-like weighted product liquidity pool of N tokens. #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WeightedProductPool { - pub id: String, - pub address: H160, pub balancer_pool_id: H256, - #[serde_as(as = "HexOrDecimalU256")] - pub gas_estimate: U256, pub tokens: HashMap, pub fee: BigDecimal, pub version: WeightedProductVersion, } +impl ToSchema<'static> for WeightedProductPool { + fn schema() -> (&'static str, RefOr) { + ( + "WeightedProductPool", + Schema::Object( + ObjectBuilder::new() + .description(Some( + "A Balancer-like weighted product liquidity pool of N tokens.", + )) + .required("kind") + .required("tokens") + .required("fee") + .required("balancer_pool_id") + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["weightedProduct"])), + ) + .property( + "tokens", + ObjectBuilder::new() + .description(Some( + "A mapping of token address to its reserve amounts with weights.", + )) + .additional_properties(Some(Schema::AllOf( + AllOfBuilder::new() + .item(Ref::from_schema_name("TokenReserve")) + .item( + ObjectBuilder::new() + .required("weight") + .property("weight", Ref::from_schema_name("Decimal")) + .property( + "scalingFactor", + Ref::from_schema_name("Decimal"), + ) + .build(), + ) + .build(), + ))), + ) + .property("fee", Ref::from_schema_name("Decimal")) + .property("balancer_pool_id", Ref::from_schema_name("BalancerPoolId")) + .property( + "version", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["v0", "v3Plus"])), + ) + .build(), + ) + .into(), + ) + } +} + #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -158,20 +492,66 @@ pub enum WeightedProductVersion { V3Plus, } +/// A Curve-like stable pool of N tokens. #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct StablePool { - pub id: String, - pub address: H160, pub balancer_pool_id: H256, - #[serde_as(as = "HexOrDecimalU256")] - pub gas_estimate: U256, pub tokens: HashMap, pub amplification_parameter: BigDecimal, pub fee: BigDecimal, } +impl ToSchema<'static> for StablePool { + fn schema() -> (&'static str, RefOr) { + ( + "StablePool", + Schema::Object( + ObjectBuilder::new() + .description(Some("A Curve-like stable pool of N tokens.")) + .required("kind") + .required("tokens") + .required("amplificationParameter") + .required("fee") + .required("balancer_pool_id") + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["stable"])), + ) + .property( + "tokens", + ObjectBuilder::new() + .description(Some( + "A mapping of token address to token balance and scaling rate.", + )) + .additional_properties(Some(Schema::AllOf( + AllOfBuilder::new() + .item(Ref::from_schema_name("TokenReserve")) + .item( + ObjectBuilder::new() + .required("scalingFactor") + .property( + "scalingFactor", + Ref::from_schema_name("Decimal"), + ) + .build(), + ) + .build(), + ))), + ) + .property("amplificationParameter", Ref::from_schema_name("Decimal")) + .property("fee", Ref::from_schema_name("Decimal")) + .property("balancer_pool_id", Ref::from_schema_name("BalancerPoolId")) + .build(), + ) + .into(), + ) + } +} + #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -185,11 +565,7 @@ pub struct StableReserve { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ConcentratedLiquidityPool { - pub id: String, - pub address: H160, pub router: H160, - #[serde_as(as = "HexOrDecimalU256")] - pub gas_estimate: U256, pub tokens: Vec, #[serde_as(as = "HexOrDecimalU256")] pub sqrt_price: U256, @@ -201,14 +577,54 @@ pub struct ConcentratedLiquidityPool { pub fee: BigDecimal, } +impl ToSchema<'static> for ConcentratedLiquidityPool { + fn schema() -> (&'static str, RefOr) { + ( + "ConcentratedLiquidityPool", + Schema::Object( + ObjectBuilder::new() + .description(Some("A Uniswap V3-like concentrated liquidity pool.")) + .required("kind") + .required("router") + .required("tokens") + .required("sqrtPrice") + .required("liquidity") + .required("tick") + .required("liquidityNet") + .required("fee") + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["concentratedLiquidity"])), + ) + .property("router", Ref::from_schema_name("Address")) + .property( + "tokens", + ArrayBuilder::new().items(Ref::from_schema_name("Token")), + ) + .property("sqrtPrice", Ref::from_schema_name("U256")) + .property("liquidity", Ref::from_schema_name("U128")) + .property("tick", Ref::from_schema_name("I32")) + .property( + "liquidityNet", + ObjectBuilder::new() + .description(Some("A map of tick indices to their liquidity values.")) + .additional_properties(Some(Ref::from_schema_name("I128"))), + ) + .property("fee", Ref::from_schema_name("Decimal")) + .build(), + ) + .into(), + ) + } +} + #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ForeignLimitOrder { - pub id: String, - pub address: H160, - #[serde_as(as = "HexOrDecimalU256")] - pub gas_estimate: U256, + // todo: seems like this is not used anywhere. #[serde_as(as = "serialize::Hex")] pub hash: [u8; 32], pub maker_token: H160, @@ -220,3 +636,268 @@ pub struct ForeignLimitOrder { #[serde_as(as = "HexOrDecimalU256")] pub taker_token_fee_amount: U256, } + +impl ToSchema<'static> for ForeignLimitOrder { + fn schema() -> (&'static str, RefOr) { + ( + "ForeignLimitOrder", + Schema::Object( + ObjectBuilder::new() + .description(Some("A 0x-like limit order external to CoW Protocol.")) + .required("kind") + .required("makerToken") + .required("takerToken") + .required("makerAmount") + .required("takerAmount") + .required("takerTokenFeeAmount") + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["limitOrder"])), + ) + .property("makerToken", Ref::from_schema_name("Token")) + .property("takerToken", Ref::from_schema_name("Token")) + .property("makerAmount", Ref::from_schema_name("TokenAmount")) + .property("takerAmount", Ref::from_schema_name("TokenAmount")) + .property("takerTokenFeeAmount", Ref::from_schema_name("TokenAmount")) + .build(), + ) + .into(), + ) + } +} + +// Structs for the utoipa OpenAPI schema generator. + +/// The price in wei of the native token (Ether on Mainnet for example) to buy +/// 10**18 of a token. +#[derive(ToSchema)] +#[schema(example = "1234567890")] +#[allow(dead_code)] +pub struct NativePrice(String); + +/// Amount of an ERC20 token. 256 bit unsigned integer in decimal notation. +#[derive(ToSchema)] +#[schema(example = "1234567890")] +#[allow(dead_code)] +pub struct TokenAmount(String); + +/// An ISO-8601 formatted date-time. +#[derive(ToSchema)] +#[schema(example = "1970-01-01T00:00:00.000Z")] +#[allow(dead_code)] +pub struct DateTime(String); + +/// An Ethereum public address. +#[derive(ToSchema)] +#[schema(example = "0x0000000000000000000000000000000000000000")] +#[allow(dead_code)] +pub struct Address(String); + +/// An arbitrary-precision integer value. +#[derive(ToSchema)] +#[schema(example = "1234567890")] +#[allow(dead_code)] +pub struct BigInt(String); + +/// An arbitrary-precision decimal value. +#[derive(ToSchema)] +#[schema(example = "13.37")] +#[allow(dead_code)] +pub struct Decimal(String); + +/// A hex-encoded 32 byte string containing the pool address (0..20), the pool +/// specialization (20..22) and the poolnonce (22..32). +#[derive(ToSchema)] +#[schema(example = "0xc88c76dd8b92408fe9bea1a54922a31e232d873c0002000000000000000005b2")] +#[allow(dead_code)] +pub struct BalancerPoolId(String); + +/// An ERC20 token address. +#[derive(ToSchema)] +#[schema(example = "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB")] +#[allow(dead_code)] +pub struct Token(String); + +#[serde_as] +#[derive(ToSchema, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct TokenReserve { + #[schema(value_type = TokenAmount)] + pub balance: U256, +} + +/// 256 bit unsigned integer in decimal notation. +#[derive(ToSchema)] +#[schema(as = U256, example = "1234567890")] +#[allow(dead_code)] +pub struct U256Schema(String); + +/// 128 bit unsigned integer in decimal notation. +#[derive(ToSchema)] +#[schema(example = "1234567890")] +#[allow(dead_code)] +pub struct U128(String); + +/// 128 bit signed integer in decimal notation. +#[derive(ToSchema)] +#[schema(example = "-1234567890")] +#[allow(dead_code)] +pub struct I128(String); + +/// 32 bit signed integer in decimal notation. +#[derive(ToSchema)] +#[schema(example = "-12345")] +#[allow(dead_code)] +pub struct I32(String); + +/// Unique identifier for the order. Order UIDs are 56 bytes long, where bytes +/// [0, 32) represent the order digest used for signing, bytes [32, 52) +/// represent the owner address and bytes [52, 56) represent the order's +/// `validTo` field. +#[derive(ToSchema)] +#[schema( + example = "0x30cff40d9f60caa68a37f0ee73253ad6ad72b45580c945fe3ab67596476937197854163b1b0d24e77dca702b97b5cc33e0f83dcb626122a6" +)] +#[allow(dead_code)] +pub struct OrderUid(String); + +/// If the order receives more than limit price, pay the protocol a factor of +/// the difference. +pub struct SurplusFee { + pub factor: f64, + pub max_volume_factor: f64, +} + +impl ToSchema<'static> for SurplusFee { + fn schema() -> (&'static str, RefOr) { + ( + "SurplusFee", + Schema::Object( + ObjectBuilder::new() + .description(Some( + "If the order receives more than limit price, pay the protocol a factor \ + of the difference.", + )) + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["surplus"])), + ) + .property( + "factor", + ObjectBuilder::new() + .description(Some( + "The factor of the user surplus that the protocol will request \ + from the solver after settling the order", + )) + .schema_type(SchemaType::Number) + .example(Number::from_f64(0.5).map(Value::Number)) + .build(), + ) + .property( + "maxVolumeFactor", + ObjectBuilder::new() + .description(Some( + "Never charge more than that percentage of the order volume.", + )) + .schema_type(SchemaType::Number) + .example(Number::from_f64(0.05).map(Value::Number)) + .minimum(Some(0.0)) + .maximum(Some(0.99999)) + .build(), + ) + .build(), + ) + .into(), + ) + } +} + +/// A cut from the price improvement over the best quote is taken as a protocol +/// fee. +pub struct PriceImprovement { + pub factor: f64, + pub max_volume_factor: f64, + pub quote: Quote, +} + +impl ToSchema<'static> for PriceImprovement { + fn schema() -> (&'static str, RefOr) { + ( + "PriceImprovement", + Schema::Object( + ObjectBuilder::new() + .description(Some( + "A cut from the price improvement over the best quote is taken as a \ + protocol fee.", + )) + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["priceImprovement"])), + ) + .property( + "factor", + ObjectBuilder::new() + .description(Some( + "The factor of the user surplus that the protocol will request \ + from the solver after settling the order", + )) + .schema_type(SchemaType::Number) + .example(Number::from_f64(0.5).map(Value::Number)), + ) + .property( + "maxVolumeFactor", + ObjectBuilder::new() + .description(Some( + "Never charge more than that percentage of the order volume.", + )) + .schema_type(SchemaType::Number) + .example(Number::from_f64(0.01).map(Value::Number)) + .minimum(Some(0.0)) + .maximum(Some(0.99999)), + ) + .property("quote", Ref::from_schema_name("Quote")) + .build(), + ) + .into(), + ) + } +} + +pub struct VolumeFee { + pub factor: f64, +} + +impl ToSchema<'static> for VolumeFee { + fn schema() -> (&'static str, RefOr) { + ( + "VolumeFee", + Schema::Object( + ObjectBuilder::new() + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["volume"])), + ) + .property( + "factor", + ObjectBuilder::new() + .description(Some( + "The fraction of the order's volume that the protocol will \ + request from the solver after settling the order.", + )) + .schema_type(SchemaType::Number) + .example(Number::from_f64(0.5).map(Value::Number)), + ) + .build(), + ) + .into(), + ) + } +} diff --git a/crates/solvers-dto/src/notification.rs b/crates/solvers-dto/src/notification.rs index a7b1d1da65..1264ba0501 100644 --- a/crates/solvers-dto/src/notification.rs +++ b/crates/solvers-dto/src/notification.rs @@ -4,20 +4,89 @@ use { serde::Deserialize, serde_with::{serde_as, DisplayFromStr}, std::collections::BTreeSet, + utoipa::{ + openapi::{ObjectBuilder, RefOr, Schema, SchemaType}, + ToSchema, + }, web3::types::{AccessList, H160, H256, U256}, }; +/// A notification that informs the solver how its solution performed in the +/// auction. Depending on the notification type additional meta data may be +/// attached but this is not guaranteed to be stable. #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Notification { + /// The auction ID of the auction that the solution was provided for. #[serde_as(as = "Option")] pub auction_id: Option, + /// The solution ID within the auction for which the notification applies pub solution_id: Option, + /// The kind of notification. #[serde(flatten)] pub kind: Kind, } +// serde(flatten) has a conflict with the current API schema +impl ToSchema<'static> for Notification { + fn schema() -> (&'static str, RefOr) { + ( + "Notification", + Schema::Object( + ObjectBuilder::new() + .description(Some( + "A notification that informs the solver how its solution performed in the \ + auction. Depending on the notification type additional meta data may be \ + attached but this is not guaranteed to be stable.", + )) + .schema_type(SchemaType::Object) + .property( + "auctionId", + ObjectBuilder::new() + .description(Some( + "The auction ID of the auction that the solution was providedfor.", + )) + .schema_type(SchemaType::String), + ) + .property( + "solutionId", + ObjectBuilder::new() + .description(Some( + "The solution ID within the auction for which the notification \ + applies", + )) + .schema_type(SchemaType::Number), + ) + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some([ + "timeout", + "emptySolution", + "duplicatedSolutionId", + "simulationFailed", + "invalidClearingPrices", + "missingPrice", + "invalidExecutedAmount", + "nonBufferableTokensUsed", + "solverAccountInsufficientBalance", + "success", + "revert", + "driverError", + "cancelled", + "fail", + "postprocessingTimedOut", + ])), + ) + .build(), + ) + .into(), + ) + } +} + #[serde_as] #[derive(Debug, Deserialize)] #[serde(untagged)] diff --git a/crates/solvers-dto/src/solution.rs b/crates/solvers-dto/src/solution.rs index ffd2c2ede8..96b528afcf 100644 --- a/crates/solvers-dto/src/solution.rs +++ b/crates/solvers-dto/src/solution.rs @@ -2,30 +2,55 @@ use { super::serialize, number::serialization::HexOrDecimalU256, serde::Serialize, + serde_json::Value, serde_with::serde_as, std::collections::HashMap, + utoipa::{ + openapi::{ + AllOfBuilder, + ArrayBuilder, + ObjectBuilder, + OneOfBuilder, + Ref, + RefOr, + Schema, + SchemaType, + }, + ToSchema, + }, web3::types::{H160, U256}, }; -#[derive(Debug, Serialize, Default)] +/// Proposed solutions to settle some of the orders in the auction. +#[derive(Debug, Serialize, Default, ToSchema)] #[serde(rename_all = "camelCase")] pub struct Solutions { pub solutions: Vec, } +/// A computed solution for a given auction. #[serde_as] -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct Solution { + /// An opaque identifier for the solution. This is a solver generated number + /// that is unique across multiple solutions within the auction. + #[schema(value_type = f64, format = Int64)] pub id: u64, + /// A clearing price map of token address to price. The price can have + /// arbitrary denomination. #[serde_as(as = "HashMap<_, HexOrDecimalU256>")] pub prices: HashMap, + /// CoW Protocol order trades included in the solution. pub trades: Vec, + /// Interactions to encode within a settlement. pub interactions: Vec, + /// How many units of gas this solution is estimated to cost. #[serde(skip_serializing_if = "Option::is_none")] pub gas: Option, } +/// A trade for a CoW Protocol order included in a solution. #[derive(Debug, Serialize)] #[serde(tag = "kind", rename_all = "camelCase")] pub enum Trade { @@ -33,6 +58,25 @@ pub enum Trade { Jit(JitTrade), } +impl ToSchema<'static> for Trade { + fn schema() -> (&'static str, RefOr) { + ( + "Trade", + Schema::OneOf( + OneOfBuilder::new() + .description(Some( + "A trade for a CoW Protocol order included in a solution.", + )) + .item(Ref::from_schema_name("Fulfillment")) + .item(Ref::from_schema_name("JitTrade")) + .build(), + ) + .into(), + ) + } +} + +/// A trade which fulfills an order from the auction. #[serde_as] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -46,6 +90,53 @@ pub struct Fulfillment { pub fee: Option, } +impl ToSchema<'static> for Fulfillment { + fn schema() -> (&'static str, RefOr) { + ( + "Fulfillment", + Schema::Object( + ObjectBuilder::new() + .required("kind") + .required("order") + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["fulfillment"])), + ) + .property( + "order", + AllOfBuilder::new() + .item(Ref::from_schema_name("OrderUid")) + .description(Some( + "A reference by UID of the order to execute in a solution. The \ + order must be included in the auction input.", + )), + ) + .property( + "executedAmount", + AllOfBuilder::new() + .description(Some( + "The amount of the order that was executed. This is denoted in \ + 'sellToken' for sell orders, and 'buyToken' for buy orders.", + )) + .item(Ref::from_schema_name("TokenAmount")), + ) + .property( + "fee", + ObjectBuilder::new().description(Some( + "The sell token amount that should be taken as a fee for this trade. \ + This only gets returned for limit orders and only refers to the \ + actual amount filled by the trade.", + )), + ) + .build(), + ) + .into(), + ) + } +} + #[serde_as] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -55,50 +146,140 @@ pub struct JitTrade { pub executed_amount: U256, } +impl ToSchema<'static> for JitTrade { + fn schema() -> (&'static str, RefOr) { + ( + "JitTrade", + Schema::Object( + ObjectBuilder::new() + .description(Some("A trade with a JIT order.")) + .required("kind") + .required("order") + .required("executedAmount") + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["jit"])), + ) + .property( + "order", + AllOfBuilder::new() + .description(Some( + "The just-in-time liquidity order to execute in a solution.", + )) + .item(Ref::from_schema_name("JitOrder")), + ) + .property( + "executedAmount", + AllOfBuilder::new() + .description(Some( + "The amount of the order that was executed. This is denoted in \ + 'sellToken' for sell orders, and 'buyToken' for buy orders.", + )) + .item(Ref::from_schema_name("TokenAmount")), + ) + .build(), + ) + .into(), + ) + } +} + +/// A just-in-time liquidity order included in a settlement. #[serde_as] -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct JitOrder { + #[schema(value_type = Token)] pub sell_token: H160, + #[schema(value_type = Token)] pub buy_token: H160, + #[schema(value_type = Address)] pub receiver: H160, #[serde_as(as = "HexOrDecimalU256")] + #[schema(value_type = TokenAmount)] pub sell_amount: U256, #[serde_as(as = "HexOrDecimalU256")] + #[schema(value_type = TokenAmount)] pub buy_amount: U256, pub valid_to: u32, #[serde_as(as = "serialize::Hex")] + #[schema(value_type = AppData)] pub app_data: [u8; 32], #[serde_as(as = "HexOrDecimalU256")] + #[schema(value_type = TokenAmount)] pub fee_amount: U256, - pub kind: Kind, + pub kind: OrderKind, pub partially_fillable: bool, pub sell_token_balance: SellTokenBalance, pub buy_token_balance: BuyTokenBalance, pub signing_scheme: SigningScheme, #[serde_as(as = "serialize::Hex")] + #[schema(value_type = Signature)] pub signature: Vec, } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] -pub enum Kind { +pub enum OrderKind { Sell, Buy, } +/// A flag indicating that the interaction should be "internalized", as +/// specified by CIP-2. #[derive(Debug, Serialize)] #[serde(tag = "kind", rename_all = "camelCase")] -pub enum Interaction { +pub struct Interaction { + pub internalize: bool, + pub interaction_type: InteractionType, +} + +impl ToSchema<'static> for Interaction { + fn schema() -> (&'static str, RefOr) { + ( + "Interaction", + Schema::AllOf( + AllOfBuilder::new() + .description(Some("An interaction to execute as part of a settlement.")) + .item( + ObjectBuilder::new().property( + "internalize", + ObjectBuilder::new() + .schema_type(SchemaType::Boolean) + .description(Some( + "A flag indicating that the interaction should be \ + 'internalized', as specified by CIP-2.", + )) + .build(), + ), + ) + .item( + OneOfBuilder::new() + .item(Ref::from_schema_name("LiquidityInteraction")) + .item(Ref::from_schema_name("CustomInteraction")), + ) + .build(), + ) + .into(), + ) + } +} + +#[derive(Debug, Serialize)] +pub enum InteractionType { Liquidity(LiquidityInteraction), Custom(CustomInteraction), } +/// Interaction representing the execution of liquidity that was passed in with +/// the auction. #[serde_as] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct LiquidityInteraction { - pub internalize: bool, + /// The ID of executed liquidity provided in the auction input. pub id: String, pub input_token: H160, pub output_token: H160, @@ -108,22 +289,127 @@ pub struct LiquidityInteraction { pub output_amount: U256, } +impl ToSchema<'static> for LiquidityInteraction { + fn schema() -> (&'static str, RefOr) { + ( + "LiquidityInteraction", + Schema::Object( + ObjectBuilder::new() + .description(Some( + "Interaction representing the execution of liquidity that was passed in \ + with the auction.", + )) + .required("kind") + .required("id") + .required("inputToken") + .required("outputToken") + .required("inputAmount") + .required("outputAmount") + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["liquidity"])), + ) + .property( + "id", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .description(Some( + "The ID of executed liquidity provided in the auction input.", + )) + .build(), + ) + .property("inputToken", Ref::from_schema_name("Token")) + .property("outputToken", Ref::from_schema_name("Token")) + .property("inputAmount", Ref::from_schema_name("TokenAmount")) + .property("outputAmount", Ref::from_schema_name("TokenAmount")) + .build(), + ) + .into(), + ) + } +} + +/// A searcher-specified custom interaction to be included in the final +/// settlement. #[serde_as] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CustomInteraction { - pub internalize: bool, pub target: H160, #[serde_as(as = "HexOrDecimalU256")] pub value: U256, + /// The EVM calldata bytes. #[serde(rename = "callData")] #[serde_as(as = "serialize::Hex")] pub calldata: Vec, + /// ERC20 allowances that are required for this custom interaction. pub allowances: Vec, pub inputs: Vec, pub outputs: Vec, } +impl ToSchema<'static> for CustomInteraction { + fn schema() -> (&'static str, RefOr) { + ( + "CustomInteraction", + Schema::Object( + ObjectBuilder::new() + .description(Some( + "A searcher-specified custom interaction to be included in the final \ + settlement.", + )) + .required("kind") + .required("target") + .required("value") + .required("callData") + .required("inputs") + .required("outputs") + .property( + "kind", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["custom"])), + ) + .property("target", Ref::from_schema_name("Address")) + .property("value", Ref::from_schema_name("TokenAmount")) + .property( + "callData", + ObjectBuilder::new() + .schema_type(SchemaType::String) + .description(Some("The EVM calldata bytes.")) + .example(Some(Value::String("0x01020304".to_string()))) + .build(), + ) + .property( + "allowances", + ArrayBuilder::new() + .items(Ref::from_schema_name("Allowance")) + .description(Some( + "ERC20 allowances that are required for this custom interaction.", + )) + .build(), + ) + .property( + "inputs", + ArrayBuilder::new() + .items(Ref::from_schema_name("Asset")) + .build(), + ) + .property( + "outputs", + ArrayBuilder::new() + .items(Ref::from_schema_name("Asset")) + .build(), + ) + .build(), + ) + .into(), + ) + } +} + /// An interaction that can be executed as part of an order's pre- or /// post-interactions. #[serde_as] @@ -138,21 +424,29 @@ pub struct OrderInteraction { pub calldata: Vec, } +/// A token address with an amount. #[serde_as] -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct Asset { + #[schema(value_type = Token)] pub token: H160, + #[schema(value_type = TokenAmount)] #[serde_as(as = "HexOrDecimalU256")] pub amount: U256, } +/// An ERC20 allowance from the settlement contract to some spender that is +/// required for a custom interaction. #[serde_as] -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct Allowance { + #[schema(value_type = Token)] pub token: H160, + #[schema(value_type = Address)] pub spender: H160, + #[schema(value_type = TokenAmount)] #[serde_as(as = "HexOrDecimalU256")] pub amount: U256, } @@ -166,6 +460,22 @@ pub enum SellTokenBalance { External, } +impl ToSchema<'static> for SellTokenBalance { + fn schema() -> (&'static str, RefOr) { + ( + "SellTokenBalance", + Schema::Object( + ObjectBuilder::new() + .description(Some("Where should the sell token be drawn from?")) + .schema_type(SchemaType::String) + .enum_values(Some(["erc20", "internal", "external"])) + .build(), + ) + .into(), + ) + } +} + #[derive(Debug, Default, Serialize)] #[serde(rename_all = "camelCase")] pub enum BuyTokenBalance { @@ -174,6 +484,22 @@ pub enum BuyTokenBalance { Internal, } +impl ToSchema<'static> for BuyTokenBalance { + fn schema() -> (&'static str, RefOr) { + ( + "BuyTokenBalance", + Schema::Object( + ObjectBuilder::new() + .description(Some("Where should the buy token be transferred to?")) + .schema_type(SchemaType::String) + .enum_values(Some(["erc20", "internal"])) + .build(), + ) + .into(), + ) + } +} + #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub enum SigningScheme { @@ -182,3 +508,35 @@ pub enum SigningScheme { PreSign, Eip1271, } + +impl ToSchema<'static> for SigningScheme { + fn schema() -> (&'static str, RefOr) { + ( + "SigningScheme", + Schema::Object( + ObjectBuilder::new() + .description(Some("How was the order signed?")) + .schema_type(SchemaType::String) + .enum_values(Some(["eip712", "ethSign", "preSign", "eip1271"])) + .build(), + ) + .into(), + ) + } +} + +/// Signature bytes. +#[derive(ToSchema)] +#[schema( + example = "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +)] +#[allow(dead_code)] +pub struct Signature(String); + +/// 32 bytes of arbitrary application specific data that can be added to an +/// order. This can also be used to ensure uniqueness between two orders with +/// otherwise the exact same parameters. +#[derive(ToSchema)] +#[schema(example = "0x0000000000000000000000000000000000000000000000000000000000000000")] +#[allow(dead_code)] +pub struct AppData(String); diff --git a/crates/solvers/Cargo.toml b/crates/solvers/Cargo.toml index 13826b32e2..9b1aa72f18 100644 --- a/crates/solvers/Cargo.toml +++ b/crates/solvers/Cargo.toml @@ -31,6 +31,7 @@ reqwest = { workspace = true } s3 = { path = "../s3" } serde = { workspace = true } serde_json = { workspace = true } +serde_yaml = { versio = "1.0" } serde_with = { workspace = true } solvers-dto = { path = "../solvers-dto" } thiserror = { workspace = true } @@ -38,6 +39,8 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread", "signal", " toml = { workspace = true } tower = "0.4" tower-http = { version = "0.4", features = ["limit", "trace"] } +utoipa = { version = "4.2.0", features = ["yaml", "axum_extras"] } +utoipauto = "0.1.10" web3 = { workspace = true } # TODO Once solvers are ported and E2E tests set up, slowly migrate code and diff --git a/crates/solvers/openapi_gen.yml b/crates/solvers/openapi_gen.yml new file mode 100644 index 0000000000..29d1f8eb0f --- /dev/null +++ b/crates/solvers/openapi_gen.yml @@ -0,0 +1,779 @@ +openapi: 3.0.3 +info: + title: Solver Engine API + description: The API implemented by solver engines interacting with the reference driver implementation. + license: + name: '' + version: 0.1.0 +paths: + /notify: + post: + tags: + - routes::notify + summary: Receive a status notification about a previously provided solution. + description: Receive a status notification about a previously provided solution. + operationId: notify + requestBody: + content: + application/json: + schema: + type: object + description: A notification that informs the solver how its solution performed in the auction. Depending on the notification type additional meta data may be attached but this is not guaranteed to be stable. + properties: + auctionId: + type: string + description: The auction ID of the auction that the solution was providedfor. + kind: + type: string + enum: + - timeout + - emptySolution + - duplicatedSolutionId + - simulationFailed + - invalidClearingPrices + - missingPrice + - invalidExecutedAmount + - nonBufferableTokensUsed + - solverAccountInsufficientBalance + - success + - revert + - driverError + - cancelled + - fail + - postprocessingTimedOut + solutionId: + type: number + description: The solution ID within the auction for which the notification applies + required: true + responses: + '200': + description: Notification successfully received. + /solve: + post: + tags: + - routes::solve + summary: Solve the passed in auction instance. + description: Solve the passed in auction instance. + operationId: solve + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Auction' + required: true + responses: + '200': + description: Auction successfully solved. + content: + application/json: + schema: + type: object + description: Proposed solutions to settle some of the orders in the auction. + required: + - solutions + properties: + solutions: + type: array + items: + $ref: '#/components/schemas/Solution' + '400': + description: There is something wrong with the request. + '429': + description: The solver cannot keep up. It is too busy to handle more requests. + '500': + description: Something went wrong when handling the request. +components: + schemas: + Address: + type: string + description: An Ethereum public address. + example: '0x0000000000000000000000000000000000000000' + Allowance: + type: object + description: |- + An ERC20 allowance from the settlement contract to some spender that is + required for a custom interaction. + required: + - token + - spender + - amount + properties: + amount: + $ref: '#/components/schemas/TokenAmount' + spender: + $ref: '#/components/schemas/Address' + token: + $ref: '#/components/schemas/Token' + AppData: + type: string + description: |- + 32 bytes of arbitrary application specific data that can be added to an + order. This can also be used to ensure uniqueness between two orders with + otherwise the exact same parameters. + example: '0x0000000000000000000000000000000000000000000000000000000000000000' + Asset: + type: object + description: A token address with an amount. + required: + - token + - amount + properties: + amount: + $ref: '#/components/schemas/TokenAmount' + token: + $ref: '#/components/schemas/Token' + Auction: + type: object + description: The abstract auction to be solved by the searcher. + required: + - tokens + - orders + - liquidity + - effectiveGasPrice + - deadline + properties: + deadline: + $ref: '#/components/schemas/DateTime' + effectiveGasPrice: + $ref: '#/components/schemas/TokenAmount' + id: + type: string + description: |- + An opaque identifier for the auction. Will be set to `null` for requests + that are not part of an auction (when quoting token prices for example). + liquidity: + type: array + items: + $ref: '#/components/schemas/Liquidity' + description: On-chain liquidity that can be used by the solution. + orders: + type: array + items: + $ref: '#/components/schemas/Order' + description: The solvable orders included in the auction. + tokens: + type: object + description: A map of token addresses to token information. + additionalProperties: + $ref: '#/components/schemas/TokenInfo' + additionalProperties: false + BalancerPoolId: + type: string + description: |- + A hex-encoded 32 byte string containing the pool address (0..20), the pool + specialization (20..22) and the poolnonce (22..32). + example: 0xc88c76dd8b92408fe9bea1a54922a31e232d873c0002000000000000000005b2 + BigInt: + type: string + description: An arbitrary-precision integer value. + example: '1234567890' + BuyTokenBalance: + type: string + description: Where should the buy token be transferred to? + enum: + - erc20 + - internal + ConcentratedLiquidityPool: + type: object + description: A Uniswap V3-like concentrated liquidity pool. + required: + - kind + - router + - tokens + - sqrtPrice + - liquidity + - tick + - liquidityNet + - fee + properties: + fee: + $ref: '#/components/schemas/Decimal' + kind: + type: string + enum: + - concentratedLiquidity + liquidity: + $ref: '#/components/schemas/U128' + liquidityNet: + type: object + description: A map of tick indices to their liquidity values. + additionalProperties: + $ref: '#/components/schemas/I128' + router: + $ref: '#/components/schemas/Address' + sqrtPrice: + $ref: '#/components/schemas/U256' + tick: + $ref: '#/components/schemas/I32' + tokens: + type: array + items: + $ref: '#/components/schemas/Token' + ConstantProductPool: + type: object + description: A UniswapV2-like constant product liquidity pool for a token pair. + required: + - kind + - router + - tokens + - fee + properties: + fee: + $ref: '#/components/schemas/Decimal' + kind: + type: string + enum: + - constantProduct + router: + $ref: '#/components/schemas/Address' + tokens: + type: object + description: A mapping of token address to its reserve amounts. + additionalProperties: + $ref: '#/components/schemas/TokenReserve' + CustomInteraction: + type: object + description: A searcher-specified custom interaction to be included in the final settlement. + required: + - kind + - target + - value + - callData + - inputs + - outputs + properties: + allowances: + type: array + items: + $ref: '#/components/schemas/Allowance' + description: ERC20 allowances that are required for this custom interaction. + callData: + type: string + description: The EVM calldata bytes. + example: '0x01020304' + inputs: + type: array + items: + $ref: '#/components/schemas/Asset' + kind: + type: string + enum: + - custom + outputs: + type: array + items: + $ref: '#/components/schemas/Asset' + target: + $ref: '#/components/schemas/Address' + value: + $ref: '#/components/schemas/TokenAmount' + DateTime: + type: string + description: An ISO-8601 formatted date-time. + example: 1970-01-01T00:00:00.000Z + Decimal: + type: string + description: An arbitrary-precision decimal value. + example: '13.37' + FeePolicy: + oneOf: + - $ref: '#/components/schemas/SurplusFee' + - $ref: '#/components/schemas/PriceImprovement' + - $ref: '#/components/schemas/VolumeFee' + description: A fee policy that applies to an order + ForeignLimitOrder: + type: object + description: A 0x-like limit order external to CoW Protocol. + required: + - kind + - makerToken + - takerToken + - makerAmount + - takerAmount + - takerTokenFeeAmount + properties: + kind: + type: string + enum: + - limitOrder + makerAmount: + $ref: '#/components/schemas/TokenAmount' + makerToken: + $ref: '#/components/schemas/Token' + takerAmount: + $ref: '#/components/schemas/TokenAmount' + takerToken: + $ref: '#/components/schemas/Token' + takerTokenFeeAmount: + $ref: '#/components/schemas/TokenAmount' + Fulfillment: + type: object + required: + - kind + - order + properties: + executedAmount: + allOf: + - $ref: '#/components/schemas/TokenAmount' + description: The amount of the order that was executed. This is denoted in 'sellToken' for sell orders, and 'buyToken' for buy orders. + fee: + type: object + description: The sell token amount that should be taken as a fee for this trade. This only gets returned for limit orders and only refers to the actual amount filled by the trade. + kind: + type: string + enum: + - fulfillment + order: + allOf: + - $ref: '#/components/schemas/OrderUid' + description: A reference by UID of the order to execute in a solution. The order must be included in the auction input. + I128: + type: string + description: 128 bit signed integer in decimal notation. + example: '-1234567890' + I32: + type: string + description: 32 bit signed integer in decimal notation. + example: '-12345' + Interaction: + allOf: + - type: object + properties: + internalize: + type: boolean + description: A flag indicating that the interaction should be 'internalized', as specified by CIP-2. + - oneOf: + - $ref: '#/components/schemas/LiquidityInteraction' + - $ref: '#/components/schemas/CustomInteraction' + description: An interaction to execute as part of a settlement. + JitOrder: + type: object + description: A just-in-time liquidity order included in a settlement. + required: + - sellToken + - buyToken + - receiver + - sellAmount + - buyAmount + - validTo + - appData + - feeAmount + - kind + - partiallyFillable + - sellTokenBalance + - buyTokenBalance + - signingScheme + - signature + properties: + appData: + $ref: '#/components/schemas/AppData' + buyAmount: + $ref: '#/components/schemas/TokenAmount' + buyToken: + $ref: '#/components/schemas/Token' + buyTokenBalance: + $ref: '#/components/schemas/BuyTokenBalance' + feeAmount: + $ref: '#/components/schemas/TokenAmount' + kind: + $ref: '#/components/schemas/OrderKind' + partiallyFillable: + type: boolean + receiver: + $ref: '#/components/schemas/Address' + sellAmount: + $ref: '#/components/schemas/TokenAmount' + sellToken: + $ref: '#/components/schemas/Token' + sellTokenBalance: + $ref: '#/components/schemas/SellTokenBalance' + signature: + $ref: '#/components/schemas/Signature' + signingScheme: + $ref: '#/components/schemas/SigningScheme' + validTo: + type: integer + format: int32 + minimum: 0 + JitTrade: + type: object + description: A trade with a JIT order. + required: + - kind + - order + - executedAmount + properties: + executedAmount: + allOf: + - $ref: '#/components/schemas/TokenAmount' + description: The amount of the order that was executed. This is denoted in 'sellToken' for sell orders, and 'buyToken' for buy orders. + kind: + type: string + enum: + - jit + order: + allOf: + - $ref: '#/components/schemas/JitOrder' + description: The just-in-time liquidity order to execute in a solution. + Liquidity: + allOf: + - $ref: '#/components/schemas/LiquidityParameters' + - type: object + required: + - id + - address + - gasEstimate + properties: + address: + allOf: + - $ref: '#/components/schemas/Address' + description: A rough approximation of gas units required to use this liquidity on-chain. + gasEstimate: + allOf: + - $ref: '#/components/schemas/BigInt' + description: A rough approximation of gas units required to use this liquidity on-chain. + id: + type: string + description: An opaque ID used for uniquely identifying the liquidity within a single auction (note that they are **not** guaranteed to be unique across auctions). This ID is used in the solution for matching interactions with the executed liquidity. + description: On-chain liquidity that can be used in a solution. This liquidity is provided to facilitate onboarding new solvers. Additional liquidity that is not included in this set may still be used in solutions. + LiquidityInteraction: + type: object + description: Interaction representing the execution of liquidity that was passed in with the auction. + required: + - kind + - id + - inputToken + - outputToken + - inputAmount + - outputAmount + properties: + id: + type: string + description: The ID of executed liquidity provided in the auction input. + inputAmount: + $ref: '#/components/schemas/TokenAmount' + inputToken: + $ref: '#/components/schemas/Token' + kind: + type: string + enum: + - liquidity + outputAmount: + $ref: '#/components/schemas/TokenAmount' + outputToken: + $ref: '#/components/schemas/Token' + LiquidityParameters: + oneOf: + - $ref: '#/components/schemas/ConstantProductPool' + - $ref: '#/components/schemas/WeightedProductPool' + - $ref: '#/components/schemas/StablePool' + - $ref: '#/components/schemas/ConcentratedLiquidityPool' + - $ref: '#/components/schemas/ForeignLimitOrder' + NativePrice: + type: string + description: |- + The price in wei of the native token (Ether on Mainnet for example) to buy + 10**18 of a token. + example: '1234567890' + Order: + type: object + description: CoW Protocol order information relevant to execution. + required: + - uid + - sellToken + - buyToken + - sellAmount + - buyAmount + - kind + - partiallyFillable + - class + properties: + buyAmount: + $ref: '#/components/schemas/TokenAmount' + buyToken: + $ref: '#/components/schemas/Token' + class: + $ref: '#/components/schemas/OrderClass' + feePolicies: + type: array + items: + $ref: '#/components/schemas/FeePolicy' + description: Any protocol fee policies that apply to the order. + nullable: true + kind: + $ref: '#/components/schemas/OrderKind' + partiallyFillable: + type: boolean + description: |- + Whether or not this order can be partially filled. If this is false, + then the order is a "fill-or-kill" order, meaning it needs to be + completely filled or not at all. + sellAmount: + $ref: '#/components/schemas/TokenAmount' + sellToken: + $ref: '#/components/schemas/Token' + uid: + $ref: '#/components/schemas/OrderUid' + additionalProperties: false + OrderClass: + type: string + description: How the CoW Protocol order was classified. + enum: + - market + - limit + - liquidity + OrderKind: + type: string + description: How the CoW Protocol order was classified. + enum: + - sell + - buy + OrderUid: + type: string + description: |- + Unique identifier for the order. Order UIDs are 56 bytes long, where bytes + [0, 32) represent the order digest used for signing, bytes [32, 52) + represent the owner address and bytes [52, 56) represent the order's + `validTo` field. + example: 0x30cff40d9f60caa68a37f0ee73253ad6ad72b45580c945fe3ab67596476937197854163b1b0d24e77dca702b97b5cc33e0f83dcb626122a6 + PriceImprovement: + type: object + description: A cut from the price improvement over the best quote is taken as a protocol fee. + properties: + factor: + type: number + description: The factor of the user surplus that the protocol will request from the solver after settling the order + example: 0.5 + kind: + type: string + enum: + - priceImprovement + maxVolumeFactor: + type: number + description: Never charge more than that percentage of the order volume. + example: 0.01 + maximum: 0.99999 + minimum: 0 + quote: + $ref: '#/components/schemas/Quote' + Quote: + type: object + required: + - sellAmount + - buyAmount + - fee + properties: + buyAmount: + $ref: '#/components/schemas/TokenAmount' + fee: + $ref: '#/components/schemas/TokenAmount' + sellAmount: + $ref: '#/components/schemas/TokenAmount' + additionalProperties: false + SellTokenBalance: + type: string + description: Where should the sell token be drawn from? + enum: + - erc20 + - internal + - external + Signature: + type: string + description: Signature bytes. + example: '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + SigningScheme: + type: string + description: How was the order signed? + enum: + - eip712 + - ethSign + - preSign + - eip1271 + Solution: + type: object + description: A computed solution for a given auction. + required: + - id + - prices + - trades + - interactions + properties: + gas: + type: integer + format: int64 + description: How many units of gas this solution is estimated to cost. + nullable: true + minimum: 0 + id: + type: number + format: int64 + description: |- + An opaque identifier for the solution. This is a solver generated number + that is unique across multiple solutions within the auction. + interactions: + type: array + items: + $ref: '#/components/schemas/Interaction' + description: Interactions to encode within a settlement. + prices: + type: object + description: |- + A clearing price map of token address to price. The price can have + arbitrary denomination. + additionalProperties: + $ref: '#/components/schemas/U256' + trades: + type: array + items: + $ref: '#/components/schemas/Trade' + description: CoW Protocol order trades included in the solution. + StablePool: + type: object + description: A Curve-like stable pool of N tokens. + required: + - kind + - tokens + - amplificationParameter + - fee + - balancer_pool_id + properties: + amplificationParameter: + $ref: '#/components/schemas/Decimal' + balancer_pool_id: + $ref: '#/components/schemas/BalancerPoolId' + fee: + $ref: '#/components/schemas/Decimal' + kind: + type: string + enum: + - stable + tokens: + type: object + description: A mapping of token address to token balance and scaling rate. + additionalProperties: + allOf: + - $ref: '#/components/schemas/TokenReserve' + - type: object + required: + - scalingFactor + properties: + scalingFactor: + $ref: '#/components/schemas/Decimal' + SurplusFee: + type: object + description: If the order receives more than limit price, pay the protocol a factor of the difference. + properties: + factor: + type: number + description: The factor of the user surplus that the protocol will request from the solver after settling the order + example: 0.5 + kind: + type: string + enum: + - surplus + maxVolumeFactor: + type: number + description: Never charge more than that percentage of the order volume. + example: 0.05 + maximum: 0.99999 + minimum: 0 + Token: + type: string + description: An ERC20 token address. + example: 0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB + TokenAmount: + type: string + description: Amount of an ERC20 token. 256 bit unsigned integer in decimal notation. + example: '1234567890' + TokenInfo: + type: object + description: Information about an ERC20 token. + required: + - availableBalance + - trusted + properties: + availableBalance: + allOf: + - $ref: '#/components/schemas/TokenAmount' + description: The balance held by the Settlement contract that is available during a settlement. + decimals: + type: integer + description: The ERC20.decimals value for this token. This may be missing for ERC20 tokens that don't implement the optional metadata extension. + referencePrice: + allOf: + - $ref: '#/components/schemas/NativePrice' + description: The reference price of this token for the auction used for scoring. This price is only included for tokens for which there are CoW Protocol orders. + symbol: + type: string + description: The ERC20.symbol value for this token. This may be missing for ERC20 tokens that don't implement the optional metadata extension. + trusted: + type: boolean + description: A flag which indicates that solvers are allowed to perform gas cost optimizations for this token by not routing the trades via an AMM, and instead use its available balances, as specified by CIP-2. + TokenReserve: + type: object + required: + - balance + properties: + balance: + $ref: '#/components/schemas/TokenAmount' + additionalProperties: false + Trade: + oneOf: + - $ref: '#/components/schemas/Fulfillment' + - $ref: '#/components/schemas/JitTrade' + description: A trade for a CoW Protocol order included in a solution. + U128: + type: string + description: 128 bit unsigned integer in decimal notation. + example: '1234567890' + U256: + type: string + description: 256 bit unsigned integer in decimal notation. + example: '1234567890' + VolumeFee: + type: object + properties: + factor: + type: number + description: The fraction of the order's volume that the protocol will request from the solver after settling the order. + example: 0.5 + kind: + type: string + enum: + - volume + WeightedProductPool: + type: object + description: A Balancer-like weighted product liquidity pool of N tokens. + required: + - kind + - tokens + - fee + - balancer_pool_id + properties: + balancer_pool_id: + $ref: '#/components/schemas/BalancerPoolId' + fee: + $ref: '#/components/schemas/Decimal' + kind: + type: string + enum: + - weightedProduct + tokens: + type: object + description: A mapping of token address to its reserve amounts with weights. + additionalProperties: + allOf: + - $ref: '#/components/schemas/TokenReserve' + - type: object + required: + - weight + properties: + scalingFactor: + $ref: '#/components/schemas/Decimal' + weight: + $ref: '#/components/schemas/Decimal' + version: + type: string + enum: + - v0 + - v3Plus diff --git a/crates/solvers/src/api/mod.rs b/crates/solvers/src/api/mod.rs index 63521187e6..44fdcace78 100644 --- a/crates/solvers/src/api/mod.rs +++ b/crates/solvers/src/api/mod.rs @@ -4,6 +4,7 @@ use { crate::domain::solver::Solver, std::{future::Future, net::SocketAddr, sync::Arc}, tokio::sync::oneshot, + utoipa::OpenApi, }; mod routes; @@ -46,3 +47,70 @@ impl Api { server.with_graceful_shutdown(shutdown).await } } + +pub fn generate_openapi_yaml() -> Result { + #[derive(OpenApi)] + #[openapi( + paths( + routes::solve::solve, + routes::notify::notify, + ), + components(schemas( + solvers_dto::auction::Auction, + solvers_dto::auction::TokenInfo, + solvers_dto::auction::NativePrice, + solvers_dto::auction::DateTime, + solvers_dto::auction::Liquidity, + solvers_dto::auction::LiquidityParameters, + solvers_dto::auction::ConstantProductPool, + solvers_dto::auction::WeightedProductPool, + solvers_dto::auction::StablePool, + solvers_dto::auction::ConcentratedLiquidityPool, + solvers_dto::auction::ForeignLimitOrder, + solvers_dto::auction::TokenReserve, + solvers_dto::auction::TokenAmount, + solvers_dto::auction::Token, + solvers_dto::auction::BalancerPoolId, + solvers_dto::auction::Decimal, + solvers_dto::auction::U256Schema, + solvers_dto::auction::U128, + solvers_dto::auction::I128, + solvers_dto::auction::I32, + solvers_dto::auction::BigInt, + solvers_dto::auction::Order, + solvers_dto::auction::OrderUid, + solvers_dto::auction::Address, + solvers_dto::auction::FeePolicy, + solvers_dto::auction::Quote, + solvers_dto::auction::OrderClass, + solvers_dto::auction::OrderKind, + solvers_dto::auction::SurplusFee, + solvers_dto::auction::PriceImprovement, + solvers_dto::auction::VolumeFee, + solvers_dto::solution::Solution, + solvers_dto::solution::Interaction, + solvers_dto::solution::CustomInteraction, + solvers_dto::solution::LiquidityInteraction, + solvers_dto::solution::Allowance, + solvers_dto::solution::Asset, + solvers_dto::solution::Trade, + solvers_dto::solution::Fulfillment, + solvers_dto::solution::JitTrade, + solvers_dto::solution::JitOrder, + solvers_dto::solution::AppData, + solvers_dto::solution::BuyTokenBalance, + solvers_dto::solution::SellTokenBalance, + solvers_dto::solution::Signature, + solvers_dto::solution::SigningScheme, + )), + info( + description = "The API implemented by solver engines interacting with the reference \ + driver implementation.", + title = "Solver Engine API", + version = "0.1.0", + ), + )] + pub struct ApiDoc; + + ApiDoc::openapi().to_yaml() +} diff --git a/crates/solvers/src/api/routes/mod.rs b/crates/solvers/src/api/routes/mod.rs index 39ab3d4f4f..2338242767 100644 --- a/crates/solvers/src/api/routes/mod.rs +++ b/crates/solvers/src/api/routes/mod.rs @@ -2,8 +2,8 @@ use serde::Serialize; mod healthz; mod metrics; -mod notify; -mod solve; +pub mod notify; +pub mod solve; pub(super) use {healthz::healthz, metrics::metrics, notify::notify, solve::solve}; diff --git a/crates/solvers/src/api/routes/notify/mod.rs b/crates/solvers/src/api/routes/notify/mod.rs index e989fe862d..c99f738109 100644 --- a/crates/solvers/src/api/routes/notify/mod.rs +++ b/crates/solvers/src/api/routes/notify/mod.rs @@ -2,6 +2,15 @@ use {crate::domain::solver::Solver, std::sync::Arc, tracing::Instrument}; mod dto; +/// Receive a status notification about a previously provided solution. +#[utoipa::path( + post, + path = "/notify", + request_body = inline(dto::Notification), + responses( + (status = 200, description = "Notification successfully received."), + ), +)] pub async fn notify( state: axum::extract::State>, notification: axum::extract::Json, diff --git a/crates/solvers/src/api/routes/solve/dto/auction.rs b/crates/solvers/src/api/routes/solve/dto/auction.rs index ea2a357740..76824bd456 100644 --- a/crates/solvers/src/api/routes/solve/dto/auction.rs +++ b/crates/solvers/src/api/routes/solve/dto/auction.rs @@ -50,13 +50,13 @@ pub fn to_domain(auction: &Auction) -> Result { amount: order.buy_amount, }, side: match order.kind { - Kind::Buy => order::Side::Buy, - Kind::Sell => order::Side::Sell, + OrderKind::Buy => order::Side::Buy, + OrderKind::Sell => order::Side::Sell, }, class: match order.class { - Class::Market => order::Class::Market, - Class::Limit => order::Class::Limit, - Class::Liquidity => order::Class::Liquidity, + OrderClass::Market => order::Class::Market, + OrderClass::Limit => order::Class::Limit, + OrderClass::Liquidity => order::Class::Liquidity, }, partially_fillable: order.partially_fillable, }) @@ -64,18 +64,39 @@ pub fn to_domain(auction: &Auction) -> Result { 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) + .map(|liquidity| match &liquidity.parameter { + LiquidityParameters::ConstantProduct(parameter) => constant_product_pool::to_domain( + liquidity.id.clone(), + liquidity.address, + liquidity.gas_estimate, + parameter, + ), + LiquidityParameters::WeightedProduct(parameter) => weighted_product_pool::to_domain( + liquidity.id.clone(), + liquidity.address, + liquidity.gas_estimate, + parameter, + ), + LiquidityParameters::Stable(parameter) => stable_pool::to_domain( + liquidity.id.clone(), + liquidity.address, + liquidity.gas_estimate, + parameter, + ), + LiquidityParameters::ConcentratedLiquidity(parameter) => { + concentrated_liquidity_pool::to_domain( + liquidity.id.clone(), + liquidity.address, + liquidity.gas_estimate, + parameter, + ) } - Liquidity::LimitOrder(liquidity) => Ok(foreign_limit_order::to_domain(liquidity)), + LiquidityParameters::LimitOrder(parameter) => Ok(foreign_limit_order::to_domain( + liquidity.id.clone(), + liquidity.address, + liquidity.gas_estimate, + parameter, + )), }) .try_collect()?, gas_price: auction::GasPrice(eth::Ether(auction.effective_gas_price)), @@ -84,9 +105,18 @@ pub fn to_domain(auction: &Auction) -> Result { } mod constant_product_pool { - use {super::*, itertools::Itertools}; + use { + super::*, + crate::domain::eth::{H160, U256}, + itertools::Itertools, + }; - pub fn to_domain(pool: &ConstantProductPool) -> Result { + pub fn to_domain( + id: String, + address: H160, + gas_estimate: U256, + pool: &ConstantProductPool, + ) -> Result { let reserves = { let (a, b) = pool .tokens @@ -102,9 +132,9 @@ mod constant_product_pool { }; Ok(liquidity::Liquidity { - id: liquidity::Id(pool.id.clone()), - address: pool.address, - gas: eth::Gas(pool.gas_estimate), + id: liquidity::Id(id), + address, + gas: eth::Gas(gas_estimate), state: liquidity::State::ConstantProduct(liquidity::constant_product::Pool { reserves, fee: conv::decimal_to_rational(&pool.fee).ok_or("invalid constant product fee")?, @@ -114,8 +144,16 @@ mod constant_product_pool { } mod weighted_product_pool { - use super::*; - pub fn to_domain(pool: &WeightedProductPool) -> Result { + use { + super::*, + crate::domain::eth::{H160, U256}, + }; + pub fn to_domain( + id: String, + address: H160, + gas_estimate: U256, + pool: &WeightedProductPool, + ) -> Result { let reserves = { let entries = pool .tokens @@ -139,9 +177,9 @@ mod weighted_product_pool { }; Ok(liquidity::Liquidity { - id: liquidity::Id(pool.id.clone()), - address: pool.address, - gas: eth::Gas(pool.gas_estimate), + id: liquidity::Id(id), + address, + gas: eth::Gas(gas_estimate), state: liquidity::State::WeightedProduct(liquidity::weighted_product::Pool { reserves, fee: conv::decimal_to_rational(&pool.fee).ok_or("invalid weighted product fee")?, @@ -155,9 +193,17 @@ mod weighted_product_pool { } mod stable_pool { - use super::*; + use { + super::*, + crate::domain::eth::{H160, U256}, + }; - pub fn to_domain(pool: &StablePool) -> Result { + pub fn to_domain( + id: String, + address: H160, + gas_estimate: U256, + pool: &StablePool, + ) -> Result { let reserves = { let entries = pool .tokens @@ -178,9 +224,9 @@ mod stable_pool { }; Ok(liquidity::Liquidity { - id: liquidity::Id(pool.id.clone()), - address: pool.address, - gas: eth::Gas(pool.gas_estimate), + id: liquidity::Id(id), + address, + gas: eth::Gas(gas_estimate), state: liquidity::State::Stable(liquidity::stable::Pool { reserves, amplification_parameter: conv::decimal_to_rational(&pool.amplification_parameter) @@ -192,9 +238,18 @@ mod stable_pool { } mod concentrated_liquidity_pool { - use {super::*, itertools::Itertools}; + use { + super::*, + crate::domain::eth::{H160, U256}, + itertools::Itertools, + }; - pub fn to_domain(pool: &ConcentratedLiquidityPool) -> Result { + pub fn to_domain( + id: String, + address: H160, + gas_estimate: U256, + pool: &ConcentratedLiquidityPool, + ) -> Result { let tokens = { let (a, b) = pool .tokens @@ -208,9 +263,9 @@ mod concentrated_liquidity_pool { }; Ok(liquidity::Liquidity { - id: liquidity::Id(pool.id.clone()), - address: pool.address, - gas: eth::Gas(pool.gas_estimate), + id: liquidity::Id(id), + address, + gas: eth::Gas(gas_estimate), state: liquidity::State::Concentrated(liquidity::concentrated::Pool { tokens, sqrt_price: liquidity::concentrated::SqrtPrice(pool.sqrt_price), @@ -236,13 +291,21 @@ mod concentrated_liquidity_pool { } mod foreign_limit_order { - use super::*; + use { + super::*, + crate::domain::eth::{H160, U256}, + }; - pub fn to_domain(order: &ForeignLimitOrder) -> liquidity::Liquidity { + pub fn to_domain( + id: String, + address: H160, + gas_estimate: U256, + order: &ForeignLimitOrder, + ) -> liquidity::Liquidity { liquidity::Liquidity { - id: liquidity::Id(order.id.clone()), - address: order.address, - gas: eth::Gas(order.gas_estimate), + id: liquidity::Id(id), + address, + gas: eth::Gas(gas_estimate), state: liquidity::State::LimitOrder(liquidity::limit_order::LimitOrder { maker: eth::Asset { token: eth::TokenAddress(order.maker_token), diff --git a/crates/solvers/src/api/routes/solve/dto/solution.rs b/crates/solvers/src/api/routes/solve/dto/solution.rs index e029269515..9bbac97e56 100644 --- a/crates/solvers/src/api/routes/solve/dto/solution.rs +++ b/crates/solvers/src/api/routes/solve/dto/solution.rs @@ -50,8 +50,8 @@ pub fn from_domain(solutions: &[solution::Solution]) -> super::Solutions { app_data: trade.order.app_data.0, fee_amount: 0.into(), kind: match trade.order.side { - crate::domain::order::Side::Buy => Kind::Buy, - crate::domain::order::Side::Sell => Kind::Sell, + crate::domain::order::Side::Buy => OrderKind::Buy, + crate::domain::order::Side::Sell => OrderKind::Sell, }, partially_fillable: trade.order.partially_fillable, sell_token_balance: SellTokenBalance::Erc20, @@ -68,22 +68,22 @@ pub fn from_domain(solutions: &[solution::Solution]) -> super::Solutions { .interactions .iter() .map(|interaction| match interaction { - solution::Interaction::Liquidity(interaction) => { - Interaction::Liquidity(LiquidityInteraction { + solution::Interaction::Liquidity(interaction) => Interaction { + internalize: interaction.internalize, + interaction_type: InteractionType::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 { + }), + }, + solution::Interaction::Custom(interaction) => Interaction { + internalize: interaction.internalize, + interaction_type: InteractionType::Custom(CustomInteraction { target: interaction.target, value: interaction.value.0, calldata: interaction.calldata.clone(), - internalize: interaction.internalize, allowances: interaction .allowances .iter() @@ -109,8 +109,8 @@ pub fn from_domain(solutions: &[solution::Solution]) -> super::Solutions { amount: o.amount, }) .collect(), - }) - } + }), + }, }) .collect(), gas: solution.gas.map(|gas| gas.0.as_u64()), diff --git a/crates/solvers/src/api/routes/solve/mod.rs b/crates/solvers/src/api/routes/solve/mod.rs index 3fd9d8834e..e7ace8054d 100644 --- a/crates/solvers/src/api/routes/solve/mod.rs +++ b/crates/solvers/src/api/routes/solve/mod.rs @@ -4,6 +4,18 @@ mod dto; use {crate::domain::solver::Solver, std::sync::Arc}; +/// Solve the passed in auction instance. +#[utoipa::path( + post, + path = "/solve", + request_body = Auction, + responses( + (status = 200, description = "Auction successfully solved.", body = inline(dto::Solutions)), + (status = 400, description = "There is something wrong with the request."), + (status = 429, description = "The solver cannot keep up. It is too busy to handle more requests."), + (status = 500, description = "Something went wrong when handling the request.") + ), +)] pub async fn solve( state: axum::extract::State>, auction: axum::extract::Json, diff --git a/crates/solvers/src/lib.rs b/crates/solvers/src/lib.rs index 23c05bfa77..cc4ce6660d 100644 --- a/crates/solvers/src/lib.rs +++ b/crates/solvers/src/lib.rs @@ -12,3 +12,5 @@ mod tests; mod util; pub use self::run::{run, start}; + +pub use api::generate_openapi_yaml; \ No newline at end of file From 4e3aff049dd3a20b275078e33565b9929713c4c6 Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 26 Apr 2024 18:18:02 +0100 Subject: [PATCH 02/19] Override existing file --- crates/openapi-generator/build.rs | 2 +- crates/solvers/openapi.yml | 1395 ++++++++++++++--------------- crates/solvers/openapi_gen.yml | 779 ---------------- 3 files changed, 652 insertions(+), 1524 deletions(-) delete mode 100644 crates/solvers/openapi_gen.yml diff --git a/crates/openapi-generator/build.rs b/crates/openapi-generator/build.rs index 30da62499e..cd62454bae 100644 --- a/crates/openapi-generator/build.rs +++ b/crates/openapi-generator/build.rs @@ -3,7 +3,7 @@ use std::fs; -const SOLVERS_OPENAPI_PATH: &str = "../solvers/openapi_gen.yml"; +const SOLVERS_OPENAPI_PATH: &str = "../solvers/openapi.yml"; fn main() { let openapi_yaml = diff --git a/crates/solvers/openapi.yml b/crates/solvers/openapi.yml index 25d988e21b..29d1f8eb0f 100644 --- a/crates/solvers/openapi.yml +++ b/crates/solvers/openapi.yml @@ -1,872 +1,779 @@ openapi: 3.0.3 info: title: Solver Engine API - description: | - The API implemented by solver engines interacting with the reference driver - implementation. - version: 0.0.1 - + description: The API implemented by solver engines interacting with the reference driver implementation. + license: + name: '' + version: 0.1.0 paths: - /solve: + /notify: post: - description: | - Solve the passed in auction instance. + tags: + - routes::notify + summary: Receive a status notification about a previously provided solution. + description: Receive a status notification about a previously provided solution. + operationId: notify requestBody: + content: + application/json: + schema: + type: object + description: A notification that informs the solver how its solution performed in the auction. Depending on the notification type additional meta data may be attached but this is not guaranteed to be stable. + properties: + auctionId: + type: string + description: The auction ID of the auction that the solution was providedfor. + kind: + type: string + enum: + - timeout + - emptySolution + - duplicatedSolutionId + - simulationFailed + - invalidClearingPrices + - missingPrice + - invalidExecutedAmount + - nonBufferableTokensUsed + - solverAccountInsufficientBalance + - success + - revert + - driverError + - cancelled + - fail + - postprocessingTimedOut + solutionId: + type: number + description: The solution ID within the auction for which the notification applies required: true + responses: + '200': + description: Notification successfully received. + /solve: + post: + tags: + - routes::solve + summary: Solve the passed in auction instance. + description: Solve the passed in auction instance. + operationId: solve + requestBody: content: application/json: schema: - $ref: "#/components/schemas/Auction" + $ref: '#/components/schemas/Auction' + required: true responses: - 200: + '200': description: Auction successfully solved. content: application/json: schema: type: object + description: Proposed solutions to settle some of the orders in the auction. required: - - solutions + - solutions properties: solutions: - description: | - Proposed solutions to settle some of the orders in the auction. type: array items: - $ref: "#/components/schemas/Solution" - 400: + $ref: '#/components/schemas/Solution' + '400': description: There is something wrong with the request. - 429: + '429': description: The solver cannot keep up. It is too busy to handle more requests. - 500: + '500': description: Something went wrong when handling the request. - /notify: - post: - description: | - Receive a status notification about a previously provided solution. - requestBody: - required: true - content: - application/json: - schema: - type: object - additionalProperties: true - description: | - A notification that informs the solver how its solution performed in the auction. - Depending on the notification type additional meta data may be attached but this - is not guaranteed to be stable. - properties: - auctionId: - description: | - The auction ID of the auction that the solution was provided - for. - type: string - solutionId: - description: | - The solution ID within the auction for which the notification applies - type: number - kind: - description: | - The kind of notification. - type: string - enum: - [ - timeout, - emptySolution, - duplicatedSolutionId, - simulationFailed, - invalidClearingPrices, - missingPrice, - invalidExecutedAmount, - nonBufferableTokensUsed, - solverAccountInsufficientBalance, - success, - revert, - driverError, - cancelled, - fail, - postprocessingTimedOut, - ] - responses: - 200: - description: notification successfully received. - components: schemas: Address: - description: | - An Ethereum public address. - type: string - example: "0x0000000000000000000000000000000000000000" - - Token: - description: | - An ERC20 token address. - type: string - example: "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB" - - TokenAmount: - description: | - Amount of an ERC20 token. 256 bit unsigned integer in decimal notation. - type: string - example: "1234567890" - - U256: - description: | - 256 bit unsigned integer in decimal notation. - type: string - example: "1234567890" - - U128: - description: | - 128 bit unsigned integer in decimal notation. - type: string - example: "1234567890" - - I128: - description: | - 128 bit signed integer in decimal notation. type: string - example: "-1234567890" - - I32: - description: | - 32 bit signed integer in decimal notation. - type: number - example: -12345 - - BalancerPoolId: - description: | - A hex-encoded 32 byte string containing the pool address (0..20), the pool specialization (20..22) and the poolnonce (22..32). - type: string - example: "0xc88c76dd8b92408fe9bea1a54922a31e232d873c0002000000000000000005b2" - - BigInt: - description: | - An arbitrary-precision integer value. - type: string - example: "1234567890" - - Decimal: - description: | - An arbitrary-precision decimal value. - type: string - example: "13.37" - - NativePrice: - description: | - The price in wei of the native token (Ether on Mainnet for example) to - buy 10**18 of a token. - type: string - example: "1234567890" - - DateTime: - description: An ISO-8601 formatted date-time. - type: string - example: "1970-01-01T00:00:00.000Z" - - TokenInfo: - description: | - Information about a token relevant to the auction. - type: object - required: - - trusted - - availableBalance - properties: - decimals: - description: | - The ERC20.decimals value for this token. This may be missing for - ERC20 tokens that don't implement the optional metadata extension. - type: integer - symbol: - description: | - The ERC20.symbol value for this token. This may be missing for ERC20 - tokens that don't implement the optional metadata extension. - type: string - referencePrice: - description: | - The reference price of this token for the auction used for scoring. - This price is only included for tokens for which there are CoW - Protocol orders. - allOf: - - $ref: "#/components/schemas/NativePrice" - availableBalance: - description: | - The balance held by the Settlement contract that is available - during a settlement. - allOf: - - $ref: "#/components/schemas/TokenAmount" - trusted: - description: | - A flag which indicates that solvers are allowed to perform gas cost - optimizations for this token by not routing the trades via an AMM, - and instead use its available balances, as specified by CIP-2. - type: boolean - - Asset: - description: | - A token address with an amount. + description: An Ethereum public address. + example: '0x0000000000000000000000000000000000000000' + Allowance: type: object + description: |- + An ERC20 allowance from the settlement contract to some spender that is + required for a custom interaction. required: - - token - - amount + - token + - spender + - amount properties: - token: - $ref: "#/components/schemas/Token" amount: - $ref: "#/components/schemas/TokenAmount" - - OrderUid: - description: | - Unique identifier for the order. Order UIDs are 56 bytes long, where - bytes [0, 32) represent the order digest used for signing, bytes - [32, 52) represent the owner address and bytes [52, 56) represent the - order's `validTo` field. - type: string - example: "0x30cff40d9f60caa68a37f0ee73253ad6ad72b45580c945fe3ab67596476937197854163b1b0d24e77dca702b97b5cc33e0f83dcb626122a6" - - OrderKind: - description: | - The trading side of the order. - type: string - enum: [ sell, buy ] - - OrderClass: - description: | - How the CoW Protocol order was classified. - type: string - enum: [ market, limit, liquidity ] - + $ref: '#/components/schemas/TokenAmount' + spender: + $ref: '#/components/schemas/Address' + token: + $ref: '#/components/schemas/Token' AppData: - description: | - 32 bytes of arbitrary application specific data that can be added to an - order. This can also be used to ensure uniqueness between two orders - with otherwise the exact same parameters. - example: "0x0000000000000000000000000000000000000000000000000000000000000000" - - SellTokenBalance: - description: | - Where should the sell token be drawn from? - type: string - enum: [ erc20, internal, external ] - - BuyTokenBalance: - description: | - Where should the buy token be transferred to? type: string - enum: [ erc20, internal ] - - SigningScheme: - description: | - How was the order signed? - type: string - enum: [ eip712, ethSign, preSign, eip1271 ] - - Signature: - description: | - Signature bytes. - type: string - example: "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - - Order: - description: | - CoW Protocol order information relevant to execution. - type: object - required: - - uid - - sellToken - - buyToken - - sellAmount - - buyAmount - - kind - - partiallyFillable - - class - properties: - uid: - $ref: "#/components/schemas/OrderUid" - sellToken: - $ref: "#/components/schemas/Token" - buyToken: - $ref: "#/components/schemas/Token" - sellAmount: - $ref: "#/components/schemas/TokenAmount" - buyAmount: - $ref: "#/components/schemas/TokenAmount" - kind: - $ref: "#/components/schemas/OrderKind" - partiallyFillable: - description: | - Whether or not this order can be partially filled. If this is false, - then the order is a "fill-or-kill" order, meaning it needs to be - completely filled or not at all. - type: boolean - class: - $ref: "#/components/schemas/OrderClass" - feePolicies: - description: | - Any protocol fee policies that apply to the order. - type: array - items: - $ref: "#/components/schemas/FeePolicy" - FeePolicy: - description: | - A fee policy that applies to an order. - type: object - oneOf: - - $ref: "#/components/schemas/SurplusFee" - - $ref: "#/components/schemas/PriceImprovement" - - $ref: "#/components/schemas/VolumeFee" - SurplusFee: - description: | - If the order receives more than limit price, pay the protocol a factor of the difference. - type: object - properties: - kind: - type: string - enum: [ "surplus" ] - maxVolumeFactor: - description: Never charge more than that percentage of the order volume. - type: number - minimum: 0.0 - maximum: 0.99999 - example: 0.05 - factor: - description: The factor of the user surplus that the protocol will request from the solver after settling the order - type: number - example: 0.5 - PriceImprovement: - description: | - A cut from the price improvement over the best quote is taken as a protocol fee. - type: object - properties: - kind: - type: string - enum: [ "priceImprovement" ] - maxVolumeFactor: - description: Never charge more than that percentage of the order volume. - type: number - example: 0.01 - factor: - description: The factor of the user surplus that the protocol will request from the solver after settling the order - type: number - example: 0.5 - quote: - $ref: "#/components/schemas/Quote" - VolumeFee: - type: object - properties: - kind: - type: string - enum: [ "volume" ] - factor: - description: The fraction of the order's volume that the protocol will request from the solver after settling the order. - type: number - example: 0.5 - Quote: - type: object - properties: - sell_amount: - $ref: "#/components/schemas/TokenAmount" - buy_amount: - $ref: "#/components/schemas/TokenAmount" - TokenReserve: - description: | - A reserve of tokens in an on-chain liquidity pool. + description: |- + 32 bytes of arbitrary application specific data that can be added to an + order. This can also be used to ensure uniqueness between two orders with + otherwise the exact same parameters. + example: '0x0000000000000000000000000000000000000000000000000000000000000000' + Asset: type: object + description: A token address with an amount. required: - - balance + - token + - amount properties: - balance: - $ref: "#/components/schemas/TokenAmount" - - ConstantProductPool: - description: | - A UniswapV2-like constant product liquidity pool for a token pair. + amount: + $ref: '#/components/schemas/TokenAmount' + token: + $ref: '#/components/schemas/Token' + Auction: type: object + description: The abstract auction to be solved by the searcher. required: - - kind - - tokens - - fee - - router + - tokens + - orders + - liquidity + - effectiveGasPrice + - deadline properties: - kind: + deadline: + $ref: '#/components/schemas/DateTime' + effectiveGasPrice: + $ref: '#/components/schemas/TokenAmount' + id: type: string - enum: [ constantProduct ] + description: |- + An opaque identifier for the auction. Will be set to `null` for requests + that are not part of an auction (when quoting token prices for example). + liquidity: + type: array + items: + $ref: '#/components/schemas/Liquidity' + description: On-chain liquidity that can be used by the solution. + orders: + type: array + items: + $ref: '#/components/schemas/Order' + description: The solvable orders included in the auction. tokens: - description: | - A mapping of token address to its reserve amounts. type: object + description: A map of token addresses to token information. additionalProperties: - $ref: "#/components/schemas/TokenReserve" - fee: - $ref: "#/components/schemas/Decimal" - router: - $ref: "#/components/schemas/Address" - - WeightedProductPool: - description: | - A Balancer-like weighted product liquidity pool of N tokens. + $ref: '#/components/schemas/TokenInfo' + additionalProperties: false + BalancerPoolId: + type: string + description: |- + A hex-encoded 32 byte string containing the pool address (0..20), the pool + specialization (20..22) and the poolnonce (22..32). + example: 0xc88c76dd8b92408fe9bea1a54922a31e232d873c0002000000000000000005b2 + BigInt: + type: string + description: An arbitrary-precision integer value. + example: '1234567890' + BuyTokenBalance: + type: string + description: Where should the buy token be transferred to? + enum: + - erc20 + - internal + ConcentratedLiquidityPool: type: object + description: A Uniswap V3-like concentrated liquidity pool. required: - - kind - - tokens - - fee - - balancer_pool_id + - kind + - router + - tokens + - sqrtPrice + - liquidity + - tick + - liquidityNet + - fee properties: + fee: + $ref: '#/components/schemas/Decimal' kind: type: string - enum: [ weightedProduct ] - tokens: - description: | - A mapping of token address to its reserve amounts with weights. + enum: + - concentratedLiquidity + liquidity: + $ref: '#/components/schemas/U128' + liquidityNet: type: object + description: A map of tick indices to their liquidity values. additionalProperties: - allOf: - - $ref: "#/components/schemas/TokenReserve" - - type: object - required: - - weight - properties: - scalingFactor: - $ref: "#/components/schemas/Decimal" - weight: - $ref: "#/components/schemas/Decimal" - fee: - $ref: "#/components/schemas/Decimal" - version: - type: string - enum: [ "v0", "v3Plus" ] - balancer_pool_id: - $ref: "#/components/schemas/BalancerPoolId" - - StablePool: - description: | - A Curve-like stable pool of N tokens. + $ref: '#/components/schemas/I128' + router: + $ref: '#/components/schemas/Address' + sqrtPrice: + $ref: '#/components/schemas/U256' + tick: + $ref: '#/components/schemas/I32' + tokens: + type: array + items: + $ref: '#/components/schemas/Token' + ConstantProductPool: type: object + description: A UniswapV2-like constant product liquidity pool for a token pair. required: - - kind - - tokens - - amplificationParameter - - fee - - balancer_pool_id + - kind + - router + - tokens + - fee properties: + fee: + $ref: '#/components/schemas/Decimal' kind: type: string - enum: [ stable ] + enum: + - constantProduct + router: + $ref: '#/components/schemas/Address' tokens: - description: | - A mapping of token address to token balance and scaling rate. type: object + description: A mapping of token address to its reserve amounts. additionalProperties: - allOf: - - $ref: "#/components/schemas/TokenReserve" - - type: object - required: - - scalingFactor - properties: - scalingFactor: - $ref: "#/components/schemas/Decimal" - amplificationParameter: - $ref: "#/components/schemas/Decimal" - fee: - $ref: "#/components/schemas/Decimal" - balancer_pool_id: - $ref: "#/components/schemas/BalancerPoolId" - - ConcentratedLiquidityPool: - description: | - A UniswapV3-like concentrated liquidity pool of 2 tokens. + $ref: '#/components/schemas/TokenReserve' + CustomInteraction: type: object + description: A searcher-specified custom interaction to be included in the final settlement. required: - - kind - - tokens - - sqrtPrice - - liquidity - - tick - - liquidityNet - - fee - - router + - kind + - target + - value + - callData + - inputs + - outputs properties: + allowances: + type: array + items: + $ref: '#/components/schemas/Allowance' + description: ERC20 allowances that are required for this custom interaction. + callData: + type: string + description: The EVM calldata bytes. + example: '0x01020304' + inputs: + type: array + items: + $ref: '#/components/schemas/Asset' kind: type: string - enum: [ concentratedLiquidity ] - tokens: + enum: + - custom + outputs: type: array items: - $ref: "#/components/schemas/Token" - sqrtPrice: - $ref: "#/components/schemas/U256" - liquidity: - $ref: "#/components/schemas/U128" - tick: - $ref: "#/components/schemas/I32" - liquidityNet: - description: | - A map of tick indices to their liquidity values. - type: object - additionalProperties: - $ref: "#/components/schemas/I128" - fee: - $ref: "#/components/schemas/Decimal" - router: - $ref: "#/components/schemas/Address" - + $ref: '#/components/schemas/Asset' + target: + $ref: '#/components/schemas/Address' + value: + $ref: '#/components/schemas/TokenAmount' + DateTime: + type: string + description: An ISO-8601 formatted date-time. + example: 1970-01-01T00:00:00.000Z + Decimal: + type: string + description: An arbitrary-precision decimal value. + example: '13.37' + FeePolicy: + oneOf: + - $ref: '#/components/schemas/SurplusFee' + - $ref: '#/components/schemas/PriceImprovement' + - $ref: '#/components/schemas/VolumeFee' + description: A fee policy that applies to an order ForeignLimitOrder: - description: | - A 0x-like limit order external to CoW Protocol. type: object + description: A 0x-like limit order external to CoW Protocol. required: - - kind - - makerToken - - takerToken - - makerAmount - - takerAmount - - takerTokenFeeAmount + - kind + - makerToken + - takerToken + - makerAmount + - takerAmount + - takerTokenFeeAmount properties: kind: type: string - enum: [ limitOrder ] - makerToken: - $ref: "#/components/schemas/Token" - takerToken: - $ref: "#/components/schemas/Token" + enum: + - limitOrder makerAmount: - $ref: "#/components/schemas/TokenAmount" + $ref: '#/components/schemas/TokenAmount' + makerToken: + $ref: '#/components/schemas/Token' takerAmount: - $ref: "#/components/schemas/TokenAmount" + $ref: '#/components/schemas/TokenAmount' + takerToken: + $ref: '#/components/schemas/Token' takerTokenFeeAmount: - $ref: "#/components/schemas/TokenAmount" - - LiquidityParameters: - oneOf: - - $ref: "#/components/schemas/ConstantProductPool" - - $ref: "#/components/schemas/WeightedProductPool" - - $ref: "#/components/schemas/StablePool" - - $ref: "#/components/schemas/ConcentratedLiquidityPool" - - $ref: "#/components/schemas/ForeignLimitOrder" - - Liquidity: - description: | - On-chain liquidity that can be used in a solution. This liquidity is - provided to facilitate onboarding new solvers. Additional liquidity that - is not included in this set may still be used in solutions. - allOf: - - $ref: "#/components/schemas/LiquidityParameters" - - type: object - required: - - id - - address - - gasEstimate - properties: - id: - description: | - An opaque ID used for uniquely identifying the liquidity within - a single auction (note that they are **not** guaranteed to be - unique across auctions). This ID is used in the solution for - matching interactions with the executed liquidity. - type: string - address: - description: | - The Ethereum public address of the liquidity. The actual address - that is specified is dependent on the kind of liquidity. - allOf: - - $ref: "#/components/schemas/Address" - gasEstimate: - description: | - A rough approximation of gas units required to use this - liquidity on-chain. - allOf: - - $ref: "#/components/schemas/BigInt" - - Auction: - description: | - The abstract auction to be solved by the searcher. + $ref: '#/components/schemas/TokenAmount' + Fulfillment: type: object required: - - tokens - - orders - - liquidity - - effectiveGasPrice - - deadline + - kind + - order properties: - id: - description: | - An opaque identifier for the auction. Will be set to `null` for - requests that are not part of an auction (when quoting token prices - for example). - type: string - tokens: - description: | - A map of token addresses to token information. - type: object - additionalProperties: - $ref: "#/components/schemas/TokenInfo" - orders: - description: | - The solvable orders included in the auction. - type: array - items: - $ref: "#/components/schemas/Order" - liquidity: - description: | - On-chain liquidity that can be used by the solution. - type: array - items: - $ref: "#/components/schemas/Liquidity" - effectiveGasPrice: - description: | - The current estimated gas price that will be paid when executing a - settlement. Additionally, this is the gas price that is multiplied - with a settlement's gas estimate for solution scoring. + executedAmount: allOf: - - $ref: "#/components/schemas/TokenAmount" - deadline: - description: | - The deadline by which a solution to the auction is required. - Requests that go beyond this deadline are expected to be cancelled - by the caller. + - $ref: '#/components/schemas/TokenAmount' + description: The amount of the order that was executed. This is denoted in 'sellToken' for sell orders, and 'buyToken' for buy orders. + fee: + type: object + description: The sell token amount that should be taken as a fee for this trade. This only gets returned for limit orders and only refers to the actual amount filled by the trade. + kind: + type: string + enum: + - fulfillment + order: allOf: - - $ref: "#/components/schemas/DateTime" - + - $ref: '#/components/schemas/OrderUid' + description: A reference by UID of the order to execute in a solution. The order must be included in the auction input. + I128: + type: string + description: 128 bit signed integer in decimal notation. + example: '-1234567890' + I32: + type: string + description: 32 bit signed integer in decimal notation. + example: '-12345' + Interaction: + allOf: + - type: object + properties: + internalize: + type: boolean + description: A flag indicating that the interaction should be 'internalized', as specified by CIP-2. + - oneOf: + - $ref: '#/components/schemas/LiquidityInteraction' + - $ref: '#/components/schemas/CustomInteraction' + description: An interaction to execute as part of a settlement. JitOrder: - description: | - A just-in-time liquidity order included in a settlement. type: object + description: A just-in-time liquidity order included in a settlement. required: - - sellToken - - buyToken - - receiver - - sellAmount - - buyAmount - - validTo - - appData - - feeAmount - - kind - - partiallyFillable - - sellTokenBalance - - buyTokenBalance - - signingScheme - - signature + - sellToken + - buyToken + - receiver + - sellAmount + - buyAmount + - validTo + - appData + - feeAmount + - kind + - partiallyFillable + - sellTokenBalance + - buyTokenBalance + - signingScheme + - signature properties: - sellToken: - $ref: "#/components/schemas/Token" - buyToken: - $ref: "#/components/schemas/Token" - receiver: - $ref: "#/components/schemas/Address" - sellAmount: - $ref: "#/components/schemas/TokenAmount" - buyAmount: - $ref: "#/components/schemas/TokenAmount" - validTo: - type: integer appData: - $ref: "#/components/schemas/AppData" + $ref: '#/components/schemas/AppData' + buyAmount: + $ref: '#/components/schemas/TokenAmount' + buyToken: + $ref: '#/components/schemas/Token' + buyTokenBalance: + $ref: '#/components/schemas/BuyTokenBalance' feeAmount: - $ref: "#/components/schemas/TokenAmount" + $ref: '#/components/schemas/TokenAmount' kind: - $ref: "#/components/schemas/OrderKind" + $ref: '#/components/schemas/OrderKind' partiallyFillable: type: boolean + receiver: + $ref: '#/components/schemas/Address' + sellAmount: + $ref: '#/components/schemas/TokenAmount' + sellToken: + $ref: '#/components/schemas/Token' sellTokenBalance: - $ref: "#/components/schemas/SellTokenBalance" - buyTokenBalance: - $ref: "#/components/schemas/BuyTokenBalance" - signingScheme: - $ref: "#/components/schemas/SigningScheme" + $ref: '#/components/schemas/SellTokenBalance' signature: - $ref: "#/components/schemas/Signature" - - Fulfillment: - description: | - A trade which fulfills an order from the auction. + $ref: '#/components/schemas/Signature' + signingScheme: + $ref: '#/components/schemas/SigningScheme' + validTo: + type: integer + format: int32 + minimum: 0 + JitTrade: type: object + description: A trade with a JIT order. required: - - kind - - order + - kind + - order + - executedAmount properties: - kind: - type: string - enum: [ fulfillment ] - order: - description: | - A reference by UID of the order to execute in a solution. The order - must be included in the auction input. - allOf: - - $ref: "#/components/schemas/OrderUid" - fee: - description: | - The sell token amount that should be taken as a fee for this - trade. This only gets returned for limit orders and only refers - to the actual amount filled by the trade. executedAmount: - description: | - The amount of the order that was executed. This is denoted in - "sellToken" for sell orders, and "buyToken" for buy orders. allOf: - - $ref: "#/components/schemas/TokenAmount" - - JitTrade: - description: | - A trade with a JIT order. - required: - - kind - - order - - executedAmount - properties: + - $ref: '#/components/schemas/TokenAmount' + description: The amount of the order that was executed. This is denoted in 'sellToken' for sell orders, and 'buyToken' for buy orders. kind: type: string - enum: [ jit ] - executedAmount: - description: | - The amount of the order that was executed. This is denoted in - "sellToken" for sell orders, and "buyToken" for buy orders. - allOf: - - $ref: "#/components/schemas/TokenAmount" + enum: + - jit order: - description: | - The just-in-time liquidity order to execute in a solution. allOf: - - $ref: "#/components/schemas/JitOrder" - Trade: - description: | - A trade for a CoW Protocol order included in a solution. - oneOf: - - $ref: "#/components/schemas/Fulfillment" - - $ref: "#/components/schemas/JitTrade" - + - $ref: '#/components/schemas/JitOrder' + description: The just-in-time liquidity order to execute in a solution. + Liquidity: + allOf: + - $ref: '#/components/schemas/LiquidityParameters' + - type: object + required: + - id + - address + - gasEstimate + properties: + address: + allOf: + - $ref: '#/components/schemas/Address' + description: A rough approximation of gas units required to use this liquidity on-chain. + gasEstimate: + allOf: + - $ref: '#/components/schemas/BigInt' + description: A rough approximation of gas units required to use this liquidity on-chain. + id: + type: string + description: An opaque ID used for uniquely identifying the liquidity within a single auction (note that they are **not** guaranteed to be unique across auctions). This ID is used in the solution for matching interactions with the executed liquidity. + description: On-chain liquidity that can be used in a solution. This liquidity is provided to facilitate onboarding new solvers. Additional liquidity that is not included in this set may still be used in solutions. LiquidityInteraction: - description: | - Interaction representing the execution of liquidity that was passed in - with the auction. type: object + description: Interaction representing the execution of liquidity that was passed in with the auction. required: - - kind - - id - - inputToken - - outputToken - - inputAmount - - outputAmount + - kind + - id + - inputToken + - outputToken + - inputAmount + - outputAmount properties: - kind: - type: string - enum: [ liquidity ] id: - description: | - The ID of executed liquidity provided in the auction input. - type: number - inputToken: - $ref: "#/components/schemas/Token" - outputToken: - $ref: "#/components/schemas/Token" + type: string + description: The ID of executed liquidity provided in the auction input. inputAmount: - $ref: "#/components/schemas/TokenAmount" + $ref: '#/components/schemas/TokenAmount' + inputToken: + $ref: '#/components/schemas/Token' + kind: + type: string + enum: + - liquidity outputAmount: - $ref: "#/components/schemas/TokenAmount" - - Allowance: - description: | - An ERC20 allowance from the settlement contract to some spender that is - required for a custom interaction. + $ref: '#/components/schemas/TokenAmount' + outputToken: + $ref: '#/components/schemas/Token' + LiquidityParameters: + oneOf: + - $ref: '#/components/schemas/ConstantProductPool' + - $ref: '#/components/schemas/WeightedProductPool' + - $ref: '#/components/schemas/StablePool' + - $ref: '#/components/schemas/ConcentratedLiquidityPool' + - $ref: '#/components/schemas/ForeignLimitOrder' + NativePrice: + type: string + description: |- + The price in wei of the native token (Ether on Mainnet for example) to buy + 10**18 of a token. + example: '1234567890' + Order: type: object + description: CoW Protocol order information relevant to execution. required: - - token - - spender - - minAmount + - uid + - sellToken + - buyToken + - sellAmount + - buyAmount + - kind + - partiallyFillable + - class properties: - token: - $ref: "#/components/schemas/Token" - spender: - $ref: "#/components/schemas/Address" - amount: - $ref: "#/components/schemas/TokenAmount" - - CustomInteraction: - description: | - A searcher-specified custom interaction to be included in the final - settlement. + buyAmount: + $ref: '#/components/schemas/TokenAmount' + buyToken: + $ref: '#/components/schemas/Token' + class: + $ref: '#/components/schemas/OrderClass' + feePolicies: + type: array + items: + $ref: '#/components/schemas/FeePolicy' + description: Any protocol fee policies that apply to the order. + nullable: true + kind: + $ref: '#/components/schemas/OrderKind' + partiallyFillable: + type: boolean + description: |- + Whether or not this order can be partially filled. If this is false, + then the order is a "fill-or-kill" order, meaning it needs to be + completely filled or not at all. + sellAmount: + $ref: '#/components/schemas/TokenAmount' + sellToken: + $ref: '#/components/schemas/Token' + uid: + $ref: '#/components/schemas/OrderUid' + additionalProperties: false + OrderClass: + type: string + description: How the CoW Protocol order was classified. + enum: + - market + - limit + - liquidity + OrderKind: + type: string + description: How the CoW Protocol order was classified. + enum: + - sell + - buy + OrderUid: + type: string + description: |- + Unique identifier for the order. Order UIDs are 56 bytes long, where bytes + [0, 32) represent the order digest used for signing, bytes [32, 52) + represent the owner address and bytes [52, 56) represent the order's + `validTo` field. + example: 0x30cff40d9f60caa68a37f0ee73253ad6ad72b45580c945fe3ab67596476937197854163b1b0d24e77dca702b97b5cc33e0f83dcb626122a6 + PriceImprovement: type: object - required: - - kind - - target - - value - - calldata - - inputs - - outputs + description: A cut from the price improvement over the best quote is taken as a protocol fee. properties: + factor: + type: number + description: The factor of the user surplus that the protocol will request from the solver after settling the order + example: 0.5 kind: type: string - enum: [ custom ] - target: - $ref: "#/components/schemas/Address" - value: - $ref: "#/components/schemas/TokenAmount" - calldata: - description: | - The EVM calldata bytes. - type: string - example: "0x01020304" - allowances: - description: | - ERC20 allowances that are required for this custom interaction. - type: array - items: - $ref: "#/components/schemas/Allowance" - inputs: - type: array - items: - $ref: "#/components/schemas/Asset" - outputs: - type: array - items: - $ref: "#/components/schemas/Asset" - - Interaction: - description: | - An interaction to execute as part of a settlement. - allOf: - - type: object - properties: - internalize: - description: | - A flag indicating that the interaction should be "internalized", - as specified by CIP-2. - type: boolean - - oneOf: - - $ref: "#/components/schemas/LiquidityInteraction" - - $ref: "#/components/schemas/CustomInteraction" - + enum: + - priceImprovement + maxVolumeFactor: + type: number + description: Never charge more than that percentage of the order volume. + example: 0.01 + maximum: 0.99999 + minimum: 0 + quote: + $ref: '#/components/schemas/Quote' + Quote: + type: object + required: + - sellAmount + - buyAmount + - fee + properties: + buyAmount: + $ref: '#/components/schemas/TokenAmount' + fee: + $ref: '#/components/schemas/TokenAmount' + sellAmount: + $ref: '#/components/schemas/TokenAmount' + additionalProperties: false + SellTokenBalance: + type: string + description: Where should the sell token be drawn from? + enum: + - erc20 + - internal + - external + Signature: + type: string + description: Signature bytes. + example: '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + SigningScheme: + type: string + description: How was the order signed? + enum: + - eip712 + - ethSign + - preSign + - eip1271 Solution: - description: | - A computed solution for a given auction. type: object + description: A computed solution for a given auction. required: - - id - - prices - - trades - - interactions + - id + - prices + - trades + - interactions properties: + gas: + type: integer + format: int64 + description: How many units of gas this solution is estimated to cost. + nullable: true + minimum: 0 id: - description: An opaque identifier for the solution. This is a solver generated number that is unique across multiple solutions within the auction. type: number + format: int64 + description: |- + An opaque identifier for the solution. This is a solver generated number + that is unique across multiple solutions within the auction. + interactions: + type: array + items: + $ref: '#/components/schemas/Interaction' + description: Interactions to encode within a settlement. prices: - description: | + type: object + description: |- A clearing price map of token address to price. The price can have arbitrary denomination. - type: object additionalProperties: - $ref: "#/components/schemas/U256" + $ref: '#/components/schemas/U256' trades: - description: | - CoW Protocol order trades included in the solution. - type: array - items: - $ref: "#/components/schemas/Trade" - interactions: - description: | - Interactions to encode within a settlement. type: array items: - $ref: "#/components/schemas/Interaction" - gas: + $ref: '#/components/schemas/Trade' + description: CoW Protocol order trades included in the solution. + StablePool: + type: object + description: A Curve-like stable pool of N tokens. + required: + - kind + - tokens + - amplificationParameter + - fee + - balancer_pool_id + properties: + amplificationParameter: + $ref: '#/components/schemas/Decimal' + balancer_pool_id: + $ref: '#/components/schemas/BalancerPoolId' + fee: + $ref: '#/components/schemas/Decimal' + kind: + type: string + enum: + - stable + tokens: + type: object + description: A mapping of token address to token balance and scaling rate. + additionalProperties: + allOf: + - $ref: '#/components/schemas/TokenReserve' + - type: object + required: + - scalingFactor + properties: + scalingFactor: + $ref: '#/components/schemas/Decimal' + SurplusFee: + type: object + description: If the order receives more than limit price, pay the protocol a factor of the difference. + properties: + factor: + type: number + description: The factor of the user surplus that the protocol will request from the solver after settling the order + example: 0.5 + kind: + type: string + enum: + - surplus + maxVolumeFactor: + type: number + description: Never charge more than that percentage of the order volume. + example: 0.05 + maximum: 0.99999 + minimum: 0 + Token: + type: string + description: An ERC20 token address. + example: 0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB + TokenAmount: + type: string + description: Amount of an ERC20 token. 256 bit unsigned integer in decimal notation. + example: '1234567890' + TokenInfo: + type: object + description: Information about an ERC20 token. + required: + - availableBalance + - trusted + properties: + availableBalance: + allOf: + - $ref: '#/components/schemas/TokenAmount' + description: The balance held by the Settlement contract that is available during a settlement. + decimals: type: integer - description: How many units of gas this solution is estimated to cost. + description: The ERC20.decimals value for this token. This may be missing for ERC20 tokens that don't implement the optional metadata extension. + referencePrice: + allOf: + - $ref: '#/components/schemas/NativePrice' + description: The reference price of this token for the auction used for scoring. This price is only included for tokens for which there are CoW Protocol orders. + symbol: + type: string + description: The ERC20.symbol value for this token. This may be missing for ERC20 tokens that don't implement the optional metadata extension. + trusted: + type: boolean + description: A flag which indicates that solvers are allowed to perform gas cost optimizations for this token by not routing the trades via an AMM, and instead use its available balances, as specified by CIP-2. + TokenReserve: + type: object + required: + - balance + properties: + balance: + $ref: '#/components/schemas/TokenAmount' + additionalProperties: false + Trade: + oneOf: + - $ref: '#/components/schemas/Fulfillment' + - $ref: '#/components/schemas/JitTrade' + description: A trade for a CoW Protocol order included in a solution. + U128: + type: string + description: 128 bit unsigned integer in decimal notation. + example: '1234567890' + U256: + type: string + description: 256 bit unsigned integer in decimal notation. + example: '1234567890' + VolumeFee: + type: object + properties: + factor: + type: number + description: The fraction of the order's volume that the protocol will request from the solver after settling the order. + example: 0.5 + kind: + type: string + enum: + - volume + WeightedProductPool: + type: object + description: A Balancer-like weighted product liquidity pool of N tokens. + required: + - kind + - tokens + - fee + - balancer_pool_id + properties: + balancer_pool_id: + $ref: '#/components/schemas/BalancerPoolId' + fee: + $ref: '#/components/schemas/Decimal' + kind: + type: string + enum: + - weightedProduct + tokens: + type: object + description: A mapping of token address to its reserve amounts with weights. + additionalProperties: + allOf: + - $ref: '#/components/schemas/TokenReserve' + - type: object + required: + - weight + properties: + scalingFactor: + $ref: '#/components/schemas/Decimal' + weight: + $ref: '#/components/schemas/Decimal' + version: + type: string + enum: + - v0 + - v3Plus diff --git a/crates/solvers/openapi_gen.yml b/crates/solvers/openapi_gen.yml deleted file mode 100644 index 29d1f8eb0f..0000000000 --- a/crates/solvers/openapi_gen.yml +++ /dev/null @@ -1,779 +0,0 @@ -openapi: 3.0.3 -info: - title: Solver Engine API - description: The API implemented by solver engines interacting with the reference driver implementation. - license: - name: '' - version: 0.1.0 -paths: - /notify: - post: - tags: - - routes::notify - summary: Receive a status notification about a previously provided solution. - description: Receive a status notification about a previously provided solution. - operationId: notify - requestBody: - content: - application/json: - schema: - type: object - description: A notification that informs the solver how its solution performed in the auction. Depending on the notification type additional meta data may be attached but this is not guaranteed to be stable. - properties: - auctionId: - type: string - description: The auction ID of the auction that the solution was providedfor. - kind: - type: string - enum: - - timeout - - emptySolution - - duplicatedSolutionId - - simulationFailed - - invalidClearingPrices - - missingPrice - - invalidExecutedAmount - - nonBufferableTokensUsed - - solverAccountInsufficientBalance - - success - - revert - - driverError - - cancelled - - fail - - postprocessingTimedOut - solutionId: - type: number - description: The solution ID within the auction for which the notification applies - required: true - responses: - '200': - description: Notification successfully received. - /solve: - post: - tags: - - routes::solve - summary: Solve the passed in auction instance. - description: Solve the passed in auction instance. - operationId: solve - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Auction' - required: true - responses: - '200': - description: Auction successfully solved. - content: - application/json: - schema: - type: object - description: Proposed solutions to settle some of the orders in the auction. - required: - - solutions - properties: - solutions: - type: array - items: - $ref: '#/components/schemas/Solution' - '400': - description: There is something wrong with the request. - '429': - description: The solver cannot keep up. It is too busy to handle more requests. - '500': - description: Something went wrong when handling the request. -components: - schemas: - Address: - type: string - description: An Ethereum public address. - example: '0x0000000000000000000000000000000000000000' - Allowance: - type: object - description: |- - An ERC20 allowance from the settlement contract to some spender that is - required for a custom interaction. - required: - - token - - spender - - amount - properties: - amount: - $ref: '#/components/schemas/TokenAmount' - spender: - $ref: '#/components/schemas/Address' - token: - $ref: '#/components/schemas/Token' - AppData: - type: string - description: |- - 32 bytes of arbitrary application specific data that can be added to an - order. This can also be used to ensure uniqueness between two orders with - otherwise the exact same parameters. - example: '0x0000000000000000000000000000000000000000000000000000000000000000' - Asset: - type: object - description: A token address with an amount. - required: - - token - - amount - properties: - amount: - $ref: '#/components/schemas/TokenAmount' - token: - $ref: '#/components/schemas/Token' - Auction: - type: object - description: The abstract auction to be solved by the searcher. - required: - - tokens - - orders - - liquidity - - effectiveGasPrice - - deadline - properties: - deadline: - $ref: '#/components/schemas/DateTime' - effectiveGasPrice: - $ref: '#/components/schemas/TokenAmount' - id: - type: string - description: |- - An opaque identifier for the auction. Will be set to `null` for requests - that are not part of an auction (when quoting token prices for example). - liquidity: - type: array - items: - $ref: '#/components/schemas/Liquidity' - description: On-chain liquidity that can be used by the solution. - orders: - type: array - items: - $ref: '#/components/schemas/Order' - description: The solvable orders included in the auction. - tokens: - type: object - description: A map of token addresses to token information. - additionalProperties: - $ref: '#/components/schemas/TokenInfo' - additionalProperties: false - BalancerPoolId: - type: string - description: |- - A hex-encoded 32 byte string containing the pool address (0..20), the pool - specialization (20..22) and the poolnonce (22..32). - example: 0xc88c76dd8b92408fe9bea1a54922a31e232d873c0002000000000000000005b2 - BigInt: - type: string - description: An arbitrary-precision integer value. - example: '1234567890' - BuyTokenBalance: - type: string - description: Where should the buy token be transferred to? - enum: - - erc20 - - internal - ConcentratedLiquidityPool: - type: object - description: A Uniswap V3-like concentrated liquidity pool. - required: - - kind - - router - - tokens - - sqrtPrice - - liquidity - - tick - - liquidityNet - - fee - properties: - fee: - $ref: '#/components/schemas/Decimal' - kind: - type: string - enum: - - concentratedLiquidity - liquidity: - $ref: '#/components/schemas/U128' - liquidityNet: - type: object - description: A map of tick indices to their liquidity values. - additionalProperties: - $ref: '#/components/schemas/I128' - router: - $ref: '#/components/schemas/Address' - sqrtPrice: - $ref: '#/components/schemas/U256' - tick: - $ref: '#/components/schemas/I32' - tokens: - type: array - items: - $ref: '#/components/schemas/Token' - ConstantProductPool: - type: object - description: A UniswapV2-like constant product liquidity pool for a token pair. - required: - - kind - - router - - tokens - - fee - properties: - fee: - $ref: '#/components/schemas/Decimal' - kind: - type: string - enum: - - constantProduct - router: - $ref: '#/components/schemas/Address' - tokens: - type: object - description: A mapping of token address to its reserve amounts. - additionalProperties: - $ref: '#/components/schemas/TokenReserve' - CustomInteraction: - type: object - description: A searcher-specified custom interaction to be included in the final settlement. - required: - - kind - - target - - value - - callData - - inputs - - outputs - properties: - allowances: - type: array - items: - $ref: '#/components/schemas/Allowance' - description: ERC20 allowances that are required for this custom interaction. - callData: - type: string - description: The EVM calldata bytes. - example: '0x01020304' - inputs: - type: array - items: - $ref: '#/components/schemas/Asset' - kind: - type: string - enum: - - custom - outputs: - type: array - items: - $ref: '#/components/schemas/Asset' - target: - $ref: '#/components/schemas/Address' - value: - $ref: '#/components/schemas/TokenAmount' - DateTime: - type: string - description: An ISO-8601 formatted date-time. - example: 1970-01-01T00:00:00.000Z - Decimal: - type: string - description: An arbitrary-precision decimal value. - example: '13.37' - FeePolicy: - oneOf: - - $ref: '#/components/schemas/SurplusFee' - - $ref: '#/components/schemas/PriceImprovement' - - $ref: '#/components/schemas/VolumeFee' - description: A fee policy that applies to an order - ForeignLimitOrder: - type: object - description: A 0x-like limit order external to CoW Protocol. - required: - - kind - - makerToken - - takerToken - - makerAmount - - takerAmount - - takerTokenFeeAmount - properties: - kind: - type: string - enum: - - limitOrder - makerAmount: - $ref: '#/components/schemas/TokenAmount' - makerToken: - $ref: '#/components/schemas/Token' - takerAmount: - $ref: '#/components/schemas/TokenAmount' - takerToken: - $ref: '#/components/schemas/Token' - takerTokenFeeAmount: - $ref: '#/components/schemas/TokenAmount' - Fulfillment: - type: object - required: - - kind - - order - properties: - executedAmount: - allOf: - - $ref: '#/components/schemas/TokenAmount' - description: The amount of the order that was executed. This is denoted in 'sellToken' for sell orders, and 'buyToken' for buy orders. - fee: - type: object - description: The sell token amount that should be taken as a fee for this trade. This only gets returned for limit orders and only refers to the actual amount filled by the trade. - kind: - type: string - enum: - - fulfillment - order: - allOf: - - $ref: '#/components/schemas/OrderUid' - description: A reference by UID of the order to execute in a solution. The order must be included in the auction input. - I128: - type: string - description: 128 bit signed integer in decimal notation. - example: '-1234567890' - I32: - type: string - description: 32 bit signed integer in decimal notation. - example: '-12345' - Interaction: - allOf: - - type: object - properties: - internalize: - type: boolean - description: A flag indicating that the interaction should be 'internalized', as specified by CIP-2. - - oneOf: - - $ref: '#/components/schemas/LiquidityInteraction' - - $ref: '#/components/schemas/CustomInteraction' - description: An interaction to execute as part of a settlement. - JitOrder: - type: object - description: A just-in-time liquidity order included in a settlement. - required: - - sellToken - - buyToken - - receiver - - sellAmount - - buyAmount - - validTo - - appData - - feeAmount - - kind - - partiallyFillable - - sellTokenBalance - - buyTokenBalance - - signingScheme - - signature - properties: - appData: - $ref: '#/components/schemas/AppData' - buyAmount: - $ref: '#/components/schemas/TokenAmount' - buyToken: - $ref: '#/components/schemas/Token' - buyTokenBalance: - $ref: '#/components/schemas/BuyTokenBalance' - feeAmount: - $ref: '#/components/schemas/TokenAmount' - kind: - $ref: '#/components/schemas/OrderKind' - partiallyFillable: - type: boolean - receiver: - $ref: '#/components/schemas/Address' - sellAmount: - $ref: '#/components/schemas/TokenAmount' - sellToken: - $ref: '#/components/schemas/Token' - sellTokenBalance: - $ref: '#/components/schemas/SellTokenBalance' - signature: - $ref: '#/components/schemas/Signature' - signingScheme: - $ref: '#/components/schemas/SigningScheme' - validTo: - type: integer - format: int32 - minimum: 0 - JitTrade: - type: object - description: A trade with a JIT order. - required: - - kind - - order - - executedAmount - properties: - executedAmount: - allOf: - - $ref: '#/components/schemas/TokenAmount' - description: The amount of the order that was executed. This is denoted in 'sellToken' for sell orders, and 'buyToken' for buy orders. - kind: - type: string - enum: - - jit - order: - allOf: - - $ref: '#/components/schemas/JitOrder' - description: The just-in-time liquidity order to execute in a solution. - Liquidity: - allOf: - - $ref: '#/components/schemas/LiquidityParameters' - - type: object - required: - - id - - address - - gasEstimate - properties: - address: - allOf: - - $ref: '#/components/schemas/Address' - description: A rough approximation of gas units required to use this liquidity on-chain. - gasEstimate: - allOf: - - $ref: '#/components/schemas/BigInt' - description: A rough approximation of gas units required to use this liquidity on-chain. - id: - type: string - description: An opaque ID used for uniquely identifying the liquidity within a single auction (note that they are **not** guaranteed to be unique across auctions). This ID is used in the solution for matching interactions with the executed liquidity. - description: On-chain liquidity that can be used in a solution. This liquidity is provided to facilitate onboarding new solvers. Additional liquidity that is not included in this set may still be used in solutions. - LiquidityInteraction: - type: object - description: Interaction representing the execution of liquidity that was passed in with the auction. - required: - - kind - - id - - inputToken - - outputToken - - inputAmount - - outputAmount - properties: - id: - type: string - description: The ID of executed liquidity provided in the auction input. - inputAmount: - $ref: '#/components/schemas/TokenAmount' - inputToken: - $ref: '#/components/schemas/Token' - kind: - type: string - enum: - - liquidity - outputAmount: - $ref: '#/components/schemas/TokenAmount' - outputToken: - $ref: '#/components/schemas/Token' - LiquidityParameters: - oneOf: - - $ref: '#/components/schemas/ConstantProductPool' - - $ref: '#/components/schemas/WeightedProductPool' - - $ref: '#/components/schemas/StablePool' - - $ref: '#/components/schemas/ConcentratedLiquidityPool' - - $ref: '#/components/schemas/ForeignLimitOrder' - NativePrice: - type: string - description: |- - The price in wei of the native token (Ether on Mainnet for example) to buy - 10**18 of a token. - example: '1234567890' - Order: - type: object - description: CoW Protocol order information relevant to execution. - required: - - uid - - sellToken - - buyToken - - sellAmount - - buyAmount - - kind - - partiallyFillable - - class - properties: - buyAmount: - $ref: '#/components/schemas/TokenAmount' - buyToken: - $ref: '#/components/schemas/Token' - class: - $ref: '#/components/schemas/OrderClass' - feePolicies: - type: array - items: - $ref: '#/components/schemas/FeePolicy' - description: Any protocol fee policies that apply to the order. - nullable: true - kind: - $ref: '#/components/schemas/OrderKind' - partiallyFillable: - type: boolean - description: |- - Whether or not this order can be partially filled. If this is false, - then the order is a "fill-or-kill" order, meaning it needs to be - completely filled or not at all. - sellAmount: - $ref: '#/components/schemas/TokenAmount' - sellToken: - $ref: '#/components/schemas/Token' - uid: - $ref: '#/components/schemas/OrderUid' - additionalProperties: false - OrderClass: - type: string - description: How the CoW Protocol order was classified. - enum: - - market - - limit - - liquidity - OrderKind: - type: string - description: How the CoW Protocol order was classified. - enum: - - sell - - buy - OrderUid: - type: string - description: |- - Unique identifier for the order. Order UIDs are 56 bytes long, where bytes - [0, 32) represent the order digest used for signing, bytes [32, 52) - represent the owner address and bytes [52, 56) represent the order's - `validTo` field. - example: 0x30cff40d9f60caa68a37f0ee73253ad6ad72b45580c945fe3ab67596476937197854163b1b0d24e77dca702b97b5cc33e0f83dcb626122a6 - PriceImprovement: - type: object - description: A cut from the price improvement over the best quote is taken as a protocol fee. - properties: - factor: - type: number - description: The factor of the user surplus that the protocol will request from the solver after settling the order - example: 0.5 - kind: - type: string - enum: - - priceImprovement - maxVolumeFactor: - type: number - description: Never charge more than that percentage of the order volume. - example: 0.01 - maximum: 0.99999 - minimum: 0 - quote: - $ref: '#/components/schemas/Quote' - Quote: - type: object - required: - - sellAmount - - buyAmount - - fee - properties: - buyAmount: - $ref: '#/components/schemas/TokenAmount' - fee: - $ref: '#/components/schemas/TokenAmount' - sellAmount: - $ref: '#/components/schemas/TokenAmount' - additionalProperties: false - SellTokenBalance: - type: string - description: Where should the sell token be drawn from? - enum: - - erc20 - - internal - - external - Signature: - type: string - description: Signature bytes. - example: '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' - SigningScheme: - type: string - description: How was the order signed? - enum: - - eip712 - - ethSign - - preSign - - eip1271 - Solution: - type: object - description: A computed solution for a given auction. - required: - - id - - prices - - trades - - interactions - properties: - gas: - type: integer - format: int64 - description: How many units of gas this solution is estimated to cost. - nullable: true - minimum: 0 - id: - type: number - format: int64 - description: |- - An opaque identifier for the solution. This is a solver generated number - that is unique across multiple solutions within the auction. - interactions: - type: array - items: - $ref: '#/components/schemas/Interaction' - description: Interactions to encode within a settlement. - prices: - type: object - description: |- - A clearing price map of token address to price. The price can have - arbitrary denomination. - additionalProperties: - $ref: '#/components/schemas/U256' - trades: - type: array - items: - $ref: '#/components/schemas/Trade' - description: CoW Protocol order trades included in the solution. - StablePool: - type: object - description: A Curve-like stable pool of N tokens. - required: - - kind - - tokens - - amplificationParameter - - fee - - balancer_pool_id - properties: - amplificationParameter: - $ref: '#/components/schemas/Decimal' - balancer_pool_id: - $ref: '#/components/schemas/BalancerPoolId' - fee: - $ref: '#/components/schemas/Decimal' - kind: - type: string - enum: - - stable - tokens: - type: object - description: A mapping of token address to token balance and scaling rate. - additionalProperties: - allOf: - - $ref: '#/components/schemas/TokenReserve' - - type: object - required: - - scalingFactor - properties: - scalingFactor: - $ref: '#/components/schemas/Decimal' - SurplusFee: - type: object - description: If the order receives more than limit price, pay the protocol a factor of the difference. - properties: - factor: - type: number - description: The factor of the user surplus that the protocol will request from the solver after settling the order - example: 0.5 - kind: - type: string - enum: - - surplus - maxVolumeFactor: - type: number - description: Never charge more than that percentage of the order volume. - example: 0.05 - maximum: 0.99999 - minimum: 0 - Token: - type: string - description: An ERC20 token address. - example: 0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB - TokenAmount: - type: string - description: Amount of an ERC20 token. 256 bit unsigned integer in decimal notation. - example: '1234567890' - TokenInfo: - type: object - description: Information about an ERC20 token. - required: - - availableBalance - - trusted - properties: - availableBalance: - allOf: - - $ref: '#/components/schemas/TokenAmount' - description: The balance held by the Settlement contract that is available during a settlement. - decimals: - type: integer - description: The ERC20.decimals value for this token. This may be missing for ERC20 tokens that don't implement the optional metadata extension. - referencePrice: - allOf: - - $ref: '#/components/schemas/NativePrice' - description: The reference price of this token for the auction used for scoring. This price is only included for tokens for which there are CoW Protocol orders. - symbol: - type: string - description: The ERC20.symbol value for this token. This may be missing for ERC20 tokens that don't implement the optional metadata extension. - trusted: - type: boolean - description: A flag which indicates that solvers are allowed to perform gas cost optimizations for this token by not routing the trades via an AMM, and instead use its available balances, as specified by CIP-2. - TokenReserve: - type: object - required: - - balance - properties: - balance: - $ref: '#/components/schemas/TokenAmount' - additionalProperties: false - Trade: - oneOf: - - $ref: '#/components/schemas/Fulfillment' - - $ref: '#/components/schemas/JitTrade' - description: A trade for a CoW Protocol order included in a solution. - U128: - type: string - description: 128 bit unsigned integer in decimal notation. - example: '1234567890' - U256: - type: string - description: 256 bit unsigned integer in decimal notation. - example: '1234567890' - VolumeFee: - type: object - properties: - factor: - type: number - description: The fraction of the order's volume that the protocol will request from the solver after settling the order. - example: 0.5 - kind: - type: string - enum: - - volume - WeightedProductPool: - type: object - description: A Balancer-like weighted product liquidity pool of N tokens. - required: - - kind - - tokens - - fee - - balancer_pool_id - properties: - balancer_pool_id: - $ref: '#/components/schemas/BalancerPoolId' - fee: - $ref: '#/components/schemas/Decimal' - kind: - type: string - enum: - - weightedProduct - tokens: - type: object - description: A mapping of token address to its reserve amounts with weights. - additionalProperties: - allOf: - - $ref: '#/components/schemas/TokenReserve' - - type: object - required: - - weight - properties: - scalingFactor: - $ref: '#/components/schemas/Decimal' - weight: - $ref: '#/components/schemas/Decimal' - version: - type: string - enum: - - v0 - - v3Plus From 7adcc21a82b661b500467134dd0fc859d9ba2152 Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 26 Apr 2024 18:32:40 +0100 Subject: [PATCH 03/19] Fail on the uncommited changes --- .github/workflows/pull-request.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 6ee5d750be..6f1641f797 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -167,6 +167,17 @@ jobs: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} + - run: rustup toolchain install stable --profile minimal + - uses: Swatinem/rust-cache@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + components: rustfmt + - run: cargo build -p openapi-generator + - name: Check for unstaged changes in generated YAML files + run: | + git diff --exit-code crates/solvers/openapi.yml || { echo "::error ::OpenAPI spec contains changes: crates/solvers/openapi.yml"; exit 1; } - run: npm install @apidevtools/swagger-cli - run: node_modules/.bin/swagger-cli validate crates/orderbook/openapi.yml - run: node_modules/.bin/swagger-cli validate crates/driver/openapi.yml From b5dc2a549d55f46a2e23972bbac8a799463cc1a1 Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 26 Apr 2024 19:00:35 +0100 Subject: [PATCH 04/19] Formatting --- crates/openapi-generator/src/lib.rs | 1 + crates/solvers/src/api/mod.rs | 7 ++--- .../src/api/routes/solve/dto/auction.rs | 28 +++++++++++-------- crates/solvers/src/lib.rs | 7 +++-- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/crates/openapi-generator/src/lib.rs b/crates/openapi-generator/src/lib.rs index e69de29bb2..8b13789179 100644 --- a/crates/openapi-generator/src/lib.rs +++ b/crates/openapi-generator/src/lib.rs @@ -0,0 +1 @@ + diff --git a/crates/solvers/src/api/mod.rs b/crates/solvers/src/api/mod.rs index 44fdcace78..212e128920 100644 --- a/crates/solvers/src/api/mod.rs +++ b/crates/solvers/src/api/mod.rs @@ -51,10 +51,7 @@ impl Api { pub fn generate_openapi_yaml() -> Result { #[derive(OpenApi)] #[openapi( - paths( - routes::solve::solve, - routes::notify::notify, - ), + paths(routes::solve::solve, routes::notify::notify,), components(schemas( solvers_dto::auction::Auction, solvers_dto::auction::TokenInfo, @@ -108,7 +105,7 @@ pub fn generate_openapi_yaml() -> Result { driver implementation.", title = "Solver Engine API", version = "0.1.0", - ), + ) )] pub struct ApiDoc; diff --git a/crates/solvers/src/api/routes/solve/dto/auction.rs b/crates/solvers/src/api/routes/solve/dto/auction.rs index 76824bd456..714d7b9cc2 100644 --- a/crates/solvers/src/api/routes/solve/dto/auction.rs +++ b/crates/solvers/src/api/routes/solve/dto/auction.rs @@ -65,18 +65,22 @@ pub fn to_domain(auction: &Auction) -> Result { .liquidity .iter() .map(|liquidity| match &liquidity.parameter { - LiquidityParameters::ConstantProduct(parameter) => constant_product_pool::to_domain( - liquidity.id.clone(), - liquidity.address, - liquidity.gas_estimate, - parameter, - ), - LiquidityParameters::WeightedProduct(parameter) => weighted_product_pool::to_domain( - liquidity.id.clone(), - liquidity.address, - liquidity.gas_estimate, - parameter, - ), + LiquidityParameters::ConstantProduct(parameter) => { + constant_product_pool::to_domain( + liquidity.id.clone(), + liquidity.address, + liquidity.gas_estimate, + parameter, + ) + } + LiquidityParameters::WeightedProduct(parameter) => { + weighted_product_pool::to_domain( + liquidity.id.clone(), + liquidity.address, + liquidity.gas_estimate, + parameter, + ) + } LiquidityParameters::Stable(parameter) => stable_pool::to_domain( liquidity.id.clone(), liquidity.address, diff --git a/crates/solvers/src/lib.rs b/crates/solvers/src/lib.rs index cc4ce6660d..77d2d3f4db 100644 --- a/crates/solvers/src/lib.rs +++ b/crates/solvers/src/lib.rs @@ -11,6 +11,7 @@ mod run; mod tests; mod util; -pub use self::run::{run, start}; - -pub use api::generate_openapi_yaml; \ No newline at end of file +pub use { + self::run::{run, start}, + api::generate_openapi_yaml, +}; From 90f3385c1a61cec3e5c2da92926ac137791828d4 Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 26 Apr 2024 19:07:48 +0100 Subject: [PATCH 05/19] Comment --- crates/solvers/src/api/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/solvers/src/api/mod.rs b/crates/solvers/src/api/mod.rs index 212e128920..1305f5f344 100644 --- a/crates/solvers/src/api/mod.rs +++ b/crates/solvers/src/api/mod.rs @@ -48,6 +48,7 @@ impl Api { } } +// migrate to utoipauto once the issue is solved https://github.com/ProbablyClem/utoipauto/issues/23 pub fn generate_openapi_yaml() -> Result { #[derive(OpenApi)] #[openapi( From 60b1ceab0607ea9830a778e80d82500519634725 Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 26 Apr 2024 19:17:25 +0100 Subject: [PATCH 06/19] Flatten --- crates/solvers-dto/src/auction.rs | 1 + crates/solvers-dto/src/solution.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/solvers-dto/src/auction.rs b/crates/solvers-dto/src/auction.rs index 2630030e37..18ef8d79e7 100644 --- a/crates/solvers-dto/src/auction.rs +++ b/crates/solvers-dto/src/auction.rs @@ -266,6 +266,7 @@ pub struct Liquidity { /// A rough approximation of gas units required to use this liquidity /// on-chain. pub gas_estimate: U256, + #[serde(flatten)] pub parameter: LiquidityParameters, } diff --git a/crates/solvers-dto/src/solution.rs b/crates/solvers-dto/src/solution.rs index 96b528afcf..9965c858aa 100644 --- a/crates/solvers-dto/src/solution.rs +++ b/crates/solvers-dto/src/solution.rs @@ -233,6 +233,7 @@ pub enum OrderKind { #[serde(tag = "kind", rename_all = "camelCase")] pub struct Interaction { pub internalize: bool, + #[serde(flatten)] pub interaction_type: InteractionType, } From 9c54fbca445a0bc4404ad8577c884ca02bfe0b25 Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 26 Apr 2024 19:42:59 +0100 Subject: [PATCH 07/19] Revert some changes --- crates/solvers-dto/src/auction.rs | 46 +++--- crates/solvers-dto/src/solution.rs | 9 +- .../src/api/routes/solve/dto/auction.rs | 135 +++++------------- .../src/api/routes/solve/dto/solution.rs | 20 +-- crates/solvers/src/api/routes/solve/mod.rs | 1 + crates/solvers/src/tests/mod.rs | 17 ++- 6 files changed, 87 insertions(+), 141 deletions(-) diff --git a/crates/solvers-dto/src/auction.rs b/crates/solvers-dto/src/auction.rs index 18ef8d79e7..990f130106 100644 --- a/crates/solvers-dto/src/auction.rs +++ b/crates/solvers-dto/src/auction.rs @@ -248,26 +248,15 @@ impl ToSchema<'static> for TokenInfo { } } -/// On-chain liquidity that can be used in a solution. This liquidity is -/// provided to facilitate onboarding new solvers. Additional liquidity that is -/// not included in this set may still be used in solutions. #[allow(clippy::enum_variant_names)] #[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct Liquidity { - /// An opaque ID used for uniquely identifying the liquidity within a single - /// auction (note that they are **not** guaranteed to be unique across - /// auctions). This ID is used in the solution for matching interactions - /// with the executed liquidity. - pub id: String, - /// The Ethereum public address of the liquidity. The actual address that is - /// specified is dependent on the kind of liquidity. - pub address: H160, - /// A rough approximation of gas units required to use this liquidity - /// on-chain. - pub gas_estimate: U256, - #[serde(flatten)] - pub parameter: LiquidityParameters, +#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)] +pub enum Liquidity { + ConstantProduct(ConstantProductPool), + WeightedProduct(WeightedProductPool), + Stable(StablePool), + ConcentratedLiquidity(ConcentratedLiquidityPool), + LimitOrder(ForeignLimitOrder), } impl ToSchema<'static> for Liquidity { @@ -360,7 +349,11 @@ impl ToSchema<'static> for LiquidityParameters { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ConstantProductPool { + pub id: String, + pub address: H160, pub router: H160, + #[serde_as(as = "HexOrDecimalU256")] + pub gas_estimate: U256, pub tokens: HashMap, pub fee: BigDecimal, } @@ -414,7 +407,11 @@ pub struct ConstantProductReserve { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WeightedProductPool { + pub id: String, + pub address: H160, pub balancer_pool_id: H256, + #[serde_as(as = "HexOrDecimalU256")] + pub gas_estimate: U256, pub tokens: HashMap, pub fee: BigDecimal, pub version: WeightedProductVersion, @@ -498,7 +495,11 @@ pub enum WeightedProductVersion { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct StablePool { + pub id: String, + pub address: H160, pub balancer_pool_id: H256, + #[serde_as(as = "HexOrDecimalU256")] + pub gas_estimate: U256, pub tokens: HashMap, pub amplification_parameter: BigDecimal, pub fee: BigDecimal, @@ -566,7 +567,11 @@ pub struct StableReserve { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ConcentratedLiquidityPool { + pub id: String, + pub address: H160, pub router: H160, + #[serde_as(as = "HexOrDecimalU256")] + pub gas_estimate: U256, pub tokens: Vec, #[serde_as(as = "HexOrDecimalU256")] pub sqrt_price: U256, @@ -625,7 +630,10 @@ impl ToSchema<'static> for ConcentratedLiquidityPool { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ForeignLimitOrder { - // todo: seems like this is not used anywhere. + pub id: String, + pub address: H160, + #[serde_as(as = "HexOrDecimalU256")] + pub gas_estimate: U256, #[serde_as(as = "serialize::Hex")] pub hash: [u8; 32], pub maker_token: H160, diff --git a/crates/solvers-dto/src/solution.rs b/crates/solvers-dto/src/solution.rs index 9965c858aa..6f538897d5 100644 --- a/crates/solvers-dto/src/solution.rs +++ b/crates/solvers-dto/src/solution.rs @@ -231,10 +231,9 @@ pub enum OrderKind { /// specified by CIP-2. #[derive(Debug, Serialize)] #[serde(tag = "kind", rename_all = "camelCase")] -pub struct Interaction { - pub internalize: bool, - #[serde(flatten)] - pub interaction_type: InteractionType, +pub enum Interaction { + Liquidity(LiquidityInteraction), + Custom(CustomInteraction), } impl ToSchema<'static> for Interaction { @@ -280,6 +279,7 @@ pub enum InteractionType { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct LiquidityInteraction { + pub internalize: bool, /// The ID of executed liquidity provided in the auction input. pub id: String, pub input_token: H160, @@ -338,6 +338,7 @@ impl ToSchema<'static> for LiquidityInteraction { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CustomInteraction { + pub internalize: bool, pub target: H160, #[serde_as(as = "HexOrDecimalU256")] pub value: U256, diff --git a/crates/solvers/src/api/routes/solve/dto/auction.rs b/crates/solvers/src/api/routes/solve/dto/auction.rs index 714d7b9cc2..dc9918201e 100644 --- a/crates/solvers/src/api/routes/solve/dto/auction.rs +++ b/crates/solvers/src/api/routes/solve/dto/auction.rs @@ -64,43 +64,18 @@ pub fn to_domain(auction: &Auction) -> Result { liquidity: auction .liquidity .iter() - .map(|liquidity| match &liquidity.parameter { - LiquidityParameters::ConstantProduct(parameter) => { - constant_product_pool::to_domain( - liquidity.id.clone(), - liquidity.address, - liquidity.gas_estimate, - parameter, - ) + .map(|liquidity| match liquidity { + Liquidity::ConstantProduct(liquidity) => { + constant_product_pool::to_domain(liquidity) } - LiquidityParameters::WeightedProduct(parameter) => { - weighted_product_pool::to_domain( - liquidity.id.clone(), - liquidity.address, - liquidity.gas_estimate, - parameter, - ) + Liquidity::WeightedProduct(liquidity) => { + weighted_product_pool::to_domain(liquidity) } - LiquidityParameters::Stable(parameter) => stable_pool::to_domain( - liquidity.id.clone(), - liquidity.address, - liquidity.gas_estimate, - parameter, - ), - LiquidityParameters::ConcentratedLiquidity(parameter) => { - concentrated_liquidity_pool::to_domain( - liquidity.id.clone(), - liquidity.address, - liquidity.gas_estimate, - parameter, - ) + Liquidity::Stable(liquidity) => stable_pool::to_domain(liquidity), + Liquidity::ConcentratedLiquidity(liquidity) => { + concentrated_liquidity_pool::to_domain(liquidity) } - LiquidityParameters::LimitOrder(parameter) => Ok(foreign_limit_order::to_domain( - liquidity.id.clone(), - liquidity.address, - liquidity.gas_estimate, - parameter, - )), + Liquidity::LimitOrder(liquidity) => Ok(foreign_limit_order::to_domain(liquidity)), }) .try_collect()?, gas_price: auction::GasPrice(eth::Ether(auction.effective_gas_price)), @@ -109,18 +84,9 @@ pub fn to_domain(auction: &Auction) -> Result { } mod constant_product_pool { - use { - super::*, - crate::domain::eth::{H160, U256}, - itertools::Itertools, - }; + use {super::*, itertools::Itertools}; - pub fn to_domain( - id: String, - address: H160, - gas_estimate: U256, - pool: &ConstantProductPool, - ) -> Result { + pub fn to_domain(pool: &ConstantProductPool) -> Result { let reserves = { let (a, b) = pool .tokens @@ -136,9 +102,9 @@ mod constant_product_pool { }; Ok(liquidity::Liquidity { - id: liquidity::Id(id), - address, - gas: eth::Gas(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(&pool.fee).ok_or("invalid constant product fee")?, @@ -148,16 +114,8 @@ mod constant_product_pool { } mod weighted_product_pool { - use { - super::*, - crate::domain::eth::{H160, U256}, - }; - pub fn to_domain( - id: String, - address: H160, - gas_estimate: U256, - pool: &WeightedProductPool, - ) -> Result { + use super::*; + pub fn to_domain(pool: &WeightedProductPool) -> Result { let reserves = { let entries = pool .tokens @@ -181,9 +139,9 @@ mod weighted_product_pool { }; Ok(liquidity::Liquidity { - id: liquidity::Id(id), - address, - gas: eth::Gas(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(&pool.fee).ok_or("invalid weighted product fee")?, @@ -197,17 +155,9 @@ mod weighted_product_pool { } mod stable_pool { - use { - super::*, - crate::domain::eth::{H160, U256}, - }; + use super::*; - pub fn to_domain( - id: String, - address: H160, - gas_estimate: U256, - pool: &StablePool, - ) -> Result { + pub fn to_domain(pool: &StablePool) -> Result { let reserves = { let entries = pool .tokens @@ -228,9 +178,9 @@ mod stable_pool { }; Ok(liquidity::Liquidity { - id: liquidity::Id(id), - address, - gas: eth::Gas(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(&pool.amplification_parameter) @@ -242,18 +192,9 @@ mod stable_pool { } mod concentrated_liquidity_pool { - use { - super::*, - crate::domain::eth::{H160, U256}, - itertools::Itertools, - }; + use {super::*, itertools::Itertools}; - pub fn to_domain( - id: String, - address: H160, - gas_estimate: U256, - pool: &ConcentratedLiquidityPool, - ) -> Result { + pub fn to_domain(pool: &ConcentratedLiquidityPool) -> Result { let tokens = { let (a, b) = pool .tokens @@ -267,9 +208,9 @@ mod concentrated_liquidity_pool { }; Ok(liquidity::Liquidity { - id: liquidity::Id(id), - address, - gas: eth::Gas(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(pool.sqrt_price), @@ -295,21 +236,13 @@ mod concentrated_liquidity_pool { } mod foreign_limit_order { - use { - super::*, - crate::domain::eth::{H160, U256}, - }; + use super::*; - pub fn to_domain( - id: String, - address: H160, - gas_estimate: U256, - order: &ForeignLimitOrder, - ) -> liquidity::Liquidity { + pub fn to_domain(order: &ForeignLimitOrder) -> liquidity::Liquidity { liquidity::Liquidity { - id: liquidity::Id(id), - address, - gas: eth::Gas(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(order.maker_token), diff --git a/crates/solvers/src/api/routes/solve/dto/solution.rs b/crates/solvers/src/api/routes/solve/dto/solution.rs index 9bbac97e56..cf799d4f17 100644 --- a/crates/solvers/src/api/routes/solve/dto/solution.rs +++ b/crates/solvers/src/api/routes/solve/dto/solution.rs @@ -68,22 +68,22 @@ pub fn from_domain(solutions: &[solution::Solution]) -> super::Solutions { .interactions .iter() .map(|interaction| match interaction { - solution::Interaction::Liquidity(interaction) => Interaction { - internalize: interaction.internalize, - interaction_type: InteractionType::Liquidity(LiquidityInteraction { + 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, - }), - }, - solution::Interaction::Custom(interaction) => Interaction { - internalize: interaction.internalize, - interaction_type: InteractionType::Custom(CustomInteraction { + 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() @@ -109,8 +109,8 @@ pub fn from_domain(solutions: &[solution::Solution]) -> super::Solutions { amount: o.amount, }) .collect(), - }), - }, + }) + } }) .collect(), gas: solution.gas.map(|gas| gas.0.as_u64()), diff --git a/crates/solvers/src/api/routes/solve/mod.rs b/crates/solvers/src/api/routes/solve/mod.rs index e7ace8054d..cee305a2ff 100644 --- a/crates/solvers/src/api/routes/solve/mod.rs +++ b/crates/solvers/src/api/routes/solve/mod.rs @@ -23,6 +23,7 @@ pub async fn solve( axum::http::StatusCode, axum::response::Json>, ) { + tracing::info!("newlog received auction={:?}", auction); let handle_request = async { let auction = match dto::auction::to_domain(&auction) { Ok(value) => value, diff --git a/crates/solvers/src/tests/mod.rs b/crates/solvers/src/tests/mod.rs index ef146ff97c..92b96ec1ab 100644 --- a/crates/solvers/src/tests/mod.rs +++ b/crates/solvers/src/tests/mod.rs @@ -72,17 +72,20 @@ impl SolverEngine { pub async fn solve(&self, auction: serde_json::Value) -> serde_json::Value { let client = reqwest::Client::new(); let url = shared::url::join(&self.url, "solve"); + tracing::info!("newlog 1"); let response = client.post(url).json(&auction).send().await.unwrap(); - + tracing::info!("newlog 2"); if !response.status().is_success() { - panic!( - "HTTP {}: {:?}", - response.status(), - response.text().await.unwrap(), - ); + let status = response.status(); + let text = response.text().await.unwrap(); + tracing::info!("newlog text={:?}", text); + panic!("HTTP {}: {:?}", status, text,); } + tracing::info!("newlog 3"); - response.json().await.unwrap() + let json = response.json().await.unwrap(); + tracing::info!("newlog json={:?}", json); + json } } From c50ac2a40607a45bb41af77d2d7011f895045120 Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 26 Apr 2024 19:44:20 +0100 Subject: [PATCH 08/19] Comments --- crates/solvers-dto/src/auction.rs | 2 ++ crates/solvers-dto/src/solution.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/crates/solvers-dto/src/auction.rs b/crates/solvers-dto/src/auction.rs index 990f130106..b61bc5e3ed 100644 --- a/crates/solvers-dto/src/auction.rs +++ b/crates/solvers-dto/src/auction.rs @@ -259,6 +259,8 @@ pub enum Liquidity { LimitOrder(ForeignLimitOrder), } +// todo: Currently, it strictly follows the manual api schema. This has to be +// automated and deleted. impl ToSchema<'static> for Liquidity { fn schema() -> (&'static str, RefOr) { ( diff --git a/crates/solvers-dto/src/solution.rs b/crates/solvers-dto/src/solution.rs index 6f538897d5..e9360d91dd 100644 --- a/crates/solvers-dto/src/solution.rs +++ b/crates/solvers-dto/src/solution.rs @@ -236,6 +236,8 @@ pub enum Interaction { Custom(CustomInteraction), } +// todo: Currently, it strictly follows the manual api schema. This has to be +// automated and deleted. impl ToSchema<'static> for Interaction { fn schema() -> (&'static str, RefOr) { ( From 5029ea0c25701022c9dd1cfb12ee6d1e1a5ecea4 Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 26 Apr 2024 20:00:01 +0100 Subject: [PATCH 09/19] License --- crates/openapi-generator/Cargo.toml | 4 ++-- crates/solvers/openapi.yml | 2 +- crates/solvers/src/api/mod.rs | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/openapi-generator/Cargo.toml b/crates/openapi-generator/Cargo.toml index eb70a0fdcd..dbe947a64d 100644 --- a/crates/openapi-generator/Cargo.toml +++ b/crates/openapi-generator/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "openapi-generator" version = "0.1.0" +authors = ["Cow Protocol Developers "] edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +license = "MIT OR Apache-2.0" [dependencies] diff --git a/crates/solvers/openapi.yml b/crates/solvers/openapi.yml index 29d1f8eb0f..79dba697ea 100644 --- a/crates/solvers/openapi.yml +++ b/crates/solvers/openapi.yml @@ -3,7 +3,7 @@ info: title: Solver Engine API description: The API implemented by solver engines interacting with the reference driver implementation. license: - name: '' + name: MIT OR Apache-2.0 version: 0.1.0 paths: /notify: diff --git a/crates/solvers/src/api/mod.rs b/crates/solvers/src/api/mod.rs index 1305f5f344..3fb82bd3ee 100644 --- a/crates/solvers/src/api/mod.rs +++ b/crates/solvers/src/api/mod.rs @@ -106,6 +106,7 @@ pub fn generate_openapi_yaml() -> Result { driver implementation.", title = "Solver Engine API", version = "0.1.0", + license(name = "MIT OR Apache-2.0") ) )] pub struct ApiDoc; From cd3a5a6a852b5adf29d6ba570558a7b769a32105 Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 26 Apr 2024 20:11:50 +0100 Subject: [PATCH 10/19] Error message --- crates/openapi-generator/build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/openapi-generator/build.rs b/crates/openapi-generator/build.rs index cd62454bae..5d685f8ecf 100644 --- a/crates/openapi-generator/build.rs +++ b/crates/openapi-generator/build.rs @@ -6,8 +6,8 @@ use std::fs; const SOLVERS_OPENAPI_PATH: &str = "../solvers/openapi.yml"; fn main() { - let openapi_yaml = - solvers::generate_openapi_yaml().expect("Error generating the OpenAPI documentation"); + let openapi_yaml = solvers::generate_openapi_yaml() + .expect("Error generating the solvers OpenAPI documentation"); fs::write(SOLVERS_OPENAPI_PATH, openapi_yaml) .expect("Error writing the solvers OpenAPI documentation"); } From 2ce5b3c37a84fe74ac4dcbe8b858bfe5aeabc02d Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 26 Apr 2024 20:17:20 +0100 Subject: [PATCH 11/19] Drop utoipauto for now --- Cargo.lock | 33 --------------------------------- crates/solvers/Cargo.toml | 1 - 2 files changed, 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9d19062e4..f6bcfa8ea9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4330,7 +4330,6 @@ dependencies = [ "tower-http", "tracing", "utoipa", - "utoipauto", "web3", ] @@ -5207,38 +5206,6 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "utoipauto" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8b0206d46c52c964fbbecbf0f4015941000ce1122a3cc6103a64496bc9997e" -dependencies = [ - "utoipauto-macro", -] - -[[package]] -name = "utoipauto-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f188b14783527b8694a62ac420f945049a8ad02ae403c59cd6d6d46ce70e7d00" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", -] - -[[package]] -name = "utoipauto-macro" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8441b6e9e8e22128550e718d08b9da55182e85920e3d307894300cb9fcd4791b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", - "utoipauto-core", -] - [[package]] name = "uuid" version = "1.4.1" diff --git a/crates/solvers/Cargo.toml b/crates/solvers/Cargo.toml index 9b1aa72f18..6cf749fcaf 100644 --- a/crates/solvers/Cargo.toml +++ b/crates/solvers/Cargo.toml @@ -40,7 +40,6 @@ toml = { workspace = true } tower = "0.4" tower-http = { version = "0.4", features = ["limit", "trace"] } utoipa = { version = "4.2.0", features = ["yaml", "axum_extras"] } -utoipauto = "0.1.10" web3 = { workspace = true } # TODO Once solvers are ported and E2E tests set up, slowly migrate code and From e4693684a44bfef158351ae4f57da3bcdbd91963 Mon Sep 17 00:00:00 2001 From: ilya Date: Sun, 28 Apr 2024 10:35:15 +0100 Subject: [PATCH 12/19] Fixes after merge --- crates/solvers-dto/src/auction.rs | 53 ++++++++++--------- crates/solvers-dto/src/common.rs | 37 ++++++++++++++ crates/solvers-dto/src/lib.rs | 1 + crates/solvers-dto/src/solution.rs | 16 ------ crates/solvers/openapi.yml | 82 ++++++++++++++++++++++++++++-- crates/solvers/src/api/mod.rs | 15 ++++-- 6 files changed, 154 insertions(+), 50 deletions(-) create mode 100644 crates/solvers-dto/src/common.rs diff --git a/crates/solvers-dto/src/auction.rs b/crates/solvers-dto/src/auction.rs index 962fe935a0..6e8f901170 100644 --- a/crates/solvers-dto/src/auction.rs +++ b/crates/solvers-dto/src/auction.rs @@ -66,36 +66,42 @@ pub struct Order { #[schema(value_type = TokenAmount)] #[serde_as(as = "HexOrDecimalU256")] pub sell_amount: U256, + #[schema(value_type = TokenAmount)] #[serde_as(as = "HexOrDecimalU256")] pub full_sell_amount: U256, #[schema(value_type = TokenAmount)] #[serde_as(as = "HexOrDecimalU256")] pub buy_amount: U256, + #[schema(value_type = TokenAmount)] #[serde_as(as = "HexOrDecimalU256")] pub full_buy_amount: U256, pub fee_policies: Option>, pub valid_to: u32, pub kind: OrderKind, + #[schema(value_type = Address)] pub receiver: Option, + #[schema(value_type = Address)] pub owner: H160, /// Whether or not this order can be partially filled. If this is false, /// then the order is a "fill-or-kill" order, meaning it needs to be /// completely filled or not at all. pub partially_fillable: bool, - pub pre_interactions: Vec, + pub pre_interactions: Vec, pub post_interactions: Vec, pub sell_token_source: SellTokenSource, pub buy_token_destination: BuyTokenDestination, pub class: OrderClass, + #[schema(value_type = AppData)] pub app_data: AppDataHash, - pub signing_scheme: SigningScheme, + pub signing_scheme: LegacySigningScheme, #[serde(with = "bytes_hex")] + #[schema(value_type = Signature)] pub signature: Vec, } /// Destination for which the buyAmount should be transferred to order's /// receiver to upon fulfillment -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum BuyTokenDestination { /// Pay trade proceeds as an ERC20 token transfer @@ -105,7 +111,7 @@ pub enum BuyTokenDestination { } /// Source from which the sellAmount should be drawn upon order fulfillment -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum SellTokenSource { /// Direct ERC20 allowances to the Vault relayer contract @@ -117,19 +123,25 @@ pub enum SellTokenSource { } #[serde_as] -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct InteractionData { + #[schema(value_type = Address)] pub target: H160, + #[schema(value_type = TokenAmount)] #[serde_as(as = "HexOrDecimalU256")] pub value: U256, + #[schema(value_type = String, example = "0x01020304")] #[serde(with = "bytes_hex")] pub call_data: Vec, } -#[derive(Debug, Deserialize)] +// todo: There is a conflict between solution's SigningScheme which is in +// camelCase. There is no way to keep 2 object with the same name in the OpenAPI +// schema. Temporarily renamed the struct. Must be migrated to the camelCase. +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "lowercase")] -pub enum SigningScheme { +pub enum LegacySigningScheme { Eip712, EthSign, Eip1271, @@ -695,6 +707,7 @@ pub struct ForeignLimitOrder { #[serde_as(as = "HexOrDecimalU256")] pub gas_estimate: U256, #[serde_as(as = "serialize::Hex")] + // todo: not used/not a part of the API schema for some reason pub hash: [u8; 32], pub maker_token: H160, pub taker_token: H160, @@ -746,24 +759,12 @@ impl ToSchema<'static> for ForeignLimitOrder { #[allow(dead_code)] pub struct NativePrice(String); -/// Amount of an ERC20 token. 256 bit unsigned integer in decimal notation. -#[derive(ToSchema)] -#[schema(example = "1234567890")] -#[allow(dead_code)] -pub struct TokenAmount(String); - /// An ISO-8601 formatted date-time. #[derive(ToSchema)] #[schema(example = "1970-01-01T00:00:00.000Z")] #[allow(dead_code)] pub struct DateTime(String); -/// An Ethereum public address. -#[derive(ToSchema)] -#[schema(example = "0x0000000000000000000000000000000000000000")] -#[allow(dead_code)] -pub struct Address(String); - /// An arbitrary-precision integer value. #[derive(ToSchema)] #[schema(example = "1234567890")] @@ -783,12 +784,6 @@ pub struct Decimal(String); #[allow(dead_code)] pub struct BalancerPoolId(String); -/// An ERC20 token address. -#[derive(ToSchema)] -#[schema(example = "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB")] -#[allow(dead_code)] -pub struct Token(String); - #[serde_as] #[derive(ToSchema, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -832,6 +827,14 @@ pub struct I32(String); #[allow(dead_code)] pub struct OrderUid(String); +/// Signature bytes. +#[derive(ToSchema)] +#[schema( + example = "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +)] +#[allow(dead_code)] +pub struct Signature(String); + /// If the order receives more than limit price, pay the protocol a factor of /// the difference. pub struct SurplusFee { diff --git a/crates/solvers-dto/src/common.rs b/crates/solvers-dto/src/common.rs new file mode 100644 index 0000000000..0f16d9380e --- /dev/null +++ b/crates/solvers-dto/src/common.rs @@ -0,0 +1,37 @@ +use utoipa::ToSchema; + +// Structs for the utoipa OpenAPI schema generator. + +/// Signature bytes. +#[derive(ToSchema)] +#[schema( + example = "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +)] +#[allow(dead_code)] +pub struct Signature(String); + +/// 32 bytes of arbitrary application specific data that can be added to an +/// order. This can also be used to ensure uniqueness between two orders with +/// otherwise the exact same parameters. +#[derive(ToSchema)] +#[schema(example = "0x0000000000000000000000000000000000000000000000000000000000000000")] +#[allow(dead_code)] +pub struct AppData(String); + +/// Amount of an ERC20 token. 256 bit unsigned integer in decimal notation. +#[derive(ToSchema)] +#[schema(example = "1234567890")] +#[allow(dead_code)] +pub struct TokenAmount(String); + +/// An Ethereum public address. +#[derive(ToSchema)] +#[schema(example = "0x0000000000000000000000000000000000000000")] +#[allow(dead_code)] +pub struct Address(String); + +/// An ERC20 token address. +#[derive(ToSchema)] +#[schema(example = "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB")] +#[allow(dead_code)] +pub struct Token(String); diff --git a/crates/solvers-dto/src/lib.rs b/crates/solvers-dto/src/lib.rs index b5cbc5a0da..d29ad90735 100644 --- a/crates/solvers-dto/src/lib.rs +++ b/crates/solvers-dto/src/lib.rs @@ -2,6 +2,7 @@ //! communicate with the driver. pub mod auction; +pub mod common; pub mod notification; pub mod solution; diff --git a/crates/solvers-dto/src/solution.rs b/crates/solvers-dto/src/solution.rs index e9360d91dd..d83f867be0 100644 --- a/crates/solvers-dto/src/solution.rs +++ b/crates/solvers-dto/src/solution.rs @@ -528,19 +528,3 @@ impl ToSchema<'static> for SigningScheme { ) } } - -/// Signature bytes. -#[derive(ToSchema)] -#[schema( - example = "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" -)] -#[allow(dead_code)] -pub struct Signature(String); - -/// 32 bytes of arbitrary application specific data that can be added to an -/// order. This can also be used to ensure uniqueness between two orders with -/// otherwise the exact same parameters. -#[derive(ToSchema)] -#[schema(example = "0x0000000000000000000000000000000000000000000000000000000000000000")] -#[allow(dead_code)] -pub struct AppData(String); diff --git a/crates/solvers/openapi.yml b/crates/solvers/openapi.yml index 79dba697ea..a56419ed4c 100644 --- a/crates/solvers/openapi.yml +++ b/crates/solvers/openapi.yml @@ -156,7 +156,6 @@ components: description: A map of token addresses to token information. additionalProperties: $ref: '#/components/schemas/TokenInfo' - additionalProperties: false BalancerPoolId: type: string description: |- @@ -173,6 +172,14 @@ components: enum: - erc20 - internal + BuyTokenDestination: + type: string + description: |- + Destination for which the buyAmount should be transferred to order's + receiver to upon fulfillment + enum: + - erc20 + - internal ConcentratedLiquidityPool: type: object description: A Uniswap V3-like concentrated liquidity pool. @@ -346,6 +353,20 @@ components: - $ref: '#/components/schemas/LiquidityInteraction' - $ref: '#/components/schemas/CustomInteraction' description: An interaction to execute as part of a settlement. + InteractionData: + type: object + required: + - target + - value + - callData + properties: + callData: + type: string + example: '0x01020304' + target: + $ref: '#/components/schemas/Address' + value: + $ref: '#/components/schemas/TokenAmount' JitOrder: type: object description: A just-in-time liquidity order included in a settlement. @@ -415,6 +436,13 @@ components: allOf: - $ref: '#/components/schemas/JitOrder' description: The just-in-time liquidity order to execute in a solution. + LegacySigningScheme: + type: string + enum: + - eip712 + - ethsign + - eip1271 + - presign Liquidity: allOf: - $ref: '#/components/schemas/LiquidityParameters' @@ -483,38 +511,78 @@ components: - sellToken - buyToken - sellAmount + - fullSellAmount - buyAmount + - fullBuyAmount + - validTo - kind + - receiver + - owner - partiallyFillable + - preInteractions + - postInteractions + - sellTokenSource + - buyTokenDestination - class + - appData + - signingScheme + - signature properties: + appData: + $ref: '#/components/schemas/AppData' buyAmount: $ref: '#/components/schemas/TokenAmount' buyToken: $ref: '#/components/schemas/Token' + buyTokenDestination: + $ref: '#/components/schemas/BuyTokenDestination' class: $ref: '#/components/schemas/OrderClass' feePolicies: type: array items: $ref: '#/components/schemas/FeePolicy' - description: Any protocol fee policies that apply to the order. nullable: true + fullBuyAmount: + $ref: '#/components/schemas/TokenAmount' + fullSellAmount: + $ref: '#/components/schemas/TokenAmount' kind: $ref: '#/components/schemas/OrderKind' + owner: + $ref: '#/components/schemas/Address' partiallyFillable: type: boolean description: |- Whether or not this order can be partially filled. If this is false, then the order is a "fill-or-kill" order, meaning it needs to be completely filled or not at all. + postInteractions: + type: array + items: + $ref: '#/components/schemas/InteractionData' + preInteractions: + type: array + items: + $ref: '#/components/schemas/InteractionData' + receiver: + $ref: '#/components/schemas/Address' sellAmount: $ref: '#/components/schemas/TokenAmount' sellToken: $ref: '#/components/schemas/Token' + sellTokenSource: + $ref: '#/components/schemas/SellTokenSource' + signature: + $ref: '#/components/schemas/Signature' + signingScheme: + $ref: '#/components/schemas/LegacySigningScheme' uid: $ref: '#/components/schemas/OrderUid' - additionalProperties: false + validTo: + type: integer + format: int32 + minimum: 0 OrderClass: type: string description: How the CoW Protocol order was classified. @@ -569,7 +637,6 @@ components: $ref: '#/components/schemas/TokenAmount' sellAmount: $ref: '#/components/schemas/TokenAmount' - additionalProperties: false SellTokenBalance: type: string description: Where should the sell token be drawn from? @@ -577,6 +644,13 @@ components: - erc20 - internal - external + SellTokenSource: + type: string + description: Source from which the sellAmount should be drawn upon order fulfillment + enum: + - erc20 + - external + - internal Signature: type: string description: Signature bytes. diff --git a/crates/solvers/src/api/mod.rs b/crates/solvers/src/api/mod.rs index 3fb82bd3ee..3f720bbaee 100644 --- a/crates/solvers/src/api/mod.rs +++ b/crates/solvers/src/api/mod.rs @@ -66,8 +66,6 @@ pub fn generate_openapi_yaml() -> Result { solvers_dto::auction::ConcentratedLiquidityPool, solvers_dto::auction::ForeignLimitOrder, solvers_dto::auction::TokenReserve, - solvers_dto::auction::TokenAmount, - solvers_dto::auction::Token, solvers_dto::auction::BalancerPoolId, solvers_dto::auction::Decimal, solvers_dto::auction::U256Schema, @@ -77,7 +75,6 @@ pub fn generate_openapi_yaml() -> Result { solvers_dto::auction::BigInt, solvers_dto::auction::Order, solvers_dto::auction::OrderUid, - solvers_dto::auction::Address, solvers_dto::auction::FeePolicy, solvers_dto::auction::Quote, solvers_dto::auction::OrderClass, @@ -85,6 +82,11 @@ pub fn generate_openapi_yaml() -> Result { solvers_dto::auction::SurplusFee, solvers_dto::auction::PriceImprovement, solvers_dto::auction::VolumeFee, + solvers_dto::auction::SellTokenSource, + solvers_dto::auction::InteractionData, + solvers_dto::auction::BuyTokenDestination, + solvers_dto::auction::SellTokenSource, + solvers_dto::auction::LegacySigningScheme, solvers_dto::solution::Solution, solvers_dto::solution::Interaction, solvers_dto::solution::CustomInteraction, @@ -95,11 +97,14 @@ pub fn generate_openapi_yaml() -> Result { solvers_dto::solution::Fulfillment, solvers_dto::solution::JitTrade, solvers_dto::solution::JitOrder, - solvers_dto::solution::AppData, solvers_dto::solution::BuyTokenBalance, solvers_dto::solution::SellTokenBalance, - solvers_dto::solution::Signature, solvers_dto::solution::SigningScheme, + solvers_dto::common::Address, + solvers_dto::common::AppData, + solvers_dto::common::Signature, + solvers_dto::common::TokenAmount, + solvers_dto::common::Token, )), info( description = "The API implemented by solver engines interacting with the reference \ From e791b277058ec131b71a71efd04c68a172d423aa Mon Sep 17 00:00:00 2001 From: ilya Date: Sun, 28 Apr 2024 10:41:54 +0100 Subject: [PATCH 13/19] Dependency fix --- crates/solvers/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/solvers/Cargo.toml b/crates/solvers/Cargo.toml index 058fd52f7e..b479c961fe 100644 --- a/crates/solvers/Cargo.toml +++ b/crates/solvers/Cargo.toml @@ -32,7 +32,7 @@ reqwest = { workspace = true } s3 = { path = "../s3" } serde = { workspace = true } serde_json = { workspace = true } -serde_yaml = { versio = "1.0" } +serde_yaml = "0.9.34+deprecated" serde_with = { workspace = true } solvers-dto = { path = "../solvers-dto" } thiserror = { workspace = true } From 5264460d23ebb424eaa3a9c00bd1992a2a2262d6 Mon Sep 17 00:00:00 2001 From: ilya Date: Mon, 29 Apr 2024 10:19:13 +0100 Subject: [PATCH 14/19] Redundant changes --- crates/solvers/src/api/routes/solve/mod.rs | 1 - crates/solvers/src/tests/mod.rs | 17 +++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/crates/solvers/src/api/routes/solve/mod.rs b/crates/solvers/src/api/routes/solve/mod.rs index cee305a2ff..e7ace8054d 100644 --- a/crates/solvers/src/api/routes/solve/mod.rs +++ b/crates/solvers/src/api/routes/solve/mod.rs @@ -23,7 +23,6 @@ pub async fn solve( axum::http::StatusCode, axum::response::Json>, ) { - tracing::info!("newlog received auction={:?}", auction); let handle_request = async { let auction = match dto::auction::to_domain(&auction) { Ok(value) => value, diff --git a/crates/solvers/src/tests/mod.rs b/crates/solvers/src/tests/mod.rs index 92b96ec1ab..ef146ff97c 100644 --- a/crates/solvers/src/tests/mod.rs +++ b/crates/solvers/src/tests/mod.rs @@ -72,20 +72,17 @@ impl SolverEngine { pub async fn solve(&self, auction: serde_json::Value) -> serde_json::Value { let client = reqwest::Client::new(); let url = shared::url::join(&self.url, "solve"); - tracing::info!("newlog 1"); let response = client.post(url).json(&auction).send().await.unwrap(); - tracing::info!("newlog 2"); + if !response.status().is_success() { - let status = response.status(); - let text = response.text().await.unwrap(); - tracing::info!("newlog text={:?}", text); - panic!("HTTP {}: {:?}", status, text,); + panic!( + "HTTP {}: {:?}", + response.status(), + response.text().await.unwrap(), + ); } - tracing::info!("newlog 3"); - let json = response.json().await.unwrap(); - tracing::info!("newlog json={:?}", json); - json + response.json().await.unwrap() } } From 9771fea1011cddae1d967aeffe31923dd0cd77de Mon Sep 17 00:00:00 2001 From: ilya Date: Mon, 29 Apr 2024 15:56:02 +0100 Subject: [PATCH 15/19] Drop duplicated rust docs from structs not deriving ToSchema --- crates/solvers-dto/src/auction.rs | 30 -------------------------- crates/solvers-dto/src/notification.rs | 6 ------ crates/solvers-dto/src/solution.rs | 23 +------------------- crates/solvers/openapi.yml | 1 + 4 files changed, 2 insertions(+), 58 deletions(-) diff --git a/crates/solvers-dto/src/auction.rs b/crates/solvers-dto/src/auction.rs index 6e8f901170..9e532c85a0 100644 --- a/crates/solvers-dto/src/auction.rs +++ b/crates/solvers-dto/src/auction.rs @@ -165,28 +165,18 @@ pub enum OrderClass { Liquidity, } -/// A fee policy that applies to an order. #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub enum FeePolicy { - /// If the order receives more than limit price, pay the protocol a factor - /// of the difference. #[serde(rename_all = "camelCase")] Surplus { - /// The factor of the user surplus that the protocol will request from - /// the solver after settling the order factor: f64, /// Never charge more than that percentage of the order volume. max_volume_factor: f64, }, - /// A cut from the price improvement over the best quote is taken as a - /// protocol fee. #[serde(rename_all = "camelCase")] PriceImprovement { - /// The factor of the user surplus that the protocol will request from - /// the solver after settling the order. factor: f64, - /// Never charge more than that percentage of the order volume. max_volume_factor: f64, quote: Quote, }, @@ -226,29 +216,16 @@ pub struct Quote { pub fee: U256, } -/// Information about a token relevant to the auction. #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TokenInfo { - /// The ERC20.decimals value for this token. This may be missing for ERC20 - /// tokens that don't implement the optional metadata extension. pub decimals: Option, - /// The ERC20.symbol value for this token. This may be missing for ERC20 - /// tokens that don't implement the optional metadata extension. pub symbol: Option, - /// The reference price of this token for the auction used for scoring. This - /// price is only included for tokens for which there are CoW Protocol - /// orders. #[serde_as(as = "Option")] pub reference_price: Option, - /// The balance held by the Settlement contract that is available during a - /// settlement. #[serde_as(as = "HexOrDecimalU256")] pub available_balance: U256, - /// A flag which indicates that solvers are allowed to perform gas cost - /// optimizations for this token by not routing the trades via an AMM, and - /// instead use its available balances, as specified by CIP-2. pub trusted: bool, } @@ -416,7 +393,6 @@ impl ToSchema<'static> for LiquidityParameters { } } -/// A UniswapV2-like constant product liquidity pool for a token pair. #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] @@ -474,7 +450,6 @@ pub struct ConstantProductReserve { pub balance: U256, } -/// A Balancer-like weighted product liquidity pool of N tokens. #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] @@ -562,7 +537,6 @@ pub enum WeightedProductVersion { V3Plus, } -/// A Curve-like stable pool of N tokens. #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] @@ -835,8 +809,6 @@ pub struct OrderUid(String); #[allow(dead_code)] pub struct Signature(String); -/// If the order receives more than limit price, pay the protocol a factor of -/// the difference. pub struct SurplusFee { pub factor: f64, pub max_volume_factor: f64, @@ -888,8 +860,6 @@ impl ToSchema<'static> for SurplusFee { } } -/// A cut from the price improvement over the best quote is taken as a protocol -/// fee. pub struct PriceImprovement { pub factor: f64, pub max_volume_factor: f64, diff --git a/crates/solvers-dto/src/notification.rs b/crates/solvers-dto/src/notification.rs index 4a330f2d64..99471e0f7f 100644 --- a/crates/solvers-dto/src/notification.rs +++ b/crates/solvers-dto/src/notification.rs @@ -11,19 +11,13 @@ use { web3::types::{AccessList, H160, H256, U256}, }; -/// A notification that informs the solver how its solution performed in the -/// auction. Depending on the notification type additional meta data may be -/// attached but this is not guaranteed to be stable. #[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Notification { - /// The auction ID of the auction that the solution was provided for. #[serde_as(as = "Option")] pub auction_id: Option, - /// The solution ID within the auction for which the notification applies pub solution_id: Option, - /// The kind of notification. #[serde(flatten)] pub kind: Kind, } diff --git a/crates/solvers-dto/src/solution.rs b/crates/solvers-dto/src/solution.rs index d83f867be0..15101c2a73 100644 --- a/crates/solvers-dto/src/solution.rs +++ b/crates/solvers-dto/src/solution.rs @@ -50,7 +50,6 @@ pub struct Solution { pub gas: Option, } -/// A trade for a CoW Protocol order included in a solution. #[derive(Debug, Serialize)] #[serde(tag = "kind", rename_all = "camelCase")] pub enum Trade { @@ -76,7 +75,6 @@ impl ToSchema<'static> for Trade { } } -/// A trade which fulfills an order from the auction. #[serde_as] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -96,6 +94,7 @@ impl ToSchema<'static> for Fulfillment { "Fulfillment", Schema::Object( ObjectBuilder::new() + .description(Some("A trade which fulfills an order from the auction.")) .required("kind") .required("order") .property( @@ -227,8 +226,6 @@ pub enum OrderKind { Buy, } -/// A flag indicating that the interaction should be "internalized", as -/// specified by CIP-2. #[derive(Debug, Serialize)] #[serde(tag = "kind", rename_all = "camelCase")] pub enum Interaction { @@ -275,8 +272,6 @@ pub enum InteractionType { Custom(CustomInteraction), } -/// Interaction representing the execution of liquidity that was passed in with -/// the auction. #[serde_as] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -334,8 +329,6 @@ impl ToSchema<'static> for LiquidityInteraction { } } -/// A searcher-specified custom interaction to be included in the final -/// settlement. #[serde_as] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -414,20 +407,6 @@ impl ToSchema<'static> for CustomInteraction { } } -/// 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")] -pub struct OrderInteraction { - pub target: H160, - #[serde_as(as = "HexOrDecimalU256")] - pub value: U256, - #[serde(rename = "callData")] - #[serde_as(as = "serialize::Hex")] - pub calldata: Vec, -} - /// A token address with an amount. #[serde_as] #[derive(Debug, Serialize, ToSchema)] diff --git a/crates/solvers/openapi.yml b/crates/solvers/openapi.yml index a56419ed4c..350a3bfaf4 100644 --- a/crates/solvers/openapi.yml +++ b/crates/solvers/openapi.yml @@ -315,6 +315,7 @@ components: $ref: '#/components/schemas/TokenAmount' Fulfillment: type: object + description: A trade which fulfills an order from the auction. required: - kind - order From ae40b5297cfccf4f15b1962c1cb1a3ea40974b2b Mon Sep 17 00:00:00 2001 From: ilya Date: Mon, 29 Apr 2024 16:18:21 +0100 Subject: [PATCH 16/19] Minor refactoring --- crates/solvers-dto/src/auction.rs | 680 +++++++++++-------------- crates/solvers-dto/src/notification.rs | 95 ++-- crates/solvers-dto/src/solution.rs | 399 ++++++--------- 3 files changed, 500 insertions(+), 674 deletions(-) diff --git a/crates/solvers-dto/src/auction.rs b/crates/solvers-dto/src/auction.rs index 9e532c85a0..8260ddfaf8 100644 --- a/crates/solvers-dto/src/auction.rs +++ b/crates/solvers-dto/src/auction.rs @@ -231,67 +231,49 @@ pub struct TokenInfo { impl ToSchema<'static> for TokenInfo { fn schema() -> (&'static str, RefOr) { - ( - "TokenInfo", - Schema::Object( - ObjectBuilder::new() - .description(Some("Information about an ERC20 token.")) - .required("availableBalance") - .required("trusted") - .property( - "decimals", - ObjectBuilder::new() - .schema_type(SchemaType::Integer) - .description(Some( - "The ERC20.decimals value for this token. This may be missing for \ - ERC20 tokens that don't implement the optional metadata \ - extension.", - )), - ) - .property( - "symbol", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .description(Some( - "The ERC20.symbol value for this token. This may be missing for \ - ERC20 tokens that don't implement the optional metadata \ - extension.", - )), - ) - .property( - "referencePrice", - AllOfBuilder::new() - .description(Some( - "The reference price of this token for the auction used for \ - scoring. This price is only included for tokens for which there \ - are CoW Protocol orders.", - )) - .item(Ref::from_schema_name("NativePrice")), - ) - .property( - "availableBalance", - AllOfBuilder::new() - .description(Some( - "The balance held by the Settlement contract that is available \ - during a settlement.", - )) - .item(Ref::from_schema_name("TokenAmount")), - ) - .property( - "trusted", - ObjectBuilder::new() - .schema_type(SchemaType::Boolean) - .description(Some( - "A flag which indicates that solvers are allowed to perform gas \ - cost optimizations for this token by not routing the trades via \ - an AMM, and instead use its available balances, as specified by \ - CIP-2.", - )), - ) - .build(), - ) - .into(), - ) + let decimals = ObjectBuilder::new() + .schema_type(SchemaType::Integer) + .description(Some( + "The ERC20.decimals value for this token. This may be missing for ERC20 tokens \ + that don't implement the optional metadata extension.", + )); + let symbol = ObjectBuilder::new() + .schema_type(SchemaType::String) + .description(Some( + "The ERC20.symbol value for this token. This may be missing for ERC20 tokens that \ + don't implement the optional metadata extension.", + )); + let reference_price = AllOfBuilder::new() + .description(Some( + "The reference price of this token for the auction used for scoring. This price \ + is only included for tokens for which there are CoW Protocol orders.", + )) + .item(Ref::from_schema_name("NativePrice")); + let available_balance = AllOfBuilder::new() + .description(Some( + "The balance held by the Settlement contract that is available during a \ + settlement.", + )) + .item(Ref::from_schema_name("TokenAmount")); + let trusted = ObjectBuilder::new() + .schema_type(SchemaType::Boolean) + .description(Some( + "A flag which indicates that solvers are allowed to perform gas cost \ + optimizations for this token by not routing the trades via an AMM, and instead \ + use its available balances, as specified by CIP-2.", + )); + let auction = ObjectBuilder::new() + .description(Some("Information about an ERC20 token.")) + .required("availableBalance") + .required("trusted") + .property("decimals", decimals) + .property("symbol", symbol) + .property("referencePrice", reference_price) + .property("availableBalance", available_balance) + .property("trusted", trusted) + .build(); + + ("TokenInfo", Schema::Object(auction).into()) } } @@ -310,57 +292,42 @@ pub enum Liquidity { // automated and deleted. impl ToSchema<'static> for Liquidity { fn schema() -> (&'static str, RefOr) { - ( - "Liquidity", - Schema::AllOf( - AllOfBuilder::new() - .description(Some( - "On-chain liquidity that can be used in a solution. This liquidity is \ - provided to facilitate onboarding new solvers. Additional liquidity that \ - is not included in this set may still be used in solutions.", - )) - .item(Ref::from_schema_name("LiquidityParameters")) - .item(Schema::Object( - ObjectBuilder::new() - .property( - "id", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .description(Some( - "An opaque ID used for uniquely identifying the liquidity \ - within a single auction (note that they are **not** \ - guaranteed to be unique across auctions). This ID is \ - used in the solution for matching interactions with the \ - executed liquidity.", - )), - ) - .property( - "address", - AllOfBuilder::new() - .description(Some( - "A rough approximation of gas units required to use this \ - liquidity on-chain.", - )) - .item(Ref::from_schema_name("Address")), - ) - .property( - "gasEstimate", - AllOfBuilder::new() - .description(Some( - "A rough approximation of gas units required to use this \ - liquidity on-chain.", - )) - .item(Ref::from_schema_name("BigInt")), - ) - .required("id") - .required("address") - .required("gasEstimate") - .build(), - )) - .build(), - ) - .into(), - ) + let id = ObjectBuilder::new() + .schema_type(SchemaType::String) + .description(Some( + "An opaque ID used for uniquely identifying the liquidity within a single auction \ + (note that they are **not** guaranteed to be unique across auctions). This ID is \ + used in the solution for matching interactions with the executed liquidity.", + )); + let address = AllOfBuilder::new() + .description(Some( + "A rough approximation of gas units required to use this liquidity on-chain.", + )) + .item(Ref::from_schema_name("Address")); + let gas_estimate = AllOfBuilder::new() + .description(Some( + "A rough approximation of gas units required to use this liquidity on-chain.", + )) + .item(Ref::from_schema_name("BigInt")); + let liquidity_obj = ObjectBuilder::new() + .property("id", id) + .property("address", address) + .property("gasEstimate", gas_estimate) + .required("id") + .required("address") + .required("gasEstimate") + .build(); + let liquidity = AllOfBuilder::new() + .description(Some( + "On-chain liquidity that can be used in a solution. This liquidity is provided to \ + facilitate onboarding new solvers. Additional liquidity that is not included in \ + this set may still be used in solutions.", + )) + .item(Ref::from_schema_name("LiquidityParameters")) + .item(Schema::Object(liquidity_obj)) + .build(); + + ("Liquidity", Schema::AllOf(liquidity).into()) } } @@ -408,34 +375,29 @@ pub struct ConstantProductPool { impl ToSchema<'static> for ConstantProductPool { fn schema() -> (&'static str, RefOr) { + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["constantProduct"])); + let tokens = ObjectBuilder::new() + .description(Some("A mapping of token address to its reserve amounts.")) + .additional_properties(Some(Ref::from_schema_name("TokenReserve"))); + let constant_product_pool = ObjectBuilder::new() + .description(Some( + "A UniswapV2-like constant product liquidity pool for a token pair.", + )) + .required("kind") + .required("router") + .required("tokens") + .required("fee") + .property("kind", kind) + .property("router", Ref::from_schema_name("Address")) + .property("tokens", tokens) + .property("fee", Ref::from_schema_name("Decimal")) + .build(); + ( "ConstantProductPool", - Schema::Object( - ObjectBuilder::new() - .description(Some( - "A UniswapV2-like constant product liquidity pool for a token pair.", - )) - .required("kind") - .required("router") - .required("tokens") - .required("fee") - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["constantProduct"])), - ) - .property("router", Ref::from_schema_name("Address")) - .property( - "tokens", - ObjectBuilder::new() - .description(Some("A mapping of token address to its reserve amounts.")) - .additional_properties(Some(Ref::from_schema_name("TokenReserve"))), - ) - .property("fee", Ref::from_schema_name("Decimal")) - .build(), - ) - .into(), + Schema::Object(constant_product_pool).into(), ) } } @@ -466,56 +428,46 @@ pub struct WeightedProductPool { impl ToSchema<'static> for WeightedProductPool { fn schema() -> (&'static str, RefOr) { - ( - "WeightedProductPool", - Schema::Object( - ObjectBuilder::new() - .description(Some( - "A Balancer-like weighted product liquidity pool of N tokens.", - )) - .required("kind") - .required("tokens") - .required("fee") - .required("balancer_pool_id") - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["weightedProduct"])), - ) - .property( - "tokens", - ObjectBuilder::new() - .description(Some( - "A mapping of token address to its reserve amounts with weights.", - )) - .additional_properties(Some(Schema::AllOf( - AllOfBuilder::new() - .item(Ref::from_schema_name("TokenReserve")) - .item( - ObjectBuilder::new() - .required("weight") - .property("weight", Ref::from_schema_name("Decimal")) - .property( - "scalingFactor", - Ref::from_schema_name("Decimal"), - ) - .build(), - ) - .build(), - ))), - ) - .property("fee", Ref::from_schema_name("Decimal")) - .property("balancer_pool_id", Ref::from_schema_name("BalancerPoolId")) - .property( - "version", + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["weightedProduct"])); + let tokens = ObjectBuilder::new() + .description(Some( + "A mapping of token address to its reserve amounts with weights.", + )) + .additional_properties(Some(Schema::AllOf( + AllOfBuilder::new() + .item(Ref::from_schema_name("TokenReserve")) + .item( ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["v0", "v3Plus"])), + .required("weight") + .property("weight", Ref::from_schema_name("Decimal")) + .property("scalingFactor", Ref::from_schema_name("Decimal")) + .build(), ) .build(), - ) - .into(), + ))); + let version = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["v0", "v3Plus"])); + let weighted_product_pool = ObjectBuilder::new() + .description(Some( + "A Balancer-like weighted product liquidity pool of N tokens.", + )) + .required("kind") + .required("tokens") + .required("fee") + .required("balancer_pool_id") + .property("kind", kind) + .property("tokens", tokens) + .property("fee", Ref::from_schema_name("Decimal")) + .property("balancer_pool_id", Ref::from_schema_name("BalancerPoolId")) + .property("version", version) + .build(); + + ( + "WeightedProductPool", + Schema::Object(weighted_product_pool).into(), ) } } @@ -553,50 +505,41 @@ pub struct StablePool { impl ToSchema<'static> for StablePool { fn schema() -> (&'static str, RefOr) { - ( - "StablePool", - Schema::Object( - ObjectBuilder::new() - .description(Some("A Curve-like stable pool of N tokens.")) - .required("kind") - .required("tokens") - .required("amplificationParameter") - .required("fee") - .required("balancer_pool_id") - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["stable"])), - ) - .property( - "tokens", + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["stable"])); + let tokens = ObjectBuilder::new() + .description(Some( + "A mapping of token address to token balance and scaling rate.", + )) + .additional_properties(Some(Schema::AllOf( + AllOfBuilder::new() + .item(Ref::from_schema_name("TokenReserve")) + .item( ObjectBuilder::new() - .description(Some( - "A mapping of token address to token balance and scaling rate.", - )) - .additional_properties(Some(Schema::AllOf( - AllOfBuilder::new() - .item(Ref::from_schema_name("TokenReserve")) - .item( - ObjectBuilder::new() - .required("scalingFactor") - .property( - "scalingFactor", - Ref::from_schema_name("Decimal"), - ) - .build(), - ) - .build(), - ))), + .required("scalingFactor") + .property("scalingFactor", Ref::from_schema_name("Decimal")) + .build(), ) - .property("amplificationParameter", Ref::from_schema_name("Decimal")) - .property("fee", Ref::from_schema_name("Decimal")) - .property("balancer_pool_id", Ref::from_schema_name("BalancerPoolId")) .build(), - ) - .into(), - ) + ))); + let stable_pool = Schema::Object( + ObjectBuilder::new() + .description(Some("A Curve-like stable pool of N tokens.")) + .required("kind") + .required("tokens") + .required("amplificationParameter") + .required("fee") + .required("balancer_pool_id") + .property("kind", kind) + .property("tokens", tokens) + .property("amplificationParameter", Ref::from_schema_name("Decimal")) + .property("fee", Ref::from_schema_name("Decimal")) + .property("balancer_pool_id", Ref::from_schema_name("BalancerPoolId")) + .build(), + ); + + ("StablePool", stable_pool.into()) } } @@ -631,43 +574,36 @@ pub struct ConcentratedLiquidityPool { impl ToSchema<'static> for ConcentratedLiquidityPool { fn schema() -> (&'static str, RefOr) { + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["concentratedLiquidity"])); + let liquidity_net = ObjectBuilder::new() + .description(Some("A map of tick indices to their liquidity values.")) + .additional_properties(Some(Ref::from_schema_name("I128"))); + let tokens = ArrayBuilder::new().items(Ref::from_schema_name("Token")); + let concentrated_liquidity_pool = ObjectBuilder::new() + .description(Some("A Uniswap V3-like concentrated liquidity pool.")) + .required("kind") + .required("router") + .required("tokens") + .required("sqrtPrice") + .required("liquidity") + .required("tick") + .required("liquidityNet") + .required("fee") + .property("kind", kind) + .property("router", Ref::from_schema_name("Address")) + .property("tokens", tokens) + .property("sqrtPrice", Ref::from_schema_name("U256")) + .property("liquidity", Ref::from_schema_name("U128")) + .property("tick", Ref::from_schema_name("I32")) + .property("liquidityNet", liquidity_net) + .property("fee", Ref::from_schema_name("Decimal")) + .build(); + ( "ConcentratedLiquidityPool", - Schema::Object( - ObjectBuilder::new() - .description(Some("A Uniswap V3-like concentrated liquidity pool.")) - .required("kind") - .required("router") - .required("tokens") - .required("sqrtPrice") - .required("liquidity") - .required("tick") - .required("liquidityNet") - .required("fee") - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["concentratedLiquidity"])), - ) - .property("router", Ref::from_schema_name("Address")) - .property( - "tokens", - ArrayBuilder::new().items(Ref::from_schema_name("Token")), - ) - .property("sqrtPrice", Ref::from_schema_name("U256")) - .property("liquidity", Ref::from_schema_name("U128")) - .property("tick", Ref::from_schema_name("I32")) - .property( - "liquidityNet", - ObjectBuilder::new() - .description(Some("A map of tick indices to their liquidity values.")) - .additional_properties(Some(Ref::from_schema_name("I128"))), - ) - .property("fee", Ref::from_schema_name("Decimal")) - .build(), - ) - .into(), + Schema::Object(concentrated_liquidity_pool).into(), ) } } @@ -695,31 +631,28 @@ pub struct ForeignLimitOrder { impl ToSchema<'static> for ForeignLimitOrder { fn schema() -> (&'static str, RefOr) { + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["limitOrder"])); + let foreign_limit_order = ObjectBuilder::new() + .description(Some("A 0x-like limit order external to CoW Protocol.")) + .required("kind") + .required("makerToken") + .required("takerToken") + .required("makerAmount") + .required("takerAmount") + .required("takerTokenFeeAmount") + .property("kind", kind) + .property("makerToken", Ref::from_schema_name("Token")) + .property("takerToken", Ref::from_schema_name("Token")) + .property("makerAmount", Ref::from_schema_name("TokenAmount")) + .property("takerAmount", Ref::from_schema_name("TokenAmount")) + .property("takerTokenFeeAmount", Ref::from_schema_name("TokenAmount")) + .build(); + ( "ForeignLimitOrder", - Schema::Object( - ObjectBuilder::new() - .description(Some("A 0x-like limit order external to CoW Protocol.")) - .required("kind") - .required("makerToken") - .required("takerToken") - .required("makerAmount") - .required("takerAmount") - .required("takerTokenFeeAmount") - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["limitOrder"])), - ) - .property("makerToken", Ref::from_schema_name("Token")) - .property("takerToken", Ref::from_schema_name("Token")) - .property("makerAmount", Ref::from_schema_name("TokenAmount")) - .property("takerAmount", Ref::from_schema_name("TokenAmount")) - .property("takerTokenFeeAmount", Ref::from_schema_name("TokenAmount")) - .build(), - ) - .into(), + Schema::Object(foreign_limit_order).into(), ) } } @@ -816,47 +749,35 @@ pub struct SurplusFee { impl ToSchema<'static> for SurplusFee { fn schema() -> (&'static str, RefOr) { - ( - "SurplusFee", - Schema::Object( - ObjectBuilder::new() - .description(Some( - "If the order receives more than limit price, pay the protocol a factor \ - of the difference.", - )) - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["surplus"])), - ) - .property( - "factor", - ObjectBuilder::new() - .description(Some( - "The factor of the user surplus that the protocol will request \ - from the solver after settling the order", - )) - .schema_type(SchemaType::Number) - .example(Number::from_f64(0.5).map(Value::Number)) - .build(), - ) - .property( - "maxVolumeFactor", - ObjectBuilder::new() - .description(Some( - "Never charge more than that percentage of the order volume.", - )) - .schema_type(SchemaType::Number) - .example(Number::from_f64(0.05).map(Value::Number)) - .minimum(Some(0.0)) - .maximum(Some(0.99999)) - .build(), - ) - .build(), - ) - .into(), - ) + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["surplus"])); + let factor = ObjectBuilder::new() + .description(Some( + "The factor of the user surplus that the protocol will request from the solver \ + after settling the order", + )) + .schema_type(SchemaType::Number) + .example(Number::from_f64(0.5).map(Value::Number)); + let max_volume_factor = ObjectBuilder::new() + .description(Some( + "Never charge more than that percentage of the order volume.", + )) + .schema_type(SchemaType::Number) + .example(Number::from_f64(0.05).map(Value::Number)) + .minimum(Some(0.0)) + .maximum(Some(0.99999)); + let surplus_fee = ObjectBuilder::new() + .description(Some( + "If the order receives more than limit price, pay the protocol a factor of the \ + difference.", + )) + .property("kind", kind) + .property("factor", factor) + .property("maxVolumeFactor", max_volume_factor) + .build(); + + ("SurplusFee", Schema::Object(surplus_fee).into()) } } @@ -868,46 +789,35 @@ pub struct PriceImprovement { impl ToSchema<'static> for PriceImprovement { fn schema() -> (&'static str, RefOr) { - ( - "PriceImprovement", - Schema::Object( - ObjectBuilder::new() - .description(Some( - "A cut from the price improvement over the best quote is taken as a \ - protocol fee.", - )) - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["priceImprovement"])), - ) - .property( - "factor", - ObjectBuilder::new() - .description(Some( - "The factor of the user surplus that the protocol will request \ - from the solver after settling the order", - )) - .schema_type(SchemaType::Number) - .example(Number::from_f64(0.5).map(Value::Number)), - ) - .property( - "maxVolumeFactor", - ObjectBuilder::new() - .description(Some( - "Never charge more than that percentage of the order volume.", - )) - .schema_type(SchemaType::Number) - .example(Number::from_f64(0.01).map(Value::Number)) - .minimum(Some(0.0)) - .maximum(Some(0.99999)), - ) - .property("quote", Ref::from_schema_name("Quote")) - .build(), - ) - .into(), - ) + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["priceImprovement"])); + let factor = ObjectBuilder::new() + .description(Some( + "The factor of the user surplus that the protocol will request from the solver \ + after settling the order", + )) + .schema_type(SchemaType::Number) + .example(Number::from_f64(0.5).map(Value::Number)); + let max_volume_factor = ObjectBuilder::new() + .description(Some( + "Never charge more than that percentage of the order volume.", + )) + .schema_type(SchemaType::Number) + .example(Number::from_f64(0.01).map(Value::Number)) + .minimum(Some(0.0)) + .maximum(Some(0.99999)); + let price_improvement = ObjectBuilder::new() + .description(Some( + "A cut from the price improvement over the best quote is taken as a protocol fee.", + )) + .property("kind", kind) + .property("factor", factor) + .property("maxVolumeFactor", max_volume_factor) + .property("quote", Ref::from_schema_name("Quote")) + .build(); + + ("PriceImprovement", Schema::Object(price_improvement).into()) } } @@ -917,29 +827,21 @@ pub struct VolumeFee { impl ToSchema<'static> for VolumeFee { fn schema() -> (&'static str, RefOr) { - ( - "VolumeFee", - Schema::Object( - ObjectBuilder::new() - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["volume"])), - ) - .property( - "factor", - ObjectBuilder::new() - .description(Some( - "The fraction of the order's volume that the protocol will \ - request from the solver after settling the order.", - )) - .schema_type(SchemaType::Number) - .example(Number::from_f64(0.5).map(Value::Number)), - ) - .build(), - ) - .into(), - ) + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["volume"])); + let factor = ObjectBuilder::new() + .description(Some( + "The fraction of the order's volume that the protocol will request from the \ + solver after settling the order.", + )) + .schema_type(SchemaType::Number) + .example(Number::from_f64(0.5).map(Value::Number)); + let volume_fee = ObjectBuilder::new() + .property("kind", kind) + .property("factor", factor) + .build(); + + ("VolumeFee", Schema::Object(volume_fee).into()) } } diff --git a/crates/solvers-dto/src/notification.rs b/crates/solvers-dto/src/notification.rs index 99471e0f7f..38d001fd90 100644 --- a/crates/solvers-dto/src/notification.rs +++ b/crates/solvers-dto/src/notification.rs @@ -25,59 +25,48 @@ pub struct Notification { // serde(flatten) has a conflict with the current API schema impl ToSchema<'static> for Notification { fn schema() -> (&'static str, RefOr) { - ( - "Notification", - Schema::Object( - ObjectBuilder::new() - .description(Some( - "A notification that informs the solver how its solution performed in the \ - auction. Depending on the notification type additional meta data may be \ - attached but this is not guaranteed to be stable.", - )) - .schema_type(SchemaType::Object) - .property( - "auctionId", - ObjectBuilder::new() - .description(Some( - "The auction ID of the auction that the solution was providedfor.", - )) - .schema_type(SchemaType::String), - ) - .property( - "solutionId", - ObjectBuilder::new() - .description(Some( - "The solution ID within the auction for which the notification \ - applies", - )) - .schema_type(SchemaType::Number), - ) - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some([ - "timeout", - "emptySolution", - "duplicatedSolutionId", - "simulationFailed", - "invalidClearingPrices", - "missingPrice", - "invalidExecutedAmount", - "nonBufferableTokensUsed", - "solverAccountInsufficientBalance", - "success", - "revert", - "driverError", - "cancelled", - "fail", - "postprocessingTimedOut", - ])), - ) - .build(), - ) - .into(), - ) + let auction_id = ObjectBuilder::new() + .description(Some( + "The auction ID of the auction that the solution was providedfor.", + )) + .schema_type(SchemaType::String); + let solution_id = ObjectBuilder::new() + .description(Some( + "The solution ID within the auction for which the notification applies", + )) + .schema_type(SchemaType::Number); + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some([ + "timeout", + "emptySolution", + "duplicatedSolutionId", + "simulationFailed", + "invalidClearingPrices", + "missingPrice", + "invalidExecutedAmount", + "nonBufferableTokensUsed", + "solverAccountInsufficientBalance", + "success", + "revert", + "driverError", + "cancelled", + "fail", + "postprocessingTimedOut", + ])); + let notification = ObjectBuilder::new() + .description(Some( + "A notification that informs the solver how its solution performed in the \ + auction. Depending on the notification type additional meta data may be attached \ + but this is not guaranteed to be stable.", + )) + .schema_type(SchemaType::Object) + .property("auctionId", auction_id) + .property("solutionId", solution_id) + .property("kind", kind) + .build(); + + ("Notification", Schema::Object(notification).into()) } } diff --git a/crates/solvers-dto/src/solution.rs b/crates/solvers-dto/src/solution.rs index 15101c2a73..44e27952bd 100644 --- a/crates/solvers-dto/src/solution.rs +++ b/crates/solvers-dto/src/solution.rs @@ -59,19 +59,15 @@ pub enum Trade { impl ToSchema<'static> for Trade { fn schema() -> (&'static str, RefOr) { - ( - "Trade", - Schema::OneOf( - OneOfBuilder::new() - .description(Some( - "A trade for a CoW Protocol order included in a solution.", - )) - .item(Ref::from_schema_name("Fulfillment")) - .item(Ref::from_schema_name("JitTrade")) - .build(), - ) - .into(), - ) + let trade = OneOfBuilder::new() + .description(Some( + "A trade for a CoW Protocol order included in a solution.", + )) + .item(Ref::from_schema_name("Fulfillment")) + .item(Ref::from_schema_name("JitTrade")) + .build(); + + ("Trade", Schema::OneOf(trade).into()) } } @@ -90,49 +86,36 @@ pub struct Fulfillment { impl ToSchema<'static> for Fulfillment { fn schema() -> (&'static str, RefOr) { - ( - "Fulfillment", - Schema::Object( - ObjectBuilder::new() - .description(Some("A trade which fulfills an order from the auction.")) - .required("kind") - .required("order") - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["fulfillment"])), - ) - .property( - "order", - AllOfBuilder::new() - .item(Ref::from_schema_name("OrderUid")) - .description(Some( - "A reference by UID of the order to execute in a solution. The \ - order must be included in the auction input.", - )), - ) - .property( - "executedAmount", - AllOfBuilder::new() - .description(Some( - "The amount of the order that was executed. This is denoted in \ - 'sellToken' for sell orders, and 'buyToken' for buy orders.", - )) - .item(Ref::from_schema_name("TokenAmount")), - ) - .property( - "fee", - ObjectBuilder::new().description(Some( - "The sell token amount that should be taken as a fee for this trade. \ - This only gets returned for limit orders and only refers to the \ - actual amount filled by the trade.", - )), - ) - .build(), - ) - .into(), - ) + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["fulfillment"])); + let order = AllOfBuilder::new() + .item(Ref::from_schema_name("OrderUid")) + .description(Some( + "A reference by UID of the order to execute in a solution. The order must be \ + included in the auction input.", + )); + let executed_amount = AllOfBuilder::new() + .description(Some( + "The amount of the order that was executed. This is denoted in 'sellToken' for \ + sell orders, and 'buyToken' for buy orders.", + )) + .item(Ref::from_schema_name("TokenAmount")); + let fee = ObjectBuilder::new().description(Some( + "The sell token amount that should be taken as a fee for this trade. This only gets \ + returned for limit orders and only refers to the actual amount filled by the trade.", + )); + let fulfillment = ObjectBuilder::new() + .description(Some("A trade which fulfills an order from the auction.")) + .required("kind") + .required("order") + .property("kind", kind) + .property("order", order) + .property("executedAmount", executed_amount) + .property("fee", fee) + .build(); + + ("Fulfillment", Schema::Object(fulfillment).into()) } } @@ -147,41 +130,31 @@ pub struct JitTrade { impl ToSchema<'static> for JitTrade { fn schema() -> (&'static str, RefOr) { - ( - "JitTrade", - Schema::Object( - ObjectBuilder::new() - .description(Some("A trade with a JIT order.")) - .required("kind") - .required("order") - .required("executedAmount") - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["jit"])), - ) - .property( - "order", - AllOfBuilder::new() - .description(Some( - "The just-in-time liquidity order to execute in a solution.", - )) - .item(Ref::from_schema_name("JitOrder")), - ) - .property( - "executedAmount", - AllOfBuilder::new() - .description(Some( - "The amount of the order that was executed. This is denoted in \ - 'sellToken' for sell orders, and 'buyToken' for buy orders.", - )) - .item(Ref::from_schema_name("TokenAmount")), - ) - .build(), - ) - .into(), - ) + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["jit"])); + let order = AllOfBuilder::new() + .description(Some( + "The just-in-time liquidity order to execute in a solution.", + )) + .item(Ref::from_schema_name("JitOrder")); + let executed_amount = AllOfBuilder::new() + .description(Some( + "The amount of the order that was executed. This is denoted in 'sellToken' for \ + sell orders, and 'buyToken' for buy orders.", + )) + .item(Ref::from_schema_name("TokenAmount")); + let jit_trade = ObjectBuilder::new() + .description(Some("A trade with a JIT order.")) + .required("kind") + .required("order") + .required("executedAmount") + .property("kind", kind) + .property("order", order) + .property("executedAmount", executed_amount) + .build(); + + ("JitTrade", Schema::Object(jit_trade).into()) } } @@ -237,32 +210,26 @@ pub enum Interaction { // automated and deleted. impl ToSchema<'static> for Interaction { fn schema() -> (&'static str, RefOr) { - ( - "Interaction", - Schema::AllOf( - AllOfBuilder::new() - .description(Some("An interaction to execute as part of a settlement.")) - .item( - ObjectBuilder::new().property( - "internalize", - ObjectBuilder::new() - .schema_type(SchemaType::Boolean) - .description(Some( - "A flag indicating that the interaction should be \ - 'internalized', as specified by CIP-2.", - )) - .build(), - ), - ) - .item( - OneOfBuilder::new() - .item(Ref::from_schema_name("LiquidityInteraction")) - .item(Ref::from_schema_name("CustomInteraction")), - ) - .build(), + let internalize = ObjectBuilder::new().property( + "internalize", + ObjectBuilder::new() + .schema_type(SchemaType::Boolean) + .description(Some( + "A flag indicating that the interaction should be 'internalized', as \ + specified by CIP-2.", + )), + ); + let interaction = AllOfBuilder::new() + .description(Some("An interaction to execute as part of a settlement.")) + .item(internalize) + .item( + OneOfBuilder::new() + .item(Ref::from_schema_name("LiquidityInteraction")) + .item(Ref::from_schema_name("CustomInteraction")), ) - .into(), - ) + .build(); + + ("Interaction", Schema::AllOf(interaction).into()) } } @@ -289,42 +256,36 @@ pub struct LiquidityInteraction { impl ToSchema<'static> for LiquidityInteraction { fn schema() -> (&'static str, RefOr) { + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["liquidity"])); + let id = ObjectBuilder::new() + .schema_type(SchemaType::String) + .description(Some( + "The ID of executed liquidity provided in the auction input.", + )); + let liquidity_interaction = ObjectBuilder::new() + .description(Some( + "Interaction representing the execution of liquidity that was passed in with the \ + auction.", + )) + .required("kind") + .required("id") + .required("inputToken") + .required("outputToken") + .required("inputAmount") + .required("outputAmount") + .property("kind", kind) + .property("id", id) + .property("inputToken", Ref::from_schema_name("Token")) + .property("outputToken", Ref::from_schema_name("Token")) + .property("inputAmount", Ref::from_schema_name("TokenAmount")) + .property("outputAmount", Ref::from_schema_name("TokenAmount")) + .build(); + ( "LiquidityInteraction", - Schema::Object( - ObjectBuilder::new() - .description(Some( - "Interaction representing the execution of liquidity that was passed in \ - with the auction.", - )) - .required("kind") - .required("id") - .required("inputToken") - .required("outputToken") - .required("inputAmount") - .required("outputAmount") - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["liquidity"])), - ) - .property( - "id", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .description(Some( - "The ID of executed liquidity provided in the auction input.", - )) - .build(), - ) - .property("inputToken", Ref::from_schema_name("Token")) - .property("outputToken", Ref::from_schema_name("Token")) - .property("inputAmount", Ref::from_schema_name("TokenAmount")) - .property("outputAmount", Ref::from_schema_name("TokenAmount")) - .build(), - ) - .into(), + Schema::Object(liquidity_interaction).into(), ) } } @@ -349,60 +310,43 @@ pub struct CustomInteraction { impl ToSchema<'static> for CustomInteraction { fn schema() -> (&'static str, RefOr) { + let kind = ObjectBuilder::new() + .schema_type(SchemaType::String) + .enum_values(Some(["custom"])); + let call_data = ObjectBuilder::new() + .schema_type(SchemaType::String) + .description(Some("The EVM calldata bytes.")) + .example(Some(Value::String("0x01020304".to_string()))); + let allowances = ArrayBuilder::new() + .items(Ref::from_schema_name("Allowance")) + .description(Some( + "ERC20 allowances that are required for this custom interaction.", + )); + let asset = ArrayBuilder::new() + .items(Ref::from_schema_name("Asset")) + .build(); + let custom_interaction = ObjectBuilder::new() + .description(Some( + "A searcher-specified custom interaction to be included in the final settlement.", + )) + .required("kind") + .required("target") + .required("value") + .required("callData") + .required("inputs") + .required("outputs") + .property("kind", kind) + .property("target", Ref::from_schema_name("Address")) + .property("value", Ref::from_schema_name("TokenAmount")) + .property("callData", call_data) + .property("allowances", allowances) + .property("inputs", asset.clone()) + .property("outputs", asset) + .build(); + ( "CustomInteraction", - Schema::Object( - ObjectBuilder::new() - .description(Some( - "A searcher-specified custom interaction to be included in the final \ - settlement.", - )) - .required("kind") - .required("target") - .required("value") - .required("callData") - .required("inputs") - .required("outputs") - .property( - "kind", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .enum_values(Some(["custom"])), - ) - .property("target", Ref::from_schema_name("Address")) - .property("value", Ref::from_schema_name("TokenAmount")) - .property( - "callData", - ObjectBuilder::new() - .schema_type(SchemaType::String) - .description(Some("The EVM calldata bytes.")) - .example(Some(Value::String("0x01020304".to_string()))) - .build(), - ) - .property( - "allowances", - ArrayBuilder::new() - .items(Ref::from_schema_name("Allowance")) - .description(Some( - "ERC20 allowances that are required for this custom interaction.", - )) - .build(), - ) - .property( - "inputs", - ArrayBuilder::new() - .items(Ref::from_schema_name("Asset")) - .build(), - ) - .property( - "outputs", - ArrayBuilder::new() - .items(Ref::from_schema_name("Asset")) - .build(), - ) - .build(), - ) - .into(), + Schema::Object(custom_interaction).into(), ) } } @@ -445,16 +389,15 @@ pub enum SellTokenBalance { impl ToSchema<'static> for SellTokenBalance { fn schema() -> (&'static str, RefOr) { + let sell_token_balance = ObjectBuilder::new() + .description(Some("Where should the sell token be drawn from?")) + .schema_type(SchemaType::String) + .enum_values(Some(["erc20", "internal", "external"])) + .build(); + ( "SellTokenBalance", - Schema::Object( - ObjectBuilder::new() - .description(Some("Where should the sell token be drawn from?")) - .schema_type(SchemaType::String) - .enum_values(Some(["erc20", "internal", "external"])) - .build(), - ) - .into(), + Schema::Object(sell_token_balance).into(), ) } } @@ -469,17 +412,13 @@ pub enum BuyTokenBalance { impl ToSchema<'static> for BuyTokenBalance { fn schema() -> (&'static str, RefOr) { - ( - "BuyTokenBalance", - Schema::Object( - ObjectBuilder::new() - .description(Some("Where should the buy token be transferred to?")) - .schema_type(SchemaType::String) - .enum_values(Some(["erc20", "internal"])) - .build(), - ) - .into(), - ) + let buy_token_balance = ObjectBuilder::new() + .description(Some("Where should the buy token be transferred to?")) + .schema_type(SchemaType::String) + .enum_values(Some(["erc20", "internal"])) + .build(); + + ("BuyTokenBalance", Schema::Object(buy_token_balance).into()) } } @@ -494,16 +433,12 @@ pub enum SigningScheme { impl ToSchema<'static> for SigningScheme { fn schema() -> (&'static str, RefOr) { - ( - "SigningScheme", - Schema::Object( - ObjectBuilder::new() - .description(Some("How was the order signed?")) - .schema_type(SchemaType::String) - .enum_values(Some(["eip712", "ethSign", "preSign", "eip1271"])) - .build(), - ) - .into(), - ) + let signing_scheme = ObjectBuilder::new() + .description(Some("How was the order signed?")) + .schema_type(SchemaType::String) + .enum_values(Some(["eip712", "ethSign", "preSign", "eip1271"])) + .build(); + + ("SigningScheme", Schema::Object(signing_scheme).into()) } } From 71f1eee5ba0cb0cc542ee4fd0bdd70eaf45db572 Mon Sep 17 00:00:00 2001 From: ilya Date: Wed, 1 May 2024 11:29:37 +0100 Subject: [PATCH 17/19] Shorten examples --- crates/solvers-dto/src/auction.rs | 10 +++------- crates/solvers-dto/src/common.rs | 10 ++++------ crates/solvers/openapi.yml | 12 ++++++------ 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/crates/solvers-dto/src/auction.rs b/crates/solvers-dto/src/auction.rs index 8260ddfaf8..e14e582f4d 100644 --- a/crates/solvers-dto/src/auction.rs +++ b/crates/solvers-dto/src/auction.rs @@ -687,7 +687,7 @@ pub struct Decimal(String); /// A hex-encoded 32 byte string containing the pool address (0..20), the pool /// specialization (20..22) and the poolnonce (22..32). #[derive(ToSchema)] -#[schema(example = "0xc88c76dd8b92408fe9bea1a54922a31e232d873c0002000000000000000005b2")] +#[schema(example = "0xc88c76dd8b92408fe9bea1a54922a31")] #[allow(dead_code)] pub struct BalancerPoolId(String); @@ -728,17 +728,13 @@ pub struct I32(String); /// represent the owner address and bytes [52, 56) represent the order's /// `validTo` field. #[derive(ToSchema)] -#[schema( - example = "0x30cff40d9f60caa68a37f0ee73253ad6ad72b45580c945fe3ab67596476937197854163b1b0d24e77dca702b97b5cc33e0f83dcb626122a6" -)] +#[schema(example = "0x30cff40d9f60caa68a37f0ee73253ad")] #[allow(dead_code)] pub struct OrderUid(String); /// Signature bytes. #[derive(ToSchema)] -#[schema( - example = "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" -)] +#[schema(example = "0x0000000000000000000000000000000")] #[allow(dead_code)] pub struct Signature(String); diff --git a/crates/solvers-dto/src/common.rs b/crates/solvers-dto/src/common.rs index 0f16d9380e..f88f497ac4 100644 --- a/crates/solvers-dto/src/common.rs +++ b/crates/solvers-dto/src/common.rs @@ -4,9 +4,7 @@ use utoipa::ToSchema; /// Signature bytes. #[derive(ToSchema)] -#[schema( - example = "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" -)] +#[schema(example = "0x0000000000000000000000000000000")] #[allow(dead_code)] pub struct Signature(String); @@ -14,7 +12,7 @@ pub struct Signature(String); /// order. This can also be used to ensure uniqueness between two orders with /// otherwise the exact same parameters. #[derive(ToSchema)] -#[schema(example = "0x0000000000000000000000000000000000000000000000000000000000000000")] +#[schema(example = "0x0000000000000000000000000000000")] #[allow(dead_code)] pub struct AppData(String); @@ -26,12 +24,12 @@ pub struct TokenAmount(String); /// An Ethereum public address. #[derive(ToSchema)] -#[schema(example = "0x0000000000000000000000000000000000000000")] +#[schema(example = "0x0000000000000000000000000000000")] #[allow(dead_code)] pub struct Address(String); /// An ERC20 token address. #[derive(ToSchema)] -#[schema(example = "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB")] +#[schema(example = "0xDEf1CA1fb7FBcDC777520aa7f396b4E")] #[allow(dead_code)] pub struct Token(String); diff --git a/crates/solvers/openapi.yml b/crates/solvers/openapi.yml index 350a3bfaf4..4a411121de 100644 --- a/crates/solvers/openapi.yml +++ b/crates/solvers/openapi.yml @@ -87,7 +87,7 @@ components: Address: type: string description: An Ethereum public address. - example: '0x0000000000000000000000000000000000000000' + example: '0x0000000000000000000000000000000' Allowance: type: object description: |- @@ -110,7 +110,7 @@ components: 32 bytes of arbitrary application specific data that can be added to an order. This can also be used to ensure uniqueness between two orders with otherwise the exact same parameters. - example: '0x0000000000000000000000000000000000000000000000000000000000000000' + example: '0x0000000000000000000000000000000' Asset: type: object description: A token address with an amount. @@ -161,7 +161,7 @@ components: description: |- A hex-encoded 32 byte string containing the pool address (0..20), the pool specialization (20..22) and the poolnonce (22..32). - example: 0xc88c76dd8b92408fe9bea1a54922a31e232d873c0002000000000000000005b2 + example: '0xc88c76dd8b92408fe9bea1a54922a31' BigInt: type: string description: An arbitrary-precision integer value. @@ -604,7 +604,7 @@ components: [0, 32) represent the order digest used for signing, bytes [32, 52) represent the owner address and bytes [52, 56) represent the order's `validTo` field. - example: 0x30cff40d9f60caa68a37f0ee73253ad6ad72b45580c945fe3ab67596476937197854163b1b0d24e77dca702b97b5cc33e0f83dcb626122a6 + example: '0x30cff40d9f60caa68a37f0ee73253ad' PriceImprovement: type: object description: A cut from the price improvement over the best quote is taken as a protocol fee. @@ -655,7 +655,7 @@ components: Signature: type: string description: Signature bytes. - example: '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + example: '0x0000000000000000000000000000000' SigningScheme: type: string description: How was the order signed? @@ -755,7 +755,7 @@ components: Token: type: string description: An ERC20 token address. - example: 0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB + example: '0xDEf1CA1fb7FBcDC777520aa7f396b4E' TokenAmount: type: string description: Amount of an ERC20 token. 256 bit unsigned integer in decimal notation. From af430199f0f77659e782b8a83118ff45e8a86969 Mon Sep 17 00:00:00 2001 From: ilya Date: Thu, 2 May 2024 19:22:23 +0100 Subject: [PATCH 18/19] Solvers JSON OpenAPI schema (#2683) --- .github/workflows/pull-request.yaml | 8 +- Cargo.lock | 21 - crates/openapi-generator/build.rs | 4 +- crates/solvers-dto/src/auction.rs | 10 +- crates/solvers-dto/src/common.rs | 10 +- crates/solvers/Cargo.toml | 3 +- crates/solvers/openapi.json | 1205 +++++++++++++++++++++++++++ crates/solvers/openapi.yml | 854 ------------------- crates/solvers/src/api/mod.rs | 5 +- crates/solvers/src/lib.rs | 2 +- 10 files changed, 1229 insertions(+), 893 deletions(-) create mode 100644 crates/solvers/openapi.json delete mode 100644 crates/solvers/openapi.yml diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 11e2455f78..9bf8193d41 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -188,8 +188,8 @@ jobs: - run: cargo build -p openapi-generator - name: Check for unstaged changes in generated YAML files run: | - git diff --exit-code crates/solvers/openapi.yml || { echo "::error ::OpenAPI spec contains changes: crates/solvers/openapi.yml"; exit 1; } + git diff --exit-code crates/solvers/openapi.json || { echo "::error ::OpenAPI spec contains changes: crates/solvers/openapi.json"; exit 1; } - run: npm install @apidevtools/swagger-cli - - run: node_modules/.bin/swagger-cli validate crates/orderbook/openapi.yml - - run: node_modules/.bin/swagger-cli validate crates/driver/openapi.yml - - run: node_modules/.bin/swagger-cli validate crates/solvers/openapi.yml + - run: node_modules/.bin/swagger-cli validate crates/orderbook/openapi.json + - run: node_modules/.bin/swagger-cli validate crates/driver/openapi.json + - run: node_modules/.bin/swagger-cli validate crates/solvers/openapi.json diff --git a/Cargo.lock b/Cargo.lock index ccbdcd1028..27c48cbe8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4089,19 +4089,6 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap 2.2.5", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - [[package]] name = "sha1" version = "0.10.5" @@ -4329,7 +4316,6 @@ dependencies = [ "serde", "serde_json", "serde_with", - "serde_yaml", "shared", "solver", "solvers-dto", @@ -5151,12 +5137,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - [[package]] name = "untrusted" version = "0.7.1" @@ -5202,7 +5182,6 @@ dependencies = [ "indexmap 2.2.5", "serde", "serde_json", - "serde_yaml", "utoipa-gen", ] diff --git a/crates/openapi-generator/build.rs b/crates/openapi-generator/build.rs index 5d685f8ecf..35b612ffbd 100644 --- a/crates/openapi-generator/build.rs +++ b/crates/openapi-generator/build.rs @@ -3,10 +3,10 @@ use std::fs; -const SOLVERS_OPENAPI_PATH: &str = "../solvers/openapi.yml"; +const SOLVERS_OPENAPI_PATH: &str = "../solvers/openapi.json"; fn main() { - let openapi_yaml = solvers::generate_openapi_yaml() + let openapi_yaml = solvers::generate_openapi_json() .expect("Error generating the solvers OpenAPI documentation"); fs::write(SOLVERS_OPENAPI_PATH, openapi_yaml) .expect("Error writing the solvers OpenAPI documentation"); diff --git a/crates/solvers-dto/src/auction.rs b/crates/solvers-dto/src/auction.rs index e14e582f4d..8260ddfaf8 100644 --- a/crates/solvers-dto/src/auction.rs +++ b/crates/solvers-dto/src/auction.rs @@ -687,7 +687,7 @@ pub struct Decimal(String); /// A hex-encoded 32 byte string containing the pool address (0..20), the pool /// specialization (20..22) and the poolnonce (22..32). #[derive(ToSchema)] -#[schema(example = "0xc88c76dd8b92408fe9bea1a54922a31")] +#[schema(example = "0xc88c76dd8b92408fe9bea1a54922a31e232d873c0002000000000000000005b2")] #[allow(dead_code)] pub struct BalancerPoolId(String); @@ -728,13 +728,17 @@ pub struct I32(String); /// represent the owner address and bytes [52, 56) represent the order's /// `validTo` field. #[derive(ToSchema)] -#[schema(example = "0x30cff40d9f60caa68a37f0ee73253ad")] +#[schema( + example = "0x30cff40d9f60caa68a37f0ee73253ad6ad72b45580c945fe3ab67596476937197854163b1b0d24e77dca702b97b5cc33e0f83dcb626122a6" +)] #[allow(dead_code)] pub struct OrderUid(String); /// Signature bytes. #[derive(ToSchema)] -#[schema(example = "0x0000000000000000000000000000000")] +#[schema( + example = "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +)] #[allow(dead_code)] pub struct Signature(String); diff --git a/crates/solvers-dto/src/common.rs b/crates/solvers-dto/src/common.rs index f88f497ac4..0f16d9380e 100644 --- a/crates/solvers-dto/src/common.rs +++ b/crates/solvers-dto/src/common.rs @@ -4,7 +4,9 @@ use utoipa::ToSchema; /// Signature bytes. #[derive(ToSchema)] -#[schema(example = "0x0000000000000000000000000000000")] +#[schema( + example = "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +)] #[allow(dead_code)] pub struct Signature(String); @@ -12,7 +14,7 @@ pub struct Signature(String); /// order. This can also be used to ensure uniqueness between two orders with /// otherwise the exact same parameters. #[derive(ToSchema)] -#[schema(example = "0x0000000000000000000000000000000")] +#[schema(example = "0x0000000000000000000000000000000000000000000000000000000000000000")] #[allow(dead_code)] pub struct AppData(String); @@ -24,12 +26,12 @@ pub struct TokenAmount(String); /// An Ethereum public address. #[derive(ToSchema)] -#[schema(example = "0x0000000000000000000000000000000")] +#[schema(example = "0x0000000000000000000000000000000000000000")] #[allow(dead_code)] pub struct Address(String); /// An ERC20 token address. #[derive(ToSchema)] -#[schema(example = "0xDEf1CA1fb7FBcDC777520aa7f396b4E")] +#[schema(example = "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB")] #[allow(dead_code)] pub struct Token(String); diff --git a/crates/solvers/Cargo.toml b/crates/solvers/Cargo.toml index b479c961fe..d0d5060e18 100644 --- a/crates/solvers/Cargo.toml +++ b/crates/solvers/Cargo.toml @@ -32,7 +32,6 @@ reqwest = { workspace = true } s3 = { path = "../s3" } serde = { workspace = true } serde_json = { workspace = true } -serde_yaml = "0.9.34+deprecated" serde_with = { workspace = true } solvers-dto = { path = "../solvers-dto" } thiserror = { workspace = true } @@ -40,7 +39,7 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread", "signal", " toml = { workspace = true } tower = "0.4" tower-http = { version = "0.4", features = ["limit", "trace"] } -utoipa = { version = "4.2.0", features = ["yaml", "axum_extras"] } +utoipa = { version = "4.2.0", features = ["axum_extras"] } web3 = { workspace = true } # TODO Once solvers are ported and E2E tests set up, slowly migrate code and diff --git a/crates/solvers/openapi.json b/crates/solvers/openapi.json new file mode 100644 index 0000000000..f4a98fbf3c --- /dev/null +++ b/crates/solvers/openapi.json @@ -0,0 +1,1205 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Solver Engine API", + "description": "The API implemented by solver engines interacting with the reference driver implementation.", + "license": { + "name": "MIT OR Apache-2.0" + }, + "version": "0.1.0" + }, + "paths": { + "/notify": { + "post": { + "tags": [ + "routes::notify" + ], + "summary": "Receive a status notification about a previously provided solution.", + "description": "Receive a status notification about a previously provided solution.", + "operationId": "notify", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "A notification that informs the solver how its solution performed in the auction. Depending on the notification type additional meta data may be attached but this is not guaranteed to be stable.", + "properties": { + "auctionId": { + "type": "string", + "description": "The auction ID of the auction that the solution was providedfor." + }, + "kind": { + "type": "string", + "enum": [ + "timeout", + "emptySolution", + "duplicatedSolutionId", + "simulationFailed", + "invalidClearingPrices", + "missingPrice", + "invalidExecutedAmount", + "nonBufferableTokensUsed", + "solverAccountInsufficientBalance", + "success", + "revert", + "driverError", + "cancelled", + "fail", + "postprocessingTimedOut" + ] + }, + "solutionId": { + "type": "number", + "description": "The solution ID within the auction for which the notification applies" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Notification successfully received." + } + } + } + }, + "/solve": { + "post": { + "tags": [ + "routes::solve" + ], + "summary": "Solve the passed in auction instance.", + "description": "Solve the passed in auction instance.", + "operationId": "solve", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Auction" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Auction successfully solved.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Proposed solutions to settle some of the orders in the auction.", + "required": [ + "solutions" + ], + "properties": { + "solutions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Solution" + } + } + } + } + } + } + }, + "400": { + "description": "There is something wrong with the request." + }, + "429": { + "description": "The solver cannot keep up. It is too busy to handle more requests." + }, + "500": { + "description": "Something went wrong when handling the request." + } + } + } + } + }, + "components": { + "schemas": { + "Address": { + "type": "string", + "description": "An Ethereum public address.", + "example": "0x0000000000000000000000000000000000000000" + }, + "Allowance": { + "type": "object", + "description": "An ERC20 allowance from the settlement contract to some spender that is\nrequired for a custom interaction.", + "required": [ + "token", + "spender", + "amount" + ], + "properties": { + "amount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "spender": { + "$ref": "#/components/schemas/Address" + }, + "token": { + "$ref": "#/components/schemas/Token" + } + } + }, + "AppData": { + "type": "string", + "description": "32 bytes of arbitrary application specific data that can be added to an\norder. This can also be used to ensure uniqueness between two orders with\notherwise the exact same parameters.", + "example": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "Asset": { + "type": "object", + "description": "A token address with an amount.", + "required": [ + "token", + "amount" + ], + "properties": { + "amount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "token": { + "$ref": "#/components/schemas/Token" + } + } + }, + "Auction": { + "type": "object", + "description": "The abstract auction to be solved by the searcher.", + "required": [ + "tokens", + "orders", + "liquidity", + "effectiveGasPrice", + "deadline" + ], + "properties": { + "deadline": { + "$ref": "#/components/schemas/DateTime" + }, + "effectiveGasPrice": { + "$ref": "#/components/schemas/TokenAmount" + }, + "id": { + "type": "string", + "description": "An opaque identifier for the auction. Will be set to `null` for requests\nthat are not part of an auction (when quoting token prices for example)." + }, + "liquidity": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Liquidity" + }, + "description": "On-chain liquidity that can be used by the solution." + }, + "orders": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Order" + }, + "description": "The solvable orders included in the auction." + }, + "tokens": { + "type": "object", + "description": "A map of token addresses to token information.", + "additionalProperties": { + "$ref": "#/components/schemas/TokenInfo" + } + } + } + }, + "BalancerPoolId": { + "type": "string", + "description": "A hex-encoded 32 byte string containing the pool address (0..20), the pool\nspecialization (20..22) and the poolnonce (22..32).", + "example": "0xc88c76dd8b92408fe9bea1a54922a31e232d873c0002000000000000000005b2" + }, + "BigInt": { + "type": "string", + "description": "An arbitrary-precision integer value.", + "example": "1234567890" + }, + "BuyTokenBalance": { + "type": "string", + "description": "Where should the buy token be transferred to?", + "enum": [ + "erc20", + "internal" + ] + }, + "BuyTokenDestination": { + "type": "string", + "description": "Destination for which the buyAmount should be transferred to order's\nreceiver to upon fulfillment", + "enum": [ + "erc20", + "internal" + ] + }, + "ConcentratedLiquidityPool": { + "type": "object", + "description": "A Uniswap V3-like concentrated liquidity pool.", + "required": [ + "kind", + "router", + "tokens", + "sqrtPrice", + "liquidity", + "tick", + "liquidityNet", + "fee" + ], + "properties": { + "fee": { + "$ref": "#/components/schemas/Decimal" + }, + "kind": { + "type": "string", + "enum": [ + "concentratedLiquidity" + ] + }, + "liquidity": { + "$ref": "#/components/schemas/U128" + }, + "liquidityNet": { + "type": "object", + "description": "A map of tick indices to their liquidity values.", + "additionalProperties": { + "$ref": "#/components/schemas/I128" + } + }, + "router": { + "$ref": "#/components/schemas/Address" + }, + "sqrtPrice": { + "$ref": "#/components/schemas/U256" + }, + "tick": { + "$ref": "#/components/schemas/I32" + }, + "tokens": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Token" + } + } + } + }, + "ConstantProductPool": { + "type": "object", + "description": "A UniswapV2-like constant product liquidity pool for a token pair.", + "required": [ + "kind", + "router", + "tokens", + "fee" + ], + "properties": { + "fee": { + "$ref": "#/components/schemas/Decimal" + }, + "kind": { + "type": "string", + "enum": [ + "constantProduct" + ] + }, + "router": { + "$ref": "#/components/schemas/Address" + }, + "tokens": { + "type": "object", + "description": "A mapping of token address to its reserve amounts.", + "additionalProperties": { + "$ref": "#/components/schemas/TokenReserve" + } + } + } + }, + "CustomInteraction": { + "type": "object", + "description": "A searcher-specified custom interaction to be included in the final settlement.", + "required": [ + "kind", + "target", + "value", + "callData", + "inputs", + "outputs" + ], + "properties": { + "allowances": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Allowance" + }, + "description": "ERC20 allowances that are required for this custom interaction." + }, + "callData": { + "type": "string", + "description": "The EVM calldata bytes.", + "example": "0x01020304" + }, + "inputs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Asset" + } + }, + "kind": { + "type": "string", + "enum": [ + "custom" + ] + }, + "outputs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Asset" + } + }, + "target": { + "$ref": "#/components/schemas/Address" + }, + "value": { + "$ref": "#/components/schemas/TokenAmount" + } + } + }, + "DateTime": { + "type": "string", + "description": "An ISO-8601 formatted date-time.", + "example": "1970-01-01T00:00:00.000Z" + }, + "Decimal": { + "type": "string", + "description": "An arbitrary-precision decimal value.", + "example": "13.37" + }, + "FeePolicy": { + "oneOf": [ + { + "$ref": "#/components/schemas/SurplusFee" + }, + { + "$ref": "#/components/schemas/PriceImprovement" + }, + { + "$ref": "#/components/schemas/VolumeFee" + } + ], + "description": "A fee policy that applies to an order" + }, + "ForeignLimitOrder": { + "type": "object", + "description": "A 0x-like limit order external to CoW Protocol.", + "required": [ + "kind", + "makerToken", + "takerToken", + "makerAmount", + "takerAmount", + "takerTokenFeeAmount" + ], + "properties": { + "kind": { + "type": "string", + "enum": [ + "limitOrder" + ] + }, + "makerAmount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "makerToken": { + "$ref": "#/components/schemas/Token" + }, + "takerAmount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "takerToken": { + "$ref": "#/components/schemas/Token" + }, + "takerTokenFeeAmount": { + "$ref": "#/components/schemas/TokenAmount" + } + } + }, + "Fulfillment": { + "type": "object", + "description": "A trade which fulfills an order from the auction.", + "required": [ + "kind", + "order" + ], + "properties": { + "executedAmount": { + "allOf": [ + { + "$ref": "#/components/schemas/TokenAmount" + } + ], + "description": "The amount of the order that was executed. This is denoted in 'sellToken' for sell orders, and 'buyToken' for buy orders." + }, + "fee": { + "type": "object", + "description": "The sell token amount that should be taken as a fee for this trade. This only gets returned for limit orders and only refers to the actual amount filled by the trade." + }, + "kind": { + "type": "string", + "enum": [ + "fulfillment" + ] + }, + "order": { + "allOf": [ + { + "$ref": "#/components/schemas/OrderUid" + } + ], + "description": "A reference by UID of the order to execute in a solution. The order must be included in the auction input." + } + } + }, + "I128": { + "type": "string", + "description": "128 bit signed integer in decimal notation.", + "example": "-1234567890" + }, + "I32": { + "type": "string", + "description": "32 bit signed integer in decimal notation.", + "example": "-12345" + }, + "Interaction": { + "allOf": [ + { + "type": "object", + "properties": { + "internalize": { + "type": "boolean", + "description": "A flag indicating that the interaction should be 'internalized', as specified by CIP-2." + } + } + }, + { + "oneOf": [ + { + "$ref": "#/components/schemas/LiquidityInteraction" + }, + { + "$ref": "#/components/schemas/CustomInteraction" + } + ] + } + ], + "description": "An interaction to execute as part of a settlement." + }, + "InteractionData": { + "type": "object", + "required": [ + "target", + "value", + "callData" + ], + "properties": { + "callData": { + "type": "string", + "example": "0x01020304" + }, + "target": { + "$ref": "#/components/schemas/Address" + }, + "value": { + "$ref": "#/components/schemas/TokenAmount" + } + } + }, + "JitOrder": { + "type": "object", + "description": "A just-in-time liquidity order included in a settlement.", + "required": [ + "sellToken", + "buyToken", + "receiver", + "sellAmount", + "buyAmount", + "validTo", + "appData", + "feeAmount", + "kind", + "partiallyFillable", + "sellTokenBalance", + "buyTokenBalance", + "signingScheme", + "signature" + ], + "properties": { + "appData": { + "$ref": "#/components/schemas/AppData" + }, + "buyAmount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "buyToken": { + "$ref": "#/components/schemas/Token" + }, + "buyTokenBalance": { + "$ref": "#/components/schemas/BuyTokenBalance" + }, + "feeAmount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "kind": { + "$ref": "#/components/schemas/OrderKind" + }, + "partiallyFillable": { + "type": "boolean" + }, + "receiver": { + "$ref": "#/components/schemas/Address" + }, + "sellAmount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "sellToken": { + "$ref": "#/components/schemas/Token" + }, + "sellTokenBalance": { + "$ref": "#/components/schemas/SellTokenBalance" + }, + "signature": { + "$ref": "#/components/schemas/Signature" + }, + "signingScheme": { + "$ref": "#/components/schemas/SigningScheme" + }, + "validTo": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + }, + "JitTrade": { + "type": "object", + "description": "A trade with a JIT order.", + "required": [ + "kind", + "order", + "executedAmount" + ], + "properties": { + "executedAmount": { + "allOf": [ + { + "$ref": "#/components/schemas/TokenAmount" + } + ], + "description": "The amount of the order that was executed. This is denoted in 'sellToken' for sell orders, and 'buyToken' for buy orders." + }, + "kind": { + "type": "string", + "enum": [ + "jit" + ] + }, + "order": { + "allOf": [ + { + "$ref": "#/components/schemas/JitOrder" + } + ], + "description": "The just-in-time liquidity order to execute in a solution." + } + } + }, + "LegacySigningScheme": { + "type": "string", + "enum": [ + "eip712", + "ethsign", + "eip1271", + "presign" + ] + }, + "Liquidity": { + "allOf": [ + { + "$ref": "#/components/schemas/LiquidityParameters" + }, + { + "type": "object", + "required": [ + "id", + "address", + "gasEstimate" + ], + "properties": { + "address": { + "allOf": [ + { + "$ref": "#/components/schemas/Address" + } + ], + "description": "A rough approximation of gas units required to use this liquidity on-chain." + }, + "gasEstimate": { + "allOf": [ + { + "$ref": "#/components/schemas/BigInt" + } + ], + "description": "A rough approximation of gas units required to use this liquidity on-chain." + }, + "id": { + "type": "string", + "description": "An opaque ID used for uniquely identifying the liquidity within a single auction (note that they are **not** guaranteed to be unique across auctions). This ID is used in the solution for matching interactions with the executed liquidity." + } + } + } + ], + "description": "On-chain liquidity that can be used in a solution. This liquidity is provided to facilitate onboarding new solvers. Additional liquidity that is not included in this set may still be used in solutions." + }, + "LiquidityInteraction": { + "type": "object", + "description": "Interaction representing the execution of liquidity that was passed in with the auction.", + "required": [ + "kind", + "id", + "inputToken", + "outputToken", + "inputAmount", + "outputAmount" + ], + "properties": { + "id": { + "type": "string", + "description": "The ID of executed liquidity provided in the auction input." + }, + "inputAmount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "inputToken": { + "$ref": "#/components/schemas/Token" + }, + "kind": { + "type": "string", + "enum": [ + "liquidity" + ] + }, + "outputAmount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "outputToken": { + "$ref": "#/components/schemas/Token" + } + } + }, + "LiquidityParameters": { + "oneOf": [ + { + "$ref": "#/components/schemas/ConstantProductPool" + }, + { + "$ref": "#/components/schemas/WeightedProductPool" + }, + { + "$ref": "#/components/schemas/StablePool" + }, + { + "$ref": "#/components/schemas/ConcentratedLiquidityPool" + }, + { + "$ref": "#/components/schemas/ForeignLimitOrder" + } + ] + }, + "NativePrice": { + "type": "string", + "description": "The price in wei of the native token (Ether on Mainnet for example) to buy\n10**18 of a token.", + "example": "1234567890" + }, + "Order": { + "type": "object", + "description": "CoW Protocol order information relevant to execution.", + "required": [ + "uid", + "sellToken", + "buyToken", + "sellAmount", + "fullSellAmount", + "buyAmount", + "fullBuyAmount", + "validTo", + "kind", + "receiver", + "owner", + "partiallyFillable", + "preInteractions", + "postInteractions", + "sellTokenSource", + "buyTokenDestination", + "class", + "appData", + "signingScheme", + "signature" + ], + "properties": { + "appData": { + "$ref": "#/components/schemas/AppData" + }, + "buyAmount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "buyToken": { + "$ref": "#/components/schemas/Token" + }, + "buyTokenDestination": { + "$ref": "#/components/schemas/BuyTokenDestination" + }, + "class": { + "$ref": "#/components/schemas/OrderClass" + }, + "feePolicies": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FeePolicy" + }, + "nullable": true + }, + "fullBuyAmount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "fullSellAmount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "kind": { + "$ref": "#/components/schemas/OrderKind" + }, + "owner": { + "$ref": "#/components/schemas/Address" + }, + "partiallyFillable": { + "type": "boolean", + "description": "Whether or not this order can be partially filled. If this is false,\nthen the order is a \"fill-or-kill\" order, meaning it needs to be\ncompletely filled or not at all." + }, + "postInteractions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InteractionData" + } + }, + "preInteractions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InteractionData" + } + }, + "receiver": { + "$ref": "#/components/schemas/Address" + }, + "sellAmount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "sellToken": { + "$ref": "#/components/schemas/Token" + }, + "sellTokenSource": { + "$ref": "#/components/schemas/SellTokenSource" + }, + "signature": { + "$ref": "#/components/schemas/Signature" + }, + "signingScheme": { + "$ref": "#/components/schemas/LegacySigningScheme" + }, + "uid": { + "$ref": "#/components/schemas/OrderUid" + }, + "validTo": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + }, + "OrderClass": { + "type": "string", + "description": "How the CoW Protocol order was classified.", + "enum": [ + "market", + "limit", + "liquidity" + ] + }, + "OrderKind": { + "type": "string", + "description": "How the CoW Protocol order was classified.", + "enum": [ + "sell", + "buy" + ] + }, + "OrderUid": { + "type": "string", + "description": "Unique identifier for the order. Order UIDs are 56 bytes long, where bytes\n[0, 32) represent the order digest used for signing, bytes [32, 52)\nrepresent the owner address and bytes [52, 56) represent the order's\n`validTo` field.", + "example": "0x30cff40d9f60caa68a37f0ee73253ad6ad72b45580c945fe3ab67596476937197854163b1b0d24e77dca702b97b5cc33e0f83dcb626122a6" + }, + "PriceImprovement": { + "type": "object", + "description": "A cut from the price improvement over the best quote is taken as a protocol fee.", + "properties": { + "factor": { + "type": "number", + "description": "The factor of the user surplus that the protocol will request from the solver after settling the order", + "example": 0.5 + }, + "kind": { + "type": "string", + "enum": [ + "priceImprovement" + ] + }, + "maxVolumeFactor": { + "type": "number", + "description": "Never charge more than that percentage of the order volume.", + "example": 0.01, + "maximum": 0.99999, + "minimum": 0 + }, + "quote": { + "$ref": "#/components/schemas/Quote" + } + } + }, + "Quote": { + "type": "object", + "required": [ + "sellAmount", + "buyAmount", + "fee" + ], + "properties": { + "buyAmount": { + "$ref": "#/components/schemas/TokenAmount" + }, + "fee": { + "$ref": "#/components/schemas/TokenAmount" + }, + "sellAmount": { + "$ref": "#/components/schemas/TokenAmount" + } + } + }, + "SellTokenBalance": { + "type": "string", + "description": "Where should the sell token be drawn from?", + "enum": [ + "erc20", + "internal", + "external" + ] + }, + "SellTokenSource": { + "type": "string", + "description": "Source from which the sellAmount should be drawn upon order fulfillment", + "enum": [ + "erc20", + "external", + "internal" + ] + }, + "Signature": { + "type": "string", + "description": "Signature bytes.", + "example": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "SigningScheme": { + "type": "string", + "description": "How was the order signed?", + "enum": [ + "eip712", + "ethSign", + "preSign", + "eip1271" + ] + }, + "Solution": { + "type": "object", + "description": "A computed solution for a given auction.", + "required": [ + "id", + "prices", + "trades", + "interactions" + ], + "properties": { + "gas": { + "type": "integer", + "format": "int64", + "description": "How many units of gas this solution is estimated to cost.", + "nullable": true, + "minimum": 0 + }, + "id": { + "type": "number", + "format": "int64", + "description": "An opaque identifier for the solution. This is a solver generated number\nthat is unique across multiple solutions within the auction." + }, + "interactions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Interaction" + }, + "description": "Interactions to encode within a settlement." + }, + "prices": { + "type": "object", + "description": "A clearing price map of token address to price. The price can have\narbitrary denomination.", + "additionalProperties": { + "$ref": "#/components/schemas/U256" + } + }, + "trades": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Trade" + }, + "description": "CoW Protocol order trades included in the solution." + } + } + }, + "StablePool": { + "type": "object", + "description": "A Curve-like stable pool of N tokens.", + "required": [ + "kind", + "tokens", + "amplificationParameter", + "fee", + "balancer_pool_id" + ], + "properties": { + "amplificationParameter": { + "$ref": "#/components/schemas/Decimal" + }, + "balancer_pool_id": { + "$ref": "#/components/schemas/BalancerPoolId" + }, + "fee": { + "$ref": "#/components/schemas/Decimal" + }, + "kind": { + "type": "string", + "enum": [ + "stable" + ] + }, + "tokens": { + "type": "object", + "description": "A mapping of token address to token balance and scaling rate.", + "additionalProperties": { + "allOf": [ + { + "$ref": "#/components/schemas/TokenReserve" + }, + { + "type": "object", + "required": [ + "scalingFactor" + ], + "properties": { + "scalingFactor": { + "$ref": "#/components/schemas/Decimal" + } + } + } + ] + } + } + } + }, + "SurplusFee": { + "type": "object", + "description": "If the order receives more than limit price, pay the protocol a factor of the difference.", + "properties": { + "factor": { + "type": "number", + "description": "The factor of the user surplus that the protocol will request from the solver after settling the order", + "example": 0.5 + }, + "kind": { + "type": "string", + "enum": [ + "surplus" + ] + }, + "maxVolumeFactor": { + "type": "number", + "description": "Never charge more than that percentage of the order volume.", + "example": 0.05, + "maximum": 0.99999, + "minimum": 0 + } + } + }, + "Token": { + "type": "string", + "description": "An ERC20 token address.", + "example": "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB" + }, + "TokenAmount": { + "type": "string", + "description": "Amount of an ERC20 token. 256 bit unsigned integer in decimal notation.", + "example": "1234567890" + }, + "TokenInfo": { + "type": "object", + "description": "Information about an ERC20 token.", + "required": [ + "availableBalance", + "trusted" + ], + "properties": { + "availableBalance": { + "allOf": [ + { + "$ref": "#/components/schemas/TokenAmount" + } + ], + "description": "The balance held by the Settlement contract that is available during a settlement." + }, + "decimals": { + "type": "integer", + "description": "The ERC20.decimals value for this token. This may be missing for ERC20 tokens that don't implement the optional metadata extension." + }, + "referencePrice": { + "allOf": [ + { + "$ref": "#/components/schemas/NativePrice" + } + ], + "description": "The reference price of this token for the auction used for scoring. This price is only included for tokens for which there are CoW Protocol orders." + }, + "symbol": { + "type": "string", + "description": "The ERC20.symbol value for this token. This may be missing for ERC20 tokens that don't implement the optional metadata extension." + }, + "trusted": { + "type": "boolean", + "description": "A flag which indicates that solvers are allowed to perform gas cost optimizations for this token by not routing the trades via an AMM, and instead use its available balances, as specified by CIP-2." + } + } + }, + "TokenReserve": { + "type": "object", + "required": [ + "balance" + ], + "properties": { + "balance": { + "$ref": "#/components/schemas/TokenAmount" + } + }, + "additionalProperties": false + }, + "Trade": { + "oneOf": [ + { + "$ref": "#/components/schemas/Fulfillment" + }, + { + "$ref": "#/components/schemas/JitTrade" + } + ], + "description": "A trade for a CoW Protocol order included in a solution." + }, + "U128": { + "type": "string", + "description": "128 bit unsigned integer in decimal notation.", + "example": "1234567890" + }, + "U256": { + "type": "string", + "description": "256 bit unsigned integer in decimal notation.", + "example": "1234567890" + }, + "VolumeFee": { + "type": "object", + "properties": { + "factor": { + "type": "number", + "description": "The fraction of the order's volume that the protocol will request from the solver after settling the order.", + "example": 0.5 + }, + "kind": { + "type": "string", + "enum": [ + "volume" + ] + } + } + }, + "WeightedProductPool": { + "type": "object", + "description": "A Balancer-like weighted product liquidity pool of N tokens.", + "required": [ + "kind", + "tokens", + "fee", + "balancer_pool_id" + ], + "properties": { + "balancer_pool_id": { + "$ref": "#/components/schemas/BalancerPoolId" + }, + "fee": { + "$ref": "#/components/schemas/Decimal" + }, + "kind": { + "type": "string", + "enum": [ + "weightedProduct" + ] + }, + "tokens": { + "type": "object", + "description": "A mapping of token address to its reserve amounts with weights.", + "additionalProperties": { + "allOf": [ + { + "$ref": "#/components/schemas/TokenReserve" + }, + { + "type": "object", + "required": [ + "weight" + ], + "properties": { + "scalingFactor": { + "$ref": "#/components/schemas/Decimal" + }, + "weight": { + "$ref": "#/components/schemas/Decimal" + } + } + } + ] + } + }, + "version": { + "type": "string", + "enum": [ + "v0", + "v3Plus" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/crates/solvers/openapi.yml b/crates/solvers/openapi.yml deleted file mode 100644 index 4a411121de..0000000000 --- a/crates/solvers/openapi.yml +++ /dev/null @@ -1,854 +0,0 @@ -openapi: 3.0.3 -info: - title: Solver Engine API - description: The API implemented by solver engines interacting with the reference driver implementation. - license: - name: MIT OR Apache-2.0 - version: 0.1.0 -paths: - /notify: - post: - tags: - - routes::notify - summary: Receive a status notification about a previously provided solution. - description: Receive a status notification about a previously provided solution. - operationId: notify - requestBody: - content: - application/json: - schema: - type: object - description: A notification that informs the solver how its solution performed in the auction. Depending on the notification type additional meta data may be attached but this is not guaranteed to be stable. - properties: - auctionId: - type: string - description: The auction ID of the auction that the solution was providedfor. - kind: - type: string - enum: - - timeout - - emptySolution - - duplicatedSolutionId - - simulationFailed - - invalidClearingPrices - - missingPrice - - invalidExecutedAmount - - nonBufferableTokensUsed - - solverAccountInsufficientBalance - - success - - revert - - driverError - - cancelled - - fail - - postprocessingTimedOut - solutionId: - type: number - description: The solution ID within the auction for which the notification applies - required: true - responses: - '200': - description: Notification successfully received. - /solve: - post: - tags: - - routes::solve - summary: Solve the passed in auction instance. - description: Solve the passed in auction instance. - operationId: solve - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Auction' - required: true - responses: - '200': - description: Auction successfully solved. - content: - application/json: - schema: - type: object - description: Proposed solutions to settle some of the orders in the auction. - required: - - solutions - properties: - solutions: - type: array - items: - $ref: '#/components/schemas/Solution' - '400': - description: There is something wrong with the request. - '429': - description: The solver cannot keep up. It is too busy to handle more requests. - '500': - description: Something went wrong when handling the request. -components: - schemas: - Address: - type: string - description: An Ethereum public address. - example: '0x0000000000000000000000000000000' - Allowance: - type: object - description: |- - An ERC20 allowance from the settlement contract to some spender that is - required for a custom interaction. - required: - - token - - spender - - amount - properties: - amount: - $ref: '#/components/schemas/TokenAmount' - spender: - $ref: '#/components/schemas/Address' - token: - $ref: '#/components/schemas/Token' - AppData: - type: string - description: |- - 32 bytes of arbitrary application specific data that can be added to an - order. This can also be used to ensure uniqueness between two orders with - otherwise the exact same parameters. - example: '0x0000000000000000000000000000000' - Asset: - type: object - description: A token address with an amount. - required: - - token - - amount - properties: - amount: - $ref: '#/components/schemas/TokenAmount' - token: - $ref: '#/components/schemas/Token' - Auction: - type: object - description: The abstract auction to be solved by the searcher. - required: - - tokens - - orders - - liquidity - - effectiveGasPrice - - deadline - properties: - deadline: - $ref: '#/components/schemas/DateTime' - effectiveGasPrice: - $ref: '#/components/schemas/TokenAmount' - id: - type: string - description: |- - An opaque identifier for the auction. Will be set to `null` for requests - that are not part of an auction (when quoting token prices for example). - liquidity: - type: array - items: - $ref: '#/components/schemas/Liquidity' - description: On-chain liquidity that can be used by the solution. - orders: - type: array - items: - $ref: '#/components/schemas/Order' - description: The solvable orders included in the auction. - tokens: - type: object - description: A map of token addresses to token information. - additionalProperties: - $ref: '#/components/schemas/TokenInfo' - BalancerPoolId: - type: string - description: |- - A hex-encoded 32 byte string containing the pool address (0..20), the pool - specialization (20..22) and the poolnonce (22..32). - example: '0xc88c76dd8b92408fe9bea1a54922a31' - BigInt: - type: string - description: An arbitrary-precision integer value. - example: '1234567890' - BuyTokenBalance: - type: string - description: Where should the buy token be transferred to? - enum: - - erc20 - - internal - BuyTokenDestination: - type: string - description: |- - Destination for which the buyAmount should be transferred to order's - receiver to upon fulfillment - enum: - - erc20 - - internal - ConcentratedLiquidityPool: - type: object - description: A Uniswap V3-like concentrated liquidity pool. - required: - - kind - - router - - tokens - - sqrtPrice - - liquidity - - tick - - liquidityNet - - fee - properties: - fee: - $ref: '#/components/schemas/Decimal' - kind: - type: string - enum: - - concentratedLiquidity - liquidity: - $ref: '#/components/schemas/U128' - liquidityNet: - type: object - description: A map of tick indices to their liquidity values. - additionalProperties: - $ref: '#/components/schemas/I128' - router: - $ref: '#/components/schemas/Address' - sqrtPrice: - $ref: '#/components/schemas/U256' - tick: - $ref: '#/components/schemas/I32' - tokens: - type: array - items: - $ref: '#/components/schemas/Token' - ConstantProductPool: - type: object - description: A UniswapV2-like constant product liquidity pool for a token pair. - required: - - kind - - router - - tokens - - fee - properties: - fee: - $ref: '#/components/schemas/Decimal' - kind: - type: string - enum: - - constantProduct - router: - $ref: '#/components/schemas/Address' - tokens: - type: object - description: A mapping of token address to its reserve amounts. - additionalProperties: - $ref: '#/components/schemas/TokenReserve' - CustomInteraction: - type: object - description: A searcher-specified custom interaction to be included in the final settlement. - required: - - kind - - target - - value - - callData - - inputs - - outputs - properties: - allowances: - type: array - items: - $ref: '#/components/schemas/Allowance' - description: ERC20 allowances that are required for this custom interaction. - callData: - type: string - description: The EVM calldata bytes. - example: '0x01020304' - inputs: - type: array - items: - $ref: '#/components/schemas/Asset' - kind: - type: string - enum: - - custom - outputs: - type: array - items: - $ref: '#/components/schemas/Asset' - target: - $ref: '#/components/schemas/Address' - value: - $ref: '#/components/schemas/TokenAmount' - DateTime: - type: string - description: An ISO-8601 formatted date-time. - example: 1970-01-01T00:00:00.000Z - Decimal: - type: string - description: An arbitrary-precision decimal value. - example: '13.37' - FeePolicy: - oneOf: - - $ref: '#/components/schemas/SurplusFee' - - $ref: '#/components/schemas/PriceImprovement' - - $ref: '#/components/schemas/VolumeFee' - description: A fee policy that applies to an order - ForeignLimitOrder: - type: object - description: A 0x-like limit order external to CoW Protocol. - required: - - kind - - makerToken - - takerToken - - makerAmount - - takerAmount - - takerTokenFeeAmount - properties: - kind: - type: string - enum: - - limitOrder - makerAmount: - $ref: '#/components/schemas/TokenAmount' - makerToken: - $ref: '#/components/schemas/Token' - takerAmount: - $ref: '#/components/schemas/TokenAmount' - takerToken: - $ref: '#/components/schemas/Token' - takerTokenFeeAmount: - $ref: '#/components/schemas/TokenAmount' - Fulfillment: - type: object - description: A trade which fulfills an order from the auction. - required: - - kind - - order - properties: - executedAmount: - allOf: - - $ref: '#/components/schemas/TokenAmount' - description: The amount of the order that was executed. This is denoted in 'sellToken' for sell orders, and 'buyToken' for buy orders. - fee: - type: object - description: The sell token amount that should be taken as a fee for this trade. This only gets returned for limit orders and only refers to the actual amount filled by the trade. - kind: - type: string - enum: - - fulfillment - order: - allOf: - - $ref: '#/components/schemas/OrderUid' - description: A reference by UID of the order to execute in a solution. The order must be included in the auction input. - I128: - type: string - description: 128 bit signed integer in decimal notation. - example: '-1234567890' - I32: - type: string - description: 32 bit signed integer in decimal notation. - example: '-12345' - Interaction: - allOf: - - type: object - properties: - internalize: - type: boolean - description: A flag indicating that the interaction should be 'internalized', as specified by CIP-2. - - oneOf: - - $ref: '#/components/schemas/LiquidityInteraction' - - $ref: '#/components/schemas/CustomInteraction' - description: An interaction to execute as part of a settlement. - InteractionData: - type: object - required: - - target - - value - - callData - properties: - callData: - type: string - example: '0x01020304' - target: - $ref: '#/components/schemas/Address' - value: - $ref: '#/components/schemas/TokenAmount' - JitOrder: - type: object - description: A just-in-time liquidity order included in a settlement. - required: - - sellToken - - buyToken - - receiver - - sellAmount - - buyAmount - - validTo - - appData - - feeAmount - - kind - - partiallyFillable - - sellTokenBalance - - buyTokenBalance - - signingScheme - - signature - properties: - appData: - $ref: '#/components/schemas/AppData' - buyAmount: - $ref: '#/components/schemas/TokenAmount' - buyToken: - $ref: '#/components/schemas/Token' - buyTokenBalance: - $ref: '#/components/schemas/BuyTokenBalance' - feeAmount: - $ref: '#/components/schemas/TokenAmount' - kind: - $ref: '#/components/schemas/OrderKind' - partiallyFillable: - type: boolean - receiver: - $ref: '#/components/schemas/Address' - sellAmount: - $ref: '#/components/schemas/TokenAmount' - sellToken: - $ref: '#/components/schemas/Token' - sellTokenBalance: - $ref: '#/components/schemas/SellTokenBalance' - signature: - $ref: '#/components/schemas/Signature' - signingScheme: - $ref: '#/components/schemas/SigningScheme' - validTo: - type: integer - format: int32 - minimum: 0 - JitTrade: - type: object - description: A trade with a JIT order. - required: - - kind - - order - - executedAmount - properties: - executedAmount: - allOf: - - $ref: '#/components/schemas/TokenAmount' - description: The amount of the order that was executed. This is denoted in 'sellToken' for sell orders, and 'buyToken' for buy orders. - kind: - type: string - enum: - - jit - order: - allOf: - - $ref: '#/components/schemas/JitOrder' - description: The just-in-time liquidity order to execute in a solution. - LegacySigningScheme: - type: string - enum: - - eip712 - - ethsign - - eip1271 - - presign - Liquidity: - allOf: - - $ref: '#/components/schemas/LiquidityParameters' - - type: object - required: - - id - - address - - gasEstimate - properties: - address: - allOf: - - $ref: '#/components/schemas/Address' - description: A rough approximation of gas units required to use this liquidity on-chain. - gasEstimate: - allOf: - - $ref: '#/components/schemas/BigInt' - description: A rough approximation of gas units required to use this liquidity on-chain. - id: - type: string - description: An opaque ID used for uniquely identifying the liquidity within a single auction (note that they are **not** guaranteed to be unique across auctions). This ID is used in the solution for matching interactions with the executed liquidity. - description: On-chain liquidity that can be used in a solution. This liquidity is provided to facilitate onboarding new solvers. Additional liquidity that is not included in this set may still be used in solutions. - LiquidityInteraction: - type: object - description: Interaction representing the execution of liquidity that was passed in with the auction. - required: - - kind - - id - - inputToken - - outputToken - - inputAmount - - outputAmount - properties: - id: - type: string - description: The ID of executed liquidity provided in the auction input. - inputAmount: - $ref: '#/components/schemas/TokenAmount' - inputToken: - $ref: '#/components/schemas/Token' - kind: - type: string - enum: - - liquidity - outputAmount: - $ref: '#/components/schemas/TokenAmount' - outputToken: - $ref: '#/components/schemas/Token' - LiquidityParameters: - oneOf: - - $ref: '#/components/schemas/ConstantProductPool' - - $ref: '#/components/schemas/WeightedProductPool' - - $ref: '#/components/schemas/StablePool' - - $ref: '#/components/schemas/ConcentratedLiquidityPool' - - $ref: '#/components/schemas/ForeignLimitOrder' - NativePrice: - type: string - description: |- - The price in wei of the native token (Ether on Mainnet for example) to buy - 10**18 of a token. - example: '1234567890' - Order: - type: object - description: CoW Protocol order information relevant to execution. - required: - - uid - - sellToken - - buyToken - - sellAmount - - fullSellAmount - - buyAmount - - fullBuyAmount - - validTo - - kind - - receiver - - owner - - partiallyFillable - - preInteractions - - postInteractions - - sellTokenSource - - buyTokenDestination - - class - - appData - - signingScheme - - signature - properties: - appData: - $ref: '#/components/schemas/AppData' - buyAmount: - $ref: '#/components/schemas/TokenAmount' - buyToken: - $ref: '#/components/schemas/Token' - buyTokenDestination: - $ref: '#/components/schemas/BuyTokenDestination' - class: - $ref: '#/components/schemas/OrderClass' - feePolicies: - type: array - items: - $ref: '#/components/schemas/FeePolicy' - nullable: true - fullBuyAmount: - $ref: '#/components/schemas/TokenAmount' - fullSellAmount: - $ref: '#/components/schemas/TokenAmount' - kind: - $ref: '#/components/schemas/OrderKind' - owner: - $ref: '#/components/schemas/Address' - partiallyFillable: - type: boolean - description: |- - Whether or not this order can be partially filled. If this is false, - then the order is a "fill-or-kill" order, meaning it needs to be - completely filled or not at all. - postInteractions: - type: array - items: - $ref: '#/components/schemas/InteractionData' - preInteractions: - type: array - items: - $ref: '#/components/schemas/InteractionData' - receiver: - $ref: '#/components/schemas/Address' - sellAmount: - $ref: '#/components/schemas/TokenAmount' - sellToken: - $ref: '#/components/schemas/Token' - sellTokenSource: - $ref: '#/components/schemas/SellTokenSource' - signature: - $ref: '#/components/schemas/Signature' - signingScheme: - $ref: '#/components/schemas/LegacySigningScheme' - uid: - $ref: '#/components/schemas/OrderUid' - validTo: - type: integer - format: int32 - minimum: 0 - OrderClass: - type: string - description: How the CoW Protocol order was classified. - enum: - - market - - limit - - liquidity - OrderKind: - type: string - description: How the CoW Protocol order was classified. - enum: - - sell - - buy - OrderUid: - type: string - description: |- - Unique identifier for the order. Order UIDs are 56 bytes long, where bytes - [0, 32) represent the order digest used for signing, bytes [32, 52) - represent the owner address and bytes [52, 56) represent the order's - `validTo` field. - example: '0x30cff40d9f60caa68a37f0ee73253ad' - PriceImprovement: - type: object - description: A cut from the price improvement over the best quote is taken as a protocol fee. - properties: - factor: - type: number - description: The factor of the user surplus that the protocol will request from the solver after settling the order - example: 0.5 - kind: - type: string - enum: - - priceImprovement - maxVolumeFactor: - type: number - description: Never charge more than that percentage of the order volume. - example: 0.01 - maximum: 0.99999 - minimum: 0 - quote: - $ref: '#/components/schemas/Quote' - Quote: - type: object - required: - - sellAmount - - buyAmount - - fee - properties: - buyAmount: - $ref: '#/components/schemas/TokenAmount' - fee: - $ref: '#/components/schemas/TokenAmount' - sellAmount: - $ref: '#/components/schemas/TokenAmount' - SellTokenBalance: - type: string - description: Where should the sell token be drawn from? - enum: - - erc20 - - internal - - external - SellTokenSource: - type: string - description: Source from which the sellAmount should be drawn upon order fulfillment - enum: - - erc20 - - external - - internal - Signature: - type: string - description: Signature bytes. - example: '0x0000000000000000000000000000000' - SigningScheme: - type: string - description: How was the order signed? - enum: - - eip712 - - ethSign - - preSign - - eip1271 - Solution: - type: object - description: A computed solution for a given auction. - required: - - id - - prices - - trades - - interactions - properties: - gas: - type: integer - format: int64 - description: How many units of gas this solution is estimated to cost. - nullable: true - minimum: 0 - id: - type: number - format: int64 - description: |- - An opaque identifier for the solution. This is a solver generated number - that is unique across multiple solutions within the auction. - interactions: - type: array - items: - $ref: '#/components/schemas/Interaction' - description: Interactions to encode within a settlement. - prices: - type: object - description: |- - A clearing price map of token address to price. The price can have - arbitrary denomination. - additionalProperties: - $ref: '#/components/schemas/U256' - trades: - type: array - items: - $ref: '#/components/schemas/Trade' - description: CoW Protocol order trades included in the solution. - StablePool: - type: object - description: A Curve-like stable pool of N tokens. - required: - - kind - - tokens - - amplificationParameter - - fee - - balancer_pool_id - properties: - amplificationParameter: - $ref: '#/components/schemas/Decimal' - balancer_pool_id: - $ref: '#/components/schemas/BalancerPoolId' - fee: - $ref: '#/components/schemas/Decimal' - kind: - type: string - enum: - - stable - tokens: - type: object - description: A mapping of token address to token balance and scaling rate. - additionalProperties: - allOf: - - $ref: '#/components/schemas/TokenReserve' - - type: object - required: - - scalingFactor - properties: - scalingFactor: - $ref: '#/components/schemas/Decimal' - SurplusFee: - type: object - description: If the order receives more than limit price, pay the protocol a factor of the difference. - properties: - factor: - type: number - description: The factor of the user surplus that the protocol will request from the solver after settling the order - example: 0.5 - kind: - type: string - enum: - - surplus - maxVolumeFactor: - type: number - description: Never charge more than that percentage of the order volume. - example: 0.05 - maximum: 0.99999 - minimum: 0 - Token: - type: string - description: An ERC20 token address. - example: '0xDEf1CA1fb7FBcDC777520aa7f396b4E' - TokenAmount: - type: string - description: Amount of an ERC20 token. 256 bit unsigned integer in decimal notation. - example: '1234567890' - TokenInfo: - type: object - description: Information about an ERC20 token. - required: - - availableBalance - - trusted - properties: - availableBalance: - allOf: - - $ref: '#/components/schemas/TokenAmount' - description: The balance held by the Settlement contract that is available during a settlement. - decimals: - type: integer - description: The ERC20.decimals value for this token. This may be missing for ERC20 tokens that don't implement the optional metadata extension. - referencePrice: - allOf: - - $ref: '#/components/schemas/NativePrice' - description: The reference price of this token for the auction used for scoring. This price is only included for tokens for which there are CoW Protocol orders. - symbol: - type: string - description: The ERC20.symbol value for this token. This may be missing for ERC20 tokens that don't implement the optional metadata extension. - trusted: - type: boolean - description: A flag which indicates that solvers are allowed to perform gas cost optimizations for this token by not routing the trades via an AMM, and instead use its available balances, as specified by CIP-2. - TokenReserve: - type: object - required: - - balance - properties: - balance: - $ref: '#/components/schemas/TokenAmount' - additionalProperties: false - Trade: - oneOf: - - $ref: '#/components/schemas/Fulfillment' - - $ref: '#/components/schemas/JitTrade' - description: A trade for a CoW Protocol order included in a solution. - U128: - type: string - description: 128 bit unsigned integer in decimal notation. - example: '1234567890' - U256: - type: string - description: 256 bit unsigned integer in decimal notation. - example: '1234567890' - VolumeFee: - type: object - properties: - factor: - type: number - description: The fraction of the order's volume that the protocol will request from the solver after settling the order. - example: 0.5 - kind: - type: string - enum: - - volume - WeightedProductPool: - type: object - description: A Balancer-like weighted product liquidity pool of N tokens. - required: - - kind - - tokens - - fee - - balancer_pool_id - properties: - balancer_pool_id: - $ref: '#/components/schemas/BalancerPoolId' - fee: - $ref: '#/components/schemas/Decimal' - kind: - type: string - enum: - - weightedProduct - tokens: - type: object - description: A mapping of token address to its reserve amounts with weights. - additionalProperties: - allOf: - - $ref: '#/components/schemas/TokenReserve' - - type: object - required: - - weight - properties: - scalingFactor: - $ref: '#/components/schemas/Decimal' - weight: - $ref: '#/components/schemas/Decimal' - version: - type: string - enum: - - v0 - - v3Plus diff --git a/crates/solvers/src/api/mod.rs b/crates/solvers/src/api/mod.rs index 3f720bbaee..756d4e9011 100644 --- a/crates/solvers/src/api/mod.rs +++ b/crates/solvers/src/api/mod.rs @@ -2,6 +2,7 @@ use { crate::domain::solver::Solver, + serde_json::Error, std::{future::Future, net::SocketAddr, sync::Arc}, tokio::sync::oneshot, utoipa::OpenApi, @@ -49,7 +50,7 @@ impl Api { } // migrate to utoipauto once the issue is solved https://github.com/ProbablyClem/utoipauto/issues/23 -pub fn generate_openapi_yaml() -> Result { +pub fn generate_openapi_json() -> Result { #[derive(OpenApi)] #[openapi( paths(routes::solve::solve, routes::notify::notify,), @@ -116,5 +117,5 @@ pub fn generate_openapi_yaml() -> Result { )] pub struct ApiDoc; - ApiDoc::openapi().to_yaml() + ApiDoc::openapi().to_pretty_json() } diff --git a/crates/solvers/src/lib.rs b/crates/solvers/src/lib.rs index 77d2d3f4db..ecce6443ce 100644 --- a/crates/solvers/src/lib.rs +++ b/crates/solvers/src/lib.rs @@ -13,5 +13,5 @@ mod util; pub use { self::run::{run, start}, - api::generate_openapi_yaml, + api::generate_openapi_json, }; From 53d4bc2612d873e9f25bb08831cabc15923022f5 Mon Sep 17 00:00:00 2001 From: ilya Date: Thu, 2 May 2024 19:30:35 +0100 Subject: [PATCH 19/19] Fixed extensions --- .github/workflows/pull-request.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 9bf8193d41..c9fb472a55 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -190,6 +190,6 @@ jobs: run: | git diff --exit-code crates/solvers/openapi.json || { echo "::error ::OpenAPI spec contains changes: crates/solvers/openapi.json"; exit 1; } - run: npm install @apidevtools/swagger-cli - - run: node_modules/.bin/swagger-cli validate crates/orderbook/openapi.json - - run: node_modules/.bin/swagger-cli validate crates/driver/openapi.json + - run: node_modules/.bin/swagger-cli validate crates/orderbook/openapi.yml + - run: node_modules/.bin/swagger-cli validate crates/driver/openapi.yml - run: node_modules/.bin/swagger-cli validate crates/solvers/openapi.json