diff --git a/Cargo.lock b/Cargo.lock index c632674da8..4e85fcc3d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3038,6 +3038,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bigdecimal", + "derive_more", "num", "primitive-types", "serde", @@ -4221,6 +4222,7 @@ dependencies = [ "itertools 0.11.0", "model", "num", + "number", "observe", "prometheus", "prometheus-metric-storage", diff --git a/crates/driver/src/boundary/mempool.rs b/crates/driver/src/boundary/mempool.rs index a362d9a66a..19871ee16e 100644 --- a/crates/driver/src/boundary/mempool.rs +++ b/crates/driver/src/boundary/mempool.rs @@ -6,6 +6,8 @@ use { }, async_trait::async_trait, ethcontract::{transaction::TransactionBuilder, transport::DynTransport}, + number::U256, + primitive_types::U256 as RawU256, solver::{ settlement_access_list::AccessListEstimating, settlement_submission::{ @@ -30,7 +32,7 @@ pub use {gas_estimation::GasPriceEstimating, solver::settlement_submission::Glob #[derive(Debug, Clone)] pub struct Config { pub min_priority_fee: eth::U256, - pub gas_price_cap: eth::U256, + pub gas_price_cap: U256, pub target_confirm_time: std::time::Duration, pub max_confirm_time: std::time::Duration, pub retry_interval: std::time::Duration, @@ -51,7 +53,7 @@ pub enum Kind { /// The MEVBlocker private mempool. MEVBlocker { url: reqwest::Url, - max_additional_tip: eth::U256, + max_additional_tip: RawU256, additional_tip_percentage: f64, use_soft_cancellations: bool, }, @@ -149,7 +151,7 @@ impl Mempool { .transaction_count(solver.address().into(), None) .await .map_err(anyhow::Error::from)?; - let max_fee_per_gas = eth::U256::from(settlement.gas.price.max()).to_f64_lossy(); + let max_fee_per_gas = RawU256::from(settlement.gas.price.max()).to_f64_lossy(); let gas_price_estimator = SubmitterGasPriceEstimator { inner: self.gas_price_estimator.as_ref(), max_fee_per_gas: max_fee_per_gas.min(self.config.gas_price_cap.to_f64_lossy()), diff --git a/crates/driver/src/domain/competition/auction.rs b/crates/driver/src/domain/competition/auction.rs index 66ce598f87..b48d12c472 100644 --- a/crates/driver/src/domain/competition/auction.rs +++ b/crates/driver/src/domain/competition/auction.rs @@ -12,6 +12,7 @@ use { }, futures::future::{join_all, BoxFuture, FutureExt, Shared}, itertools::Itertools, + number::U256, std::{ collections::{HashMap, HashSet}, sync::{Arc, Mutex}, @@ -410,8 +411,14 @@ impl From for eth::U256 { } } -impl From for Price { - fn from(value: eth::U256) -> Self { +impl From for U256 { + fn from(value: Price) -> Self { + value.0.into() + } +} + +impl From for Price { + fn from(value: U256) -> Self { Self(value.into()) } } diff --git a/crates/driver/src/domain/competition/order/mod.rs b/crates/driver/src/domain/competition/order/mod.rs index f8629f55fe..5a3f3e4339 100644 --- a/crates/driver/src/domain/competition/order/mod.rs +++ b/crates/driver/src/domain/competition/order/mod.rs @@ -7,6 +7,7 @@ use { }, bigdecimal::Zero, num::CheckedDiv, + number::U256, }; pub use {fees::FeePolicy, signature::Signature}; @@ -56,6 +57,12 @@ impl From for SellAmount { } } +impl From for SellAmount { + fn from(value: U256) -> Self { + Self(value.into()) + } +} + impl From for SellAmount { fn from(value: eth::TokenAmount) -> Self { Self(value.into()) @@ -68,6 +75,12 @@ impl From for eth::U256 { } } +impl From for U256 { + fn from(sell_amount: SellAmount) -> Self { + sell_amount.0.into() + } +} + /// An amount denominated in the sell token for [`Side::Sell`] [`Order`]s, or in /// the buy token for [`Side::Buy`] [`Order`]s. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -79,6 +92,12 @@ impl From for TargetAmount { } } +impl From for TargetAmount { + fn from(value: U256) -> Self { + Self(value.into()) + } +} + impl From for eth::U256 { fn from(value: TargetAmount) -> Self { value.0 diff --git a/crates/driver/src/domain/competition/score.rs b/crates/driver/src/domain/competition/score.rs index 72a94d1f8f..79e25ea6f2 100644 --- a/crates/driver/src/domain/competition/score.rs +++ b/crates/driver/src/domain/competition/score.rs @@ -1,5 +1,6 @@ use { crate::{boundary, domain::eth}, + number::U256, std::cmp::Ordering, }; @@ -22,6 +23,14 @@ impl TryFrom for Score { } } +impl TryFrom for Score { + type Error = Error; + + fn try_from(value: U256) -> Result { + eth::U256::from(value).try_into() + } +} + /// Represents the observed quality of a solution. It's defined as surplus + /// fees. #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] diff --git a/crates/driver/src/domain/eth/gas.rs b/crates/driver/src/domain/eth/gas.rs index 77c63d855b..e8cda782f0 100644 --- a/crates/driver/src/domain/eth/gas.rs +++ b/crates/driver/src/domain/eth/gas.rs @@ -2,6 +2,7 @@ use { super::{Ether, U256}, bigdecimal::Zero, num::zero, + number::U256 as NumberU256, std::{ops, ops::Add}, }; @@ -18,6 +19,12 @@ impl From for Gas { } } +impl From for Gas { + fn from(value: NumberU256) -> Self { + Self(value.into()) + } +} + impl From for Gas { fn from(value: u64) -> Self { Self(value.into()) @@ -30,6 +37,12 @@ impl From for U256 { } } +impl From for NumberU256 { + fn from(value: Gas) -> Self { + value.0.into() + } +} + impl Add for Gas { type Output = Self; @@ -147,6 +160,12 @@ impl From for FeePerGas { } } +impl From for FeePerGas { + fn from(value: NumberU256) -> Self { + Self(value.into()) + } +} + impl ops::Add for FeePerGas { type Output = FeePerGas; @@ -161,6 +180,12 @@ impl From for U256 { } } +impl From for NumberU256 { + fn from(value: FeePerGas) -> Self { + value.0.into() + } +} + impl ops::Mul for Gas { type Output = Ether; @@ -181,12 +206,24 @@ impl From for EffectiveGasPrice { } } +impl From for EffectiveGasPrice { + fn from(value: NumberU256) -> Self { + Self(value.into()) + } +} + impl From for U256 { fn from(value: EffectiveGasPrice) -> Self { value.0.into() } } +impl From for NumberU256 { + fn from(value: EffectiveGasPrice) -> Self { + value.0.into() + } +} + impl Add for EffectiveGasPrice { type Output = Self; diff --git a/crates/driver/src/domain/eth/mod.rs b/crates/driver/src/domain/eth/mod.rs index 86ea183208..5b63f77f15 100644 --- a/crates/driver/src/domain/eth/mod.rs +++ b/crates/driver/src/domain/eth/mod.rs @@ -8,6 +8,7 @@ pub mod allowance; mod eip712; mod gas; +use number::U256 as NumberU256; pub use { allowance::Allowance, eip712::{DomainFields, DomainSeparator}, @@ -201,12 +202,24 @@ impl From for TokenAmount { } } +impl From for TokenAmount { + fn from(value: NumberU256) -> Self { + Self(value.into()) + } +} + impl From for U256 { fn from(value: TokenAmount) -> Self { value.0 } } +impl From for NumberU256 { + fn from(value: TokenAmount) -> Self { + value.0.into() + } +} + impl From for TokenAmount { fn from(value: u128) -> Self { Self(value.into()) @@ -271,6 +284,12 @@ impl From for Ether { } } +impl From for Ether { + fn from(value: NumberU256) -> Self { + Self(value.into()) + } +} + impl From for num::BigInt { fn from(value: Ether) -> Self { let mut bytes = [0; 32]; @@ -285,6 +304,12 @@ impl From for U256 { } } +impl From for NumberU256 { + fn from(value: Ether) -> Self { + value.0.into() + } +} + impl From for Ether { fn from(value: i32) -> Self { Self(value.into()) diff --git a/crates/driver/src/infra/api/routes/quote/dto/order.rs b/crates/driver/src/infra/api/routes/quote/dto/order.rs index 6bf7115560..ef80510339 100644 --- a/crates/driver/src/infra/api/routes/quote/dto/order.rs +++ b/crates/driver/src/infra/api/routes/quote/dto/order.rs @@ -2,10 +2,9 @@ use { crate::{ domain::{competition, eth, quote, time}, infra::solver::Timeouts, - util::serialize, }, + number::U256, serde::Deserialize, - serde_with::serde_as, }; impl Order { @@ -23,14 +22,12 @@ impl Order { } } -#[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct Order { sell_token: eth::H160, buy_token: eth::H160, - #[serde_as(as = "serialize::U256")] - amount: eth::U256, + amount: U256, kind: Kind, deadline: chrono::DateTime, } diff --git a/crates/driver/src/infra/api/routes/quote/dto/quote.rs b/crates/driver/src/infra/api/routes/quote/dto/quote.rs index dc72e3729d..9c3ef2ba63 100644 --- a/crates/driver/src/infra/api/routes/quote/dto/quote.rs +++ b/crates/driver/src/infra/api/routes/quote/dto/quote.rs @@ -3,6 +3,7 @@ use { domain::{eth, quote}, util::serialize, }, + number::U256, serde::Serialize, serde_with::serde_as, }; @@ -10,7 +11,7 @@ use { impl Quote { pub fn new(quote: "e::Quote) -> Self { Self { - amount: quote.amount, + amount: quote.amount.into(), interactions: quote .interactions .iter() @@ -25,12 +26,10 @@ impl Quote { } } -#[serde_as] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Quote { - #[serde_as(as = "serialize::U256")] - amount: eth::U256, + amount: U256, interactions: Vec, solver: eth::H160, } @@ -40,8 +39,7 @@ pub struct Quote { #[serde(rename_all = "camelCase")] struct Interaction { target: eth::H160, - #[serde_as(as = "serialize::U256")] - value: eth::U256, + value: U256, #[serde_as(as = "serialize::Hex")] call_data: Vec, } diff --git a/crates/driver/src/infra/api/routes/solve/dto/auction.rs b/crates/driver/src/infra/api/routes/solve/dto/auction.rs index f602e79919..888f2e826a 100644 --- a/crates/driver/src/infra/api/routes/solve/dto/auction.rs +++ b/crates/driver/src/infra/api/routes/solve/dto/auction.rs @@ -8,6 +8,7 @@ use { infra::{solver::Timeouts, tokens, Ethereum}, util::serialize, }, + number::U256, serde::Deserialize, serde_with::serde_as, }; @@ -200,8 +201,7 @@ pub struct Auction { tokens: Vec, orders: Vec, deadline: chrono::DateTime, - #[serde_as(as = "serialize::U256")] - score_cap: eth::U256, + score_cap: U256, } impl Auction { @@ -210,13 +210,11 @@ impl Auction { } } -#[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Token { pub address: eth::H160, - #[serde_as(as = "Option")] - pub price: Option, + pub price: Option, pub trusted: bool, } @@ -228,12 +226,9 @@ struct Order { uid: [u8; order::UID_LEN], sell_token: eth::H160, buy_token: eth::H160, - #[serde_as(as = "serialize::U256")] - sell_amount: eth::U256, - #[serde_as(as = "serialize::U256")] - buy_amount: eth::U256, - #[serde_as(as = "serialize::U256")] - user_fee: eth::U256, + sell_amount: U256, + buy_amount: U256, + user_fee: U256, protocol_fees: Vec, valid_to: u32, kind: Kind, @@ -241,8 +236,7 @@ struct Order { owner: eth::H160, partially_fillable: bool, /// Always zero if the order is not partially fillable. - #[serde_as(as = "serialize::U256")] - executed: eth::U256, + executed: U256, pre_interactions: Vec, post_interactions: Vec, #[serde(default)] @@ -269,8 +263,7 @@ enum Kind { #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Interaction { target: eth::H160, - #[serde_as(as = "serialize::U256")] - value: eth::U256, + value: U256, #[serde_as(as = "serialize::Hex")] call_data: Vec, } diff --git a/crates/driver/src/infra/api/routes/solve/dto/solved.rs b/crates/driver/src/infra/api/routes/solve/dto/solved.rs index e6af9b01a0..e10e815d8c 100644 --- a/crates/driver/src/infra/api/routes/solve/dto/solved.rs +++ b/crates/driver/src/infra/api/routes/solve/dto/solved.rs @@ -4,6 +4,7 @@ use { infra::Solver, util::serialize, }, + number::U256, serde::Serialize, serde_with::serde_as, std::collections::HashMap, @@ -30,7 +31,7 @@ impl Solution { pub fn new(solution_id: u64, solved: competition::Solved, solver: &Solver) -> Self { Self { solution_id, - score: solved.score.0.get(), + score: solved.score.0.get().into(), submission_address: solver.address().into(), orders: solved .trades @@ -54,16 +55,13 @@ impl Solution { } } -#[serde_as] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct TradedAmounts { /// The effective amount that left the user's wallet including all fees. - #[serde_as(as = "serialize::U256")] - pub sell_amount: eth::U256, + pub sell_amount: U256, /// The effective amount the user received after all fees. - #[serde_as(as = "serialize::U256")] - pub buy_amount: eth::U256, + pub buy_amount: U256, } type OrderId = [u8; order::UID_LEN]; @@ -76,11 +74,10 @@ pub struct Solution { /// in subsequent requests (reveal, settle). #[serde_as(as = "serde_with::DisplayFromStr")] solution_id: u64, - #[serde_as(as = "serialize::U256")] - score: eth::U256, + score: U256, submission_address: eth::H160, #[serde_as(as = "HashMap")] orders: HashMap, - #[serde_as(as = "HashMap<_, serialize::U256>")] - clearing_prices: HashMap, + #[serde_as(as = "HashMap<_, _>")] + clearing_prices: HashMap, } diff --git a/crates/driver/src/infra/blockchain/gas.rs b/crates/driver/src/infra/blockchain/gas.rs index d7ea86310b..6330ceee45 100644 --- a/crates/driver/src/infra/blockchain/gas.rs +++ b/crates/driver/src/infra/blockchain/gas.rs @@ -57,7 +57,7 @@ impl GasPriceEstimator { Ok(Self { gas, additional_tip, - max_fee_per_gas, + max_fee_per_gas: max_fee_per_gas.into(), min_priority_fee, }) } diff --git a/crates/driver/src/infra/config/file/load.rs b/crates/driver/src/infra/config/file/load.rs index f8168a94bf..b0122fa3d9 100644 --- a/crates/driver/src/infra/config/file/load.rs +++ b/crates/driver/src/infra/config/file/load.rs @@ -69,7 +69,7 @@ pub async fn load(network: &blockchain::Network, path: &Path) -> infra::Config { name: config.name.into(), slippage: solver::Slippage { relative: config.slippage.relative, - absolute: config.slippage.absolute.map(eth::Ether), + absolute: config.slippage.absolute.map(eth::Ether::from), }, liquidity: if config.skip_liquidity { solver::Liquidity::Skip @@ -254,7 +254,7 @@ pub async fn load(network: &blockchain::Network, path: &Path) -> infra::Config { .mempools .iter() .map(|mempool| mempool::Config { - min_priority_fee: config.submission.min_priority_fee, + min_priority_fee: config.submission.min_priority_fee.into(), gas_price_cap: config.submission.gas_price_cap, target_confirm_time: config.submission.target_confirm_time, max_confirm_time: config.submission.max_confirm_time, @@ -284,7 +284,7 @@ pub async fn load(network: &blockchain::Network, path: &Path) -> infra::Config { use_soft_cancellations, } => mempool::Kind::MEVBlocker { url: url.to_owned(), - max_additional_tip: *max_additional_tip, + max_additional_tip: (*max_additional_tip).into(), additional_tip_percentage: *additional_tip_percentage, use_soft_cancellations: *use_soft_cancellations, }, diff --git a/crates/driver/src/infra/config/file/mod.rs b/crates/driver/src/infra/config/file/mod.rs index 60b2f7ae97..f98b43eea5 100644 --- a/crates/driver/src/infra/config/file/mod.rs +++ b/crates/driver/src/infra/config/file/mod.rs @@ -1,6 +1,7 @@ pub use load::load; use { - crate::{domain::eth, util::serialize}, + crate::domain::eth, + number::U256, reqwest::Url, serde::Deserialize, serde_with::serde_as, @@ -10,7 +11,6 @@ use { mod load; -#[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] struct Config { @@ -18,7 +18,7 @@ struct Config { /// Note that the actual chain ID is fetched from the configured Ethereum /// RPC endpoint, and the driver will exit if it does not match this /// value. - chain_id: Option, + chain_id: Option, /// Disable access list simulation, useful for environments that don't /// support this, such as less popular blockchains. @@ -28,8 +28,7 @@ struct Config { /// Disable gas simulation and always use this fixed gas value instead. This /// can be useful for testing, but shouldn't be used in production since it /// will cause the driver to return invalid scores. - #[serde_as(as = "Option")] - disable_gas_simulation: Option, + disable_gas_simulation: Option, /// Parameters related to settlement submission. #[serde(default)] @@ -52,21 +51,18 @@ struct Config { liquidity: LiquidityConfig, } -#[serde_as] #[derive(Debug, Default, Deserialize)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] struct SubmissionConfig { /// The minimum priority fee in Gwei the solver is ensuring to pay in a /// settlement. #[serde(default)] - #[serde_as(as = "serialize::U256")] - min_priority_fee: eth::U256, + min_priority_fee: U256, /// The maximum gas price in Gwei the solver is willing to pay in a /// settlement. #[serde(default = "default_gas_price_cap")] - #[serde_as(as = "serialize::U256")] - gas_price_cap: eth::U256, + gas_price_cap: U256, /// The target confirmation time for settlement transactions used /// to estimate gas price. @@ -92,7 +88,6 @@ struct SubmissionConfig { logic: Logic, } -#[serde_as] #[derive(Debug, Deserialize)] #[serde(tag = "mempool")] #[serde(rename_all = "kebab-case", deny_unknown_fields)] @@ -105,8 +100,7 @@ enum Mempool { /// Maximum additional tip in Gwei that we are willing to give to /// MEVBlocker above regular gas price estimation. #[serde(default = "default_max_additional_tip")] - #[serde_as(as = "serialize::U256")] - max_additional_tip: eth::U256, + max_additional_tip: U256, /// Additional tip in percentage of max_fee_per_gas we are giving to /// MEVBlocker above regular gas price estimation. Expects a /// floating point value between 0 and 1. @@ -127,8 +121,8 @@ fn default_additional_tip_percentage() -> f64 { } /// 1000 gwei -fn default_gas_price_cap() -> eth::U256 { - eth::U256::from(1000) * eth::U256::exp10(9) +fn default_gas_price_cap() -> U256 { + U256::from(eth::U256::from(1000) * eth::U256::exp10(9)) } fn default_target_confirm_time() -> Duration { @@ -144,8 +138,8 @@ fn default_max_confirm_time() -> Duration { } /// 3 gwei -fn default_max_additional_tip() -> eth::U256 { - eth::U256::from(3) * eth::U256::exp10(9) +fn default_max_additional_tip() -> U256 { + U256::from(eth::U256::from(3) * eth::U256::exp10(9)) } fn default_soft_cancellations_flag() -> bool { @@ -230,8 +224,7 @@ struct Slippage { /// The absolute slippage allowed by the solver. #[serde(rename = "absolute-slippage")] - #[serde_as(as = "Option")] - absolute: Option, + absolute: Option, } #[derive(Debug, Default, Deserialize)] diff --git a/crates/driver/src/infra/solver/dto/auction.rs b/crates/driver/src/infra/solver/dto/auction.rs index a89491d75a..0917b26df7 100644 --- a/crates/driver/src/infra/solver/dto/auction.rs +++ b/crates/driver/src/infra/solver/dto/auction.rs @@ -7,6 +7,7 @@ use { }, }, indexmap::IndexMap, + number::U256, serde::Serialize, serde_with::serde_as, std::collections::{BTreeMap, HashMap}, @@ -28,7 +29,7 @@ impl Auction { decimals: token.decimals, symbol: token.symbol.clone(), reference_price: token.price.map(Into::into), - available_balance: token.available_balance, + available_balance: token.available_balance.into(), trusted: token.trusted, }, ) @@ -107,9 +108,9 @@ impl Auction { Liquidity::ConcentratedLiquidity(ConcentratedLiquidityPool { id: liquidity.id.into(), address: pool.address.0, - gas_estimate: liquidity.gas.0, + gas_estimate: liquidity.gas.0.into(), tokens: vec![pool.tokens.get().0.into(), pool.tokens.get().1.into()], - sqrt_price: pool.sqrt_price.0, + sqrt_price: pool.sqrt_price.0.into(), liquidity: pool.liquidity.0, tick: pool.tick.0, liquidity_net: pool @@ -204,7 +205,6 @@ impl Auction { } } -#[serde_as] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Auction { @@ -212,8 +212,7 @@ pub struct Auction { tokens: HashMap, orders: Vec, liquidity: Vec, - #[serde_as(as = "serialize::U256")] - effective_gas_price: eth::U256, + effective_gas_price: U256, deadline: chrono::DateTime, } @@ -225,12 +224,9 @@ struct Order { uid: [u8; order::UID_LEN], sell_token: eth::H160, buy_token: eth::H160, - #[serde_as(as = "serialize::U256")] - sell_amount: eth::U256, - #[serde_as(as = "serialize::U256")] - buy_amount: eth::U256, - #[serde_as(as = "serialize::U256")] - fee_amount: eth::U256, + sell_amount: U256, + buy_amount: U256, + fee_amount: U256, kind: Kind, partially_fillable: bool, class: Class, @@ -251,16 +247,13 @@ enum Class { Liquidity, } -#[serde_as] #[derive(Default, Debug, Serialize)] #[serde(rename_all = "camelCase")] struct Token { decimals: Option, symbol: Option, - #[serde_as(as = "Option")] - reference_price: Option, - #[serde_as(as = "serialize::U256")] - available_balance: eth::U256, + reference_price: Option, + available_balance: U256, trusted: bool, } @@ -283,18 +276,15 @@ struct ConstantProductPool { #[serde_as(as = "serde_with::DisplayFromStr")] id: usize, address: eth::H160, - #[serde_as(as = "serialize::U256")] - gas_estimate: eth::U256, + gas_estimate: U256, tokens: BTreeMap, #[serde_as(as = "serde_with::DisplayFromStr")] fee: bigdecimal::BigDecimal, } -#[serde_as] #[derive(Debug, Serialize)] struct ConstantProductReserve { - #[serde_as(as = "serialize::U256")] - balance: eth::U256, + balance: U256, } #[serde_as] @@ -304,8 +294,7 @@ struct WeightedProductPool { #[serde_as(as = "serde_with::DisplayFromStr")] id: usize, address: eth::H160, - #[serde_as(as = "serialize::U256")] - gas_estimate: eth::U256, + gas_estimate: U256, tokens: IndexMap, #[serde_as(as = "serde_with::DisplayFromStr")] fee: bigdecimal::BigDecimal, @@ -316,8 +305,7 @@ struct WeightedProductPool { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct WeightedProductReserve { - #[serde_as(as = "serialize::U256")] - balance: eth::U256, + balance: U256, #[serde_as(as = "serde_with::DisplayFromStr")] scaling_factor: bigdecimal::BigDecimal, #[serde_as(as = "serde_with::DisplayFromStr")] @@ -338,8 +326,7 @@ struct StablePool { #[serde_as(as = "serde_with::DisplayFromStr")] id: usize, address: eth::H160, - #[serde_as(as = "serialize::U256")] - gas_estimate: eth::U256, + gas_estimate: U256, tokens: IndexMap, #[serde_as(as = "serde_with::DisplayFromStr")] amplification_parameter: bigdecimal::BigDecimal, @@ -351,8 +338,7 @@ struct StablePool { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct StableReserve { - #[serde_as(as = "serialize::U256")] - balance: eth::U256, + balance: U256, #[serde_as(as = "serde_with::DisplayFromStr")] scaling_factor: bigdecimal::BigDecimal, } @@ -364,11 +350,9 @@ struct ConcentratedLiquidityPool { #[serde_as(as = "serde_with::DisplayFromStr")] id: usize, address: eth::H160, - #[serde_as(as = "serialize::U256")] - gas_estimate: eth::U256, + gas_estimate: U256, tokens: Vec, - #[serde_as(as = "serialize::U256")] - sqrt_price: eth::U256, + sqrt_price: U256, #[serde_as(as = "serde_with::DisplayFromStr")] liquidity: u128, tick: i32, @@ -385,18 +369,14 @@ struct ForeignLimitOrder { #[serde_as(as = "serde_with::DisplayFromStr")] id: usize, address: eth::H160, - #[serde_as(as = "serialize::U256")] - gas_estimate: eth::U256, + gas_estimate: U256, #[serde_as(as = "serialize::Hex")] hash: [u8; 32], maker_token: eth::H160, taker_token: eth::H160, - #[serde_as(as = "serialize::U256")] - maker_amount: eth::U256, - #[serde_as(as = "serialize::U256")] - taker_amount: eth::U256, - #[serde_as(as = "serialize::U256")] - taker_token_fee_amount: eth::U256, + maker_amount: U256, + taker_amount: U256, + taker_token_fee_amount: U256, } fn fee_to_decimal(fee: liquidity::balancer::v2::Fee) -> bigdecimal::BigDecimal { diff --git a/crates/driver/src/infra/solver/dto/notification.rs b/crates/driver/src/infra/solver/dto/notification.rs index b8cb5d1f5c..4fbd3b3b32 100644 --- a/crates/driver/src/infra/solver/dto/notification.rs +++ b/crates/driver/src/infra/solver/dto/notification.rs @@ -7,6 +7,7 @@ use { infra::notify, util::serialize, }, + number::U256, serde::Serialize, serde_with::serde_as, std::collections::BTreeSet, @@ -43,8 +44,8 @@ impl Notification { score, quality, )) => Kind::ScoreHigherThanQuality { - score: score.0.get(), - quality: quality.0, + score: score.0.get().into(), + quality: quality.0.into(), }, notify::Kind::ScoringFailed(notify::ScoreKind::SuccessProbabilityOutOfRange( success_probability, @@ -55,15 +56,15 @@ impl Notification { quality, gas_cost, )) => Kind::ObjectiveValueNonPositive { - quality: quality.0, - gas_cost: gas_cost.get().0, + quality: quality.0.into(), + gas_cost: gas_cost.get().0.into(), }, notify::Kind::NonBufferableTokensUsed(tokens) => Kind::NonBufferableTokensUsed { tokens: tokens.into_iter().map(|token| token.0 .0).collect(), }, notify::Kind::SolverAccountInsufficientBalance(required) => { Kind::SolverAccountInsufficientBalance { - required: required.0, + required: required.0.into(), } } notify::Kind::DuplicatedSolutionId => Kind::DuplicatedSolutionId, @@ -94,7 +95,6 @@ pub struct Notification { kind: Kind, } -#[serde_as] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase", tag = "kind")] pub enum Kind { @@ -109,27 +109,22 @@ pub enum Kind { }, ZeroScore, ScoreHigherThanQuality { - #[serde_as(as = "serialize::U256")] - score: eth::U256, - #[serde_as(as = "serialize::U256")] - quality: eth::U256, + score: U256, + quality: U256, }, SuccessProbabilityOutOfRange { probability: f64, }, #[serde(rename_all = "camelCase")] ObjectiveValueNonPositive { - #[serde_as(as = "serialize::U256")] - quality: eth::U256, - #[serde_as(as = "serialize::U256")] - gas_cost: eth::U256, + quality: U256, + gas_cost: U256, }, NonBufferableTokensUsed { tokens: BTreeSet, }, SolverAccountInsufficientBalance { - #[serde_as(as = "serialize::U256")] - required: eth::U256, + required: U256, }, Success { transaction: eth::H256, @@ -155,7 +150,6 @@ pub struct Tx { pub to: eth::H160, #[serde_as(as = "serialize::Hex")] pub input: Vec, - #[serde_as(as = "serialize::U256")] - pub value: eth::U256, + pub value: U256, pub access_list: AccessList, } diff --git a/crates/driver/src/infra/solver/dto/solution.rs b/crates/driver/src/infra/solver/dto/solution.rs index 2ed5bc3772..0445ba177e 100644 --- a/crates/driver/src/infra/solver/dto/solution.rs +++ b/crates/driver/src/infra/solver/dto/solution.rs @@ -5,6 +5,7 @@ use { util::serialize, }, itertools::Itertools, + number::U256, serde::Deserialize, serde_with::serde_as, std::collections::HashMap, @@ -43,7 +44,7 @@ impl Solutions { fulfillment.executed_amount.into(), match fulfillment.fee { Some(fee) => competition::solution::trade::Fee::Dynamic( - competition::order::SellAmount(fee), + competition::order::SellAmount(fee.into()), ), None => competition::solution::trade::Fee::Static, }, @@ -118,7 +119,7 @@ impl Solutions { solution .prices .into_iter() - .map(|(address, price)| (address.into(), price)) + .map(|(address, price)| (address.into(), price.into())) .collect(), solution .interactions @@ -137,7 +138,7 @@ impl Solutions { eth::Allowance { token: allowance.token.into(), spender: allowance.spender.into(), - amount: allowance.amount, + amount: allowance.amount.into(), } .into() }) @@ -190,7 +191,7 @@ impl Solutions { solver.clone(), match solution.score { Score::Solver { score } => { - competition::solution::SolverScore::Solver(score) + competition::solution::SolverScore::Solver(score.into()) } Score::RiskAdjusted { success_probability, @@ -223,8 +224,8 @@ pub struct Solutions { #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct Solution { id: u64, - #[serde_as(as = "HashMap<_, serialize::U256>")] - prices: HashMap, + #[serde_as(as = "HashMap<_, _>")] + prices: HashMap, trades: Vec, interactions: Vec, score: Score, @@ -243,19 +244,15 @@ enum Trade { struct Fulfillment { #[serde_as(as = "serialize::Hex")] order: [u8; order::UID_LEN], - #[serde_as(as = "serialize::U256")] - executed_amount: eth::U256, - #[serde_as(as = "Option")] - fee: Option, + executed_amount: U256, + fee: Option, } -#[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct JitTrade { order: JitOrder, - #[serde_as(as = "serialize::U256")] - executed_amount: eth::U256, + executed_amount: U256, } #[serde_as] @@ -265,15 +262,12 @@ struct JitOrder { sell_token: eth::H160, buy_token: eth::H160, receiver: eth::H160, - #[serde_as(as = "serialize::U256")] - sell_amount: eth::U256, - #[serde_as(as = "serialize::U256")] - buy_amount: eth::U256, + sell_amount: U256, + buy_amount: U256, valid_to: u32, #[serde_as(as = "serialize::Hex")] app_data: [u8; order::APP_DATA_LEN], - #[serde_as(as = "serialize::U256")] - fee_amount: eth::U256, + fee_amount: U256, kind: Kind, partially_fillable: bool, sell_token_balance: SellTokenBalance, @@ -306,10 +300,8 @@ struct LiquidityInteraction { id: usize, input_token: eth::H160, output_token: eth::H160, - #[serde_as(as = "serialize::U256")] - input_amount: eth::U256, - #[serde_as(as = "serialize::U256")] - output_amount: eth::U256, + input_amount: U256, + output_amount: U256, } #[serde_as] @@ -318,8 +310,7 @@ struct LiquidityInteraction { struct CustomInteraction { internalize: bool, target: eth::H160, - #[serde_as(as = "serialize::U256")] - value: eth::U256, + value: U256, #[serde_as(as = "serialize::Hex")] call_data: Vec, allowances: Vec, @@ -327,23 +318,19 @@ struct CustomInteraction { outputs: Vec, } -#[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Asset { token: eth::H160, - #[serde_as(as = "serialize::U256")] - amount: eth::U256, + amount: U256, } -#[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Allowance { token: eth::H160, spender: eth::H160, - #[serde_as(as = "serialize::U256")] - amount: eth::U256, + amount: U256, } #[derive(Debug, Default, Deserialize)] @@ -372,14 +359,14 @@ enum SigningScheme { Eip1271, } -#[serde_as] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields, tag = "kind")] pub enum Score { Solver { - #[serde_as(as = "serialize::U256")] - score: eth::U256, + score: U256, }, #[serde(rename_all = "camelCase")] - RiskAdjusted { success_probability: f64 }, + RiskAdjusted { + success_probability: f64, + }, } diff --git a/crates/driver/src/tests/setup/mod.rs b/crates/driver/src/tests/setup/mod.rs index ac97b81a7d..681abd001b 100644 --- a/crates/driver/src/tests/setup/mod.rs +++ b/crates/driver/src/tests/setup/mod.rs @@ -23,14 +23,14 @@ use { }, setup::blockchain::Blockchain, }, - util::{self, serialize}, + util::{self}, }, bigdecimal::FromPrimitive, ethcontract::BlockId, futures::future::join_all, hyper::StatusCode, + number::U256, secp256k1::SecretKey, - serde_with::serde_as, std::{ collections::{HashMap, HashSet}, path::PathBuf, @@ -59,16 +59,16 @@ pub enum Partial { }, } -#[serde_as] #[derive(Debug, Clone, serde::Serialize)] #[serde(rename_all = "camelCase", tag = "kind")] pub enum Score { Solver { - #[serde_as(as = "serialize::U256")] - score: eth::U256, + score: U256, }, #[serde(rename_all = "camelCase")] - RiskAdjusted { success_probability: f64 }, + RiskAdjusted { + success_probability: f64, + }, } impl Default for Score { diff --git a/crates/driver/src/tests/setup/solver.rs b/crates/driver/src/tests/setup/solver.rs index ec8b717c44..dbe47b29f6 100644 --- a/crates/driver/src/tests/setup/solver.rs +++ b/crates/driver/src/tests/setup/solver.rs @@ -204,7 +204,7 @@ impl Solver { rpc.web3(), &[infra::mempool::Config { min_priority_fee: Default::default(), - gas_price_cap: eth::U256::MAX, + gas_price_cap: eth::U256::MAX.into(), target_confirm_time: Default::default(), max_confirm_time: Default::default(), retry_interval: Default::default(), diff --git a/crates/driver/src/util/serialize/mod.rs b/crates/driver/src/util/serialize/mod.rs index 71ee479979..afb1458ea0 100644 --- a/crates/driver/src/util/serialize/mod.rs +++ b/crates/driver/src/util/serialize/mod.rs @@ -1,6 +1,6 @@ //! Serialization utilities for use with [`serde_with::serde_as`] macros. mod hex; -mod u256; +// mod u256; -pub use {self::hex::Hex, u256::U256}; +pub use {self::hex::Hex /* , u256::U256 */}; diff --git a/crates/number/Cargo.toml b/crates/number/Cargo.toml index b23587ca86..942e7df405 100644 --- a/crates/number/Cargo.toml +++ b/crates/number/Cargo.toml @@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] anyhow = { workspace = true } bigdecimal = { workspace = true } +derive_more = "0.99.0" num = { workspace = true } primitive-types = { workspace = true } serde_with = { workspace = true } diff --git a/crates/number/src/lib.rs b/crates/number/src/lib.rs index 5d277018a1..c8c00067c9 100644 --- a/crates/number/src/lib.rs +++ b/crates/number/src/lib.rs @@ -1,3 +1,5 @@ pub mod conversions; pub mod nonzero; pub mod serialization; + +pub use serialization::U256; diff --git a/crates/number/src/serialization.rs b/crates/number/src/serialization.rs index 3f57cffa11..97462b814e 100644 --- a/crates/number/src/serialization.rs +++ b/crates/number/src/serialization.rs @@ -1,14 +1,21 @@ use { - primitive_types::U256, - serde::{de, Deserializer, Serializer}, + derive_more::{From, Into}, + primitive_types::U256 as RawU256, + serde::{ + de::{self, Visitor}, + Deserialize, + Deserializer, + Serialize, + Serializer, + }, serde_with::{DeserializeAs, SerializeAs}, - std::fmt, + std::{cmp::Ordering, fmt}, }; pub struct HexOrDecimalU256; -impl<'de> DeserializeAs<'de, U256> for HexOrDecimalU256 { - fn deserialize_as(deserializer: D) -> Result +impl<'de> DeserializeAs<'de, RawU256> for HexOrDecimalU256 { + fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { @@ -16,8 +23,8 @@ impl<'de> DeserializeAs<'de, U256> for HexOrDecimalU256 { } } -impl SerializeAs for HexOrDecimalU256 { - fn serialize_as(source: &U256, serializer: S) -> Result +impl SerializeAs for HexOrDecimalU256 { + fn serialize_as(source: &RawU256, serializer: S) -> Result where S: Serializer, { @@ -25,20 +32,20 @@ impl SerializeAs for HexOrDecimalU256 { } } -pub fn serialize(value: &U256, serializer: S) -> Result +pub fn serialize(value: &RawU256, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&value.to_string()) } -pub fn deserialize<'de, D>(deserializer: D) -> Result +pub fn deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { struct Visitor {} impl<'de> de::Visitor<'de> for Visitor { - type Value = U256; + type Value = RawU256; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!( @@ -52,11 +59,11 @@ where E: de::Error, { if s.trim().starts_with("0x") { - U256::from_str_radix(s, 16).map_err(|err| { + RawU256::from_str_radix(s, 16).map_err(|err| { de::Error::custom(format!("failed to decode {s:?} as hex u256: {err}")) }) } else { - U256::from_dec_str(s).map_err(|err| { + RawU256::from_dec_str(s).map_err(|err| { de::Error::custom(format!("failed to decode {s:?} as decimal u256: {err}")) }) } @@ -66,6 +73,112 @@ where deserializer.deserialize_str(Visitor {}) } +/// Serialize and deserialize [`U256`] as a decimal string. +#[derive(Debug, Clone, Copy, Default, From, Into, Eq)] +pub struct U256(RawU256); + +impl<'de> DeserializeAs<'de, RawU256> for U256 { + fn deserialize_as>(deserializer: D) -> Result { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = RawU256; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a 256-bit decimal string") + } + + fn visit_str(self, s: &str) -> Result + where + E: de::Error, + { + RawU256::from_dec_str(s).map_err(|err| { + de::Error::custom(format!("failed to decode {s:?} as a 256-bit number: {err}")) + }) + } + } + + deserializer.deserialize_str(Visitor) + } +} + +impl<'de> Deserialize<'de> for U256 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct U256Visitor; + + impl<'de> Visitor<'de> for U256Visitor { + type Value = U256; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a 256-bit decimal string") + } + + fn visit_str(self, s: &str) -> Result + where + E: de::Error, + { + RawU256::from_dec_str(s).map(U256).map_err(|err| { + E::custom(format!("failed to decode {s:?} as a 256-bit number: {err}")) + }) + } + } + + deserializer.deserialize_str(U256Visitor) + } +} + +impl SerializeAs for U256 { + fn serialize_as(source: &RawU256, serializer: S) -> Result { + serializer.serialize_str(&source.to_string()) + } +} + +impl Serialize for U256 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + U256::serialize_as(&self.0, serializer) + } +} + +impl U256 { + pub fn saturating_sub(&self, other: Self) -> Self { + Self(self.0.saturating_sub(other.into())) + } + + pub fn to_f64_lossy(&self) -> f64 { + self.0.to_f64_lossy() + } +} + +impl PartialEq for U256 { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl PartialOrd for U256 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for U256 { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +impl From for U256 { + fn from(value: u128) -> Self { + Self(RawU256::from(value)) + } +} + #[cfg(test)] mod tests { use { diff --git a/crates/solvers/Cargo.toml b/crates/solvers/Cargo.toml index a20d616c99..b31c43b89f 100644 --- a/crates/solvers/Cargo.toml +++ b/crates/solvers/Cargo.toml @@ -25,6 +25,7 @@ humantime-serde = { workspace = true } hyper = "0.14" itertools = "0.11" num = "0.4" +number = { path = "../number" } prometheus = { workspace = true } prometheus-metric-storage = { workspace = true } rate-limit = { path = "../rate-limit" } diff --git a/crates/solvers/src/domain/eth/mod.rs b/crates/solvers/src/domain/eth/mod.rs index 82023089dd..9cde9b8f9b 100644 --- a/crates/solvers/src/domain/eth/mod.rs +++ b/crates/solvers/src/domain/eth/mod.rs @@ -1,4 +1,4 @@ -use {crate::util::bytes::Bytes, web3::types::AccessList}; +use {crate::util::bytes::Bytes, number::U256 as NumberU256, web3::types::AccessList}; mod chain; @@ -45,6 +45,12 @@ impl From for Ether { } } +impl From for Ether { + fn from(value: NumberU256) -> Self { + Self(value.into()) + } +} + /// Gas amount. #[derive(Clone, Copy, Debug, Default)] pub struct Gas(pub U256);