From cc150d4298ea8dbc76d0fc836a16764df1520687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Duarte?= Date: Fri, 28 Nov 2025 11:40:40 +0000 Subject: [PATCH 1/4] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 28e44b05da4222b77408741dbd8528378c9548f9 Author: José Duarte Date: Fri Nov 28 11:38:22 2025 +0000 Squashed commit of the following: commit 0e89882f1685db3f521d8564ee87cfeb5d71fb35 Author: Martin Magnus Date: Fri Nov 28 12:19:50 2025 +0100 Migrate DomainSeparator to alloy (#3929) Implements the existing `DomainSeparator` using `alloy`. We can also consider to use `alloy`'s `Domain` type directly but it probably makes more sense to do that in a later PR. existing e2e and unit tests If there is a bug in this code none of the signatures generated for orders would work commit 66a5e283be3c69af6bfb83f138b276aa61edc3ec Author: José Duarte Date: Fri Nov 28 10:40:53 2025 +0000 [TRIVIAL] Migrate from H160 to alloy Address types (#3927) Simple despite being huge This PR continues the migration from the `ethcontract` library to the `alloy` library by * replacing instances of `ethcontract::H160` and `primitive_types::H160` with `alloy::primitives::Address` * replacing instances of `ethcontract::U256` and `primitives_types::U256` with `alloy::primitives::U256` There are plenty left, the current commit has a focus on some structures are related code, however some of the structures only got a partial migration to avoid an even more complex PR This migration simplifies the codebase by reducing the need for type conversions and aligns the entire codebase with alloy's native types. - [x] Replace `H160` with `Address` - [x] Replace `ethcontract::U256` with `alloy::primitives::U256` - [x] Remove unnecessary `.into_alloy()` and `.into_legacy()` conversion calls - [x] Update function signatures across all affected crates - [x] Simplify bad token detection code - [x] Update liquidity source implementations (Balancer V2, Uniswap V2/V3) - [x] Update baseline solver and price estimation modules - [x] Simplify token owner finder implementations - [x] Update Cargo dependencies Existing tests --------- Co-authored-by: Claude commit 4ba764f8001eea40b5d8e4812a8c92a7655dca25 Merge: 2237370af 013690fb1 Author: José Duarte Date: Fri Nov 28 09:22:33 2025 +0000 Merge branch 'jmgd/alloy/h160_contract' into jmgd/alloy/tokens_addr commit 013690fb15f3fe6912db23d159a5c5f3ee014d1d Author: José Duarte Date: Thu Nov 27 19:09:59 2025 +0000 clippy commit 63449016d95fecb59b4b6c31e8ec944345546d0a Author: José Duarte Date: Thu Nov 27 19:01:52 2025 +0000 fix clippy commit ec1faf562c9bf1a99c1b300c8812873166e35683 Author: José Duarte Date: Thu Nov 27 18:58:15 2025 +0000 some issues with mocking commit 2fbfdab561daa63198b6fb5d429715a6eea418f8 Merge: 3030db310 476bdff42 Author: José Duarte Date: Thu Nov 27 16:25:11 2025 +0000 Merge branch 'main' into jmgd/alloy/h160_contract commit 3030db310d07c0e5406329654e733bdc20824d9b Author: José Duarte Date: Thu Nov 27 16:24:19 2025 +0000 fix compilation errors commit 2237370af872490f4d4b4f4bec1f29952faf7823 Author: José Duarte Date: Thu Nov 27 16:00:58 2025 +0000 wip commit 01900d705a5bde01257b45d6facfe06a1dbaedb2 Author: José Duarte Date: Thu Nov 27 14:12:46 2025 +0000 wip commit 3d0a652864bf15d4507ba1248fbfeba21e3ca0ce Merge: 03275450c 593ad3a40 Author: José Duarte Date: Thu Nov 27 11:53:13 2025 +0000 Merge branch 'jmgd/alloy/models' into jmgd/alloy/h160_contract commit 593ad3a40172af248f24a031d8898adfca7d3a92 Author: José Duarte Date: Thu Nov 27 11:31:13 2025 +0000 fix bug commit e280d5aa366eea3fc1a02a5e5459980a55ae9056 Author: José Duarte Date: Thu Nov 27 10:52:25 2025 +0000 simplifications commit 03275450ca64c7883972d65839341e4a61932306 Author: José Duarte Date: Wed Nov 26 19:26:06 2025 +0000 Migrate from H160 to alloy Address types commit c7333d66be9da075464e837c7ecf193deb7c22c3 Merge: 16653336c bcb5e34c0 Author: José Duarte Date: Wed Nov 26 19:04:16 2025 +0000 Merge branch 'main' into jmgd/alloy/models commit 16653336c51d0c7b70453d091d51537a2fb42422 Merge: 318205372 5923b4753 Author: José Duarte Date: Wed Nov 26 16:19:05 2025 +0000 Merge branch 'main' into jmgd/alloy/models commit 31820537201672a012cc2aa15bf1d0e1cd61191e Author: José Duarte Date: Wed Nov 26 15:18:11 2025 +0000 fmt commit d88c44619f31b847025d6d566f18c762c610b941 Author: José Duarte Date: Wed Nov 26 15:09:13 2025 +0000 Migrate more things commit 9a2dfcd9a7dea7cc10d1732247e04197fc23d788 Author: José Duarte Date: Wed Nov 26 11:22:58 2025 +0000 Migrate model::order,quote;solver_comp commit 7b46762294cbc38fcdb23bf90a46d6e7a6039223 Author: José Duarte Date: Tue Nov 25 17:54:32 2025 +0000 Migrate OrderQuoteRequest to use alloy Address types commit 4dc1e4e41de726ff30d3aa12d80a2a64405c6bde Author: José Duarte Date: Tue Nov 25 16:43:31 2025 +0000 Replace ethcontract primitives with alloy types in quote handling, order validation, and fee calculations commit 0763f8cce1e0e22a98788b6707f6022b25e0881a Author: José Duarte Date: Tue Nov 25 14:57:46 2025 +0000 clippy commit 5754902e07eac22d0ff3e5d68bf8b0dad91f1431 Merge: e01f953a7 93f9c210b Author: José Duarte Date: Tue Nov 25 14:48:16 2025 +0000 Merge branch 'jmgd/alloy/quote_data' into jmgd/temp_main commit 93f9c210b683165486b3d04140fe01e391367e9f Author: José Duarte Date: Tue Nov 25 14:46:06 2025 +0000 fix merge commit 008b832a573086f57c38a2265179f2d5dd246235 Merge: ff199b28a a9b6dcbbc Author: José Duarte Date: Tue Nov 25 14:44:34 2025 +0000 Merge branch 'main' into jmgd/alloy/quote_data commit e01f953a77566548716eeca7be31ac8d945d0c93 Author: José Duarte Date: Tue Nov 25 14:17:36 2025 +0000 Migrate DTOs and validation to alloy types Migrate types from ethcontract's primitive_types to alloy::primitives across DTOs, database layer, and validation: - Replace H160 with Address in all DTOs and traits - Replace U256 with alloy::primitives::U256 where needed - Add IntoAlloy conversions in autopilot run loop - Update database helpers to use alloy-specific conversions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude commit 4d30fd4723f10e3b53ad37ee4d005809844365c9 Merge: 17315f0e5 0d37d9b85 Author: José Duarte Date: Tue Nov 25 10:46:28 2025 +0000 Merge branch 'jmgd/alloy/ethflow_data' into jmgd/temp_main commit 17315f0e53cf12ad2600876602b396ed9132174e Merge: d5a8b95c0 ff199b28a Author: José Duarte Date: Tue Nov 25 10:46:19 2025 +0000 Merge branch 'jmgd/alloy/quote_data' into jmgd/temp_main commit d5a8b95c01aab7ec9cf2ff6cf2ce87e41b73568c Merge: 337b4b5f7 2cbc736f0 Author: José Duarte Date: Tue Nov 25 10:45:59 2025 +0000 Merge branch 'jmgd/alloy/quote_search_parameters' into jmgd/temp_main commit 337b4b5f73e620b8212a35f428df0b2fa39c639d Merge: 9868db5e3 ee05631d9 Author: José Duarte Date: Tue Nov 25 10:45:45 2025 +0000 Merge branch 'jmgd/alloy/quoter' into jmgd/temp_main commit 9868db5e3e5032fc729e3ab64d752c3738f963c2 Merge: 6cafabff8 9543cc463 Author: José Duarte Date: Tue Nov 25 10:45:26 2025 +0000 Merge branch 'jmgd/alloy/orderbook' into jmgd/temp_main commit 0d37d9b85e558c18752aa32eee6117ad33dcfa07 Author: José Duarte Date: Tue Nov 25 10:34:06 2025 +0000 Migrate EthflowData into alloy commit ff199b28a14c011ce33544da6be3e4ba75b5590e Author: José Duarte Date: Mon Nov 24 18:41:38 2025 +0000 Migrate QuoteData to alloy commit 2cbc736f011940052c0adfcedc3452c554da4f64 Author: José Duarte Date: Mon Nov 24 18:39:18 2025 +0000 fix fmt commit aed17b3bc12cf4e024da8956d84123c57941ea9b Author: José Duarte Date: Mon Nov 24 18:11:57 2025 +0000 Migrate QuoteSearchParameters into alloy commit ee05631d926bb624dc85aa4e50729e791257b2f1 Author: José Duarte Date: Mon Nov 24 17:54:15 2025 +0000 force uint sizes through types commit e2b30b300f939dd6cd9639c91c7e1cba07ac3300 Author: José Duarte Date: Mon Nov 24 17:31:27 2025 +0000 Migrate orderbook::quoter into alloy commit 9543cc463937392080d156b1963752c61a282332 Author: José Duarte Date: Wed Nov 19 21:41:18 2025 -0300 Migrate orderbook::orderbook to alloy commit 9e23e125a790ad35ef5cc6e1475f7a977b532792 Author: José Duarte Date: Wed Nov 19 21:33:57 2025 -0300 compilation fixes commit 3b939f03267d88b813b9c5ad45c8d03901d57cfe Merge: f10166b45 4808b24dd Author: José Duarte Date: Wed Nov 19 21:32:39 2025 -0300 Merge branch 'main' into jmgd/alloy/models_order_data commit f10166b459d7ba6371ec02ec82a555614a3ae299 Author: José Duarte Date: Mon Nov 10 16:50:09 2025 +0000 Migrate OrderMetadata and OnchainOrderData models to alloy commit cb99ac4899640696f2e66c86f49e600e580a5f34 Author: José Duarte Date: Mon Nov 10 14:13:28 2025 +0000 compilation fix commit d10497150dde090c3c52ce9f44488c02a53c6f20 Author: José Duarte Date: Mon Nov 10 11:52:10 2025 +0000 Migrate orderbook::database::solver_competition_v2 into alloy commit 8b15cd9b0a61c54cd206d52eb0ae388432b3544b Author: José Duarte Date: Mon Nov 10 11:12:14 2025 +0000 Migrate orderbook::database::solver_competition to alloy commit 7e4b069c4c020b27ed0a7b63517406552cf142aa Author: José Duarte Date: Fri Nov 7 17:19:37 2025 +0000 Migrate orderbook::database::auction_prices to alloy commit f5018ab0c1eaf9f7e7a670912de4443892c4bc35 Author: José Duarte Date: Fri Nov 7 17:01:26 2025 +0000 Migrate get_user_orders endpoint to alloy commit e5b167151573b2ea605ad3bb22993679ead940cf Author: José Duarte Date: Fri Nov 7 16:54:24 2025 +0000 Migrate get_trades endpoint to alloy commit 38f47fb2a2860ad3668a385bf8c63393d0a30703 Author: José Duarte Date: Fri Nov 7 16:19:26 2025 +0000 Migrate get_total_surplus_endpoint to alloy commit 93293d70a45975849fa257ad7ae961295cda9d7f Author: José Duarte Date: Fri Nov 7 15:36:25 2025 +0000 Migrate get_token_metadata endpoint to alloy commit cd115d9069fdf6c85328543ad42922ae9e42409c Author: José Duarte Date: Fri Nov 7 15:31:19 2025 +0000 Migrate solver competition endpoint to alloy commit dd25d86aaae99e1b2bb1ea3a5f31d2d0cf4f78dc Merge: 09209ba98 0c8d46986 Author: José Duarte Date: Fri Nov 7 15:09:38 2025 +0000 Merge branch 'jmgd/alloy/native-price' into jmgd/alloy/get_orders_by_tx commit 0c8d46986d58922ac1dff7df76f4c88c7e28dd98 Author: José Duarte Date: Fri Nov 7 15:07:06 2025 +0000 fix compilation error commit 7b2c90ec84359029807228e0da26e22cd3669e22 Merge: cffad67d5 2873159a1 Author: José Duarte Date: Fri Nov 7 15:04:44 2025 +0000 Merge branch 'jmgd/alloy/quote-dto' into jmgd/alloy/native-price commit 2873159a188c1f7614965843be8ab794715d414c Merge: 1cf04a309 1784fe704 Author: José Duarte Date: Fri Nov 7 15:02:54 2025 +0000 Merge branch 'main' into jmgd/alloy/quote-dto commit 09209ba98d00b6278a9dfef92dc74e484d589b49 Author: José Duarte Date: Fri Nov 7 12:54:14 2025 +0000 Migrate get_orders_by_tx endpoint to alloy commit cffad67d571bfc1f050b8eedb6badd84637eca1c Merge: a360fb37f 1cf04a309 Author: José Duarte Date: Fri Nov 7 12:37:27 2025 +0000 Merge branch 'jmgd/alloy/quote-dto' into jmgd/alloy/native-price commit 1cf04a3097468e9cdd938d4370852a1b8c0e6731 Author: José Duarte Date: Fri Nov 7 12:18:44 2025 +0000 Clippy commit a360fb37feb19a0697e41327c47d5c8588fa2d36 Author: José Duarte Date: Fri Nov 7 12:12:23 2025 +0000 Migrate the native price estimation endpoint and trait to alloy commit 379285ece995b0f9e38e2a64261fb2c3d8e156e6 Author: José Duarte Date: Fri Nov 7 09:43:42 2025 +0000 Migrate quote DTO to alloy --- crates/alerter/src/lib.rs | 2 +- .../autopilot/src/database/auction_prices.rs | 6 +- crates/autopilot/src/run.rs | 15 +- crates/autopilot/src/solvable_orders.rs | 116 +++++----- .../src/domain/competition/pre_processing.rs | 8 +- crates/driver/src/infra/tokens.rs | 4 +- crates/e2e/tests/e2e/eth_integration.rs | 17 +- crates/e2e/tests/e2e/eth_safe.rs | 2 +- crates/e2e/tests/e2e/quote_verification.rs | 12 +- crates/model/src/order.rs | 2 +- crates/number/src/conversions.rs | 49 +++++ crates/orderbook/src/run.rs | 19 +- crates/shared/src/account_balances/cached.rs | 6 +- crates/shared/src/account_balances/mod.rs | 19 +- .../shared/src/account_balances/simulation.rs | 28 +-- crates/shared/src/encoded_settlement.rs | 4 +- crates/shared/src/external_prices.rs | 7 +- crates/shared/src/order_quoting.rs | 50 +++-- crates/shared/src/order_validation.rs | 77 ++++--- .../src/price_estimation/competition/mod.rs | 12 +- .../src/price_estimation/competition/quote.rs | 8 +- crates/shared/src/price_estimation/factory.rs | 32 ++- crates/shared/src/price_estimation/mod.rs | 21 +- .../shared/src/price_estimation/native/mod.rs | 37 ++-- .../price_estimation/native_price_cache.rs | 73 +++---- .../shared/src/price_estimation/sanitized.rs | 58 +++-- .../src/price_estimation/trade_finder.rs | 6 +- .../price_estimation/trade_verifier/mod.rs | 204 +++++++++--------- crates/shared/src/token_info.rs | 7 +- crates/shared/src/trade_finding/mod.rs | 2 +- crates/solver/src/settlement/mod.rs | 47 ++-- .../src/settlement/settlement_encoder.rs | 155 +++++++------ 32 files changed, 556 insertions(+), 549 deletions(-) diff --git a/crates/alerter/src/lib.rs b/crates/alerter/src/lib.rs index fe6d412428..76cf64a6b5 100644 --- a/crates/alerter/src/lib.rs +++ b/crates/alerter/src/lib.rs @@ -92,7 +92,7 @@ impl OrderBookApi { // untouched. fn convert_eth_to_weth(token: Address) -> Address { const WETH: Address = address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); - if token.as_slice() == BUY_ETH_ADDRESS.as_bytes() { + if token == BUY_ETH_ADDRESS { WETH } else { token diff --git a/crates/autopilot/src/database/auction_prices.rs b/crates/autopilot/src/database/auction_prices.rs index 7411a8e0f5..19a0ae0145 100644 --- a/crates/autopilot/src/database/auction_prices.rs +++ b/crates/autopilot/src/database/auction_prices.rs @@ -1,13 +1,13 @@ use { super::Postgres, + alloy::primitives::Address, anyhow::Result, bigdecimal::BigDecimal, - primitive_types::H160, std::collections::HashMap, }; impl Postgres { - pub async fn fetch_latest_prices(&self) -> Result> { + pub async fn fetch_latest_prices(&self) -> Result> { let _timer = super::Metrics::get() .database_queries .with_label_values(&["fetch_latest_prices"]) @@ -17,7 +17,7 @@ impl Postgres { Ok(database::auction_prices::fetch_latest_prices(&mut ex) .await? .into_iter() - .map(|auction_price| (H160::from(auction_price.token.0), auction_price.price)) + .map(|auction_price| (Address::new(auction_price.token.0), auction_price.price)) .collect::>()) } } diff --git a/crates/autopilot/src/run.rs b/crates/autopilot/src/run.rs index 1db5c26fa7..72cd5bd12a 100644 --- a/crates/autopilot/src/run.rs +++ b/crates/autopilot/src/run.rs @@ -31,7 +31,7 @@ use { ethcontract::H160, ethrpc::{ Web3, - alloy::conversions::{IntoAlloy, IntoLegacy}, + alloy::conversions::IntoLegacy, block_stream::block_number_to_block_number_hash, }, futures::StreamExt, @@ -321,7 +321,7 @@ pub async fn run(args: Arguments, shutdown_controller: ShutdownController) { )); let mut allowed_tokens = args.allowed_tokens.clone(); allowed_tokens.extend(base_tokens.tokens().iter()); - allowed_tokens.push(model::order::BUY_ETH_ADDRESS.into_alloy()); + allowed_tokens.push(model::order::BUY_ETH_ADDRESS); let unsupported_tokens = args.unsupported_tokens.clone(); let finder = token_owner_finder::init( @@ -380,16 +380,15 @@ pub async fn run(args: Arguments, shutdown_controller: ShutdownController) { web3: web3.clone(), simulation_web3, chain, - settlement: eth.contracts().settlement().address().into_legacy(), - native_token: eth.contracts().weth().address().into_legacy(), + settlement: *eth.contracts().settlement().address(), + native_token: *eth.contracts().weth().address(), authenticator: eth .contracts() .settlement() .authenticator() .call() .await - .expect("failed to query solver authenticator address") - .into_legacy(), + .expect("failed to query solver authenticator address"), base_tokens: base_tokens.clone(), block_stream: eth.current_block().clone(), }, @@ -528,14 +527,14 @@ pub async fn run(args: Arguments, shutdown_controller: ShutdownController) { bad_token_detector.clone(), native_price_estimator.clone(), signature_validator.clone(), - eth.contracts().weth().address().into_legacy(), + *eth.contracts().weth().address(), args.limit_order_price_factor .try_into() .expect("limit order price factor can't be converted to BigDecimal"), domain::ProtocolFees::new(&args.fee_policies_config), cow_amm_registry.clone(), args.run_loop_native_price_timeout, - eth.contracts().settlement().address().into_legacy(), + *eth.contracts().settlement().address(), args.disable_order_balance_filter, args.disable_1271_order_sig_filter, args.disable_1271_order_balance_filter, diff --git a/crates/autopilot/src/solvable_orders.rs b/crates/autopilot/src/solvable_orders.rs index 835c875ac0..2710551d1a 100644 --- a/crates/autopilot/src/solvable_orders.rs +++ b/crates/autopilot/src/solvable_orders.rs @@ -18,7 +18,7 @@ use { time::now_in_epoch_seconds, }, number::conversions::alloy::u256_to_big_decimal, - primitive_types::{H160, H256, U256}, + primitive_types::{H256, U256}, prometheus::{Histogram, HistogramVec, IntCounter, IntCounterVec, IntGauge, IntGaugeVec}, shared::{ account_balances::{BalanceFetching, Query}, @@ -96,12 +96,12 @@ pub struct SolvableOrdersCache { native_price_estimator: Arc, signature_validator: Arc, metrics: &'static Metrics, - weth: H160, + weth: Address, limit_order_price_factor: BigDecimal, protocol_fees: domain::ProtocolFees, cow_amm_registry: cow_amm::Registry, native_price_timeout: Duration, - settlement_contract: H160, + settlement_contract: Address, disable_order_balance_filter: bool, disable_1271_order_sig_filter: bool, disable_1271_order_balance_filter: bool, @@ -124,12 +124,12 @@ impl SolvableOrdersCache { bad_token_detector: Arc, native_price_estimator: Arc, signature_validator: Arc, - weth: H160, + weth: Address, limit_order_price_factor: BigDecimal, protocol_fees: domain::ProtocolFees, cow_amm_registry: cow_amm::Registry, native_price_timeout: Duration, - settlement_contract: H160, + settlement_contract: Address, disable_order_balance_filter: bool, disable_1271_order_sig_filter: bool, disable_1271_order_balance_filter: bool, @@ -204,7 +204,7 @@ impl SolvableOrdersCache { let orders = orders_with_balance( orders, &balances, - self.settlement_contract.into_alloy(), + self.settlement_contract, self.disable_1271_order_balance_filter, ); let removed = counter.checkpoint("insufficient_balance", &orders); @@ -219,7 +219,7 @@ impl SolvableOrdersCache { let cow_amm_tokens = cow_amms .iter() - .flat_map(|cow_amm| cow_amm.traded_tokens().iter().map(|t| t.into_legacy())) + .flat_map(|cow_amm| cow_amm.traded_tokens().iter().copied()) .collect::>(); // create auction @@ -242,7 +242,7 @@ impl SolvableOrdersCache { .timed_future( "weth_price_fetch", self.native_price_estimator - .estimate_native_price(self.weth.into_alloy(), Default::default()), + .estimate_native_price(self.weth, Default::default()), ) .await .expect("weth price fetching can never fail"); @@ -281,7 +281,7 @@ impl SolvableOrdersCache { .iter() .filter(|cow_amm| { cow_amm.traded_tokens().iter().all(|token| { - let price_exist = prices.contains_key(&token.into_legacy()); + let price_exist = prices.contains_key(token); if !price_exist { tracing::debug!( cow_amm = ?cow_amm.address(), @@ -310,7 +310,8 @@ impl SolvableOrdersCache { prices: prices .into_iter() .map(|(key, value)| { - Price::try_new(value.into()).map(|price| (eth::TokenAddress(key), price)) + Price::try_new(value.into_legacy().into()) + .map(|price| (eth::TokenAddress(key.into_legacy()), price)) }) .collect::>()?, surplus_capturing_jit_order_owners, @@ -471,10 +472,10 @@ async fn find_banned_user_orders(orders: &[Order], banned_users: &banned::Users) } async fn get_native_prices( - tokens: &[H160], + tokens: &[Address], native_price_estimator: &CachingNativePriceEstimator, timeout: Duration, -) -> BTreeMap { +) -> BTreeMap { native_price_estimator .estimate_native_prices_with_timeout(tokens, timeout) .await @@ -619,17 +620,12 @@ async fn get_orders_with_native_prices( orders: Vec, native_price_estimator: &CachingNativePriceEstimator, metrics: &Metrics, - additional_tokens: impl IntoIterator, + additional_tokens: impl IntoIterator, timeout: Duration, -) -> (Vec, BTreeMap) { +) -> (Vec, BTreeMap) { let traded_tokens = orders .iter() - .flat_map(|order| { - [ - order.data.sell_token.into_legacy(), - order.data.buy_token.into_legacy(), - ] - }) + .flat_map(|order| [order.data.sell_token, order.data.buy_token]) .chain(additional_tokens) .collect::>(); @@ -644,7 +640,7 @@ async fn get_orders_with_native_prices( let mut filtered_market_orders = 0_i64; let (usable, filtered): (Vec<_>, Vec<_>) = orders.into_iter().partition(|order| { let (t0, t1) = (&order.data.sell_token, &order.data.buy_token); - match (prices.get(&t0.into_legacy()), prices.get(&t1.into_legacy())) { + match (prices.get(t0), prices.get(t1)) { (Some(_), Some(_)) => true, _ => { filtered_market_orders += i64::from(order.metadata.class == OrderClass::Market); @@ -670,7 +666,7 @@ async fn get_orders_with_native_prices( /// For the remaining orders we prioritize token prices that are needed the most /// often. That way we have the chance to make a majority of orders solvable /// with very few fetch requests. -fn prioritize_missing_prices(mut orders: Vec) -> IndexSet { +fn prioritize_missing_prices(mut orders: Vec) -> IndexSet
{ /// How old an order can be at most to be considered a market order. const MARKET_ORDER_AGE: chrono::Duration = chrono::Duration::minutes(30); let now = chrono::Utc::now(); @@ -679,10 +675,10 @@ fn prioritize_missing_prices(mut orders: Vec) -> IndexSet { orders.sort_by_key(|o| std::cmp::Reverse(o.metadata.creation_date)); let mut high_priority_tokens = IndexSet::new(); - let mut most_used_tokens = HashMap::::new(); + let mut most_used_tokens = HashMap::::new(); for order in orders { - let sell_token = order.data.sell_token.into_legacy(); - let buy_token = order.data.buy_token.into_legacy(); + let sell_token = order.data.sell_token; + let buy_token = order.data.buy_token; let is_market = now.signed_duration_since(order.metadata.creation_date) <= MARKET_ORDER_AGE; if is_market { @@ -754,7 +750,7 @@ async fn find_unsupported_tokens( /// token price. fn filter_mispriced_limit_orders( mut orders: Vec, - prices: &BTreeMap, + prices: &BTreeMap, price_factor: &BigDecimal, ) -> Vec { orders.retain(|order| { @@ -762,14 +758,8 @@ fn filter_mispriced_limit_orders( return true; } - let sell_price = prices - .get(&order.data.sell_token.into_legacy()) - .unwrap() - .into_alloy(); - let buy_price = prices - .get(&order.data.buy_token.into_legacy()) - .unwrap() - .into_alloy(); + let sell_price = *prices.get(&order.data.sell_token).unwrap(); + let buy_price = *prices.get(&order.data.buy_token).unwrap(); // Convert the sell and buy price to the native token (ETH) and make sure that // sell is higher than buy with the configurable price factor. @@ -993,8 +983,8 @@ mod tests { assert_eq!( prices, btreemap! { - token1.into_legacy() => U256::from(2_000_000_000_000_000_000_u128), - token3.into_legacy() => U256::from(250_000_000_000_000_000_u128), + token1 => alloy::primitives::U256::from(2_000_000_000_000_000_000_u128), + token3 => alloy::primitives::U256::from(250_000_000_000_000_000_u128), } ); } @@ -1079,7 +1069,7 @@ mod tests { orders.clone(), &native_price_estimator, metrics, - vec![token5.into_legacy()], + vec![token5], Duration::ZERO, ) .await; @@ -1094,7 +1084,7 @@ mod tests { orders.clone(), &native_price_estimator, metrics, - vec![token5.into_legacy()], + vec![token5], Duration::ZERO, ) .await; @@ -1103,9 +1093,9 @@ mod tests { assert_eq!( prices, btreemap! { - token1.into_legacy() => U256::from(2_000_000_000_000_000_000_u128), - token3.into_legacy() => U256::from(250_000_000_000_000_000_u128), - token5.into_legacy() => U256::from(5_000_000_000_000_000_000_u128), + token1 => alloy::primitives::U256::from(2_000_000_000_000_000_000_u128), + token3 => alloy::primitives::U256::from(250_000_000_000_000_000_u128), + token5 => alloy::primitives::U256::from(5_000_000_000_000_000_000_u128), } ); } @@ -1116,8 +1106,8 @@ mod tests { let token2 = Address::repeat_byte(2); let token3 = Address::repeat_byte(3); - let token_approx1 = H160([4; 20]); - let token_approx2 = H160([5; 20]); + let token_approx1 = Address::repeat_byte(4); + let token_approx2 = Address::repeat_byte(5); let orders = vec![ OrderBuilder::default() @@ -1149,12 +1139,12 @@ mod tests { native_price_estimator .expect_estimate_native_price() .times(1) - .withf(move |token, _| *token == token_approx1.into_alloy()) + .withf(move |token, _| *token == token_approx1) .returning(|_, _| async { Ok(40.) }.boxed()); native_price_estimator .expect_estimate_native_price() .times(1) - .withf(move |token, _| *token == token_approx2.into_alloy()) + .withf(move |token, _| *token == token_approx2) .returning(|_, _| async { Ok(50.) }.boxed()); let native_price_estimator = CachingNativePriceEstimator::new( @@ -1165,10 +1155,7 @@ mod tests { Default::default(), 3, // Set to use native price approximations for the following tokens - HashMap::from([ - (token1.into_legacy(), token_approx1), - (token2.into_legacy(), token_approx2), - ]), + HashMap::from([(token1, token_approx1), (token2, token_approx2)]), HEALTHY_PRICE_ESTIMATION_TIME, ); let metrics = Metrics::instance(observe::metrics::get_storage_registry()).unwrap(); @@ -1185,9 +1172,9 @@ mod tests { assert_eq!( prices, btreemap! { - token1.into_legacy() => U256::from(40_000_000_000_000_000_000_u128), - token2.into_legacy() => U256::from(50_000_000_000_000_000_000_u128), - token3.into_legacy() => U256::from(3_000_000_000_000_000_000_u128), + token1 => alloy::primitives::U256::from(40_000_000_000_000_000_000_u128), + token2 => alloy::primitives::U256::from(50_000_000_000_000_000_000_u128), + token3 => alloy::primitives::U256::from(3_000_000_000_000_000_000_u128), } ); } @@ -1379,22 +1366,22 @@ mod tests { #[test] fn filters_mispriced_orders() { - let sell_token = H160([1; 20]); - let buy_token = H160([2; 20]); + let sell_token = Address::repeat_byte(1); + let buy_token = Address::repeat_byte(2); // Prices are set such that 1 sell token is equivalent to 2 buy tokens. // Additionally, they are scaled to large values to allow for overflows. let prices = btreemap! { - sell_token => U256::MAX / 100, - buy_token => U256::MAX / 200, + sell_token => alloy::primitives::U256::MAX / alloy::primitives::U256::from(100), + buy_token => alloy::primitives::U256::MAX / alloy::primitives::U256::from(200), }; let price_factor = "0.95".parse().unwrap(); let order = |sell_amount: u8, buy_amount: u8| Order { data: OrderData { - sell_token: sell_token.into_alloy(), + sell_token, sell_amount: alloy::primitives::U256::from(sell_amount), - buy_token: buy_token.into_alloy(), + buy_token, buy_amount: alloy::primitives::U256::from(buy_amount), ..Default::default() }, @@ -1557,7 +1544,6 @@ mod tests { #[test] fn prioritizes_missing_prices() { let now = chrono::Utc::now(); - let token = H160::from_low_u64_be; let order = |sell_token, buy_token, age| Order { metadata: OrderMetadata { @@ -1583,12 +1569,12 @@ mod tests { ]; let result = prioritize_missing_prices(orders); assert!(result.into_iter().eq([ - token(1), // coming from youngest market order - token(3), // coming from youngest market order - token(2), // coming from older market order - token(6), // coming from limit order (part of 3 orders) - token(4), // coming from limit order (part of 2 orders) - token(5), // coming from limit order (part of 1 orders) + Address::with_last_byte(1), // coming from youngest market order + Address::with_last_byte(3), // coming from youngest market order + Address::with_last_byte(2), // coming from older market order + Address::with_last_byte(6), // coming from limit order (part of 3 orders) + Address::with_last_byte(4), // coming from limit order (part of 2 orders) + Address::with_last_byte(5), // coming from limit order (part of 1 orders) ])); } } diff --git a/crates/driver/src/domain/competition/pre_processing.rs b/crates/driver/src/domain/competition/pre_processing.rs index 15bb8d3e8a..1630051ee8 100644 --- a/crates/driver/src/domain/competition/pre_processing.rs +++ b/crates/driver/src/domain/competition/pre_processing.rs @@ -273,8 +273,8 @@ impl Utilities { && order.app_data.flashloan() == first.app_data.flashloan() }); Query { - owner: trader.0.0, - token: token.0.0, + owner: trader.0.0.into_alloy(), + token: token.0.0.into_alloy(), source: match source { SellTokenBalance::Erc20 => SellTokenSource::Erc20, SellTokenBalance::Internal => SellTokenSource::Internal, @@ -318,8 +318,8 @@ impl Utilities { let balance = balance.ok()?; Some(( ( - order::Trader(query.owner.into()), - query.token.into(), + order::Trader(query.owner.into_legacy().into()), + query.token.into_legacy().into(), match query.source { SellTokenSource::Erc20 => SellTokenBalance::Erc20, SellTokenSource::Internal => SellTokenBalance::Internal, diff --git a/crates/driver/src/infra/tokens.rs b/crates/driver/src/infra/tokens.rs index cd8eca650b..376b581c46 100644 --- a/crates/driver/src/infra/tokens.rs +++ b/crates/driver/src/infra/tokens.rs @@ -5,7 +5,7 @@ use { }, anyhow::Result, ethrpc::{ - alloy::conversions::IntoLegacy, + alloy::conversions::{IntoAlloy, IntoLegacy}, block_stream::{self, CurrentBlockWatcher}, }, futures::{FutureExt, StreamExt}, @@ -213,7 +213,7 @@ impl Inner { .iter() // BUY_ETH_ADDRESS is just a marker and not a real address. We'll never be able to // fetch data for it so ignore it to avoid taking exclusive locks all the time. - .filter(|address| !cache.contains_key(*address) && address.0.0 != BUY_ETH_ADDRESS) + .filter(|address| !cache.contains_key(*address) && address.0.0.into_alloy() != BUY_ETH_ADDRESS) .cloned() .unique() .collect() diff --git a/crates/e2e/tests/e2e/eth_integration.rs b/crates/e2e/tests/e2e/eth_integration.rs index 23b2cdd7f4..3f28cc54ae 100644 --- a/crates/e2e/tests/e2e/eth_integration.rs +++ b/crates/e2e/tests/e2e/eth_integration.rs @@ -77,25 +77,18 @@ async fn eth_integration(web3: Web3) { services.submit_quote(&request).await } }; - quote(*token.address(), BUY_ETH_ADDRESS.into_alloy()) - .await - .unwrap(); + quote(*token.address(), BUY_ETH_ADDRESS).await.unwrap(); // Eth is only supported as the buy token - let (status, body) = quote(BUY_ETH_ADDRESS.into_alloy(), *token.address()) - .await - .unwrap_err(); + let (status, body) = quote(BUY_ETH_ADDRESS, *token.address()).await.unwrap_err(); assert_eq!(status, 400, "{body}"); // Place Orders - assert_ne!( - onchain.contracts().weth.address().into_legacy(), - BUY_ETH_ADDRESS - ); + assert_ne!(*onchain.contracts().weth.address(), BUY_ETH_ADDRESS); let order_buy_eth_a = OrderCreation { kind: OrderKind::Buy, sell_token: token.address().into_legacy(), sell_amount: to_wei(50), - buy_token: BUY_ETH_ADDRESS, + buy_token: BUY_ETH_ADDRESS.into_legacy(), buy_amount: to_wei(49), valid_to: model::time::now_in_epoch_seconds() + 300, ..Default::default() @@ -110,7 +103,7 @@ async fn eth_integration(web3: Web3) { kind: OrderKind::Sell, sell_token: token.address().into_legacy(), sell_amount: to_wei(50), - buy_token: BUY_ETH_ADDRESS, + buy_token: BUY_ETH_ADDRESS.into_legacy(), buy_amount: to_wei(49), valid_to: model::time::now_in_epoch_seconds() + 300, ..Default::default() diff --git a/crates/e2e/tests/e2e/eth_safe.rs b/crates/e2e/tests/e2e/eth_safe.rs index b3569ce341..6987c9567d 100644 --- a/crates/e2e/tests/e2e/eth_safe.rs +++ b/crates/e2e/tests/e2e/eth_safe.rs @@ -71,7 +71,7 @@ async fn test(web3: Web3) { from: Some(safe.address().into_legacy()), sell_token: token.address().into_legacy(), sell_amount: to_wei(4), - buy_token: BUY_ETH_ADDRESS, + buy_token: BUY_ETH_ADDRESS.into_legacy(), buy_amount: to_wei(3), valid_to: model::time::now_in_epoch_seconds() + 300, partially_fillable: true, diff --git a/crates/e2e/tests/e2e/quote_verification.rs b/crates/e2e/tests/e2e/quote_verification.rs index c67e307270..329280e63b 100644 --- a/crates/e2e/tests/e2e/quote_verification.rs +++ b/crates/e2e/tests/e2e/quote_verification.rs @@ -152,8 +152,8 @@ async fn test_bypass_verification_for_rfq_quotes(web3: Web3) { Arc::new(web3.clone()), Arc::new(BalanceOverrides::default()), block_stream, - onchain.contracts().gp_settlement.address().into_legacy(), - onchain.contracts().weth.address().into_legacy(), + *onchain.contracts().gp_settlement.address(), + *onchain.contracts().weth.address(), BigDecimal::zero(), Default::default(), ) @@ -166,10 +166,8 @@ async fn test_bypass_verification_for_rfq_quotes(web3: Web3) { verifier .verify( &PriceQuery { - sell_token: H160::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599") - .unwrap(), - buy_token: H160::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2") - .unwrap(), + sell_token: address!("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"), + buy_token: address!("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"), kind: OrderKind::Sell, in_amount: NonZeroU256::new(12.into()).unwrap(), }, @@ -199,7 +197,7 @@ async fn test_bypass_verification_for_rfq_quotes(web3: Web3) { }; let verified_quote = Estimate { - out_amount: 16380122291179526144u128.into(), + out_amount: U256::from(16380122291179526144u128), gas: 225000, solver: H160::from_str("0xe3067c7c27c1038de4e8ad95a83b927d23dfbd99").unwrap(), verified: true, diff --git a/crates/model/src/order.rs b/crates/model/src/order.rs index 970c50db0f..3f20e87896 100644 --- a/crates/model/src/order.rs +++ b/crates/model/src/order.rs @@ -32,7 +32,7 @@ use { /// The flag denoting that an order is buying ETH (or the chain's native token). /// It is used in place of an actual buy token address in an order. -pub const BUY_ETH_ADDRESS: H160 = H160([0xee; 20]); +pub const BUY_ETH_ADDRESS: Address = Address::repeat_byte(0xee); #[derive(Eq, PartialEq, Clone, Debug, Default, Deserialize, Serialize)] pub struct Interactions { diff --git a/crates/number/src/conversions.rs b/crates/number/src/conversions.rs index feae5770b4..7729cc6cd4 100644 --- a/crates/number/src/conversions.rs +++ b/crates/number/src/conversions.rs @@ -63,6 +63,55 @@ pub mod alloy { bigdecimal::{BigDecimal, num_bigint::ToBigInt}, num::{BigInt, BigRational, BigUint, Zero, bigint::Sign}, }; + + pub trait ToF64Lossy { + fn to_f64_lossy(self) -> f64; + } + + impl ToF64Lossy for U256 { + fn to_f64_lossy(self) -> f64 { + // Reference: https://blog.m-ou.se/floats/ + // Step 1: Get leading zeroes + let leading_zeroes = self.leading_zeros(); + // Step 2: Get msb to be farthest left bit + let left_aligned = self << leading_zeroes; + // Step 3: Shift msb to fit in lower 53 bits of the first u64 (64-53=11) + let quarter_aligned = left_aligned >> 11usize; + let mantissa = quarter_aligned.as_limbs()[3]; + // Step 4: For the dropped bits (all bits beyond the 53 most significant + // We want to know only 2 things. If the msb of the dropped bits is 1 or 0, + // and if any of the other bits are 1. (See blog for explanation) + // So we take care to preserve the msb bit, while jumbling the rest of the bits + // together so that any 1s will survive. If all 0s, then the result will also be + // 0. + let dropped_bits = quarter_aligned.as_limbs()[1] + | quarter_aligned.as_limbs()[0] + | (left_aligned.as_limbs()[0] & 0xFFFF_FFFF); + let dropped_bits = (dropped_bits & 0x7FFF_FFFF_FFFF_FFFF) | (dropped_bits >> 63); + let dropped_bits = quarter_aligned.as_limbs()[2] | dropped_bits; + // Step 5: dropped_bits contains the msb of the original bits and an OR-mixed 63 + // bits. If msb of dropped bits is 0, it is mantissa + 0 + // If msb of dropped bits is 1, it is mantissa + 0 only if mantissa lowest bit + // is 0 and other bits of the dropped bits are all 0 (which both can + // be tested with the below all at once) + let mantissa = mantissa + ((dropped_bits - (dropped_bits >> 63 & !mantissa)) >> 63); + // Step 6: Calculate the exponent + // If self is 0, exponent should be 0 (special meaning) and mantissa will end up + // 0 too Otherwise, (255 - n) + 1022 so it simplifies to 1277 - n + // 1023 and 1022 are the cutoffs for the exponent having the msb next to the + // decimal point + let exponent = if self.is_zero() { + 0 + } else { + 1277 - leading_zeroes as u64 + }; + // Step 7: sign bit is always 0, exponent is shifted into place + // Use addition instead of bitwise OR to saturate the exponent if mantissa + // overflows + f64::from_bits((exponent << 52) + mantissa) + } + } + pub fn big_uint_to_u256(input: &BigUint) -> Result { let bytes = input.to_bytes_be(); ensure!(bytes.len() <= 32, "too large"); diff --git a/crates/orderbook/src/run.rs b/crates/orderbook/src/run.rs index 8baa897800..e5052391f0 100644 --- a/crates/orderbook/src/run.rs +++ b/crates/orderbook/src/run.rs @@ -22,7 +22,7 @@ use { WETH9, support::Balances, }, - ethrpc::alloy::conversions::{IntoAlloy, IntoLegacy}, + ethrpc::alloy::conversions::IntoLegacy, futures::{FutureExt, StreamExt}, model::{DomainSeparator, order::BUY_ETH_ADDRESS}, num::ToPrimitive, @@ -230,7 +230,7 @@ pub async fn run(args: Arguments) { )); let mut allowed_tokens = args.allowed_tokens.clone(); allowed_tokens.extend(base_tokens.tokens().iter()); - allowed_tokens.push(BUY_ETH_ADDRESS.into_alloy()); + allowed_tokens.push(BUY_ETH_ADDRESS); let unsupported_tokens = args.unsupported_tokens.clone(); let uniswapv3_factory = IUniswapV3Factory::Instance::deployed(&web3.alloy) @@ -299,14 +299,13 @@ pub async fn run(args: Arguments) { web3: web3.clone(), simulation_web3, chain, - settlement: settlement_contract.address().into_legacy(), - native_token: native_token.address().into_legacy(), + settlement: *settlement_contract.address(), + native_token: *native_token.address(), authenticator: settlement_contract .authenticator() .call() .await - .expect("failed to query solver authenticator address") - .into_legacy(), + .expect("failed to query solver authenticator address"), base_tokens: base_tokens.clone(), block_stream: current_block_stream.clone(), }, @@ -328,13 +327,7 @@ pub async fn run(args: Arguments) { ) .await .unwrap(); - let prices = postgres_write - .fetch_latest_prices() - .await - .unwrap() - .into_iter() - .map(|(k, v)| (k.into_legacy(), v)) - .collect(); + let prices = postgres_write.fetch_latest_prices().await.unwrap(); native_price_estimator.initialize_cache(prices); let price_estimator = price_estimator_factory diff --git a/crates/shared/src/account_balances/cached.rs b/crates/shared/src/account_balances/cached.rs index 4f87854ef0..101d5d0a1e 100644 --- a/crates/shared/src/account_balances/cached.rs +++ b/crates/shared/src/account_balances/cached.rs @@ -202,15 +202,15 @@ mod tests { use { super::*, crate::account_balances::MockBalanceFetching, - ethcontract::H160, + alloy::primitives::Address, ethrpc::block_stream::BlockInfo, model::order::SellTokenSource, }; fn query(token: u8) -> Query { Query { - owner: H160([1; 20]), - token: H160([token; 20]), + owner: Address::repeat_byte(1), + token: Address::repeat_byte(token), source: SellTokenSource::Erc20, interactions: vec![], balance_override: None, diff --git a/crates/shared/src/account_balances/mod.rs b/crates/shared/src/account_balances/mod.rs index 2f8a2be1c1..e61eb89d94 100644 --- a/crates/shared/src/account_balances/mod.rs +++ b/crates/shared/src/account_balances/mod.rs @@ -3,14 +3,13 @@ use { BalanceOverrideRequest, BalanceOverriding, }, - alloy::sol_types::{SolCall, SolType, sol_data}, + alloy::{ + primitives::Address, + sol_types::{SolCall, SolType, sol_data}, + }, contracts::alloy::{GPv2Settlement, support::Balances}, ethcontract::state_overrides::StateOverrides, - ethrpc::{ - Web3, - alloy::conversions::{IntoAlloy, IntoLegacy}, - block_stream::CurrentBlockWatcher, - }, + ethrpc::{Web3, alloy::conversions::IntoAlloy, block_stream::CurrentBlockWatcher}, model::{ interaction::InteractionData, order::{Order, SellTokenSource}, @@ -25,8 +24,8 @@ mod simulation; #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Query { - pub owner: H160, - pub token: H160, + pub owner: Address, + pub token: Address, pub source: SellTokenSource, pub interactions: Vec, pub balance_override: Option, @@ -35,8 +34,8 @@ pub struct Query { impl Query { pub fn from_order(o: &Order) -> Self { Self { - owner: o.metadata.owner.into_legacy(), - token: o.data.sell_token.into_legacy(), + owner: o.metadata.owner, + token: o.data.sell_token, source: o.data.sell_token_balance, interactions: o.interactions.pre.clone(), // TODO eventually delete together with the balance diff --git a/crates/shared/src/account_balances/simulation.rs b/crates/shared/src/account_balances/simulation.rs index e0319989cc..7e55fd46c2 100644 --- a/crates/shared/src/account_balances/simulation.rs +++ b/crates/shared/src/account_balances/simulation.rs @@ -51,8 +51,8 @@ impl Balances { let simulation = self .balance_simulator .simulate( - query.owner, - query.token, + query.owner.into_legacy(), + query.token.into_legacy(), query.source, &query.interactions, None, @@ -73,9 +73,9 @@ impl Balances { ) -> Result { let usable_balance = match query.source { SellTokenSource::Erc20 => { - let balance = token.balanceOf(query.owner.into_alloy()); + let balance = token.balanceOf(query.owner); let allowance = - token.allowance(query.owner.into_alloy(), self.vault_relayer().into_alloy()); + token.allowance(query.owner, self.vault_relayer().into_alloy()); let (balance, allowance) = futures::try_join!( balance.call().into_future(), allowance.call().into_future() @@ -84,13 +84,13 @@ impl Balances { } SellTokenSource::External => { let vault = BalancerV2Vault::new(self.vault().into_alloy(), &self.web3.alloy); - let balance = token.balanceOf(query.owner.into_alloy()); + let balance = token.balanceOf(query.owner); let approved = vault.hasApprovedRelayer( - query.owner.into_alloy(), + query.owner, self.vault_relayer().into_alloy(), ); let allowance = - token.allowance(query.owner.into_alloy(), self.vault().into_alloy()); + token.allowance(query.owner, self.vault().into_alloy()); let (balance, approved, allowance) = futures::try_join!( balance.call().into_future(), approved.call().into_future(), @@ -105,9 +105,9 @@ impl Balances { SellTokenSource::Internal => { let vault = BalancerV2Vault::new(self.vault().into_alloy(), &self.web3.alloy); let balance = vault - .getInternalBalance(query.owner.into_alloy(), vec![query.token.into_alloy()]); + .getInternalBalance(query.owner, vec![query.token]); let approved = vault.hasApprovedRelayer( - query.owner.into_alloy(), + query.owner, self.vault_relayer().into_alloy(), ); let (balance, approved) = futures::try_join!( @@ -135,7 +135,7 @@ impl BalanceFetching for Balances { .map(|query| async { if query.interactions.is_empty() { let token = - ERC20::Instance::new(query.token.into_alloy(), self.web3.alloy.clone()); + ERC20::Instance::new(query.token, self.web3.alloy.clone()); self.tradable_balance_simple(query, &token).await } else { self.tradable_balance_simulated(query).await @@ -154,8 +154,8 @@ impl BalanceFetching for Balances { let simulation = self .balance_simulator .simulate( - query.owner, - query.token, + query.owner.into_legacy(), + query.token.into_legacy(), query.source, &query.interactions, Some(amount), @@ -221,8 +221,8 @@ mod tests { balances .can_transfer( &Query { - owner, - token, + owner: owner.into_alloy(), + token: token.into_alloy(), source, interactions: vec![], balance_override: None, diff --git a/crates/shared/src/encoded_settlement.rs b/crates/shared/src/encoded_settlement.rs index dc20aac189..6908b3f724 100644 --- a/crates/shared/src/encoded_settlement.rs +++ b/crates/shared/src/encoded_settlement.rs @@ -80,8 +80,8 @@ fn order_flags(order: &OrderData, signature: &Signature) -> U256 { #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct EncodedSettlement { - pub tokens: Vec, - pub clearing_prices: Vec, + pub tokens: Vec
, + pub clearing_prices: Vec, pub trades: Vec, pub interactions: [Vec; 3], } diff --git a/crates/shared/src/external_prices.rs b/crates/shared/src/external_prices.rs index 63da862b19..c9607aa06a 100644 --- a/crates/shared/src/external_prices.rs +++ b/crates/shared/src/external_prices.rs @@ -9,7 +9,6 @@ use { crate::conversions::U256Ext, alloy::primitives::{Address, U256}, anyhow::{Result, bail}, - ethrpc::alloy::conversions::IntoAlloy, model::order::BUY_ETH_ADDRESS, num::{BigInt, BigRational, One as _, ToPrimitive as _}, std::{ @@ -33,7 +32,7 @@ impl ExternalPrices { // wrapped asset price exist with a value of 1. This protects us from // malformed input (in case there are issues with the prices from the // `/auction` endpoint for example). - for token in [native_token, BUY_ETH_ADDRESS.into_alloy()] { + for token in [native_token, BUY_ETH_ADDRESS] { match xrates.get(&token) { Some(price) if !price.is_one() => { let price = price.to_f64().unwrap_or(f64::NAN); @@ -138,7 +137,7 @@ mod tests { hashmap! { Address::repeat_byte(1) => BigRational::new(1.into(), 10.into()), native_token => BigRational::one(), - BUY_ETH_ADDRESS.into_alloy() => BigRational::one(), + BUY_ETH_ADDRESS => BigRational::one(), }, ); } @@ -159,7 +158,7 @@ mod tests { ExternalPrices::try_from_auction_prices( native_token, btreemap! { - BUY_ETH_ADDRESS.into_alloy() => U256::from(13_370_000_000_000_000_000_u128), + BUY_ETH_ADDRESS => U256::from(13_370_000_000_000_000_000_u128), }, ) .is_err() diff --git a/crates/shared/src/order_quoting.rs b/crates/shared/src/order_quoting.rs index 9d7bd35de6..2f63570e5f 100644 --- a/crates/shared/src/order_quoting.rs +++ b/crates/shared/src/order_quoting.rs @@ -17,7 +17,7 @@ use { anyhow::{Context, Result}, chrono::{DateTime, Duration, Utc}, database::quotes::{Quote as QuoteRow, QuoteKind}, - ethcontract::{H160, U256}, + ethcontract::U256, ethrpc::alloy::conversions::{IntoAlloy, IntoLegacy}, futures::TryFutureExt, gas_estimation::GasPriceEstimating, @@ -492,10 +492,10 @@ impl OrderQuoter { } | OrderQuoteSide::Sell { sell_amount: SellAmount::AfterFee { value: sell_amount }, - } => (sell_amount.get(), trade_estimate.out_amount), + } => (sell_amount.get().into_alloy(), trade_estimate.out_amount), OrderQuoteSide::Buy { buy_amount_after_fee: buy_amount, - } => (trade_estimate.out_amount, buy_amount.get()), + } => (trade_estimate.out_amount, buy_amount.get().into_alloy()), }; let fee_parameters = FeeParameters { gas_amount: trade_estimate.gas as _, @@ -510,8 +510,8 @@ impl OrderQuoter { let quote = QuoteData { sell_token: parameters.sell_token, buy_token: parameters.buy_token, - quoted_sell_amount: quoted_sell_amount.into_alloy(), - quoted_buy_amount: quoted_buy_amount.into_alloy(), + quoted_sell_amount, + quoted_buy_amount, fee_parameters, kind: trade_query.kind, expiration, @@ -534,7 +534,7 @@ impl OrderQuoter { &self, estimate: &Estimate, parameters: &QuoteParameters, - sell_amount: U256, + sell_amount: alloy::primitives::U256, ) -> Result<(), CalculateQuoteError> { if estimate.verified || !matches!( @@ -547,10 +547,7 @@ impl OrderQuoter { } let balance = match self - .get_balance( - ¶meters.verification, - parameters.sell_token.into_legacy(), - ) + .get_balance(¶meters.verification, parameters.sell_token) .await { Ok(balance) => balance, @@ -569,9 +566,13 @@ impl OrderQuoter { Ok(()) } - async fn get_balance(&self, verification: &Verification, token: H160) -> Result { + async fn get_balance( + &self, + verification: &Verification, + token: Address, + ) -> Result { let query = Query { - owner: verification.from.into_legacy(), + owner: verification.from, token, source: verification.sell_token_source, interactions: verification @@ -587,7 +588,10 @@ impl OrderQuoter { balance_override: None, }; let mut balances = self.balance_fetcher.get_balances(&[query]).await; - balances.pop().context("missing balance result")? + balances + .pop() + .map(|head| head.map(IntoAlloy::into_alloy)) + .context("missing balance result")? } } @@ -694,10 +698,10 @@ impl From<&OrderQuoteRequest> for PreOrderData { fn from(quote_request: &OrderQuoteRequest) -> Self { let owner = quote_request.from; Self { - owner: owner.into_legacy(), - sell_token: quote_request.sell_token.into_legacy(), - buy_token: quote_request.buy_token.into_legacy(), - receiver: quote_request.receiver.unwrap_or(owner).into_legacy(), + owner, + sell_token: quote_request.sell_token, + buy_token: quote_request.buy_token, + receiver: quote_request.receiver.unwrap_or(owner), valid_to: quote_request.validity.actual_valid_to(), partially_fillable: false, buy_token_balance: quote_request.buy_token_balance, @@ -796,7 +800,7 @@ mod tests { native::MockNativePriceEstimating, }, }, - alloy::primitives::Address, + alloy::primitives::{Address, U256 as AlloyU256}, chrono::Utc, ethcontract::H160, futures::FutureExt, @@ -883,7 +887,7 @@ mod tests { .returning(|_| { async { Ok(price_estimation::Estimate { - out_amount: 42.into(), + out_amount: AlloyU256::from(42), gas: 3, solver: H160([1; 20]), verified: false, @@ -1024,7 +1028,7 @@ mod tests { .returning(|_| { async { Ok(price_estimation::Estimate { - out_amount: 42.into(), + out_amount: AlloyU256::from(42), gas: 3, solver: H160([1; 20]), verified: false, @@ -1160,7 +1164,7 @@ mod tests { .returning(|_| { async { Ok(price_estimation::Estimate { - out_amount: 100.into(), + out_amount: AlloyU256::from(100), gas: 3, solver: H160([1; 20]), verified: false, @@ -1281,7 +1285,7 @@ mod tests { price_estimator.expect_estimate().returning(|_| { async { Ok(price_estimation::Estimate { - out_amount: 100.into(), + out_amount: AlloyU256::from(100), gas: 200, solver: H160([1; 20]), verified: false, @@ -1355,7 +1359,7 @@ mod tests { price_estimator.expect_estimate().returning(|_| { async { Ok(price_estimation::Estimate { - out_amount: 100.into(), + out_amount: AlloyU256::from(100), gas: 200, solver: H160([1; 20]), verified: false, diff --git a/crates/shared/src/order_validation.rs b/crates/shared/src/order_validation.rs index 0b04f7bab8..7ab7aa6455 100644 --- a/crates/shared/src/order_validation.rs +++ b/crates/shared/src/order_validation.rs @@ -113,7 +113,7 @@ pub enum PartialValidationError { UnsupportedBuyTokenDestination(BuyTokenDestination), UnsupportedSellTokenSource(SellTokenSource), UnsupportedOrderType, - UnsupportedToken { token: H160, reason: String }, + UnsupportedToken { token: Address, reason: String }, Other(anyhow::Error), } @@ -187,10 +187,9 @@ impl From for ValidationError { CalculateQuoteError::Price { source: PriceEstimationError::UnsupportedToken { token, reason }, .. - } => ValidationError::Partial(PartialValidationError::UnsupportedToken { - token: token.into_legacy(), - reason, - }), + } => { + ValidationError::Partial(PartialValidationError::UnsupportedToken { token, reason }) + } CalculateQuoteError::Price { source: PriceEstimationError::ProtocolInternal(err), .. @@ -238,10 +237,10 @@ pub struct OrderValidator { #[derive(Debug, Eq, PartialEq, Default)] pub struct PreOrderData { - pub owner: H160, - pub sell_token: H160, - pub buy_token: H160, - pub receiver: H160, + pub owner: Address, + pub sell_token: Address, + pub buy_token: Address, + pub receiver: Address, pub valid_to: u32, pub partially_fillable: bool, pub buy_token_balance: BuyTokenDestination, @@ -250,25 +249,21 @@ pub struct PreOrderData { pub class: OrderClass, } -fn actual_receiver(owner: H160, order: &OrderData) -> H160 { +fn actual_receiver(owner: Address, order: &OrderData) -> Address { let receiver = order.receiver.unwrap_or_default(); - if receiver.is_zero() { - owner - } else { - receiver.into_legacy() - } + if receiver.is_zero() { owner } else { receiver } } impl PreOrderData { pub fn from_order_creation( - owner: H160, + owner: Address, order: &OrderData, signing_scheme: SigningScheme, ) -> Self { Self { owner, - sell_token: order.sell_token.into_legacy(), - buy_token: order.buy_token.into_legacy(), + sell_token: order.sell_token, + buy_token: order.buy_token, receiver: actual_receiver(owner, order), valid_to: order.valid_to, partially_fillable: order.partially_fillable, @@ -398,8 +393,8 @@ impl OrderValidator { .balance_fetcher .can_transfer( &account_balances::Query { - token: order.data().sell_token.into_legacy(), - owner, + token: order.data().sell_token, + owner: owner.into_alloy(), source: order.data().sell_token_balance, interactions: app_data.interactions.pre.clone(), balance_override: app_data.inner.protocol.flashloan.as_ref().map(|loan| { @@ -459,7 +454,7 @@ impl OrderValidating for OrderValidator { async fn partial_validate(&self, order: PreOrderData) -> Result<(), PartialValidationError> { if !self .banned_users - .banned([order.receiver.into_alloy(), order.owner.into_alloy()]) + .banned([order.receiver, order.owner]) .await .is_empty() { @@ -489,14 +484,14 @@ impl OrderValidating for OrderValidator { if has_same_buy_and_sell_token(&order, self.native_token.address()) { return Err(PartialValidationError::SameBuyAndSellToken); } - if order.sell_token.into_alloy() == BUY_ETH_ADDRESS.into_alloy() { + if order.sell_token == BUY_ETH_ADDRESS { return Err(PartialValidationError::InvalidNativeSellToken); } for &token in &[order.sell_token, order.buy_token] { if let TokenQuality::Bad { reason } = self .bad_token_detector - .detect(token.into_alloy()) + .detect(token) .await .map_err(PartialValidationError::Other)? { @@ -629,7 +624,8 @@ impl OrderValidating for OrderValidator { return Err(ValidationError::ZeroAmount); } - let pre_order = PreOrderData::from_order_creation(owner, &data, signing_scheme); + let pre_order = + PreOrderData::from_order_creation(owner.into_alloy(), &data, signing_scheme); let class = pre_order.class; self.partial_validate(pre_order) .await @@ -858,8 +854,7 @@ pub enum OrderValidToError { /// This also checks for orders selling wrapped native token for native token. fn has_same_buy_and_sell_token(order: &PreOrderData, native_token: &Address) -> bool { order.sell_token == order.buy_token - || (order.sell_token == native_token.into_legacy() - && order.buy_token.into_alloy() == BUY_ETH_ADDRESS.into_alloy()) + || (order.sell_token == *native_token && order.buy_token == BUY_ETH_ADDRESS) } /// Retrieves the quote for an order that is being created and verify that its @@ -1046,18 +1041,18 @@ mod tests { #[test] fn detects_orders_with_same_buy_and_sell_token() { - let native_token = [0xef; 20].into(); + let native_token = Address::repeat_byte(0xef); assert!(has_same_buy_and_sell_token( &PreOrderData { - sell_token: H160([0x01; 20]), - buy_token: H160([0x01; 20]), + sell_token: Address::repeat_byte(0x01), + buy_token: Address::repeat_byte(0x01), ..Default::default() }, &native_token, )); assert!(has_same_buy_and_sell_token( &PreOrderData { - sell_token: native_token.into_legacy(), + sell_token: native_token, buy_token: BUY_ETH_ADDRESS, ..Default::default() }, @@ -1066,8 +1061,8 @@ mod tests { assert!(!has_same_buy_and_sell_token( &PreOrderData { - sell_token: H160([0x01; 20]), - buy_token: H160([0x02; 20]), + sell_token: Address::repeat_byte(0x01), + buy_token: Address::repeat_byte(0x02), ..Default::default() }, &native_token, @@ -1077,7 +1072,7 @@ mod tests { assert!(!has_same_buy_and_sell_token( &PreOrderData { sell_token: BUY_ETH_ADDRESS, - buy_token: native_token.into_legacy(), + buy_token: native_token, ..Default::default() }, &native_token, @@ -1131,7 +1126,7 @@ mod tests { assert!(matches!( validator .partial_validate(PreOrderData { - owner: H160::from_low_u64_be(1), + owner: Address::with_last_byte(1), ..Default::default() }) .await, @@ -1140,7 +1135,7 @@ mod tests { assert!(matches!( validator .partial_validate(PreOrderData { - receiver: H160::from_low_u64_be(1), + receiver: Address::with_last_byte(1), ..Default::default() }) .await, @@ -1210,8 +1205,8 @@ mod tests { validator .partial_validate(PreOrderData { valid_to: legit_valid_to, - buy_token: H160::from_low_u64_be(2), - sell_token: H160::from_low_u64_be(2), + buy_token: Address::with_last_byte(2), + sell_token: Address::with_last_byte(2), ..Default::default() }) .await, @@ -1275,8 +1270,8 @@ mod tests { valid_to: time::now_in_epoch_seconds() + validity_configuration.min.as_secs() as u32 + 2, - sell_token: H160::from_low_u64_be(1), - buy_token: H160::from_low_u64_be(2), + sell_token: Address::with_last_byte(1), + buy_token: Address::with_last_byte(2), ..Default::default() }; @@ -1295,7 +1290,7 @@ mod tests { validator .partial_validate(PreOrderData { class: OrderClass::Limit, - owner: H160::from_low_u64_be(0x42), + owner: Address::with_last_byte(0x42), valid_to: time::now_in_epoch_seconds() + validity_configuration.max_market.as_secs() as u32 + 2, @@ -1309,7 +1304,7 @@ mod tests { .partial_validate(PreOrderData { partially_fillable: true, class: OrderClass::Liquidity, - owner: H160::from_low_u64_be(0x42), + owner: Address::with_last_byte(0x42), valid_to: u32::MAX, ..order() }) diff --git a/crates/shared/src/price_estimation/competition/mod.rs b/crates/shared/src/price_estimation/competition/mod.rs index 9db12164b4..41722a2f05 100644 --- a/crates/shared/src/price_estimation/competition/mod.rs +++ b/crates/shared/src/price_estimation/competition/mod.rs @@ -248,7 +248,7 @@ mod tests { PriceEstimating, Query, }, - alloy::primitives::Address, + alloy::primitives::{Address, U256}, anyhow::anyhow, futures::channel::oneshot::channel, model::order::OrderKind, @@ -308,12 +308,12 @@ mod tests { ]; let estimates = [ Estimate { - out_amount: 1.into(), + out_amount: U256::ONE, gas: 1, ..Default::default() }, Estimate { - out_amount: 2.into(), + out_amount: U256::from(2), gas: 1, ..Default::default() }, @@ -398,7 +398,7 @@ mod tests { fn estimate(amount: u64) -> Estimate { Estimate { - out_amount: amount.into(), + out_amount: U256::from(amount), gas: 1, ..Default::default() } @@ -459,7 +459,7 @@ mod tests { fn estimate(amount: u64) -> Estimate { Estimate { - out_amount: amount.into(), + out_amount: U256::from(amount), gas: 1, ..Default::default() } @@ -536,7 +536,7 @@ mod tests { fn estimate(amount: u64) -> Estimate { Estimate { - out_amount: amount.into(), + out_amount: U256::from(amount), gas: 1, ..Default::default() } diff --git a/crates/shared/src/price_estimation/competition/quote.rs b/crates/shared/src/price_estimation/competition/quote.rs index b061127cd2..76b6d66e3a 100644 --- a/crates/shared/src/price_estimation/competition/quote.rs +++ b/crates/shared/src/price_estimation/competition/quote.rs @@ -12,6 +12,7 @@ use { ethrpc::alloy::conversions::{IntoAlloy, IntoLegacy}, futures::future::{BoxFuture, FutureExt, TryFutureExt}, model::order::OrderKind, + number::conversions::alloy::ToF64Lossy, primitive_types::{H160, U256}, std::{cmp::Ordering, sync::Arc, time::Duration}, tracing::instrument, @@ -163,13 +164,14 @@ mod tests { native::MockNativePriceEstimating, }, }, + alloy::primitives::U256, gas_estimation::GasPrice1559, model::order::OrderKind, }; fn price(out_amount: u128, gas: u64) -> PriceEstimateResult { Ok(Estimate { - out_amount: out_amount.into(), + out_amount: U256::from(out_amount), gas, ..Default::default() }) @@ -352,13 +354,13 @@ mod tests { #[tokio::test] async fn prefer_verified_over_unverified() { let worse_verified_quote = Ok(Estimate { - out_amount: 900_000.into(), + out_amount: U256::from(900_000), gas: 2_000, verified: true, ..Default::default() }); let better_unverified_quote = Ok(Estimate { - out_amount: 1_000_000.into(), + out_amount: U256::from(1_000_000), gas: 1_000, verified: false, ..Default::default() diff --git a/crates/shared/src/price_estimation/factory.rs b/crates/shared/src/price_estimation/factory.rs index 800dcfc311..3e99ae703b 100644 --- a/crates/shared/src/price_estimation/factory.rs +++ b/crates/shared/src/price_estimation/factory.rs @@ -27,13 +27,10 @@ use { tenderly_api::TenderlyCodeSimulator, token_info::TokenInfoFetching, }, + alloy::primitives::Address, anyhow::{Context as _, Result}, contracts::alloy::WETH9, - ethcontract::H160, - ethrpc::{ - alloy::conversions::{IntoAlloy, IntoLegacy}, - block_stream::CurrentBlockWatcher, - }, + ethrpc::{alloy::conversions::IntoLegacy, block_stream::CurrentBlockWatcher}, gas_estimation::GasPriceEstimating, number::nonzero::U256 as NonZeroU256, rate_limit::RateLimiter, @@ -62,9 +59,9 @@ pub struct Network { pub web3: Web3, pub simulation_web3: Option, pub chain: chain::Chain, - pub native_token: H160, - pub settlement: H160, - pub authenticator: H160, + pub native_token: Address, + pub settlement: Address, + pub authenticator: Address, pub base_tokens: Arc, pub block_stream: CurrentBlockWatcher, } @@ -131,15 +128,12 @@ impl<'a> PriceEstimatorFactory<'a> { NonZeroU256::try_from( self.args .amount_to_estimate_prices_with - .or_else(|| { - Some( - self.network - .chain - .default_amount_to_estimate_native_prices_with() - .into_legacy(), - ) + .unwrap_or_else(|| { + self.network + .chain + .default_amount_to_estimate_native_prices_with() }) - .context("No amount to estimate prices with set.")?, + .into_legacy(), ) } @@ -307,7 +301,7 @@ impl<'a> PriceEstimatorFactory<'a> { fn sanitized(&self, estimator: Arc) -> SanitizedPriceEstimator { SanitizedPriceEstimator::new( estimator, - self.network.native_token.into_alloy(), + self.network.native_token, self.components.bad_token_detector.clone(), ) } @@ -379,8 +373,8 @@ impl<'a> PriceEstimatorFactory<'a> { self.args.native_price_cache_concurrent_requests, self.args .native_price_approximation_tokens - .clone() - .into_iter() + .iter() + .copied() .collect(), self.args.quote_timeout, )); diff --git a/crates/shared/src/price_estimation/mod.rs b/crates/shared/src/price_estimation/mod.rs index 3d2d624ba6..fc7535e058 100644 --- a/crates/shared/src/price_estimation/mod.rs +++ b/crates/shared/src/price_estimation/mod.rs @@ -4,14 +4,15 @@ use { arguments::{self, display_option, display_secret_option}, trade_finding::{Interaction, QuoteExecution}, }, - alloy::primitives::Address, + alloy::primitives::{Address, U256}, anyhow::{Result, ensure}, bigdecimal::BigDecimal, - ethcontract::{H160, U256}, + ethcontract::H160, + ethrpc::alloy::conversions::IntoAlloy, futures::future::BoxFuture, itertools::Itertools, model::order::{BuyTokenDestination, OrderKind, SellTokenSource}, - number::nonzero::U256 as NonZeroU256, + number::{conversions::alloy::ToF64Lossy, nonzero::U256 as NonZeroU256}, rate_limit::{RateLimiter, Strategy}, reqwest::Url, serde::{Deserialize, Serialize}, @@ -200,8 +201,8 @@ pub struct Arguments { /// The amount in native tokens atoms to use for price estimation. Should be /// reasonably large so that small pools do not influence the prices. If /// not set a reasonable default is used based on network id. - #[clap(long, env, value_parser = U256::from_dec_str)] - pub amount_to_estimate_prices_with: Option, + #[clap(long, env)] + pub amount_to_estimate_prices_with: Option, /// The API endpoint for the Balancer SOR API for solving. #[clap(long, env)] @@ -260,16 +261,16 @@ pub struct Arguments { long, env, value_delimiter = ',', - value_parser = parse_tuple:: + value_parser = parse_tuple:: )] - pub native_price_approximation_tokens: Vec<(H160, H160)>, + pub native_price_approximation_tokens: Vec<(Address, Address)>, /// Tokens for which quote verification should not be attempted. This is an /// escape hatch when there is a very bad but verifiable liquidity source /// that would win against a very good but unverifiable liquidity source /// (e.g. private liquidity that exists but can't be verified). #[clap(long, env, value_delimiter = ',')] - pub tokens_without_verification: Vec, + pub tokens_without_verification: Vec
, } /// Custom Clap parser for tuple pair @@ -542,8 +543,8 @@ impl Estimate { /// Returns (sell_amount, buy_amount). pub fn amounts(&self, query: &Query) -> (U256, U256) { match query.kind { - OrderKind::Buy => (self.out_amount, query.in_amount.get()), - OrderKind::Sell => (query.in_amount.get(), self.out_amount), + OrderKind::Buy => (self.out_amount, query.in_amount.get().into_alloy()), + OrderKind::Sell => (query.in_amount.get().into_alloy(), self.out_amount), } } diff --git a/crates/shared/src/price_estimation/native/mod.rs b/crates/shared/src/price_estimation/native/mod.rs index 5070e93aa8..0b18d6fefd 100644 --- a/crates/shared/src/price_estimation/native/mod.rs +++ b/crates/shared/src/price_estimation/native/mod.rs @@ -2,11 +2,9 @@ use { crate::price_estimation::{PriceEstimating, PriceEstimationError, Query}, alloy::primitives::Address, bigdecimal::{BigDecimal, ToPrimitive}, - ethrpc::alloy::conversions::{IntoAlloy, IntoLegacy}, futures::FutureExt, model::order::OrderKind, number::nonzero::U256 as NonZeroU256, - primitive_types::{H160, U256}, std::{ sync::{Arc, LazyLock}, time::Duration, @@ -37,12 +35,12 @@ pub fn from_normalized_price(price: BigDecimal) -> Option { } /// Convert from floating point price to normalized price -pub fn to_normalized_price(price: f64) -> Option { +pub fn to_normalized_price(price: f64) -> Option { let uint_max = 2.0_f64.powi(256); let price_in_eth = 1e18 * price; (price_in_eth.is_normal() && price_in_eth >= 1. && price_in_eth < uint_max) - .then_some(U256::from_f64_lossy(price_in_eth)) + .then_some(alloy::primitives::U256::saturating_from(price_in_eth)) } #[cfg_attr(any(test, feature = "test-util"), mockall::automock)] @@ -62,14 +60,14 @@ pub trait NativePriceEstimating: Send + Sync { /// compared to the current chain's native token. pub struct NativePriceEstimator { inner: Arc, - native_token: H160, + native_token: Address, price_estimation_amount: NonZeroU256, } impl NativePriceEstimator { pub fn new( inner: Arc, - native_token: H160, + native_token: Address, price_estimation_amount: NonZeroU256, ) -> Self { Self { @@ -80,10 +78,10 @@ impl NativePriceEstimator { } // TODO explain why we use BUY order type (shallow liquidity) - fn query(&self, token: &H160, timeout: Duration) -> Query { + fn query(&self, token: &Address, timeout: Duration) -> Query { Query { - sell_token: token.into_alloy(), - buy_token: self.native_token.into_alloy(), + sell_token: *token, + buy_token: self.native_token, in_amount: self.price_estimation_amount, kind: OrderKind::Buy, verification: Default::default(), @@ -101,7 +99,7 @@ impl NativePriceEstimating for NativePriceEstimator { timeout: Duration, ) -> futures::future::BoxFuture<'_, NativePriceEstimateResult> { async move { - let query = Arc::new(self.query(&token.into_legacy(), timeout)); + let query = Arc::new(self.query(&token, timeout)); let estimate = self.inner.estimate(query.clone()).await?; let price = estimate.price_in_buy_token_f64(&query); if is_price_malformed(price) { @@ -124,7 +122,8 @@ mod tests { use { super::*, crate::price_estimation::{Estimate, HEALTHY_PRICE_ESTIMATION_TIME, MockPriceEstimating}, - alloy::primitives::Address, + alloy::primitives::{Address, U256}, + ethrpc::alloy::conversions::IntoLegacy, primitive_types::H160, std::str::FromStr, }; @@ -137,7 +136,7 @@ mod tests { assert!(query.sell_token == Address::with_last_byte(3)); async { Ok(Estimate { - out_amount: 123_456_789_000_000_000u128.into(), + out_amount: U256::from(123_456_789_000_000_000u128), gas: 0, solver: H160([1; 20]), verified: false, @@ -149,8 +148,11 @@ mod tests { let native_price_estimator = NativePriceEstimator { inner: Arc::new(inner), - native_token: H160::from_low_u64_be(7), - price_estimation_amount: NonZeroU256::try_from(U256::exp10(18)).unwrap(), + native_token: Address::with_last_byte(7), + price_estimation_amount: NonZeroU256::try_from( + U256::from(10).pow(U256::from(18)).into_legacy(), + ) + .unwrap(), }; let result = native_price_estimator @@ -170,8 +172,11 @@ mod tests { let native_price_estimator = NativePriceEstimator { inner: Arc::new(inner), - native_token: H160::from_low_u64_be(7), - price_estimation_amount: NonZeroU256::try_from(U256::exp10(18)).unwrap(), + native_token: Address::with_last_byte(7), + price_estimation_amount: NonZeroU256::try_from( + U256::from(10).pow(U256::from(18)).into_legacy(), + ) + .unwrap(), }; let result = native_price_estimator diff --git a/crates/shared/src/price_estimation/native_price_cache.rs b/crates/shared/src/price_estimation/native_price_cache.rs index adb6802366..25613796c2 100644 --- a/crates/shared/src/price_estimation/native_price_cache.rs +++ b/crates/shared/src/price_estimation/native_price_cache.rs @@ -7,10 +7,8 @@ use { }, alloy::primitives::Address, bigdecimal::BigDecimal, - ethrpc::alloy::conversions::{IntoAlloy, IntoLegacy}, futures::{FutureExt, StreamExt}, indexmap::IndexSet, - primitive_types::H160, prometheus::{IntCounter, IntCounterVec, IntGauge}, rand::Rng, std::{ @@ -51,8 +49,8 @@ impl Metrics { pub struct CachingNativePriceEstimator(Arc); struct Inner { - cache: Mutex>, - high_priority: Mutex>, + cache: Mutex>, + high_priority: Mutex>, estimator: Box, max_age: Duration, concurrent_requests: usize, @@ -65,7 +63,7 @@ struct Inner { /// safe (e.g. csUSDL => Dai). /// It's very important that the 2 tokens have the same number of decimals. /// After startup this is a read only value. - approximation_tokens: HashMap, + approximation_tokens: HashMap, quote_timeout: Duration, } @@ -123,9 +121,9 @@ impl CachedResult { impl Inner { // Returns a single cached price and updates its `requested_at` field. fn get_cached_price( - token: H160, + token: Address, now: Instant, - cache: &mut MutexGuard>, + cache: &mut MutexGuard>, max_age: &Duration, create_missing_entry: bool, ) -> Option { @@ -157,9 +155,9 @@ impl Inner { } fn get_ready_to_use_cached_price( - token: H160, + token: Address, now: Instant, - cache: &mut MutexGuard>, + cache: &mut MutexGuard>, max_age: &Duration, create_missing_entry: bool, ) -> Option { @@ -174,10 +172,10 @@ impl Inner { /// have fetched some requested price in the meantime. fn estimate_prices_and_update_cache<'a>( &'a self, - tokens: &'a [H160], + tokens: &'a [Address], max_age: Duration, request_timeout: Duration, - ) -> futures::stream::BoxStream<'a, (H160, NativePriceEstimateResult)> { + ) -> futures::stream::BoxStream<'a, (Address, NativePriceEstimateResult)> { let estimates = tokens.iter().map(move |token| async move { let current_accumulative_errors_count = { // check if the price is cached by now @@ -197,7 +195,7 @@ impl Inner { let result = self .estimator - .estimate_native_price(token_to_fetch.into_alloy(), request_timeout) + .estimate_native_price(token_to_fetch, request_timeout) .await; // update price in cache @@ -219,7 +217,7 @@ impl Inner { } /// Tokens with highest priority first. - fn sorted_tokens_to_update(&self, max_age: Duration, now: Instant) -> Vec { + fn sorted_tokens_to_update(&self, max_age: Duration, now: Instant) -> Vec
{ let mut outdated: Vec<_> = self .cache .lock() @@ -230,7 +228,7 @@ impl Inner { .collect(); let high_priority = self.high_priority.lock().unwrap().clone(); - let index = |token: &H160| high_priority.get_index_of(token).unwrap_or(usize::MAX); + let index = |token: &Address| high_priority.get_index_of(token).unwrap_or(usize::MAX); outdated.sort_by_cached_key(|entry| { ( index(&entry.0), // important items have a low index @@ -300,7 +298,7 @@ impl UpdateTask { } impl CachingNativePriceEstimator { - pub fn initialize_cache(&self, prices: HashMap) { + pub fn initialize_cache(&self, prices: HashMap) { let mut rng = rand::thread_rng(); let now = std::time::Instant::now(); @@ -343,7 +341,7 @@ impl CachingNativePriceEstimator { update_size: Option, prefetch_time: Duration, concurrent_requests: usize, - approximation_tokens: HashMap, + approximation_tokens: HashMap, quote_timeout: Duration, ) -> Self { let inner = Arc::new(Inner { @@ -374,8 +372,8 @@ impl CachingNativePriceEstimator { /// background task. fn get_cached_prices( &self, - tokens: &[H160], - ) -> HashMap> { + tokens: &[Address], + ) -> HashMap> { let now = Instant::now(); let mut cache = self.0.cache.lock().unwrap(); let mut results = HashMap::default(); @@ -399,24 +397,24 @@ impl CachingNativePriceEstimator { results } - pub fn replace_high_priority(&self, tokens: IndexSet) { + pub fn replace_high_priority(&self, tokens: IndexSet
) { tracing::trace!(?tokens, "update high priority tokens"); *self.0.high_priority.lock().unwrap() = tokens; } pub async fn estimate_native_prices_with_timeout<'a>( &'a self, - tokens: &'a [H160], + tokens: &'a [Address], timeout: Duration, - ) -> HashMap { + ) -> HashMap { let mut prices = self.get_cached_prices(tokens); if timeout.is_zero() { return prices; } let uncached_tokens: Vec<_> = tokens - .iter() - .filter(|t| !prices.contains_key(t)) + .into_iter() + .filter(|t| !prices.contains_key(*t)) .copied() .collect(); let price_stream = @@ -448,13 +446,7 @@ impl NativePriceEstimating for CachingNativePriceEstimator { let cached = { let now = Instant::now(); let mut cache = self.0.cache.lock().unwrap(); - Inner::get_ready_to_use_cached_price( - token.into_legacy(), - now, - &mut cache, - &self.0.max_age, - false, - ) + Inner::get_ready_to_use_cached_price(token, now, &mut cache, &self.0.max_age, false) }; let label = if cached.is_some() { "hits" } else { "misses" }; @@ -468,7 +460,7 @@ impl NativePriceEstimating for CachingNativePriceEstimator { } self.0 - .estimate_prices_and_update_cache(&[token.into_legacy()], self.0.max_age, timeout) + .estimate_prices_and_update_cache(&[token], self.0.max_age, timeout) .next() .await .unwrap() @@ -505,9 +497,8 @@ mod tests { let min_age = Duration::from_secs(MAX_AGE_SECS * 49 / 100); let max_age = Duration::from_secs(MAX_AGE_SECS * 91 / 100); - let prices = HashMap::from_iter( - (0..10).map(|t| (token(t).into_legacy(), BigDecimal::try_from(1e18).unwrap())), - ); + let prices = + HashMap::from_iter((0..10).map(|t| (token(t), BigDecimal::try_from(1e18).unwrap()))); let estimator = CachingNativePriceEstimator::new( Box::new(inner), Duration::from_secs(MAX_AGE_SECS), @@ -593,8 +584,8 @@ mod tests { 1, // set token approximations for tokens 1 and 2 HashMap::from([ - (token(1).into_legacy(), token(100).into_legacy()), - (token(2).into_legacy(), token(200).into_legacy()), + (Address::with_last_byte(1), Address::with_last_byte(100)), + (Address::with_last_byte(2), Address::with_last_byte(200)), ]), HEALTHY_PRICE_ESTIMATION_TIME, ); @@ -602,7 +593,7 @@ mod tests { // no approximation token used for token 0 assert_eq!( estimator - .estimate_native_price(token(0), HEALTHY_PRICE_ESTIMATION_TIME) + .estimate_native_price(Address::with_last_byte(0), HEALTHY_PRICE_ESTIMATION_TIME) .await .unwrap() .to_i64() @@ -613,7 +604,7 @@ mod tests { // approximation price used for tokens 1 and 2 assert_eq!( estimator - .estimate_native_price(token(1), HEALTHY_PRICE_ESTIMATION_TIME) + .estimate_native_price(Address::with_last_byte(1), HEALTHY_PRICE_ESTIMATION_TIME) .await .unwrap() .to_i64() @@ -622,7 +613,7 @@ mod tests { ); assert_eq!( estimator - .estimate_native_price(token(2), HEALTHY_PRICE_ESTIMATION_TIME) + .estimate_native_price(Address::with_last_byte(2), HEALTHY_PRICE_ESTIMATION_TIME) .await .unwrap() .to_i64() @@ -978,8 +969,8 @@ mod tests { #[test] fn outdated_entries_prioritized() { - let t0 = H160::from_low_u64_be(0); - let t1 = H160::from_low_u64_be(1); + let t0 = Address::with_last_byte(0); + let t1 = Address::with_last_byte(1); let now = Instant::now(); let inner = Inner { cache: Mutex::new( diff --git a/crates/shared/src/price_estimation/sanitized.rs b/crates/shared/src/price_estimation/sanitized.rs index a8b20b6838..6144340bb9 100644 --- a/crates/shared/src/price_estimation/sanitized.rs +++ b/crates/shared/src/price_estimation/sanitized.rs @@ -66,7 +66,7 @@ impl PriceEstimating for SanitizedPriceEstimator { // buy_token == sell_token => 1 to 1 conversion if query.buy_token == query.sell_token { let estimation = Estimate { - out_amount: query.in_amount.get(), + out_amount: query.in_amount.get().into_alloy(), gas: 0, solver: Default::default(), verified: true, @@ -77,11 +77,9 @@ impl PriceEstimating for SanitizedPriceEstimator { } // sell WETH for ETH => 1 to 1 conversion with cost for unwrapping - if query.sell_token == self.native_token - && query.buy_token == BUY_ETH_ADDRESS.into_alloy() - { + if query.sell_token == self.native_token && query.buy_token == BUY_ETH_ADDRESS { let estimation = Estimate { - out_amount: query.in_amount.get(), + out_amount: query.in_amount.get().into_alloy(), gas: GAS_PER_WETH_UNWRAP, solver: Default::default(), verified: true, @@ -92,11 +90,9 @@ impl PriceEstimating for SanitizedPriceEstimator { } // sell ETH for WETH => 1 to 1 conversion with cost for wrapping - if query.sell_token == BUY_ETH_ADDRESS.into_alloy() - && query.buy_token == self.native_token - { + if query.sell_token == BUY_ETH_ADDRESS && query.buy_token == self.native_token { let estimation = Estimate { - out_amount: query.in_amount.get(), + out_amount: query.in_amount.get().into_alloy(), gas: GAS_PER_WETH_WRAP, solver: Default::default(), verified: true, @@ -112,14 +108,12 @@ impl PriceEstimating for SanitizedPriceEstimator { let mut adjusted_query = Query::clone(&*query); let modification = if query.sell_token != self.native_token - && query.buy_token == BUY_ETH_ADDRESS.into_alloy() + && query.buy_token == BUY_ETH_ADDRESS { tracing::debug!(?query, "estimate price for buying native asset"); adjusted_query.buy_token = self.native_token; Some(Modification::AddGas(GAS_PER_WETH_UNWRAP)) - } else if query.sell_token == BUY_ETH_ADDRESS.into_alloy() - && query.buy_token != self.native_token - { + } else if query.sell_token == BUY_ETH_ADDRESS && query.buy_token != self.native_token { tracing::debug!(?query, "estimate price for selling native asset"); adjusted_query.sell_token = self.native_token; Some(Modification::AddGas(GAS_PER_WETH_WRAP)) @@ -158,7 +152,7 @@ mod tests { bad_token::{MockBadTokenDetecting, TokenQuality}, price_estimation::{HEALTHY_PRICE_ESTIMATION_TIME, MockPriceEstimating}, }, - alloy::primitives::Address, + alloy::primitives::{Address, U256 as AlloyU256}, ethrpc::alloy::conversions::IntoAlloy, model::order::OrderKind, number::nonzero::U256 as NonZeroU256, @@ -196,7 +190,7 @@ mod tests { timeout: HEALTHY_PRICE_ESTIMATION_TIME, }, Ok(Estimate { - out_amount: 1.into(), + out_amount: AlloyU256::ONE, gas: 100, solver: Default::default(), verified: false, @@ -210,14 +204,14 @@ mod tests { Query { verification: Default::default(), sell_token: Address::with_last_byte(1), - buy_token: BUY_ETH_ADDRESS.into_alloy(), + buy_token: BUY_ETH_ADDRESS, in_amount: NonZeroU256::try_from(1).unwrap(), kind: OrderKind::Buy, block_dependent: false, timeout: HEALTHY_PRICE_ESTIMATION_TIME, }, Ok(Estimate { - out_amount: 1.into(), + out_amount: AlloyU256::ONE, //sanitized_estimator will add ETH_UNWRAP_COST to the gas of any //Query with ETH as the buy_token. gas: GAS_PER_WETH_UNWRAP + 100, @@ -231,7 +225,7 @@ mod tests { Query { verification: Default::default(), sell_token: Address::with_last_byte(1), - buy_token: BUY_ETH_ADDRESS.into_alloy(), + buy_token: BUY_ETH_ADDRESS, in_amount: NonZeroU256::try_from(U256::MAX).unwrap(), kind: OrderKind::Buy, block_dependent: false, @@ -247,7 +241,7 @@ mod tests { ( Query { verification: Default::default(), - sell_token: BUY_ETH_ADDRESS.into_alloy(), + sell_token: BUY_ETH_ADDRESS, buy_token: Address::with_last_byte(1), in_amount: NonZeroU256::try_from(1).unwrap(), kind: OrderKind::Buy, @@ -255,7 +249,7 @@ mod tests { timeout: HEALTHY_PRICE_ESTIMATION_TIME, }, Ok(Estimate { - out_amount: 1.into(), + out_amount: AlloyU256::ONE, //sanitized_estimator will add ETH_WRAP_COST to the gas of any //Query with ETH as the sell_token. gas: GAS_PER_WETH_WRAP + 100, @@ -277,7 +271,7 @@ mod tests { timeout: HEALTHY_PRICE_ESTIMATION_TIME, }, Ok(Estimate { - out_amount: 1.into(), + out_amount: AlloyU256::ONE, gas: 0, solver: Default::default(), verified: true, @@ -288,15 +282,15 @@ mod tests { ( Query { verification: Default::default(), - sell_token: BUY_ETH_ADDRESS.into_alloy(), - buy_token: BUY_ETH_ADDRESS.into_alloy(), + sell_token: BUY_ETH_ADDRESS, + buy_token: BUY_ETH_ADDRESS, in_amount: NonZeroU256::try_from(1).unwrap(), kind: OrderKind::Sell, block_dependent: false, timeout: HEALTHY_PRICE_ESTIMATION_TIME, }, Ok(Estimate { - out_amount: 1.into(), + out_amount: AlloyU256::ONE, gas: 0, solver: Default::default(), verified: true, @@ -308,14 +302,14 @@ mod tests { Query { verification: Default::default(), sell_token: native_token, - buy_token: BUY_ETH_ADDRESS.into_alloy(), + buy_token: BUY_ETH_ADDRESS, in_amount: NonZeroU256::try_from(1).unwrap(), kind: OrderKind::Sell, block_dependent: false, timeout: HEALTHY_PRICE_ESTIMATION_TIME, }, Ok(Estimate { - out_amount: 1.into(), + out_amount: AlloyU256::ONE, // Sanitized estimator will report a 1:1 estimate when unwrapping native token. gas: GAS_PER_WETH_UNWRAP, solver: Default::default(), @@ -327,7 +321,7 @@ mod tests { ( Query { verification: Default::default(), - sell_token: BUY_ETH_ADDRESS.into_alloy(), + sell_token: BUY_ETH_ADDRESS, buy_token: native_token, in_amount: NonZeroU256::try_from(1).unwrap(), kind: OrderKind::Sell, @@ -335,7 +329,7 @@ mod tests { timeout: HEALTHY_PRICE_ESTIMATION_TIME, }, Ok(Estimate { - out_amount: 1.into(), + out_amount: AlloyU256::ONE, // Sanitized estimator will report a 1:1 estimate when wrapping native token. gas: GAS_PER_WETH_WRAP, solver: Default::default(), @@ -403,7 +397,7 @@ mod tests { .returning(|_| { async { Ok(Estimate { - out_amount: 1.into(), + out_amount: AlloyU256::ONE, gas: 100, solver: Default::default(), verified: false, @@ -419,7 +413,7 @@ mod tests { .returning(|_| { async { Ok(Estimate { - out_amount: 1.into(), + out_amount: AlloyU256::ONE, gas: 100, solver: Default::default(), verified: false, @@ -435,7 +429,7 @@ mod tests { .returning(|_| { async { Ok(Estimate { - out_amount: 1.into(), + out_amount: AlloyU256::ONE, gas: u64::MAX, solver: Default::default(), verified: false, @@ -451,7 +445,7 @@ mod tests { .returning(|_| { async { Ok(Estimate { - out_amount: 1.into(), + out_amount: AlloyU256::ONE, gas: 100, solver: Default::default(), verified: false, diff --git a/crates/shared/src/price_estimation/trade_finder.rs b/crates/shared/src/price_estimation/trade_finder.rs index fa1314eca8..3775830ff1 100644 --- a/crates/shared/src/price_estimation/trade_finder.rs +++ b/crates/shared/src/price_estimation/trade_finder.rs @@ -72,8 +72,8 @@ impl Inner { if let Some(verifier) = &self.verifier { let trade = self.finder.get_trade(&query).await?; let price_query = PriceQuery { - sell_token: query.sell_token.into_legacy(), - buy_token: query.buy_token.into_legacy(), + sell_token: query.sell_token, + buy_token: query.buy_token, in_amount: query.in_amount, kind: query.kind, }; @@ -86,7 +86,7 @@ impl Inner { let quote = self.finder.get_quote(&query).await?; Ok(Estimate { - out_amount: quote.out_amount.into_legacy(), + out_amount: quote.out_amount, gas: quote.gas_estimate, solver: quote.solver.into_legacy(), verified: false, diff --git a/crates/shared/src/price_estimation/trade_verifier/mod.rs b/crates/shared/src/price_estimation/trade_verifier/mod.rs index 91073f1e97..07fbecf85a 100644 --- a/crates/shared/src/price_estimation/trade_verifier/mod.rs +++ b/crates/shared/src/price_estimation/trade_verifier/mod.rs @@ -70,16 +70,16 @@ pub struct TradeVerifier { balance_overrides: Arc, block_stream: CurrentBlockWatcher, settlement: GPv2Settlement::Instance, - native_token: H160, + native_token: Address, quote_inaccuracy_limit: BigRational, domain_separator: DomainSeparator, - tokens_without_verification: HashSet, + tokens_without_verification: HashSet
, } impl TradeVerifier { const DEFAULT_GAS: u64 = 12_000_000; const SPARDOSE: Address = address!("0000000000000000000000000000000000020000"); - const TRADER_IMPL: H160 = addr!("0000000000000000000000000000000000010000"); + const TRADER_IMPL: Address = address!("0000000000000000000000000000000000010000"); #[expect(clippy::too_many_arguments)] pub async fn new( @@ -88,13 +88,13 @@ impl TradeVerifier { code_fetcher: Arc, balance_overrides: Arc, block_stream: CurrentBlockWatcher, - settlement: H160, - native_token: H160, + settlement: Address, + native_token: Address, quote_inaccuracy_limit: BigDecimal, - tokens_without_verification: HashSet, + tokens_without_verification: HashSet
, ) -> Result { let settlement_contract = - GPv2Settlement::GPv2Settlement::new(settlement.into_alloy(), web3.alloy.clone()); + GPv2Settlement::GPv2Settlement::new(settlement, web3.alloy.clone()); let domain_separator = DomainSeparator(settlement_contract.domainSeparator().call().await?.0); Ok(Self { @@ -116,7 +116,7 @@ impl TradeVerifier { query: &PriceQuery, mut verification: Verification, trade: &TradeKind, - out_amount: &U256, + out_amount: &alloy::primitives::U256, ) -> Result { let start = std::time::Instant::now(); @@ -136,16 +136,16 @@ impl TradeVerifier { TradeKind::Legacy(_) => { let tokens = vec![query.sell_token, query.buy_token]; let prices = match query.kind { - OrderKind::Sell => vec![*out_amount, query.in_amount.get()], - OrderKind::Buy => vec![query.in_amount.get(), *out_amount], + OrderKind::Sell => { + vec![*out_amount, query.in_amount.get().into_alloy()] + } + OrderKind::Buy => { + vec![query.in_amount.get().into_alloy(), *out_amount] + } }; (tokens, prices) } - TradeKind::Regular(trade) => trade - .clearing_prices - .iter() - .map(|(addr, value)| (addr.into_legacy(), value.into_legacy())) - .unzip(), + TradeKind::Regular(trade) => trade.clearing_prices.iter().unzip(), }; let settlement = encode_settlement( @@ -157,7 +157,7 @@ impl TradeVerifier { out_amount, self.native_token, &self.domain_separator, - self.settlement.address().into_legacy(), + *self.settlement.address(), )?; let settlement = add_balance_queries(settlement, query, &verification, solver_address); @@ -167,7 +167,7 @@ impl TradeVerifier { let solver = Solver::Instance::new(solver_address, self.web3.alloy.clone()); let swap_simulation = solver.swap( *self.settlement.address(), - tokens.iter().cloned().map(IntoAlloy::into_alloy).collect(), + tokens.clone(), verification.receiver, settle_call.into(), ) @@ -236,8 +236,8 @@ impl TradeVerifier { // settlement buffers to make the quote happen. When the settlement contract // itself is the trader or receiver these values need to be adjusted slightly. let (sell_amount, buy_amount) = match query.kind { - OrderKind::Sell => (query.in_amount.get(), summary.out_amount), - OrderKind::Buy => (summary.out_amount, query.in_amount.get()), + OrderKind::Sell => (query.in_amount.get().into_alloy(), summary.out_amount), + OrderKind::Buy => (summary.out_amount, query.in_amount.get().into_alloy()), }; // It looks like the contract lost a lot of sell tokens but only because it was @@ -246,7 +246,9 @@ impl TradeVerifier { summary .tokens_lost .entry(query.sell_token) - .and_modify(|balance| *balance -= u256_to_big_rational(&sell_amount)); + .and_modify(|balance| { + *balance -= number::conversions::alloy::u256_to_big_rational(&sell_amount) + }); } // It looks like the contract gained a lot of buy tokens (negative loss) but // only because it was the receiver and got the payout. Adjust the tokens lost @@ -255,13 +257,15 @@ impl TradeVerifier { summary .tokens_lost .entry(query.buy_token) - .and_modify(|balance| *balance += u256_to_big_rational(&buy_amount)); + .and_modify(|balance| { + *balance += number::conversions::alloy::u256_to_big_rational(&buy_amount) + }); } } tracing::debug!( tokens_lost = ?summary.tokens_lost, - gas_diff = ?trade.gas_estimate().unwrap_or_default().abs_diff(summary.gas_used.as_u64()), + gas_diff = ?trade.gas_estimate().unwrap_or_default().abs_diff(summary.gas_used.saturating_to()), time = ?start.elapsed(), promised_out_amount = ?out_amount, verified_out_amount = ?summary.out_amount, @@ -298,14 +302,14 @@ impl TradeVerifier { match self .balance_overrides .state_override(BalanceOverrideRequest { - token: query.sell_token, + token: query.sell_token.into_legacy(), holder: Self::SPARDOSE.into_legacy(), amount: match query.kind { OrderKind::Sell => query.in_amount.get(), OrderKind::Buy => trade .out_amount( - &query.buy_token.into_alloy(), - &query.sell_token.into_alloy(), + &query.buy_token, + &query.sell_token, &query.in_amount.get().into_alloy(), &query.kind, )? @@ -351,7 +355,7 @@ impl TradeVerifier { .context("failed to fetch trader code")?; if !trader_impl.0.is_empty() { overrides.insert( - Self::TRADER_IMPL, + Self::TRADER_IMPL.into_legacy(), StateOverride { code: Some(trader_impl), ..Default::default() @@ -414,16 +418,8 @@ fn legacy_settlement_to_alloy( settlement: EncodedSettlement, ) -> GPv2Settlement::GPv2Settlement::settleCall { GPv2Settlement::GPv2Settlement::settleCall { - tokens: settlement - .tokens - .into_iter() - .map(|t| t.into_alloy()) - .collect(), - clearingPrices: settlement - .clearing_prices - .into_iter() - .map(|p| p.into_alloy()) - .collect(), + tokens: settlement.tokens, + clearingPrices: settlement.clearing_prices, interactions: settlement.interactions.map(|interactions| { interactions .into_iter() @@ -465,8 +461,8 @@ impl TradeVerifying for TradeVerifier { ) -> Result { let out_amount = trade .out_amount( - &query.buy_token.into_alloy(), - &query.sell_token.into_alloy(), + &query.buy_token, + &query.sell_token, &query.in_amount.get().into_alloy(), &query.kind, ) @@ -475,7 +471,7 @@ impl TradeVerifying for TradeVerifier { let unverified_result = trade .gas_estimate() .map(|gas| Estimate { - out_amount: out_amount.into_legacy(), + out_amount, gas, solver: trade.solver().into_legacy(), verified: false, @@ -487,7 +483,7 @@ impl TradeVerifying for TradeVerifier { }) .context("solver provided no gas estimate"); - let skip_verification = [&query.buy_token, &query.sell_token] + let skip_verification = [query.buy_token, query.sell_token] .iter() .any(|token| self.tokens_without_verification.contains(token)); if skip_verification { @@ -496,12 +492,7 @@ impl TradeVerifying for TradeVerifier { } match self - .verify_inner( - query, - verification.clone(), - &trade, - &out_amount.into_legacy(), - ) + .verify_inner(query, verification.clone(), &trade, &out_amount) .await { Ok(verified) => Ok(verified), @@ -526,12 +517,12 @@ fn encode_settlement( query: &PriceQuery, verification: &Verification, trade: &TradeKind, - tokens: &[H160], - clearing_prices: &[U256], - out_amount: &U256, - native_token: H160, + tokens: &[Address], + clearing_prices: &[alloy::primitives::U256], + out_amount: &alloy::primitives::U256, + native_token: Address, domain_separator: &DomainSeparator, - settlement: H160, + settlement: Address, ) -> Result { let mut trade_interactions = encode_interactions(&trade.interactions()); if query.buy_token == BUY_ETH_ADDRESS { @@ -541,16 +532,14 @@ fn encode_settlement( // ourselves here. let buy_amount = match query.kind { OrderKind::Sell => *out_amount, - OrderKind::Buy => query.in_amount.get(), + OrderKind::Buy => query.in_amount.get().into_alloy(), }; trade_interactions.push(( - native_token.into_alloy(), + native_token, alloy::primitives::U256::ZERO, - WETH9::WETH9::withdrawCall { - wad: buy_amount.into_alloy(), - } - .abi_encode() - .into(), + WETH9::WETH9::withdrawCall { wad: buy_amount } + .abi_encode() + .into(), )); tracing::trace!("adding unwrap interaction for paying out ETH"); } @@ -572,16 +561,16 @@ fn encode_settlement( // with helpful error messages. let trade_setup_interaction = { let sell_amount = match query.kind { - OrderKind::Sell => query.in_amount.get(), + OrderKind::Sell => query.in_amount.get().into_alloy(), OrderKind::Buy => *out_amount, }; let solver_address = trade.solver(); let setup_call = Solver::Solver::ensureTradePreconditionsCall { trader: verification.from, - settlementContract: settlement.into_alloy(), - sellToken: query.sell_token.into_alloy(), - sellAmount: sell_amount.into_alloy(), - nativeToken: native_token.into_alloy(), + settlementContract: settlement, + sellToken: query.sell_token, + sellAmount: sell_amount, + nativeToken: native_token, spardose: TradeVerifier::SPARDOSE, } .abi_encode(); @@ -613,25 +602,28 @@ fn encode_settlement( fn encode_fake_trade( query: &PriceQuery, verification: &Verification, - out_amount: &U256, - tokens: &[H160], + out_amount: &alloy::primitives::U256, + tokens: &[Address], ) -> Result { // Configure the most disadvantageous trade possible (while taking possible // overflows into account). Should the trader not receive the amount promised by // the [`Trade`] the simulation will still work and we can compute the actual // [`Trade::out_amount`] afterwards. let (sell_amount, buy_amount) = match query.kind { - OrderKind::Sell => (query.in_amount.get(), 0.into()), + OrderKind::Sell => ( + query.in_amount.get().into_alloy(), + alloy::primitives::U256::ZERO, + ), OrderKind::Buy => ( - (*out_amount).max(U256::from(u128::MAX)), - query.in_amount.get(), + (*out_amount).max(alloy::primitives::U256::from(u128::MAX)), + query.in_amount.get().into_alloy(), ), }; let fake_order = OrderData { - sell_token: query.sell_token.into_alloy(), - sell_amount: sell_amount.into_alloy(), - buy_token: query.buy_token.into_alloy(), - buy_amount: buy_amount.into_alloy(), + sell_token: query.sell_token, + sell_amount, + buy_token: query.buy_token, + buy_amount, receiver: Some(verification.receiver), valid_to: u32::MAX, app_data: Default::default(), @@ -664,7 +656,7 @@ fn encode_fake_trade( fn encode_jit_orders( jit_orders: &[dto::JitOrder], - tokens: &[H160], + tokens: &[Address], domain_separator: &DomainSeparator, ) -> Result, Error> { jit_orders @@ -697,11 +689,11 @@ fn encode_jit_orders( // the tokens set length is small so the linear search is acceptable tokens .iter() - .position(|token| token == &jit_order.sell_token) + .position(|token| token.into_legacy() == jit_order.sell_token) .context("missing jit order sell token index")?, tokens .iter() - .position(|token| token == &jit_order.buy_token) + .position(|token| token.into_legacy() == jit_order.buy_token) .context("missing jit order buy token index")?, &jit_order.executed_amount, )) @@ -763,7 +755,7 @@ fn add_balance_queries( OrderKind::Buy => (query.sell_token, verification.from), }; let query_balance_call = Solver::Solver::storeBalanceCall { - token: token.into_alloy(), + token, owner, countGas: true, } @@ -785,12 +777,12 @@ fn add_balance_queries( #[derive(Debug)] struct SettleOutput { /// Gas used for the `settle()` call. - gas_used: U256, + gas_used: alloy::primitives::U256, /// `out_amount` perceived by the trader (sell token for buy orders or buy /// token for sell order) - out_amount: U256, + out_amount: alloy::primitives::U256, /// Tokens difference of the settlement contract before and after the trade. - tokens_lost: HashMap, + tokens_lost: HashMap, } impl SettleOutput { @@ -800,7 +792,7 @@ impl SettleOutput { queriedBalances, }: Solver::Solver::swapReturn, kind: OrderKind, - tokens_vec: &[H160], + tokens_vec: &[Address], ) -> Result { // The balances are stored in the following order: // [...tokens_before, user_balance_before, user_balance_after, ...tokens_after] @@ -836,8 +828,8 @@ impl SettleOutput { let out_amount = out_amount.context("underflow during out_amount computation")?; Ok(SettleOutput { - gas_used: gasUsed.into_legacy(), - out_amount: out_amount.into_legacy(), + gas_used: gasUsed, + out_amount, tokens_lost, }) } @@ -853,12 +845,12 @@ fn ensure_quote_accuracy( ) -> std::result::Result { // amounts verified by the simulation let (sell_amount, buy_amount) = match query.kind { - OrderKind::Buy => (summary.out_amount, query.in_amount.get()), - OrderKind::Sell => (query.in_amount.get(), summary.out_amount), + OrderKind::Buy => (summary.out_amount, query.in_amount.get().into_alloy()), + OrderKind::Sell => (query.in_amount.get().into_alloy(), summary.out_amount), }; let (sell_amount, buy_amount) = ( - u256_to_big_rational(&sell_amount), - u256_to_big_rational(&buy_amount), + number::conversions::alloy::u256_to_big_rational(&sell_amount), + number::conversions::alloy::u256_to_big_rational(&buy_amount), ); let sell_token_lost_limit = inaccuracy_limit * &sell_amount; let buy_token_lost_limit = inaccuracy_limit * &buy_amount; @@ -878,7 +870,7 @@ fn ensure_quote_accuracy( Ok(Estimate { out_amount: summary.out_amount, - gas: summary.gas_used.as_u64(), + gas: summary.gas_used.saturating_to(), solver: trade.solver().into_legacy(), verified: true, execution: QuoteExecution { @@ -891,9 +883,9 @@ fn ensure_quote_accuracy( #[derive(Debug)] pub struct PriceQuery { - pub sell_token: H160, + pub sell_token: Address, // This should be `BUY_ETH_ADDRESS` if you actually want to trade `ETH` - pub buy_token: H160, + pub buy_token: Address, pub kind: OrderKind, pub in_amount: NonZeroU256, } @@ -911,7 +903,7 @@ enum Error { #[cfg(test)] mod tests { - use {super::*, maplit::hashmap, std::str::FromStr}; + use {super::*, alloy::primitives::U256, maplit::hashmap, std::str::FromStr}; #[test] fn discards_inaccurate_quotes() { @@ -920,8 +912,8 @@ mod tests { let low_threshold = big_decimal_to_big_rational(&BigDecimal::from_str("0.5").unwrap()); let high_threshold = big_decimal_to_big_rational(&BigDecimal::from_str("0.51").unwrap()); - let sell_token = H160([1u8; 20]); - let buy_token = H160([2u8; 20]); + let sell_token = Address::repeat_byte(1); + let buy_token = Address::repeat_byte(2); let query = PriceQuery { in_amount: 1_000.try_into().unwrap(), @@ -935,8 +927,8 @@ mod tests { sell_token => BigRational::from_integer(500.into()), }; let summary = SettleOutput { - gas_used: 0.into(), - out_amount: 2_000.into(), + gas_used: U256::ZERO, + out_amount: U256::from(2_000), tokens_lost, }; let estimate = @@ -948,8 +940,8 @@ mod tests { buy_token => BigRational::from_integer(0.into()), }; let summary = SettleOutput { - gas_used: 0.into(), - out_amount: 2_000.into(), + gas_used: U256::ZERO, + out_amount: U256::from(2_000), tokens_lost, }; @@ -963,8 +955,8 @@ mod tests { buy_token => BigRational::from_integer(0.into()), }; let summary = SettleOutput { - gas_used: 0.into(), - out_amount: 2_000.into(), + gas_used: U256::ZERO, + out_amount: U256::from(2_000), tokens_lost, }; let estimate = @@ -977,8 +969,8 @@ mod tests { }; let sell_more = SettleOutput { - gas_used: 0.into(), - out_amount: 2_000.into(), + gas_used: U256::ZERO, + out_amount: U256::from(2_000), tokens_lost, }; @@ -997,8 +989,8 @@ mod tests { }; let pay_out_more = SettleOutput { - gas_used: 0.into(), - out_amount: 2_000.into(), + gas_used: U256::ZERO, + out_amount: U256::from(2_000), tokens_lost, }; @@ -1017,8 +1009,8 @@ mod tests { }; let sell_less = SettleOutput { - gas_used: 0.into(), - out_amount: 2_000.into(), + gas_used: U256::ZERO, + out_amount: U256::from(2_000), tokens_lost, }; // Ending up with surplus in the buffers is always fine @@ -1032,8 +1024,8 @@ mod tests { }; let pay_out_less = SettleOutput { - gas_used: 0.into(), - out_amount: 2_000.into(), + gas_used: U256::ZERO, + out_amount: U256::from(2_000), tokens_lost, }; // Ending up with surplus in the buffers is always fine diff --git a/crates/shared/src/token_info.rs b/crates/shared/src/token_info.rs index 5e94e19ece..844316a5e4 100644 --- a/crates/shared/src/token_info.rs +++ b/crates/shared/src/token_info.rs @@ -3,10 +3,7 @@ use { anyhow::Result, async_trait::async_trait, contracts::alloy::ERC20, - ethrpc::{ - Web3, - alloy::{conversions::IntoAlloy, errors::ignore_non_node_error}, - }, + ethrpc::{Web3, alloy::errors::ignore_non_node_error}, futures::{ FutureExt, future::{BoxFuture, Shared}, @@ -48,7 +45,7 @@ pub struct TokenInfoFetcher { impl TokenInfoFetcher { async fn fetch_token(&self, address: Address) -> Result { - if address == BUY_ETH_ADDRESS.into_alloy() { + if address == BUY_ETH_ADDRESS { return Ok(TokenInfo { decimals: Some(18), symbol: Some("NATIVE_ASSET".to_string()), diff --git a/crates/shared/src/trade_finding/mod.rs b/crates/shared/src/trade_finding/mod.rs index 540289ddd8..d25483f7bf 100644 --- a/crates/shared/src/trade_finding/mod.rs +++ b/crates/shared/src/trade_finding/mod.rs @@ -138,7 +138,7 @@ pub struct LegacyTrade { /// A trade with JIT orders. #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct Trade { - pub clearing_prices: HashMap, + pub clearing_prices: HashMap, /// How many units of gas this trade will roughly cost. pub gas_estimate: Option, /// The onchain calls to run before sending user funds to the settlement diff --git a/crates/solver/src/settlement/mod.rs b/crates/solver/src/settlement/mod.rs index 5228053081..ca27ac43be 100644 --- a/crates/solver/src/settlement/mod.rs +++ b/crates/solver/src/settlement/mod.rs @@ -2,10 +2,11 @@ mod settlement_encoder; use { crate::liquidity::Settleable, + alloy::primitives::Address, anyhow::Result, ethrpc::alloy::conversions::IntoLegacy, model::order::{Order, OrderKind}, - primitive_types::{H160, U256}, + primitive_types::U256, shared::{ conversions::U256Ext as _, encoded_settlement::{EncodedSettlement, EncodedTrade, encode_trade}, @@ -25,8 +26,8 @@ pub struct Trade { #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct TradeExecution { - pub sell_token: H160, - pub buy_token: H160, + pub sell_token: Address, + pub buy_token: Address, pub sell_amount: U256, pub buy_amount: U256, pub fee_amount: U256, @@ -75,8 +76,8 @@ impl Trade { }; Some(TradeExecution { - sell_token: order.sell_token.into_legacy(), - buy_token: order.buy_token.into_legacy(), + sell_token: order.sell_token, + buy_token: order.buy_token, sell_amount, buy_amount, fee_amount: self.executed_fee()?, @@ -112,7 +113,7 @@ pub enum Revertable { impl Settlement { /// Creates a new settlement builder for the specified clearing prices. - pub fn new(clearing_prices: HashMap) -> Self { + pub fn new(clearing_prices: HashMap) -> Self { Self { encoder: SettlementEncoder::new(clearing_prices), } @@ -128,7 +129,7 @@ impl Settlement { } /// Returns the clearing prices map. - pub fn clearing_prices(&self) -> &HashMap { + pub fn clearing_prices(&self) -> &HashMap { self.encoder.clearing_prices() } @@ -157,13 +158,12 @@ pub mod tests { use { super::*, crate::liquidity::SettlementHandling, - ethrpc::alloy::conversions::IntoAlloy, maplit::hashmap, model::order::{OrderClass, OrderData, OrderKind, OrderMetadata}, }; pub fn assert_settlement_encoded_with( - prices: HashMap, + prices: HashMap, handler: S, execution: L::Execution, exec: impl FnOnce(&mut SettlementEncoder), @@ -187,7 +187,10 @@ pub mod tests { /// Helper function for creating a settlement for the specified prices and /// trades for testing objective value computations. - fn test_settlement(prices: HashMap, trades: Vec) -> Settlement { + fn test_settlement( + prices: HashMap, + trades: Vec, + ) -> Settlement { Settlement { encoder: SettlementEncoder::with_trades(prices, trades), } @@ -277,13 +280,13 @@ pub mod tests { // Test if passing a clearing price of zero makes it not possible to add // trades. - let token0 = H160::from_low_u64_be(0); - let token1 = H160::from_low_u64_be(1); + let token0 = Address::with_last_byte(0); + let token1 = Address::with_last_byte(1); let order = Order { data: OrderData { - sell_token: token0.into_alloy(), - buy_token: token1.into_alloy(), + sell_token: token0, + buy_token: token1, sell_amount: alloy::primitives::U256::from(10), buy_amount: alloy::primitives::U256::from(9), kind: OrderKind::Sell, @@ -413,8 +416,8 @@ pub mod tests { #[test] fn includes_limit_order_ucp() { - let sell_token = H160([1; 20]); - let buy_token = H160([2; 20]); + let sell_token = alloy::primitives::Address::repeat_byte(1); + let buy_token = alloy::primitives::Address::repeat_byte(2); let settlement = test_settlement( hashmap! { @@ -424,8 +427,8 @@ pub mod tests { vec![Trade { order: Order { data: OrderData { - sell_token: sell_token.into_alloy(), - buy_token: buy_token.into_alloy(), + sell_token, + buy_token, sell_amount: alloy::primitives::U256::from(100_000_u128), buy_amount: alloy::primitives::U256::from(99_000_u128), kind: OrderKind::Sell, @@ -453,10 +456,10 @@ pub mod tests { assert_eq!( settlement.clearing_prices, [ - 100_000_u128.into(), - 100_000_u128.into(), - 99_000_u128.into(), - 100_000_u128.into(), + alloy::primitives::U256::from(100_000_u128), + alloy::primitives::U256::from(100_000_u128), + alloy::primitives::U256::from(99_000_u128), + alloy::primitives::U256::from(100_000_u128), ], ); } diff --git a/crates/solver/src/settlement/settlement_encoder.rs b/crates/solver/src/settlement/settlement_encoder.rs index bc37bd788d..3e6a1da055 100644 --- a/crates/solver/src/settlement/settlement_encoder.rs +++ b/crates/solver/src/settlement/settlement_encoder.rs @@ -1,6 +1,7 @@ use { super::{Trade, TradeExecution}, crate::interactions::UnwrapWethInteraction, + alloy::primitives::Address, anyhow::{Context as _, Result, ensure}, ethrpc::alloy::conversions::{IntoAlloy, IntoLegacy}, itertools::Either, @@ -8,7 +9,7 @@ use { interaction::InteractionData, order::{Order, OrderClass, OrderKind}, }, - primitive_types::{H160, U256}, + primitive_types::U256, shared::{ conversions::U256Ext, encoded_settlement::EncodedSettlement, @@ -39,8 +40,8 @@ pub struct SettlementEncoder { // Make sure to update the `merge` method when adding new fields. // Invariant: tokens is all keys in clearing_prices sorted. - tokens: Vec, - clearing_prices: HashMap, + tokens: Vec, + clearing_prices: HashMap, trades: Vec, // This is an Arc so that this struct is Clone. Cannot require `Interaction: Clone` because it // would make the trait not be object safe which prevents using it through `dyn`. @@ -92,7 +93,7 @@ impl SettlementEncoder { /// /// The prices must be provided up front in order to ensure that all tokens /// included in the settlement are known when encoding trades. - pub(crate) fn new(clearing_prices: HashMap) -> Self { + pub(crate) fn new(clearing_prices: HashMap) -> Self { // Explicitly define a token ordering based on the supplied clearing // prices. This is done since `HashMap::keys` returns an iterator in // arbitrary order ([1]), meaning that we can't rely that the ordering @@ -116,7 +117,7 @@ impl SettlementEncoder { } #[cfg(test)] - pub fn with_trades(clearing_prices: HashMap, trades: Vec) -> Self { + pub fn with_trades(clearing_prices: HashMap, trades: Vec) -> Self { let mut result = Self::new(clearing_prices); for trade in trades { result @@ -126,7 +127,7 @@ impl SettlementEncoder { result } - pub(crate) fn clearing_prices(&self) -> &HashMap { + pub(crate) fn clearing_prices(&self) -> &HashMap { &self.clearing_prices } @@ -170,18 +171,18 @@ impl SettlementEncoder { verify_executed_amount(&order, executed_amount)?; let sell_price = self .clearing_prices - .get(&order.data.sell_token.into_legacy()) + .get(&order.data.sell_token) .context("settlement missing sell token")?; let sell_token_index = self - .token_index(order.data.sell_token.into_legacy()) + .token_index(order.data.sell_token) .expect("missing sell token with price"); let buy_price = self .clearing_prices - .get(&order.data.buy_token.into_legacy()) + .get(&order.data.buy_token) .context("settlement missing buy token")?; let buy_token_index = self - .token_index(order.data.buy_token.into_legacy()) + .token_index(order.data.buy_token) .expect("missing buy token with price"); let trade = EncoderTrade { @@ -286,11 +287,11 @@ impl SettlementEncoder { // while pocketing the `surplus_fee` from the `sell_token`s. let uniform_buy_price = *self .clearing_prices - .get(&order.data.buy_token.into_legacy()) + .get(&order.data.buy_token) .context("buy token price is missing")?; let uniform_sell_price = *self .clearing_prices - .get(&order.data.sell_token.into_legacy()) + .get(&order.data.sell_token) .context("sell token price is missing")?; let (sell_amount, buy_amount) = match order.data.kind { @@ -384,7 +385,11 @@ impl SettlementEncoder { } #[cfg(test)] - pub(crate) fn add_token_equivalency(&mut self, token_a: H160, token_b: H160) -> Result<()> { + pub(crate) fn add_token_equivalency( + &mut self, + token_a: Address, + token_b: Address, + ) -> Result<()> { let (new_token, existing_price) = match ( self.clearing_prices.get(&token_a), self.clearing_prices.get(&token_b), @@ -421,10 +426,10 @@ impl SettlementEncoder { self.trades[i].tokens = match self.trades[i].tokens { TokenReference::Indexed { .. } => TokenReference::Indexed { sell_token_index: self - .token_index(self.trades[i].data.order.data.sell_token.into_legacy()) + .token_index(self.trades[i].data.order.data.sell_token) .expect("missing sell token for existing trade"), buy_token_index: self - .token_index(self.trades[i].data.order.data.buy_token.into_legacy()) + .token_index(self.trades[i].data.order.data.buy_token) .expect("missing buy token for existing trade"), }, original @ TokenReference::CustomPrice { .. } => original, @@ -432,7 +437,7 @@ impl SettlementEncoder { } } - fn token_index(&self, token: H160) -> Option { + fn token_index(&self, token: Address) -> Option { self.tokens.binary_search(&token).ok() } @@ -457,10 +462,9 @@ impl SettlementEncoder { }) .collect(); - self.tokens - .retain(|token| traded_tokens.contains(&token.into_alloy())); + self.tokens.retain(|token| traded_tokens.contains(token)); self.clearing_prices - .retain(|token, _| traded_tokens.contains(&token.into_alloy())); + .retain(|token, _| traded_tokens.contains(token)); self.sort_tokens_and_update_indices(); } @@ -473,24 +477,21 @@ impl SettlementEncoder { let uniform_clearing_price_vec_length = self.tokens.len(); let mut tokens = self.tokens.clone(); - let mut clearing_prices: Vec = self + let mut clearing_prices: Vec<_> = self .tokens .iter() .map(|token| { - *self - .clearing_prices + self.clearing_prices .get(token) .expect("missing clearing price for token") + .into_alloy() }) .collect(); { // add tokens/prices for custom price orders, since they are not contained in // the UCP vector - let (mut custom_price_order_tokens, mut custom_price_order_prices): ( - Vec, - Vec, - ) = self + let (mut custom_price_order_tokens, mut custom_price_order_prices) = self .trades .iter() .filter_map(|trade| match trade.tokens { @@ -499,12 +500,12 @@ impl SettlementEncoder { buy_token_price, } => Some(vec![ ( - trade.data.order.data.sell_token.into_legacy(), - sell_token_price, + trade.data.order.data.sell_token, + sell_token_price.into_alloy(), ), ( - trade.data.order.data.buy_token.into_legacy(), - buy_token_price, + trade.data.order.data.buy_token, + buy_token_price.into_alloy(), ), ]), _ => None, @@ -608,7 +609,6 @@ pub mod tests { super::*, alloy::primitives::Address, contracts::alloy::WETH9, - ethrpc::alloy::conversions::IntoAlloy, maplit::hashmap, model::order::{Interactions, OrderBuilder, OrderData}, shared::interaction::{EncodedInteraction, Interaction}, @@ -640,8 +640,8 @@ pub mod tests { }; let mut settlement = SettlementEncoder::new(maplit::hashmap! { - token0.into_legacy() => 1.into(), - token1.into_legacy() => 1.into(), + token0 => 1.into(), + token1 => 1.into(), }); assert!(settlement.add_trade(order0, 1.into(), 1.into()).is_ok()); @@ -677,8 +677,8 @@ pub mod tests { #[test] fn settlement_reflects_different_price_for_normal_and_liquidity_order() { let mut settlement = SettlementEncoder::new(maplit::hashmap! { - token(0) => 3.into(), - token(1) => 10.into(), + Address::with_last_byte(0) => 3.into(), + Address::with_last_byte(1) => 10.into(), }); let order01 = OrderBuilder::default() @@ -702,11 +702,21 @@ pub mod tests { settlement.finish(InternalizationStrategy::SkipInternalizableInteraction); assert_eq!( finished_settlement.tokens, - vec![token(0), token(1), token(1), token(0)] + vec![ + Address::with_last_byte(0), + Address::with_last_byte(1), + Address::with_last_byte(1), + Address::with_last_byte(0) + ] ); assert_eq!( finished_settlement.clearing_prices, - vec![3.into(), 10.into(), 20.into(), 10.into()] + vec![ + alloy::primitives::U256::from(3), + alloy::primitives::U256::from(10), + alloy::primitives::U256::from(20), + alloy::primitives::U256::from(10) + ] ); assert_eq!( finished_settlement.trades[1].1, // <-- is the buy token index of liquidity order @@ -721,7 +731,7 @@ pub mod tests { #[test] fn settlement_inserts_sell_price_for_new_liquidity_order_if_price_did_not_exist() { let mut settlement = SettlementEncoder::new(maplit::hashmap! { - token(1) => 9.into(), + Address::with_last_byte(1) => 9.into(), }); let order01 = OrderBuilder::default() .with_sell_token(Address::with_last_byte(0)) @@ -737,20 +747,20 @@ pub mod tests { ); // ensures that the output of add_liquidity_order is not changed after adding // liquidity order - assert_eq!(settlement.tokens, vec![token(1)]); + assert_eq!(settlement.tokens, vec![Address::with_last_byte(1)]); let finished_settlement = settlement.finish(InternalizationStrategy::SkipInternalizableInteraction); // the initial price from:SettlementEncoder::new(maplit::hashmap! { // token(1) => 9.into(), // }); // gets dropped and replaced by the liquidity price - assert_eq!(finished_settlement.tokens, vec![token(0), token(1)]); + assert_eq!( + finished_settlement.tokens, + vec![Address::with_last_byte(0), Address::with_last_byte(1)] + ); assert_eq!( finished_settlement.clearing_prices, - vec![ - order01.data.buy_amount.into_legacy(), - order01.data.sell_amount.into_legacy() - ] + vec![order01.data.buy_amount, order01.data.sell_amount] ); assert_eq!( finished_settlement.trades[0].0, // <-- is the sell token index of liquidity order @@ -813,8 +823,8 @@ pub mod tests { #[test] fn settlement_encoder_add_token_equivalency() { - let token_a = H160([0x00; 20]); - let token_b = H160([0xff; 20]); + let token_a = Address::repeat_byte(0x00); + let token_b = Address::repeat_byte(0xff); let mut encoder = SettlementEncoder::new(hashmap! { token_a => 1.into(), token_b => 2.into(), @@ -823,9 +833,9 @@ pub mod tests { .add_trade( Order { data: OrderData { - sell_token: token_a.into_alloy(), + sell_token: token_a, sell_amount: alloy::primitives::U256::from(6), - buy_token: token_b.into_alloy(), + buy_token: token_b, buy_amount: alloy::primitives::U256::from(3), ..Default::default() }, @@ -845,7 +855,7 @@ pub mod tests { } ); - let token_c = H160([0xee; 20]); + let token_c = Address::repeat_byte(0xee); encoder.add_token_equivalency(token_a, token_c).unwrap(); assert_eq!(encoder.tokens, [token_a, token_c, token_b]); @@ -867,15 +877,15 @@ pub mod tests { let mut encoder = SettlementEncoder::new(HashMap::new()); assert!( encoder - .add_token_equivalency(H160([0; 20]), H160([1; 20])) + .add_token_equivalency(Address::repeat_byte(0), Address::repeat_byte(1)) .is_err() ); } #[test] fn settlement_encoder_non_equivalent_tokens() { - let token_a = H160([1; 20]); - let token_b = H160([2; 20]); + let token_a = Address::repeat_byte(1); + let token_b = Address::repeat_byte(2); let mut encoder = SettlementEncoder::new(hashmap! { token_a => 1.into(), token_b => 2.into(), @@ -883,13 +893,9 @@ pub mod tests { assert!(encoder.add_token_equivalency(token_a, token_b).is_err()); } - fn token(number: u64) -> H160 { - H160::from_low_u64_be(number) - } - #[test] fn trades_add_interactions_to_the_encoded_and_later_get_encoded() { - let prices = hashmap! { token(1) => 1.into(), token(3) => 3.into() }; + let prices = hashmap! { Address::with_last_byte(1) => 1.into(), Address::with_last_byte(3) => 3.into() }; let mut encoder = SettlementEncoder::new(prices); let i1 = InteractionData { target: Address::from_slice(&[12; 20]), @@ -960,8 +966,12 @@ pub mod tests { #[test] fn encoding_strips_unnecessary_tokens_and_prices() { - let prices = hashmap! {token(1) => 7.into(), token(2) => 2.into(), - token(3) => 9.into(), token(4) => 44.into()}; + let prices = hashmap! { + Address::with_last_byte(1) => 7.into(), + Address::with_last_byte(2) => 2.into(), + Address::with_last_byte(3) => 9.into(), + Address::with_last_byte(4) => 44.into() + }; let mut encoder = SettlementEncoder::new(prices); @@ -973,7 +983,7 @@ pub mod tests { .build(); encoder.add_trade(order_1_3, 11.into(), 0.into()).unwrap(); - let weth = WETH9::Instance::new(token(2).into_alloy(), ethrpc::mock::web3().alloy); + let weth = WETH9::Instance::new(Address::with_last_byte(2), ethrpc::mock::web3().alloy); encoder.add_unwrap(UnwrapWethInteraction { weth, amount: alloy::primitives::U256::from(12), @@ -982,11 +992,14 @@ pub mod tests { let encoded = encoder.finish(InternalizationStrategy::SkipInternalizableInteraction); // only token 1 and 2 have been included in orders by traders - let expected_tokens: Vec<_> = [1, 3].into_iter().map(token).collect(); + let expected_tokens: Vec<_> = [1, 3].into_iter().map(Address::with_last_byte).collect(); assert_eq!(expected_tokens, encoded.tokens); // only the prices for token 1 and 2 remain and they are in the correct order - let expected_prices: Vec<_> = [7, 9].into_iter().map(U256::from).collect(); + let expected_prices: Vec<_> = [7, 9] + .into_iter() + .map(alloy::primitives::U256::from) + .collect(); assert_eq!(expected_prices, encoded.clearing_prices); let encoded_trade = &encoded.trades[0]; @@ -1014,7 +1027,7 @@ pub mod tests { #[test] fn optionally_encodes_internalizable_transactions() { - let prices = hashmap! {token(1) => 7.into() }; + let prices = hashmap! {Address::with_last_byte(1) => 7.into() }; let mut encoder = SettlementEncoder::new(prices); encoder.append_to_execution_plan_internalizable(Arc::new(TestInteraction), true); @@ -1031,8 +1044,8 @@ pub mod tests { #[test] fn computes_custom_price_for_sell_limit_order_correctly() { - let weth = token(1); - let usdc = token(2); + let weth = Address::with_last_byte(1); + let usdc = Address::with_last_byte(2); let prices = hashmap! { // assumption 1 WETH == 1_000 USDC (all prices multiplied by 10^18) weth => U256::exp10(18), @@ -1043,9 +1056,9 @@ pub mod tests { // sell 1.01 WETH for 1_000 USDC with a fee of 0.01 WETH (or 10 USDC) let order = OrderBuilder::default() .with_class(OrderClass::Limit) - .with_sell_token(weth.into_alloy()) + .with_sell_token(weth) .with_sell_amount(alloy::primitives::U256::from(1_010_000_000_000_000_000u128)) // 1.01 WETH - .with_buy_token(usdc.into_alloy()) + .with_buy_token(usdc) .with_buy_amount(alloy::primitives::U256::from(10).pow(alloy::primitives::U256::from(9))) // 1_000 USDC .with_fee_amount(alloy::primitives::U256::ZERO) .with_kind(OrderKind::Sell) @@ -1085,8 +1098,8 @@ pub mod tests { #[test] fn computes_custom_price_for_buy_limit_order_correctly() { - let weth = token(1); - let usdc = token(2); + let weth = Address::with_last_byte(1); + let usdc = Address::with_last_byte(2); // assuming 1 WETH == 1_000 USDC let prices = hashmap! { weth => U256::exp10(18), @@ -1097,9 +1110,9 @@ pub mod tests { // buy 1 WETH for 1_010 USDC with a fee of 10 USDC let order = OrderBuilder::default() .with_class(OrderClass::Limit) - .with_buy_token(weth.into_alloy()) + .with_buy_token(weth) .with_buy_amount(alloy::primitives::U256::from(10).pow(alloy::primitives::U256::from(18))) // 1 WETH - .with_sell_token(usdc.into_alloy()) + .with_sell_token(usdc) .with_sell_amount(alloy::primitives::U256::from(1_010_000_000u128)) // 1_010 USDC .with_fee_amount(alloy::primitives::U256::ZERO) .with_kind(OrderKind::Buy) From 8fa6366114288e99f3a32627c4bfca0e386ba9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Duarte?= Date: Fri, 28 Nov 2025 12:31:09 +0000 Subject: [PATCH 2/4] no need for float magic, ruint has got us --- crates/number/src/conversions.rs | 48 ------------------- .../src/price_estimation/competition/quote.rs | 3 +- crates/shared/src/price_estimation/mod.rs | 4 +- 3 files changed, 3 insertions(+), 52 deletions(-) diff --git a/crates/number/src/conversions.rs b/crates/number/src/conversions.rs index 7729cc6cd4..e9327b42c9 100644 --- a/crates/number/src/conversions.rs +++ b/crates/number/src/conversions.rs @@ -64,54 +64,6 @@ pub mod alloy { num::{BigInt, BigRational, BigUint, Zero, bigint::Sign}, }; - pub trait ToF64Lossy { - fn to_f64_lossy(self) -> f64; - } - - impl ToF64Lossy for U256 { - fn to_f64_lossy(self) -> f64 { - // Reference: https://blog.m-ou.se/floats/ - // Step 1: Get leading zeroes - let leading_zeroes = self.leading_zeros(); - // Step 2: Get msb to be farthest left bit - let left_aligned = self << leading_zeroes; - // Step 3: Shift msb to fit in lower 53 bits of the first u64 (64-53=11) - let quarter_aligned = left_aligned >> 11usize; - let mantissa = quarter_aligned.as_limbs()[3]; - // Step 4: For the dropped bits (all bits beyond the 53 most significant - // We want to know only 2 things. If the msb of the dropped bits is 1 or 0, - // and if any of the other bits are 1. (See blog for explanation) - // So we take care to preserve the msb bit, while jumbling the rest of the bits - // together so that any 1s will survive. If all 0s, then the result will also be - // 0. - let dropped_bits = quarter_aligned.as_limbs()[1] - | quarter_aligned.as_limbs()[0] - | (left_aligned.as_limbs()[0] & 0xFFFF_FFFF); - let dropped_bits = (dropped_bits & 0x7FFF_FFFF_FFFF_FFFF) | (dropped_bits >> 63); - let dropped_bits = quarter_aligned.as_limbs()[2] | dropped_bits; - // Step 5: dropped_bits contains the msb of the original bits and an OR-mixed 63 - // bits. If msb of dropped bits is 0, it is mantissa + 0 - // If msb of dropped bits is 1, it is mantissa + 0 only if mantissa lowest bit - // is 0 and other bits of the dropped bits are all 0 (which both can - // be tested with the below all at once) - let mantissa = mantissa + ((dropped_bits - (dropped_bits >> 63 & !mantissa)) >> 63); - // Step 6: Calculate the exponent - // If self is 0, exponent should be 0 (special meaning) and mantissa will end up - // 0 too Otherwise, (255 - n) + 1022 so it simplifies to 1277 - n - // 1023 and 1022 are the cutoffs for the exponent having the msb next to the - // decimal point - let exponent = if self.is_zero() { - 0 - } else { - 1277 - leading_zeroes as u64 - }; - // Step 7: sign bit is always 0, exponent is shifted into place - // Use addition instead of bitwise OR to saturate the exponent if mantissa - // overflows - f64::from_bits((exponent << 52) + mantissa) - } - } - pub fn big_uint_to_u256(input: &BigUint) -> Result { let bytes = input.to_bytes_be(); ensure!(bytes.len() <= 32, "too large"); diff --git a/crates/shared/src/price_estimation/competition/quote.rs b/crates/shared/src/price_estimation/competition/quote.rs index 76b6d66e3a..30a168950d 100644 --- a/crates/shared/src/price_estimation/competition/quote.rs +++ b/crates/shared/src/price_estimation/competition/quote.rs @@ -12,7 +12,6 @@ use { ethrpc::alloy::conversions::{IntoAlloy, IntoLegacy}, futures::future::{BoxFuture, FutureExt, TryFutureExt}, model::order::OrderKind, - number::conversions::alloy::ToF64Lossy, primitive_types::{H160, U256}, std::{cmp::Ordering, sync::Arc, time::Duration}, tracing::instrument, @@ -139,7 +138,7 @@ impl RankingContext { /// trade route would report a higher `out_amount_in_eth`. This is also /// referred to as "bang-for-buck" and what matters most to traders. fn effective_eth_out(&self, estimate: &Estimate, kind: OrderKind) -> U256 { - let eth_out = estimate.out_amount.to_f64_lossy() * self.native_price; + let eth_out = f64::from(estimate.out_amount) * self.native_price; let fees = estimate.gas as f64 * self.gas_price; let effective_eth_out = match kind { // High fees mean receiving less `buy_token` from your sell order. diff --git a/crates/shared/src/price_estimation/mod.rs b/crates/shared/src/price_estimation/mod.rs index fc7535e058..f9c37e4dd0 100644 --- a/crates/shared/src/price_estimation/mod.rs +++ b/crates/shared/src/price_estimation/mod.rs @@ -12,7 +12,7 @@ use { futures::future::BoxFuture, itertools::Itertools, model::order::{BuyTokenDestination, OrderKind, SellTokenSource}, - number::{conversions::alloy::ToF64Lossy, nonzero::U256 as NonZeroU256}, + number::nonzero::U256 as NonZeroU256, rate_limit::{RateLimiter, Strategy}, reqwest::Url, serde::{Deserialize, Serialize}, @@ -554,7 +554,7 @@ impl Estimate { /// unit of sell_token (buy_amount / sell_amount). pub fn price_in_buy_token_f64(&self, query: &Query) -> f64 { let (sell_amount, buy_amount) = self.amounts(query); - buy_amount.to_f64_lossy() / sell_amount.to_f64_lossy() + f64::from(buy_amount) / f64::from(sell_amount) } } From ccb16fe03ca958c3a8be86004f3650da923494b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Duarte?= Date: Fri, 28 Nov 2025 12:40:22 +0000 Subject: [PATCH 3/4] cleanup --- crates/shared/src/price_estimation/native_price_cache.rs | 2 +- crates/solver/src/settlement/settlement_encoder.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/shared/src/price_estimation/native_price_cache.rs b/crates/shared/src/price_estimation/native_price_cache.rs index 25613796c2..5e95f7e45f 100644 --- a/crates/shared/src/price_estimation/native_price_cache.rs +++ b/crates/shared/src/price_estimation/native_price_cache.rs @@ -413,7 +413,7 @@ impl CachingNativePriceEstimator { } let uncached_tokens: Vec<_> = tokens - .into_iter() + .iter() .filter(|t| !prices.contains_key(*t)) .copied() .collect(); diff --git a/crates/solver/src/settlement/settlement_encoder.rs b/crates/solver/src/settlement/settlement_encoder.rs index 3e6a1da055..445e494b87 100644 --- a/crates/solver/src/settlement/settlement_encoder.rs +++ b/crates/solver/src/settlement/settlement_encoder.rs @@ -40,8 +40,8 @@ pub struct SettlementEncoder { // Make sure to update the `merge` method when adding new fields. // Invariant: tokens is all keys in clearing_prices sorted. - tokens: Vec, - clearing_prices: HashMap, + tokens: Vec
, + clearing_prices: HashMap, trades: Vec, // This is an Arc so that this struct is Clone. Cannot require `Interaction: Clone` because it // would make the trait not be object safe which prevents using it through `dyn`. From 15e3ba11f4f72328d3ae095cc384dbbcaf52350d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Duarte?= Date: Fri, 28 Nov 2025 12:50:56 +0000 Subject: [PATCH 4/4] fmt --- .../shared/src/account_balances/simulation.rs | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/crates/shared/src/account_balances/simulation.rs b/crates/shared/src/account_balances/simulation.rs index 7e55fd46c2..e453ac95af 100644 --- a/crates/shared/src/account_balances/simulation.rs +++ b/crates/shared/src/account_balances/simulation.rs @@ -74,8 +74,7 @@ impl Balances { let usable_balance = match query.source { SellTokenSource::Erc20 => { let balance = token.balanceOf(query.owner); - let allowance = - token.allowance(query.owner, self.vault_relayer().into_alloy()); + let allowance = token.allowance(query.owner, self.vault_relayer().into_alloy()); let (balance, allowance) = futures::try_join!( balance.call().into_future(), allowance.call().into_future() @@ -85,12 +84,9 @@ impl Balances { SellTokenSource::External => { let vault = BalancerV2Vault::new(self.vault().into_alloy(), &self.web3.alloy); let balance = token.balanceOf(query.owner); - let approved = vault.hasApprovedRelayer( - query.owner, - self.vault_relayer().into_alloy(), - ); - let allowance = - token.allowance(query.owner, self.vault().into_alloy()); + let approved = + vault.hasApprovedRelayer(query.owner, self.vault_relayer().into_alloy()); + let allowance = token.allowance(query.owner, self.vault().into_alloy()); let (balance, approved, allowance) = futures::try_join!( balance.call().into_future(), approved.call().into_future(), @@ -104,12 +100,9 @@ impl Balances { } SellTokenSource::Internal => { let vault = BalancerV2Vault::new(self.vault().into_alloy(), &self.web3.alloy); - let balance = vault - .getInternalBalance(query.owner, vec![query.token]); - let approved = vault.hasApprovedRelayer( - query.owner, - self.vault_relayer().into_alloy(), - ); + let balance = vault.getInternalBalance(query.owner, vec![query.token]); + let approved = + vault.hasApprovedRelayer(query.owner, self.vault_relayer().into_alloy()); let (balance, approved) = futures::try_join!( balance.call().into_future(), approved.call().into_future() @@ -134,8 +127,7 @@ impl BalanceFetching for Balances { .iter() .map(|query| async { if query.interactions.is_empty() { - let token = - ERC20::Instance::new(query.token, self.web3.alloy.clone()); + let token = ERC20::Instance::new(query.token, self.web3.alloy.clone()); self.tradable_balance_simple(query, &token).await } else { self.tradable_balance_simulated(query).await