From c3d392d4367e4d0f0aa363bf3d397e18eb530866 Mon Sep 17 00:00:00 2001 From: Felix Leupold Date: Tue, 4 Jun 2024 15:56:37 +0200 Subject: [PATCH] Remove boundary encoding (#2769) # Description Now that domain based settlement encoding is stable we can remove the boundary encoding and configuration variable # Changes - [x] Remove boundary encoding logic - [x] Remove config param This will also require a infrastructure PR to remove the domain encoding config variable ## How to test CI --- crates/driver/src/boundary/mod.rs | 2 - crates/driver/src/boundary/settlement.rs | 403 ------------------ crates/driver/src/domain/competition/mod.rs | 4 +- .../domain/competition/solution/encoding.rs | 9 - .../src/domain/competition/solution/mod.rs | 3 +- .../domain/competition/solution/settlement.rs | 81 +--- crates/driver/src/infra/api/mod.rs | 2 - crates/driver/src/infra/config/file/load.rs | 1 - crates/driver/src/infra/config/file/mod.rs | 27 -- crates/driver/src/infra/config/mod.rs | 3 - crates/driver/src/run.rs | 1 - crates/e2e/src/setup/mod.rs | 3 +- crates/e2e/src/setup/services.rs | 1 + crates/e2e/tests/e2e/liquidity.rs | 20 +- 14 files changed, 35 insertions(+), 525 deletions(-) delete mode 100644 crates/driver/src/boundary/settlement.rs diff --git a/crates/driver/src/boundary/mod.rs b/crates/driver/src/boundary/mod.rs index db03539d4f..981b8a896b 100644 --- a/crates/driver/src/boundary/mod.rs +++ b/crates/driver/src/boundary/mod.rs @@ -23,7 +23,6 @@ //! Software (2014) pub mod liquidity; -pub mod settlement; // The [`anyhow::Error`] type is re-exported because the legacy code mostly // returns that error. This will change as the legacy code gets refactored away. @@ -32,7 +31,6 @@ pub use { anyhow::{Error, Result}, contracts, model::order::OrderData, - settlement::Settlement, shared::ethrpc::Web3, }; diff --git a/crates/driver/src/boundary/settlement.rs b/crates/driver/src/boundary/settlement.rs deleted file mode 100644 index 79ac6b6eee..0000000000 --- a/crates/driver/src/boundary/settlement.rs +++ /dev/null @@ -1,403 +0,0 @@ -use { - crate::{ - domain::{ - competition::{ - self, - auction, - order, - solution::settlement::{self, Internalization}, - }, - eth, - liquidity, - }, - infra::{solver::ManageNativeToken, Ethereum}, - }, - anyhow::{anyhow, Context, Ok, Result}, - app_data::AppDataHash, - model::{ - interaction::InteractionData, - order::{ - BuyTokenDestination, - Interactions, - Order, - OrderClass, - OrderData, - OrderKind, - OrderMetadata, - OrderUid, - SellTokenSource, - }, - DomainSeparator, - }, - shared::{ - external_prices::ExternalPrices, - http_solver::model::{InternalizationStrategy, TokenAmount}, - }, - solver::{ - interactions::Erc20ApproveInteraction, - liquidity::{ - order_converter::OrderConverter, - slippage::{SlippageCalculator, SlippageContext}, - AmmOrderExecution, - LimitOrderExecution, - }, - settlement::Revertable, - }, - std::{collections::HashMap, sync::Arc}, -}; - -#[derive(Debug, Clone)] -pub struct Settlement { - pub(super) inner: solver::settlement::Settlement, - pub solver: eth::Address, -} - -impl Settlement { - pub async fn encode( - eth: &Ethereum, - solution: &competition::Solution, - auction: &competition::Auction, - manage_native_token: ManageNativeToken, - ) -> Result { - let native_token = eth.contracts().weth(); - let order_converter = OrderConverter { - native_token: native_token.clone(), - }; - - let settlement_contract = eth.contracts().settlement(); - let domain = order::signature::domain_separator( - eth.network(), - settlement_contract.clone().address().into(), - ); - - let mut settlement = solver::settlement::Settlement::new( - solution - .clearing_prices() - .into_iter() - .map(|asset| (asset.token.into(), asset.amount.into())) - .collect(), - ); - - for trade in solution.trades() { - let (boundary_order, execution) = match trade { - competition::solution::Trade::Fulfillment(trade) => { - // TODO: The `http_solver` module filters out orders with 0 - // executed amounts which seems weird to me... why is a - // solver specifying trades with 0 executed amounts? - if eth::U256::from(trade.executed()).is_zero() { - return Err(anyhow!("unexpected empty execution")); - } - - ( - to_boundary_order(trade.order()), - LimitOrderExecution { - filled: trade.executed().into(), - fee: trade.fee().into(), - }, - ) - } - competition::solution::Trade::Jit(trade) => ( - to_boundary_jit_order(&DomainSeparator(domain.0), trade.order()), - LimitOrderExecution { - filled: trade.executed().into(), - fee: 0.into(), - }, - ), - }; - - let boundary_limit_order = order_converter.normalize_limit_order( - solver::liquidity::BalancedOrder::full(boundary_order), - manage_native_token.insert_unwraps, - )?; - settlement.with_liquidity(&boundary_limit_order, execution)?; - } - - let approvals = solution - .approvals(eth, settlement::Internalization::Disable) - .await?; - for approval in approvals { - settlement - .encoder - .append_to_execution_plan(Arc::new(Erc20ApproveInteraction { - token: eth.contract_at(approval.0.token.into()), - spender: approval.0.spender.into(), - amount: approval.0.amount, - })); - } - - let slippage_calculator = SlippageCalculator { - relative: solution.solver().slippage().relative.clone(), - absolute: solution.solver().slippage().absolute.map(Into::into), - }; - let external_prices = ExternalPrices::try_from_auction_prices( - native_token.address(), - auction - .tokens() - .iter() - .filter_map(|token| { - token - .price - .map(|price| (token.address.into(), price.into())) - }) - .collect(), - )?; - let slippage_context = slippage_calculator.context(&external_prices); - - for interaction in solution.interactions() { - let boundary_interaction = to_boundary_interaction( - &slippage_context, - settlement_contract.address().into(), - interaction, - )?; - settlement.encoder.append_to_execution_plan_internalizable( - Arc::new(boundary_interaction), - interaction.internalize(), - ); - } - - Ok(Self { - inner: settlement, - solver: solution.solver().address(), - }) - } - - pub fn tx( - &self, - auction_id: auction::Id, - contract: &contracts::GPv2Settlement, - internalization: Internalization, - ) -> eth::Tx { - let encoded_settlement = self.inner.clone().encode(match internalization { - settlement::Internalization::Enable => { - InternalizationStrategy::SkipInternalizableInteraction - } - settlement::Internalization::Disable => InternalizationStrategy::EncodeAllInteractions, - }); - - let account = ethcontract::Account::Local(self.solver.into(), None); - let tx = contract - .settle( - encoded_settlement.tokens, - encoded_settlement.clearing_prices, - encoded_settlement.trades, - encoded_settlement.interactions, - ) - .from(account) - .into_inner(); - - let mut input = tx.data.unwrap().0; - input.extend(auction_id.to_be_bytes()); - eth::Tx { - from: self.solver, - to: tx.to.unwrap().into(), - value: tx.value.unwrap_or_default().into(), - input: input.into(), - access_list: Default::default(), - } - } - - pub fn clearing_prices(&self) -> HashMap { - self.inner - .clearing_prices() - .iter() - .map(|(&token, &amount)| (token.into(), amount.into())) - .collect() - } - - pub fn revertable(&self) -> bool { - self.inner.revertable() != Revertable::NoRisk - } -} - -fn to_boundary_order(order: &competition::Order) -> Order { - Order { - data: OrderData { - sell_token: order.sell.token.into(), - buy_token: order.buy.token.into(), - sell_amount: order.sell.amount.into(), - buy_amount: order.buy.amount.into(), - // The fee amount is guaranteed to be 0 and it no longer exists in the domain, but for - // the proper encoding of the order where the `model::OrderData` struct is used, we must - // set it to 0. - fee_amount: 0.into(), - receiver: order.receiver.map(Into::into), - valid_to: order.valid_to.into(), - app_data: AppDataHash(order.app_data.into()), - kind: match order.side { - competition::order::Side::Buy => OrderKind::Buy, - competition::order::Side::Sell => OrderKind::Sell, - }, - partially_fillable: order.is_partial(), - sell_token_balance: match order.sell_token_balance { - competition::order::SellTokenBalance::Erc20 => SellTokenSource::Erc20, - competition::order::SellTokenBalance::Internal => SellTokenSource::Internal, - competition::order::SellTokenBalance::External => SellTokenSource::External, - }, - buy_token_balance: match order.buy_token_balance { - competition::order::BuyTokenBalance::Erc20 => BuyTokenDestination::Erc20, - competition::order::BuyTokenBalance::Internal => BuyTokenDestination::Internal, - }, - }, - metadata: OrderMetadata { - full_fee_amount: Default::default(), - solver_fee: 0.into(), - class: match order.kind { - competition::order::Kind::Market => OrderClass::Market, - competition::order::Kind::Liquidity => OrderClass::Liquidity, - competition::order::Kind::Limit => OrderClass::Limit, - }, - creation_date: Default::default(), - owner: order.signature.signer.into(), - uid: OrderUid(order.uid.into()), - available_balance: Default::default(), - executed_buy_amount: Default::default(), - executed_sell_amount: Default::default(), - executed_sell_amount_before_fees: Default::default(), - executed_fee_amount: Default::default(), - executed_surplus_fee: Default::default(), - invalidated: Default::default(), - status: Default::default(), - settlement_contract: Default::default(), - ethflow_data: Default::default(), - onchain_user: Default::default(), - onchain_order_data: Default::default(), - is_liquidity_order: order.is_liquidity(), - full_app_data: Default::default(), - }, - signature: order.signature.to_boundary_signature(), - interactions: Interactions { - pre: order - .pre_interactions - .iter() - .map(|interaction| model::interaction::InteractionData { - target: interaction.target.into(), - value: interaction.value.into(), - call_data: interaction.call_data.clone().into(), - }) - .collect(), - post: order - .post_interactions - .iter() - .map(|interaction| model::interaction::InteractionData { - target: interaction.target.into(), - value: interaction.value.into(), - call_data: interaction.call_data.clone().into(), - }) - .collect(), - }, - } -} - -fn to_boundary_jit_order(domain: &DomainSeparator, order: &order::Jit) -> Order { - let data = OrderData { - sell_token: order.sell.token.into(), - buy_token: order.buy.token.into(), - receiver: Some(order.receiver.into()), - sell_amount: order.sell.amount.into(), - buy_amount: order.buy.amount.into(), - valid_to: order.valid_to.into(), - app_data: AppDataHash(order.app_data.into()), - fee_amount: order.fee().0, - kind: match order.side { - competition::order::Side::Buy => OrderKind::Buy, - competition::order::Side::Sell => OrderKind::Sell, - }, - partially_fillable: match order.partially_fillable() { - order::Partial::No => false, - order::Partial::Yes { .. } => true, - }, - sell_token_balance: match order.sell_token_balance { - competition::order::SellTokenBalance::Erc20 => SellTokenSource::Erc20, - competition::order::SellTokenBalance::Internal => SellTokenSource::Internal, - competition::order::SellTokenBalance::External => SellTokenSource::External, - }, - buy_token_balance: match order.buy_token_balance { - competition::order::BuyTokenBalance::Erc20 => BuyTokenDestination::Erc20, - competition::order::BuyTokenBalance::Internal => BuyTokenDestination::Internal, - }, - }; - let metadata = OrderMetadata { - owner: order.signature.signer.into(), - full_fee_amount: order.fee().into(), - // All foreign orders **MUST** be liquidity, this is - // important so they cannot be used to affect the objective. - class: OrderClass::Liquidity, - // Not needed for encoding but nice to have for logs and competition info. - uid: data.uid(domain, &order.signature.signer.into()), - // These fields do not seem to be used at all for order - // encoding, so we just use the default values. - ..Default::default() - }; - let signature = order.signature.to_boundary_signature(); - - Order { - data, - metadata, - signature, - interactions: Interactions::default(), - } -} - -pub fn to_boundary_interaction( - slippage_context: &SlippageContext, - settlement_contract: eth::ContractAddress, - interaction: &competition::solution::Interaction, -) -> Result { - match interaction { - competition::solution::Interaction::Custom(custom) => Ok(InteractionData { - target: custom.target.into(), - value: custom.value.into(), - call_data: custom.call_data.clone().into(), - }), - competition::solution::Interaction::Liquidity(liquidity) => { - let boundary_execution = - slippage_context.apply_to_amm_execution(AmmOrderExecution { - input_max: TokenAmount::new( - liquidity.input.token.into(), - liquidity.input.amount, - ), - output: TokenAmount::new( - liquidity.output.token.into(), - liquidity.output.amount, - ), - internalizable: interaction.internalize(), - })?; - - let input = liquidity::MaxInput(eth::Asset { - token: boundary_execution.input_max.token.into(), - amount: boundary_execution.input_max.amount.into(), - }); - let output = liquidity::ExactOutput(eth::Asset { - token: boundary_execution.output.token.into(), - amount: boundary_execution.output.amount.into(), - }); - - let interaction = match &liquidity.liquidity.kind { - liquidity::Kind::UniswapV2(pool) => pool - .swap(&input, &output, &settlement_contract.into()) - .context("invalid uniswap V2 execution")?, - liquidity::Kind::UniswapV3(pool) => pool - .swap(&input, &output, &settlement_contract.into()) - .context("invalid uniswap v3 execution")?, - liquidity::Kind::BalancerV2Stable(pool) => pool - .swap(&input, &output, &settlement_contract.into()) - .context("invalid balancer v2 stable execution")?, - liquidity::Kind::BalancerV2Weighted(pool) => pool - .swap(&input, &output, &settlement_contract.into()) - .context("invalid balancer v2 weighted execution")?, - liquidity::Kind::Swapr(pool) => pool - .swap(&input, &output, &settlement_contract.into()) - .context("invalid swapr execution")?, - liquidity::Kind::ZeroEx(limit_order) => limit_order - .to_interaction(&input) - .context("invalid zeroex execution")?, - }; - - Ok(InteractionData { - target: interaction.target.into(), - value: interaction.value.into(), - call_data: interaction.call_data.into(), - }) - } - } -} diff --git a/crates/driver/src/domain/competition/mod.rs b/crates/driver/src/domain/competition/mod.rs index c24ac90a03..6c2a8a43c1 100644 --- a/crates/driver/src/domain/competition/mod.rs +++ b/crates/driver/src/domain/competition/mod.rs @@ -1,5 +1,5 @@ use { - self::solution::{encoding, settlement}, + self::solution::settlement, super::{ time::{self, Remaining}, Mempools, @@ -50,7 +50,6 @@ pub struct Competition { pub simulator: Simulator, pub mempools: Mempools, pub settlement: Mutex>, - pub encoding: encoding::Strategy, } impl Competition { @@ -120,7 +119,6 @@ impl Competition { auction, &self.eth, &self.simulator, - self.encoding, self.solver.solver_native_token(), ) .await; diff --git a/crates/driver/src/domain/competition/solution/encoding.rs b/crates/driver/src/domain/competition/solution/encoding.rs index c82bb46e16..9e610e10a5 100644 --- a/crates/driver/src/domain/competition/solution/encoding.rs +++ b/crates/driver/src/domain/competition/solution/encoding.rs @@ -16,15 +16,6 @@ use { itertools::Itertools, }; -/// The type of strategy used to encode the solution. -#[derive(Debug, Copy, Clone)] -pub enum Strategy { - /// Use logic from the legacy solver crate - Boundary, - /// Use logic from this module for encoding - Domain, -} - #[derive(Debug, thiserror::Error)] pub enum Error { #[error("invalid interaction: {0:?}")] diff --git a/crates/driver/src/domain/competition/solution/mod.rs b/crates/driver/src/domain/competition/solution/mod.rs index ae89c00702..26d133d112 100644 --- a/crates/driver/src/domain/competition/solution/mod.rs +++ b/crates/driver/src/domain/competition/solution/mod.rs @@ -393,10 +393,9 @@ impl Solution { auction: &competition::Auction, eth: &Ethereum, simulator: &Simulator, - encoding: encoding::Strategy, solver_native_token: ManageNativeToken, ) -> Result { - Settlement::encode(self, auction, eth, simulator, encoding, solver_native_token).await + Settlement::encode(self, auction, eth, simulator, solver_native_token).await } /// Token prices settled by this solution, expressed using an arbitrary diff --git a/crates/driver/src/domain/competition/solution/settlement.rs b/crates/driver/src/domain/competition/solution/settlement.rs index 5d1a89811f..67a936c657 100644 --- a/crates/driver/src/domain/competition/solution/settlement.rs +++ b/crates/driver/src/domain/competition/solution/settlement.rs @@ -1,7 +1,6 @@ use { super::{encoding, trade::ClearingPrices, Error, Solution}, crate::{ - boundary, domain::{ competition::{self, auction, order, solution}, eth, @@ -69,7 +68,6 @@ impl Settlement { auction: &competition::Auction, eth: &Ethereum, simulator: &Simulator, - encoding: encoding::Strategy, solver_native_token: ManageNativeToken, ) -> Result { // For a settlement to be valid, the solution has to respect some rules which @@ -90,67 +88,24 @@ impl Settlement { } // Encode the solution into a settlement. - let tx = match encoding { - encoding::Strategy::Boundary => { - let boundary = - boundary::Settlement::encode(eth, &solution, auction, solver_native_token) - .await?; - let tx = SettlementTx { - internalized: boundary.tx( - auction.id().unwrap(), - eth.contracts().settlement(), - Internalization::Enable, - ), - uninternalized: boundary.tx( - auction.id().unwrap(), - eth.contracts().settlement(), - Internalization::Disable, - ), - may_revert: boundary.revertable(), - }; - - // To prepare rollout, ensure that the domain settlement encoding works and - // matches the boundary settlement encoding - match encoding::tx( - auction, - &solution, - eth.contracts(), - solution.approvals(eth, Internalization::Enable).await?, - Internalization::Enable, - solver_native_token, - ) { - Ok(domain) => { - if domain.input != tx.internalized.input { - tracing::warn!( - ?domain, - boundary = ?tx.internalized, - "boundary settlement does not match domain settlement" - ); - } - } - Err(err) => tracing::warn!(?err, "failed to encode domain settlement"), - }; - tx - } - encoding::Strategy::Domain => SettlementTx { - internalized: encoding::tx( - auction, - &solution, - eth.contracts(), - solution.approvals(eth, Internalization::Enable).await?, - Internalization::Enable, - solver_native_token, - )?, - uninternalized: encoding::tx( - auction, - &solution, - eth.contracts(), - solution.approvals(eth, Internalization::Disable).await?, - Internalization::Disable, - solver_native_token, - )?, - may_revert: solution.revertable(), - }, + let tx = SettlementTx { + internalized: encoding::tx( + auction, + &solution, + eth.contracts(), + solution.approvals(eth, Internalization::Enable).await?, + Internalization::Enable, + solver_native_token, + )?, + uninternalized: encoding::tx( + auction, + &solution, + eth.contracts(), + solution.approvals(eth, Internalization::Disable).await?, + Internalization::Disable, + solver_native_token, + )?, + may_revert: solution.revertable(), }; Self::new(auction.id().unwrap(), solution, tx, eth, simulator).await } diff --git a/crates/driver/src/infra/api/mod.rs b/crates/driver/src/infra/api/mod.rs index 9eec96536b..7ad6061c31 100644 --- a/crates/driver/src/infra/api/mod.rs +++ b/crates/driver/src/infra/api/mod.rs @@ -31,7 +31,6 @@ pub struct Api { /// If this channel is specified, the bound address will be sent to it. This /// allows the driver to bind to 0.0.0.0:0 during testing. pub addr_sender: Option>, - pub encoding: infra::config::encoding::Strategy, } impl Api { @@ -77,7 +76,6 @@ impl Api { simulator: self.simulator.clone(), mempools: self.mempools.clone(), settlement: Default::default(), - encoding: self.encoding.to_domain(), }, liquidity: self.liquidity.clone(), tokens: tokens.clone(), diff --git a/crates/driver/src/infra/config/file/load.rs b/crates/driver/src/infra/config/file/load.rs index 7a6a495e8e..6b4910b310 100644 --- a/crates/driver/src/infra/config/file/load.rs +++ b/crates/driver/src/infra/config/file/load.rs @@ -320,7 +320,6 @@ pub async fn load(chain: eth::ChainId, path: &Path) -> infra::Config { }, disable_access_list_simulation: config.disable_access_list_simulation, disable_gas_simulation: config.disable_gas_simulation.map(Into::into), - encoding: config.encoding, gas_estimator: config.gas_estimator, } } diff --git a/crates/driver/src/infra/config/file/mod.rs b/crates/driver/src/infra/config/file/mod.rs index 8e539c5066..e9b56ea1e7 100644 --- a/crates/driver/src/infra/config/file/mod.rs +++ b/crates/driver/src/infra/config/file/mod.rs @@ -54,9 +54,6 @@ struct Config { #[serde(default)] liquidity: LiquidityConfig, - - #[serde(default)] - encoding: encoding::Strategy, } #[serde_as] @@ -153,30 +150,6 @@ impl ManageNativeToken { } } -pub mod encoding { - use {crate::domain::competition, serde::Deserialize}; - - /// Which logic to use to encode solutions into settlement transactions. - #[derive(Debug, Deserialize, Default)] - #[serde(rename_all = "kebab-case")] - pub enum Strategy { - /// Legacy solver crate strategy - #[default] - Boundary, - /// New encoding strategy - Domain, - } - - impl Strategy { - pub fn to_domain(&self) -> competition::solution::encoding::Strategy { - match self { - Self::Boundary => competition::solution::encoding::Strategy::Boundary, - Self::Domain => competition::solution::encoding::Strategy::Domain, - } - } - } -} - fn default_additional_tip_percentage() -> f64 { 0.05 } diff --git a/crates/driver/src/infra/config/mod.rs b/crates/driver/src/infra/config/mod.rs index 7cc809976c..a70632af61 100644 --- a/crates/driver/src/infra/config/mod.rs +++ b/crates/driver/src/infra/config/mod.rs @@ -1,5 +1,3 @@ -pub use file::encoding; - use crate::{ domain::eth, infra::{blockchain, config::file::GasEstimatorType, liquidity, mempool, simulator, solver}, @@ -18,5 +16,4 @@ pub struct Config { pub gas_estimator: GasEstimatorType, pub mempools: Vec, pub contracts: blockchain::contracts::Addresses, - pub encoding: encoding::Strategy, } diff --git a/crates/driver/src/run.rs b/crates/driver/src/run.rs index 331fea8bca..8301321d37 100644 --- a/crates/driver/src/run.rs +++ b/crates/driver/src/run.rs @@ -69,7 +69,6 @@ async fn run_with(args: cli::Args, addr_sender: Option( macro_rules! assert_approximately_eq { ($executed_value:expr, $expected_value:expr) => {{ let lower = $expected_value * U256::from(99999999999u128) / U256::from(100000000000u128); - let upper = $expected_value * U256::from(100000000001u128) / U256::from(100000000000u128); + let upper = + ($expected_value * U256::from(100000000001u128) / U256::from(100000000000u128)) + 1; assert!( $executed_value >= lower && $executed_value <= upper, "Expected: ~{}, got: {}", diff --git a/crates/e2e/src/setup/services.rs b/crates/e2e/src/setup/services.rs index d29248590d..d10cbfd120 100644 --- a/crates/e2e/src/setup/services.rs +++ b/crates/e2e/src/setup/services.rs @@ -381,6 +381,7 @@ impl<'a> Services<'a> { &self, order: &OrderCreation, ) -> Result { + tracing::info!("Creating order: {order:?}"); let placement = self .http .post(format!("{API_HOST}{ORDERS_ENDPOINT}")) diff --git a/crates/e2e/tests/e2e/liquidity.rs b/crates/e2e/tests/e2e/liquidity.rs index e6fa744ff3..710587a149 100644 --- a/crates/e2e/tests/e2e/liquidity.rs +++ b/crates/e2e/tests/e2e/liquidity.rs @@ -4,6 +4,7 @@ use { driver::domain::eth::H160, e2e::{ api::zeroex::{Eip712TypedZeroExOrder, ZeroExApi}, + assert_approximately_eq, nodes::forked_node::ForkedNodeApi, setup::{ colocation::{self, SolverEngine}, @@ -202,11 +203,14 @@ async fn zero_ex_liquidity(web3: Web3) { // crates/e2e/src/setup/colocation.rs:110 which is then applied to the // original filled amount crates/solver/src/liquidity/slippage.rs:110 let expected_filled_amount = amount.as_u128() + amount.as_u128() / 10u128; - assert_eq!(zeroex_order_amounts.filled, expected_filled_amount); + assert_approximately_eq!( + U256::from(zeroex_order_amounts.filled), + U256::from(expected_filled_amount) + ); assert!(zeroex_order_amounts.fillable > 0u128); - assert_eq!( - zeroex_order_amounts.fillable, - amount.as_u128() * 2 - expected_filled_amount + assert_approximately_eq!( + U256::from(zeroex_order_amounts.fillable), + U256::from(amount.as_u128() * 2 - expected_filled_amount) ); // Fill the remaining part of the 0x order @@ -233,11 +237,11 @@ async fn zero_ex_liquidity(web3: Web3) { let zeroex_order_amounts = get_zeroex_order_amounts(&zeroex, &zeroex_order) .await .unwrap(); - assert_eq!( - zeroex_order_amounts.filled, - amount.as_u128() * 2 - expected_filled_amount + assert_approximately_eq!( + U256::from(zeroex_order_amounts.filled), + U256::from(amount.as_u128() * 2 - expected_filled_amount) ); - assert_eq!(zeroex_order_amounts.fillable, 0u128); + assert_approximately_eq!(U256::from(zeroex_order_amounts.fillable), U256::zero()); } fn create_zeroex_liquidity_orders(