Repository: gakonst/ethers-rs
Pull request #554 (2021-11-03 18:12:12)

Feat/add call deployer
## Motivation

As it can be useful, for plentiful reasons, to perform one or several dry test runs of a contract's deployment, I hereby propose an implementation of `.call()` for the `Deployer` struct.

## Solution

The implementation is straightforward, as it consist in invoking the client's`.call()` method from the `Middleware` trait to simulate the deployment. Such doing leaves plenty of room to handle reverts in a more structured way in the future.

## PR Checklist

- [x] Added Tests
- [x] Added Documentation
- [x] Updated the changelog


frjnn: Looks like something's wrong with [this test](https://github.com/gakonst/ethers-rs/blob/23fb877c16c70a81825506ba54ea1c2bf915e19a/ethers-middleware/tests/nonce_manager.rs#L20), I don't understand what's causing it, seems unrelated to me.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 594bf543..34933a34 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Unreleased
 
+- Add `.call()` method to `Deployer` for performing dry runs of contract deployments. [#554](https://github.com/gakonst/ethers-rs/pull/554)
 - Improve error message from failure in `ethers_contract_abigen::Source::parse` [#552](https://github.com/gakonst/ethers-rs/pull/552)
 - use enumerated aliases for overloaded functions [#545](https://github.com/gakonst/ethers-rs/pull/545)
 - move `AbiEncode` `AbiDecode` trait to ethers-core and implement for core types [#531](https://github.com/gakonst/ethers-rs/pull/531)
diff --git a/ethers-contract/src/factory.rs b/ethers-contract/src/factory.rs
index 6be23ba8..5bc1a761 100644
--- a/ethers-contract/src/factory.rs
+++ b/ethers-contract/src/factory.rs
@@ -46,6 +46,19 @@ impl<M: Middleware> Deployer<M> {
         self
     }
 
+    /// Dry runs the deployment of the contract
+    ///
+    /// Note: this function _does not_ send a transaction from your account
+    pub async fn call(&self) -> Result<(), ContractError<M>> {
+        self.client
+            .call(&self.tx, Some(self.block.into()))
+            .await
+            .map_err(ContractError::MiddlewareError)?;
+
+        // TODO: It would be nice to handle reverts in a structured way.
+        Ok(())
+    }
+
     /// Broadcasts the contract deployment transaction and after waiting for it to
     /// be sufficiently confirmed (default: 1), it returns a [`Contract`](crate::Contract)
     /// struct at the deployed contract's address.


Test patch:
diff --git a/ethers-contract/tests/common/mod.rs b/ethers-contract/tests/common/mod.rs
index 1ebf5002..5328b9eb 100644
--- a/ethers-contract/tests/common/mod.rs
+++ b/ethers-contract/tests/common/mod.rs
@@ -49,5 +49,7 @@ pub fn connect(ganache: &GanacheInstance, idx: usize) -> Arc<Provider<Http>> {
 /// Launches a ganache instance and deploys the SimpleStorage contract
 pub async fn deploy<M: Middleware>(client: Arc<M>, abi: Abi, bytecode: Bytes) -> Contract<M> {
     let factory = ContractFactory::new(abi, bytecode, client);
-    factory.deploy("initial value".to_string()).unwrap().legacy().send().await.unwrap()
+    let deployer = factory.deploy("initial value".to_string()).unwrap();
+    assert!(deployer.call().await.is_ok());
+    deployer.legacy().send().await.unwrap()
 }
diff --git a/ethers-contract/tests/contract.rs b/ethers-contract/tests/contract.rs
index 2aa0e75f..606a5f34 100644
--- a/ethers-contract/tests/contract.rs
+++ b/ethers-contract/tests/contract.rs
@@ -39,6 +39,9 @@ mod eth_tests {
         // (practically it's not expected that you'll need to deploy multiple instances of
         // the _same_ deployer, so it's fine to clone here from a dev UX vs perf tradeoff)
         let deployer = factory.deploy("initial value".to_string()).unwrap().legacy();
+        // dry runs the deployment of the contract. takes the deployer by reference, no need to
+        // clone.
+        assert!(deployer.call().await.is_ok());
         let contract = deployer.clone().send().await.unwrap();
 
         let get_value = contract.method::<_, String>("getValue", ()).unwrap();