diff --git a/crates/e2e/tests/e2e/eip4626.rs b/crates/e2e/tests/e2e/eip4626.rs index cfcb2c5607..3d2975c28c 100644 --- a/crates/e2e/tests/e2e/eip4626.rs +++ b/crates/e2e/tests/e2e/eip4626.rs @@ -12,7 +12,10 @@ use { e2e::setup::*, ethrpc::alloy::CallBuilderExt, futures::{FutureExt, future::BoxFuture}, - model::quote::{OrderQuoteRequest, OrderQuoteSide, SellAmount}, + model::{ + order::BUY_ETH_ADDRESS, + quote::{OrderQuoteRequest, OrderQuoteSide, SellAmount}, + }, number::units::EthUnit, price_estimation::{ HEALTHY_PRICE_ESTIMATION_TIME, @@ -20,6 +23,7 @@ use { }, shared::web3::Web3, std::time::Duration, + testlib::tokens::GNO, }; /// The block number from which we will fetch state for the forked test. @@ -265,11 +269,14 @@ async fn eip4626_empty_revert_terminal_token_test(web3: Web3) { let expected_price = 0.0001; let inner = FixedPrice(expected_price); let estimator = Eip4626::new(Box::new(inner), web3.provider); - let price = estimator - .estimate_native_price(USDC, HEALTHY_PRICE_ESTIMATION_TIME) - .await - .expect("empty-revert on terminal token must not abort the unwrap"); - assert_eq!(price, expected_price); + + for token in [BUY_ETH_ADDRESS, USDC, GNO] { + let price = estimator + .estimate_native_price(token, HEALTHY_PRICE_ESTIMATION_TIME) + .await + .expect("empty-revert on terminal token must not abort the unwrap"); + assert_eq!(price, expected_price); + } } struct FixedPrice(f64); diff --git a/crates/price-estimation/Cargo.toml b/crates/price-estimation/Cargo.toml index 42f08ca145..98ea29733c 100644 --- a/crates/price-estimation/Cargo.toml +++ b/crates/price-estimation/Cargo.toml @@ -55,6 +55,7 @@ tracing = { workspace = true } url = { workspace = true } [dev-dependencies] +ethrpc = { workspace = true, features = ["test-util"] } hex-literal = { workspace = true } maplit = { workspace = true } mockall = { workspace = true } diff --git a/crates/price-estimation/src/native/eip4626.rs b/crates/price-estimation/src/native/eip4626.rs index 802380c45e..f3191fa382 100644 --- a/crates/price-estimation/src/native/eip4626.rs +++ b/crates/price-estimation/src/native/eip4626.rs @@ -7,6 +7,7 @@ use { dashmap::DashSet, ethrpc::{AlloyProvider, alloy::errors::ContractErrorExt}, futures::{FutureExt, future::BoxFuture}, + model::order::BUY_ETH_ADDRESS, num::{BigInt, BigRational, ToPrimitive}, number::conversions::u256_to_big_rational, std::time::{Duration, Instant}, @@ -37,7 +38,9 @@ impl Eip4626 { Self { inner, provider, - non_vault_tokens: DashSet::new(), + // BUY_ETH_ADDRESS is not ERC-20, but it is a valid estimation address + // so we need to make sure it bypasses the EIP-4626 estimator + non_vault_tokens: DashSet::from_iter([BUY_ETH_ADDRESS]), } } @@ -224,6 +227,8 @@ mod tests { use { super::*, crate::{HEALTHY_PRICE_ESTIMATION_TIME, native::MockNativePriceEstimating}, + alloy::providers::mock::Asserter, + std::borrow::Cow, }; #[test] @@ -253,6 +258,41 @@ mod tests { assert!((rate - 2.0).abs() < 1e-9, "rate={rate}"); } + /// Tests two (related) things: + /// * Cached tokens bypass the EIP-4626 provider calls — i.e. calling + /// decimals, assets, etc + /// * That the BUY_ETH_ADDRESS is cached by default (and the previous + /// applies to it) + #[tokio::test] + async fn buy_eth_address_bypasses_eth_calls() { + let mut inner = MockNativePriceEstimating::new(); + let token = BUY_ETH_ADDRESS; + let expected_price = 1.5; + inner + .expect_estimate_native_price() + .withf(move |t, _| *t == token) + .returning(move |_, _| Box::pin(async move { Ok(expected_price) })); + + let asserter = Asserter::new(); + asserter.push_failure_msg(Cow::from("calls are not being bypassed")); + let web3 = ethrpc::Web3::with_asserter(asserter); + + let estimator = Eip4626::new(Box::new(inner), web3.provider); + + let result = estimator + .estimate(token, HEALTHY_PRICE_ESTIMATION_TIME) + .await; + assert_eq!(result.unwrap(), expected_price); + + let result = estimator + .estimate(Address::random(), HEALTHY_PRICE_ESTIMATION_TIME) + .await; + assert!( + matches!(result, Err(PriceEstimationError::EstimatorInternal(_))), + "{result:?}" + ); + } + #[tokio::test] async fn non_vault_tokens_delegate_to_inner() { let mut inner = MockNativePriceEstimating::new();