Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions crates/e2e/src/nodes/forked_node.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use {
ethcontract::{Account, H160, U256},
alloy::primitives::{Address, U256},
reqwest::Url,
serde_json::json,
std::fmt::Debug,
Expand Down Expand Up @@ -36,13 +36,13 @@ impl<T: Transport> ForkedNodeApi<T> {
))
}

pub async fn impersonate(&self, address: &H160) -> Result<Account, web3::Error> {
pub async fn impersonate(&self, address: &Address) -> Result<(), web3::Error> {
let json_address = serde_json::json!(address);
self.transport
.execute("anvil_impersonateAccount", vec![json_address])
.await?;

Ok(Account::Local(*address, None))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The side effect of anvil_impersonateAccount still happens
And since this return values wasn't actually being used where this function was called, I changed this to return unit type (also specified in PR description).

I could use some feedback on what should be the equivalent return type in alloyrs for ethcontract::Account, especially if the value isn't being used.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are mostly 2 options:

  1. impersonate() returns an alloy provider that doesn't have any signers (like here)
  2. ForkedNodeApi has a impersonating_provider() that you are supposed to use for any number of different impersonated account.

I think I prefer 2 because it's pretty easy to communicate the correct way to use the API with doc comments.

#[derive(Debug, Clone)]
pub struct ForkedNodeApi {
    provider: AlloyProvider,
}

impl ForkedNodeApi {
    pub fn new(provider: AlloyProvider) -> Self {
        Self { provider: provider.without_wallet() }
    }

    /// Provider that can sign transactions on behalf of accounts
    /// previously impersonated with `[Self::impersonate()]`.
    pub fn impersonating_provider(&self) -> &AlloyProvider {
        &self.provider
    }

    /// Configures the node to sign any transaction for the given account.
    /// Such transactions should be sent from `[Self::impersonating_provider()]`.
    ///
    /// # Panics
    ///
    /// Panics if the connected RPC does not support impersonating account.
    pub async fn impersonate(&self, address: &Address);
    // all the other existing functions
}

Ultimately both versions are not optimal because you can set tx.from arbitrarily so nothing on the type level stops you from impersonating some account and then using any other impersonation incompatible provider to send transactions for that account.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use stop_impersonating_accounthttps://docs.rs/alloy-provider/latest/alloy_provider/ext/trait.AnvilApi.html#tymethod.anvil_stop_impersonating_account

The reason we need an empty wallet is that if the wallet has at least one signer, it will have a default signer, meaning all TXs will be signed using it. The alternative is to send the TX "manually" as in, outside the regular alloy pipeline that fills in gaps for the TX (like nonce, signature, etc)

Personally, I don't think the current state API is great, a better way of solving this would be having the impersonate taking an async future/closure that executes within a scope of impersonation:

Kinda like:

provider.impersonate(address, |empty_provider| async move {
	ERC20::instance(provider)::balanceOf(address)...
}).await

And inside, what really happens is:

self.transport
.execute("anvil_impersonateAccount", vec![json_address])
.await?;

let closure_result = closure.await;

self.transport
.execute("anvil_stopImpersonateAccount", vec![json_address])
.await?;

return closure_result

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, better yet, remove this whole API and just use what anvil already provides: https://docs.rs/alloy-provider/latest/alloy_provider/ext/trait.AnvilApi.html#tymethod.anvil_send_impersonated_transaction_with_config

Maybe you can make it more ergonomic

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking into this I opened #4004

Removed the forked node altogether, used alloy/anvil instead, no need to argue about the right return type :P

Ok(())
}

pub fn set_chain_id(&self, chain_id: u64) -> CallFuture<(), T::Out> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is unused, IMO we can remove this

Expand All @@ -53,7 +53,7 @@ impl<T: Transport> ForkedNodeApi<T> {
)
}

pub fn set_balance(&self, address: &H160, balance: U256) -> CallFuture<(), T::Out> {
pub fn set_balance(&self, address: &Address, balance: U256) -> CallFuture<(), T::Out> {
let json_address = serde_json::json!(address);
let json_balance = serde_json::json!(format!("{:#032x}", balance));
Comment on lines +56 to 58
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nerd nit: if you expand the macro you'll see that json! simply calls serde_json::to_value (at least for these two cases)

But it also makes the value you pass, a reference, meaning address becomes a double reference (&&Address)

Unlike other parts of the codebase, where address is immediately de-referenced turning it into a copy, here we can simply remove the macro (which usually hides more magic than in this case) use the expanded API and avoid a double reference

For the second call, we could do .as_str() or just the reference even.

I know this is not directly related to the migration, but a migration is the perfect opportunity to improve the codebase like this

CallFuture::new(
Expand Down
4 changes: 2 additions & 2 deletions crates/e2e/src/nodes/local_node.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
alloy::primitives::{Address, U256},
chrono::{DateTime, Utc},
ethcontract::{H160, U256},
std::fmt::Debug,
web3::{Transport, api::Namespace, helpers::CallFuture},
};
Expand Down Expand Up @@ -70,7 +70,7 @@ impl<T: Transport> TestNodeApi<T> {
)
}

pub fn set_balance(&self, address: &H160, balance: &U256) -> CallFuture<(), T::Out> {
pub fn set_balance(&self, address: &Address, balance: &U256) -> CallFuture<(), T::Out> {
let json_address = serde_json::json!(address);
let json_balance = serde_json::json!(format!("{:#032x}", balance));
CallFuture::new(
Expand Down
2 changes: 1 addition & 1 deletion crates/e2e/src/setup/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use {
crate::nodes::{NODE_HOST, Node},
::alloy::signers::local::{MnemonicBuilder, coins_bip39::English},
anyhow::{Result, anyhow},
ethcontract::futures::FutureExt,
ethrpc::Web3,
futures::FutureExt,
std::{
future::Future,
io::Write,
Expand Down
14 changes: 5 additions & 9 deletions crates/e2e/src/setup/onchain_components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ use {
GPv2AllowListAuthentication::GPv2AllowListAuthentication,
test::CowProtocolToken,
},
ethrpc::alloy::{
CallBuilderExt,
ProviderSignerExt,
conversions::{IntoAlloy, IntoLegacy},
},
ethrpc::alloy::{CallBuilderExt, ProviderSignerExt},
hex_literal::hex,
model::{
DomainSeparator,
Expand Down Expand Up @@ -284,12 +280,12 @@ impl OnchainComponents {
) -> [TestAccount; N] {
let authenticator = &self.contracts.gp_authenticator;

let auth_manager = authenticator.manager().call().await.unwrap().into_legacy();
let auth_manager = authenticator.manager().call().await.unwrap();

let forked_node_api = self.web3.api::<ForkedNodeApi<_>>();

forked_node_api
.set_balance(&auth_manager, 100u64.eth().into_legacy())
.set_balance(&auth_manager, 100u64.eth())
.await
.expect("could not set auth_manager balance");

Expand All @@ -312,7 +308,7 @@ impl OnchainComponents {
for solver in &solvers {
impersonated_authenticator
.addSolver(solver.address())
.from(auth_manager.into_alloy())
.from(auth_manager)
.send_and_watch()
.await
.expect("failed to add solver");
Expand All @@ -321,7 +317,7 @@ impl OnchainComponents {
if let Some(router) = &self.contracts.flashloan_router {
impersonated_authenticator
.addSolver(*router.address())
.from(auth_manager.into_alloy())
.from(auth_manager)
.send_and_watch()
.await
.expect("failed to add flashloan wrapper");
Expand Down
41 changes: 13 additions & 28 deletions crates/e2e/tests/e2e/ethflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,7 @@ use {
run_test,
wait_for_condition,
},
ethcontract::H160,
ethrpc::{
Web3,
alloy::{
CallBuilderExt,
conversions::{IntoAlloy, IntoLegacy},
},
block_stream::timestamp_of_current_block_in_seconds,
},
ethrpc::{Web3, alloy::CallBuilderExt, block_stream::timestamp_of_current_block_in_seconds},
model::{
DomainSeparator,
order::{
Expand Down Expand Up @@ -207,7 +199,7 @@ async fn eth_flow_tx(web3: Web3) {
test_trade_availability_in_api(
services.client(),
&ethflow_order,
&trader.address().into_legacy(),
&trader.address(),
onchain.contracts(),
ethflow_contract,
)
Expand Down Expand Up @@ -457,23 +449,16 @@ async fn test_order_availability_in_api(
let is_available = || async { services.get_order(&uid).await.is_ok() };
wait_for_condition(TIMEOUT, is_available).await.unwrap();

test_orders_query(
services,
order,
&owner.into_legacy(),
contracts,
ethflow_contract,
)
.await;
test_orders_query(services, order, owner, contracts, ethflow_contract).await;

// Api returns eth flow orders for both eth-flow contract address and actual
// owner
for address in [owner, ethflow_contract.address()] {
test_account_query(
&address.into_legacy(),
address,
services.client(),
order,
&owner.into_legacy(),
owner,
contracts,
ethflow_contract,
)
Expand All @@ -484,7 +469,7 @@ async fn test_order_availability_in_api(
async fn test_trade_availability_in_api(
client: &Client,
order: &ExtendedEthFlowOrder,
owner: &H160,
owner: &Address,
contracts: &Contracts,
ethflow_contract: &CoWSwapEthFlow::Instance,
) {
Expand All @@ -498,7 +483,7 @@ async fn test_trade_availability_in_api(

// Api returns eth flow orders for both eth-flow contract address and actual
// owner
for address in [owner, &ethflow_contract.address().into_legacy()] {
for address in [owner, ethflow_contract.address()] {
test_trade_query(
&TradeQuery::ByOwner(*address),
client,
Expand Down Expand Up @@ -529,7 +514,7 @@ async fn test_order_was_settled(ethflow_order: &ExtendedEthFlowOrder, onchain: &
async fn test_orders_query(
services: &Services<'_>,
order: &ExtendedEthFlowOrder,
owner: &H160,
owner: &Address,
contracts: &Contracts,
ethflow_contract: &CoWSwapEthFlow::Instance,
) {
Expand All @@ -541,10 +526,10 @@ async fn test_orders_query(
}

async fn test_account_query(
queried_account: &H160,
queried_account: &Address,
client: &Client,
order: &ExtendedEthFlowOrder,
owner: &H160,
owner: &Address,
contracts: &Contracts,
ethflow_contract: &CoWSwapEthFlow::Instance,
) {
Expand All @@ -564,7 +549,7 @@ async fn test_account_query(

enum TradeQuery {
ByUid(OrderUid),
ByOwner(H160),
ByOwner(Address),
}

async fn test_trade_query(
Expand Down Expand Up @@ -594,7 +579,7 @@ async fn test_trade_query(
async fn test_order_parameters(
response: &Order,
order: &ExtendedEthFlowOrder,
owner: &H160,
owner: &Address,
contracts: &Contracts,
ethflow_contract: &CoWSwapEthFlow::Instance,
) {
Expand All @@ -614,7 +599,7 @@ async fn test_order_parameters(
assert_eq!(
response.metadata.onchain_order_data,
Some(OnchainOrderData {
sender: owner.into_alloy(),
sender: *owner,
placement_error: None,
})
);
Expand Down
12 changes: 4 additions & 8 deletions crates/e2e/tests/e2e/liquidity.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
alloy::{
primitives::{Address, address},
primitives::{Address, B256, address},
providers::{
Provider,
ext::{AnvilApi, ImpersonateConfig},
Expand All @@ -21,11 +21,7 @@ use {
wait_for_condition,
},
},
ethcontract::H256,
ethrpc::{
Web3,
alloy::{CallBuilderExt, conversions::IntoLegacy},
},
ethrpc::{Web3, alloy::CallBuilderExt},
model::{
order::{OrderCreation, OrderKind},
signature::EcdsaSigningScheme,
Expand Down Expand Up @@ -403,7 +399,7 @@ async fn fill_or_kill_zeroex_limit_order(
zeroex: &IZeroex::Instance,
zeroex_order: &shared::zeroex_api::OrderRecord,
from: Address,
) -> anyhow::Result<H256> {
) -> anyhow::Result<B256> {
let order = zeroex_order.order();
let tx_hash = zeroex
.fillOrKillLimitOrder(
Expand Down Expand Up @@ -435,5 +431,5 @@ async fn fill_or_kill_zeroex_limit_order(
.watch()
.await?;

Ok(tx_hash.into_legacy())
Ok(tx_hash)
}
Loading