Repository: gakonst/ethers-rs (rust)

Issue:
Expose builder functions in ContractDeployer
The `ContractDeployer` helper type is used to deploy contracts

https://github.com/gakonst/ethers-rs/blob/82d5741bcce57901f0a0bee5139ccd57d28e33d8/ethers-contract/src/factory.rs#L24

it is also utilized by `abigen!` for the constructor call, currently it lacks the builder functions the `TransactionRequest` type has. Modifying a deploy tx looks like this rn

```rust
let mut tx =
        Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()).unwrap().deployer.tx;
    tx.set_nonce(nonce);
    tx.set_gas(300_000u64);
```

instead the `ContractDeployer` should have builder functions on par with `TransactionRequest`

ref #1239

Pull request:
feat(ContractDeployer): add tx builder methods
Closes #1262 

<!--
Thank you for your Pull Request. Please provide a description above and review
the requirements below.

Bug fixes and new features should include tests.

Contributors guide: https://github.com/gakonst/ethers-rs/blob/master/CONTRIBUTING.md

The contributors guide includes instructions for running rustfmt and building the
documentation.
-->

## Motivation

Convenience
<!--
Explain the context and why you're making that change. What is the problem
you're trying to solve? In some cases there is not a problem and this can be
thought of as being the motivation for your change.
-->

## Solution

<!--
Summarize the solution and provide any necessary context needed to understand
the code change.
-->

## PR Checklist

- [ ] Added Tests
- [x] Added Documentation
- [x] Updated the changelog


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e15b3a61..b7db676d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -213,6 +213,8 @@
 
 ### Unreleased
 
+- Added tx builder methods to `ContractFactory`
+  [#1289](https://github.com/gakonst/ethers-rs/pull/1289)
 - Relax Clone requirements when Arc<Middleware> is used
   [#1183](https://github.com/gakonst/ethers-rs/pull/1183)
 - Add `EventStream::select` to combine streams with different event types
diff --git a/ethers-contract/src/factory.rs b/ethers-contract/src/factory.rs
index 23ef6bb9..cb493e0b 100644
--- a/ethers-contract/src/factory.rs
+++ b/ethers-contract/src/factory.rs
@@ -4,8 +4,8 @@ use std::marker::PhantomData;
 use ethers_core::{
     abi::{Abi, Token, Tokenize},
     types::{
-        transaction::eip2718::TypedTransaction, BlockNumber, Bytes, TransactionReceipt,
-        TransactionRequest,
+        transaction::eip2718::TypedTransaction, Address, BlockNumber, Bytes, NameOrAddress,
+        TransactionReceipt, TransactionRequest, U256, U64,
     },
 };
 use ethers_providers::Middleware;
@@ -59,6 +59,54 @@ impl<M: Middleware, C: From<Contract<M>>> ContractDeployer<M, C> {
         self
     }
 
+    /// Sets the `from` field in the deploy transaction to the provided value
+    pub fn from<T: Into<Address>>(mut self, from: T) -> Self {
+        self.deployer.tx.set_from(from.into());
+        self
+    }
+
+    /// Sets the `to` field in the deploy transaction to the provided value
+    pub fn to<T: Into<NameOrAddress>>(mut self, to: T) -> Self {
+        self.deployer.tx.set_to(to.into());
+        self
+    }
+
+    /// Sets the `gas` field in the deploy transaction to the provided value
+    pub fn gas<T: Into<U256>>(mut self, gas: T) -> Self {
+        self.deployer.tx.set_gas(gas.into());
+        self
+    }
+
+    /// Sets the `gas_price` field in the deploy transaction to the provided value
+    pub fn gas_price<T: Into<U256>>(mut self, gas_price: T) -> Self {
+        self.deployer.tx.set_gas_price(gas_price.into());
+        self
+    }
+
+    /// Sets the `value` field in the deploy transaction to the provided value
+    pub fn value<T: Into<U256>>(mut self, value: T) -> Self {
+        self.deployer.tx.set_value(value.into());
+        self
+    }
+
+    /// Sets the `data` field in the deploy transaction to the provided value
+    pub fn data<T: Into<Bytes>>(mut self, data: T) -> Self {
+        self.deployer.tx.set_data(data.into());
+        self
+    }
+
+    /// Sets the `nonce` field in the deploy transaction to the provided value
+    pub fn nonce<T: Into<U256>>(mut self, nonce: T) -> Self {
+        self.deployer.tx.set_nonce(nonce.into());
+        self
+    }
+
+    /// Sets the `chain_id` field in the deploy transaction to the provided value
+    pub fn chain_id<T: Into<U64>>(mut self, chain_id: T) -> Self {
+        self.deployer.tx.set_chain_id(chain_id.into());
+        self
+    }
+
     /// Dry runs the deployment of the contract
     ///
     /// Note: this function _does not_ send a transaction from your account
diff --git a/ethers-solc/src/config.rs b/ethers-solc/src/config.rs
index 90307818..0311c286 100644
--- a/ethers-solc/src/config.rs
+++ b/ethers-solc/src/config.rs
@@ -311,41 +311,40 @@ impl ProjectPathsConfig {
         let mut content = content.as_bytes().to_vec();
         let mut offset = 0_isize;
 
-        if strip_license {
-            if let Some(license) = target_node.license() {
-                let license_range = license.loc_by_offset(offset);
-                offset -= license_range.len() as isize;
-                content.splice(license_range, std::iter::empty());
+        let mut statements = [
+            (target_node.license(), strip_license),
+            (target_node.version(), strip_version_pragma),
+            (target_node.experimental(), strip_experimental_pragma),
+        ]
+        .iter()
+        .filter_map(|(data, condition)| if *condition { data.to_owned().as_ref() } else { None })
+        .collect::<Vec<_>>();
+        statements.sort_by_key(|x| x.loc().start);
+
+        let (mut imports, mut statements) =
+            (imports.iter().peekable(), statements.iter().peekable());
+        while imports.peek().is_some() || statements.peek().is_some() {
+            let (next_import_start, next_statement_start) = (
+                imports.peek().map_or(usize::max_value(), |x| x.loc().start),
+                statements.peek().map_or(usize::max_value(), |x| x.loc().start),
+            );
+            if next_statement_start < next_import_start {
+                let repl_range = statements.next().unwrap().loc_by_offset(offset);
+                offset -= repl_range.len() as isize;
+                content.splice(repl_range, std::iter::empty());
+            } else {
+                let import = imports.next().unwrap();
+                let import_path = self.resolve_import(target_dir, import.data().path())?;
+                let s = self.flatten_node(&import_path, graph, imported, true, true, true)?;
+
+                let import_content = s.as_bytes();
+                let import_content_len = import_content.len() as isize;
+                let import_range = import.loc_by_offset(offset);
+                offset += import_content_len - (import_range.len() as isize);
+                content.splice(import_range, import_content.iter().copied());
             }
         }
 
-        if strip_version_pragma {
-            if let Some(version) = target_node.version() {
-                let version_range = version.loc_by_offset(offset);
-                offset -= version_range.len() as isize;
-                content.splice(version_range, std::iter::empty());
-            }
-        }
-
-        if strip_experimental_pragma {
-            if let Some(experiment) = target_node.experimental() {
-                let experimental_pragma_range = experiment.loc_by_offset(offset);
-                offset -= experimental_pragma_range.len() as isize;
-                content.splice(experimental_pragma_range, std::iter::empty());
-            }
-        }
-
-        for import in imports.iter() {
-            let import_path = self.resolve_import(target_dir, import.data().path())?;
-            let s = self.flatten_node(&import_path, graph, imported, true, true, true)?;
-
-            let import_content = s.as_bytes();
-            let import_content_len = import_content.len() as isize;
-            let import_range = import.loc_by_offset(offset);
-            offset += import_content_len - (import_range.len() as isize);
-            content.splice(import_range, import_content.iter().copied());
-        }
-
         let result = String::from_utf8(content).map_err(|err| {
             SolcError::msg(format!("failed to convert extended bytes to string: {}", err))
         })?;


Test patch:
diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs
index 55529d51..be809dea 100644
--- a/ethers-solc/tests/project.rs
+++ b/ethers-solc/tests/project.rs
@@ -873,6 +873,71 @@ contract Contract is ParentContract,
     );
 }
 
+#[test]
+fn can_flatten_with_version_pragma_after_imports() {
+    let project = TempProject::dapptools().unwrap();
+
+    let f = project
+        .add_source(
+            "A",
+            r#"
+pragma solidity ^0.8.10;
+
+import * as B from "./B.sol";
+
+contract A { }
+"#,
+        )
+        .unwrap();
+
+    project
+        .add_source(
+            "B",
+            r#"
+import D from "./D.sol";
+pragma solidity ^0.8.10;
+import * as C from "./C.sol";
+contract B { }
+"#,
+        )
+        .unwrap();
+
+    project
+        .add_source(
+            "C",
+            r#"
+pragma solidity ^0.8.10;
+contract C { }
+"#,
+        )
+        .unwrap();
+
+    project
+        .add_source(
+            "D",
+            r#"
+pragma solidity ^0.8.10;
+contract D { }
+"#,
+        )
+        .unwrap();
+
+    let result = project.flatten(&f).unwrap();
+    assert_eq!(
+        result,
+        r#"pragma solidity ^0.8.10;
+
+contract D { }
+
+contract C { }
+
+contract B { }
+
+contract A { }
+"#
+    );
+}
+
 #[test]
 fn can_detect_type_error() {
     let project = TempProject::<ConfigurableArtifacts>::dapptools().unwrap();