Repository: gakonst/ethers-rs
Pull request #848 (2022-01-31 15:45:53)

add eth_syncing RPC
Add `eth_syncing` endpoint: https://eth.wiki/json-rpc/API#eth_syncing

Tested it with infure and return `false` successfully, don't have any syncing node to check return when it is syncing.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a92aa447..c2b6255e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Unreleased
 
+- Add `eth_syncing` [848](https://github.com/gakonst/ethers-rs/pull/848)
 - Fix overflow and possible divide-by-zero in `estimate_priority_fee`
 - Add BSC mainnet and testnet to the list of known chains
   [831](https://github.com/gakonst/ethers-rs/pull/831)
@@ -182,6 +183,7 @@
   [#568](https://github.com/gakonst/ethers-rs/pull/568)
 - Removes GasNow as a gas price oracle
   [#508](https://github.com/gakonst/ethers-rs/pull/508)
+- add initialize_nonce public function to initialize NonceMiddleManager
 
 ### 0.5.3
 
diff --git a/ethers-middleware/src/nonce_manager.rs b/ethers-middleware/src/nonce_manager.rs
index 1ff15405..f9e221a0 100644
--- a/ethers-middleware/src/nonce_manager.rs
+++ b/ethers-middleware/src/nonce_manager.rs
@@ -30,6 +30,24 @@ where
         nonce.into()
     }
 
+    pub async fn initialize_nonce(
+        &self,
+        block: Option<BlockId>,
+    ) -> Result<U256, NonceManagerError<M>> {
+        // initialize the nonce the first time the manager is called
+        if !self.initialized.load(Ordering::SeqCst) {
+            let nonce = self
+                .inner
+                .get_transaction_count(self.address, block)
+                .await
+                .map_err(FromErr::from)?;
+            self.nonce.store(nonce.as_u64(), Ordering::SeqCst);
+            self.initialized.store(true, Ordering::SeqCst);
+        }
+        // return current nonce
+        Ok(self.nonce.load(Ordering::SeqCst).into())
+    }
+
     async fn get_transaction_count_with_manager(
         &self,
         block: Option<BlockId>,
diff --git a/ethers-providers/src/lib.rs b/ethers-providers/src/lib.rs
index 8d932317..632d1a43 100644
--- a/ethers-providers/src/lib.rs
+++ b/ethers-providers/src/lib.rs
@@ -79,6 +79,15 @@ where
     }
 }
 
+/// Structure used in eth_syncing RPC
+#[derive(Clone, Debug)]
+pub enum SyncingStatus {
+    /// When client is synced to highest block, eth_syncing with return string "false"
+    IsFalse,
+    /// When client is still syncing past blocks we get IsSyncing information.
+    IsSyncing { starting_block: U256, current_block: U256, highest_block: U256 },
+}
+
 /// A middleware allows customizing requests send and received from an ethereum node.
 ///
 /// Writing a middleware is as simple as:
@@ -303,6 +312,10 @@ pub trait Middleware: Sync + Send + Debug {
         self.inner().call(tx, block).await.map_err(FromErr::from)
     }
 
+    async fn syncing(&self) -> Result<SyncingStatus, Self::Error> {
+        self.inner().syncing().await.map_err(FromErr::from)
+    }
+
     async fn get_chainid(&self) -> Result<U256, Self::Error> {
         self.inner().get_chainid().await.map_err(FromErr::from)
     }
diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs
index d5a9b23c..15eae5fa 100644
--- a/ethers-providers/src/provider.rs
+++ b/ethers-providers/src/provider.rs
@@ -3,7 +3,7 @@ use crate::{
     pubsub::{PubsubClient, SubscriptionStream},
     stream::{FilterWatcher, DEFAULT_POLL_INTERVAL},
     FromErr, Http as HttpProvider, JsonRpcClient, JsonRpcClientWrapper, MockProvider,
-    PendingTransaction, QuorumProvider,
+    PendingTransaction, QuorumProvider, SyncingStatus,
 };
 
 #[cfg(feature = "celo")]
@@ -23,7 +23,7 @@ use ethers_core::{
     utils,
 };
 use hex::FromHex;
-use serde::{de::DeserializeOwned, Serialize};
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use thiserror::Error;
 use url::{ParseError, Url};
 
@@ -497,6 +497,30 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
         self.request("eth_chainId", ()).await
     }
 
+    /// Return current client syncing status. If IsFalse sync is over.
+    async fn syncing(&self) -> Result<SyncingStatus, Self::Error> {
+        #[derive(Debug, Serialize, Deserialize)]
+        #[serde(untagged)]
+        pub enum SyncingStatusIntermediate {
+            /// When client is synced to highest block, eth_syncing with return string "false"
+            IsFalse(bool),
+            /// When client is still syncing past blocks we get IsSyncing information.
+            IsSyncing { starting_block: U256, current_block: U256, highest_block: U256 },
+        }
+        let intermediate: SyncingStatusIntermediate = self.request("eth_syncing", ()).await?;
+        match intermediate {
+            SyncingStatusIntermediate::IsFalse(false) => Ok(SyncingStatus::IsFalse),
+            SyncingStatusIntermediate::IsFalse(true) => Err(ProviderError::CustomError(
+                "eth_syncing returned `true` that is undefined value.".to_owned(),
+            )),
+            SyncingStatusIntermediate::IsSyncing {
+                starting_block,
+                current_block,
+                highest_block,
+            } => Ok(SyncingStatus::IsSyncing { starting_block, current_block, highest_block }),
+        }
+    }
+
     /// Returns the network version.
     async fn get_net_version(&self) -> Result<U64, ProviderError> {
         self.request("net_version", ()).await
diff --git a/ethers-solc/src/artifacts.rs b/ethers-solc/src/artifacts.rs
index 8a154da9..3df38461 100644
--- a/ethers-solc/src/artifacts.rs
+++ b/ethers-solc/src/artifacts.rs
@@ -752,6 +752,7 @@ impl<'a> fmt::Display for OutputDiagnostics<'a> {
 
 /// Represents a compiled solidity contract
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
+#[serde(rename_all = "camelCase")]
 pub struct Contract {
     /// The Ethereum Contract Metadata.
     /// See https://docs.soliditylang.org/en/develop/metadata.html
@@ -764,7 +765,7 @@ pub struct Contract {
     pub devdoc: DevDoc,
     #[serde(default, skip_serializing_if = "Option::is_none")]
     pub ir: Option<String>,
-    #[serde(default, rename = "storageLayout", skip_serializing_if = "StorageLayout::is_empty")]
+    #[serde(default, skip_serializing_if = "StorageLayout::is_empty")]
     pub storage_layout: StorageLayout,
     /// EVM-related outputs
     #[serde(default, skip_serializing_if = "Option::is_none")]
@@ -772,6 +773,8 @@ pub struct Contract {
     /// Ewasm related outputs
     #[serde(default, skip_serializing_if = "Option::is_none")]
     pub ewasm: Option<Ewasm>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub ir_optimized: Option<String>,
 }
 
 /// Minimal representation of a contract with a present abi and bytecode.
@@ -1730,7 +1733,7 @@ pub struct Error {
     #[serde(default, skip_serializing_if = "Option::is_none")]
     pub source_location: Option<SourceLocation>,
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
-    pub secondary_source_locations: Vec<SourceLocation>,
+    pub secondary_source_locations: Vec<SecondarySourceLocation>,
     pub r#type: String,
     pub component: String,
     pub severity: Severity,
@@ -1842,6 +1845,13 @@ pub struct SourceLocation {
     pub file: String,
     pub start: i32,
     pub end: i32,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
+pub struct SecondarySourceLocation {
+    pub file: Option<String>,
+    pub start: Option<i32>,
+    pub end: Option<i32>,
     pub message: Option<String>,
 }
 
diff --git a/ethers-solc/src/config.rs b/ethers-solc/src/config.rs
index 93cad0de..80208792 100644
--- a/ethers-solc/src/config.rs
+++ b/ethers-solc/src/config.rs
@@ -653,6 +653,29 @@ impl ArtifactOutput for MinimalCombinedArtifacts {
                         ))
                     })?;
                 }
+
+                if let Some(iropt) = &contract.ir_optimized {
+                    fs::write(&file.with_extension("iropt"), iropt)
+                        .map_err(|err| SolcError::io(err, file.with_extension("iropt")))?
+                }
+
+                if let Some(ir) = &contract.ir {
+                    fs::write(&file.with_extension("ir"), ir)
+                        .map_err(|err| SolcError::io(err, file.with_extension("ir")))?
+                }
+
+                if let Some(ewasm) = &contract.ewasm {
+                    fs::write(&file.with_extension("ewasm"), serde_json::to_vec_pretty(&ewasm)?)
+                        .map_err(|err| SolcError::io(err, file.with_extension("ewasm")))?;
+                }
+
+                if let Some(evm) = &contract.evm {
+                    if let Some(asm) = &evm.assembly {
+                        fs::write(&file.with_extension("asm"), asm)
+                            .map_err(|err| SolcError::io(err, file.with_extension("asm")))?
+                    }
+                }
+
                 let min = CompactContractBytecode::from(contract.clone());
                 fs::write(&file, serde_json::to_vec_pretty(&min)?)
                     .map_err(|err| SolcError::io(err, file))?


Test patch:
diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs
index f7b362d7..e095b01e 100644
--- a/ethers-solc/tests/project.rs
+++ b/ethers-solc/tests/project.rs
@@ -378,3 +378,26 @@ fn can_flatten_file_with_duplicates() {
     assert_eq!(result.matches("contract FooBar {").count(), 1);
     assert_eq!(result.matches(";").count(), 1);
 }
+
+#[test]
+fn can_detect_type_error() {
+    let project = TempProject::<MinimalCombinedArtifacts>::dapptools().unwrap();
+
+    project
+        .add_source(
+            "Contract",
+            r#"
+    pragma solidity ^0.8.10;
+
+   contract Contract {
+        function xyz() public {
+            require(address(0), "Error");
+        }
+   }
+   "#,
+        )
+        .unwrap();
+
+    let compiled = project.compile().unwrap();
+    assert!(compiled.has_compiler_errors());
+}