diff --git a/ethers-core/src/utils/anvil.rs b/ethers-core/src/utils/anvil.rs index 4a8e62a8..ac941b5b 100644 --- a/ethers-core/src/utils/anvil.rs +++ b/ethers-core/src/utils/anvil.rs @@ -5,6 +5,7 @@ use crate::{ use k256::{ecdsa::SigningKey, SecretKey as K256SecretKey}; use std::{ io::{BufRead, BufReader}, + path::PathBuf, process::{Child, Command}, time::{Duration, Instant}, }; @@ -78,6 +79,7 @@ impl Drop for AnvilInstance { /// ``` #[derive(Debug, Clone, Default)] pub struct Anvil { + program: Option, port: Option, block_time: Option, mnemonic: Option, @@ -89,10 +91,47 @@ pub struct Anvil { impl Anvil { /// Creates an empty Anvil builder. /// The default port is 8545. The mnemonic is chosen randomly. + /// + /// # Example + /// + /// ``` + /// # use ethers_core::utils::Anvil; + /// fn a() { + /// let anvil = Anvil::default().spawn(); + /// + /// println!("Anvil running at `{}`", anvil.endpoint()); + /// # } + /// ``` pub fn new() -> Self { Self::default() } + /// Creates an Anvil builder which will execute `anvil` at the given path. + /// + /// # Example + /// + /// ``` + /// # use ethers_core::utils::Anvil; + /// fn a() { + /// let anvil = Anvil::at("~/.foundry/bin/anvil").spawn(); + /// + /// println!("Anvil running at `{}`", anvil.endpoint()); + /// # } + /// ``` + pub fn at(path: impl Into) -> Self { + Self::new().path(path) + } + + /// Sets the `path` to the `anvil` cli + /// + /// By default, it's expected that `anvil` is in `$PATH`, see also + /// [`std::process::Command::new()`] + #[must_use] + pub fn path>(mut self, path: T) -> Self { + self.program = Some(path.into()); + self + } + /// Sets the port which will be used when the `anvil` instance is launched. #[must_use] pub fn port>(mut self, port: T) -> Self { @@ -156,7 +195,11 @@ impl Anvil { /// Consumes the builder and spawns `anvil` with stdout redirected /// to /dev/null. pub fn spawn(self) -> AnvilInstance { - let mut cmd = Command::new("anvil"); + let mut cmd = if let Some(ref prg) = self.program { + Command::new(prg) + } else { + Command::new("anvil") + }; cmd.stdout(std::process::Stdio::piped()); let port = if let Some(port) = self.port { port } else { unused_port() }; cmd.arg("-p").arg(port.to_string()); diff --git a/examples/anvil_fork.rs b/examples/anvil_fork.rs new file mode 100644 index 00000000..31da8670 --- /dev/null +++ b/examples/anvil_fork.rs @@ -0,0 +1,14 @@ +//! Spawn an [anvil](https://github.com/foundry-rs/foundry/tree/master/anvil) instance in forking mode + +use ethers::utils::Anvil; + +#[tokio::main] +async fn main() -> eyre::Result<()> { + // ensure `anvil` is available in $PATH + let anvil = + Anvil::new().fork("https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27").spawn(); + + println!("Anvil running at `{}`", anvil.endpoint()); + + Ok(()) +}