Repository: gakonst/ethers-rs
Pull request #1366 (2022-06-10 03:14:41)

chore(deps): bump semver from 1.0.9 to 1.0.10
Bumps [semver](https://github.com/dtolnay/semver) from 1.0.9 to 1.0.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/dtolnay/semver/releases">semver's releases</a>.</em></p>
<blockquote>
<h2>1.0.10</h2>
<ul>
<li>Work around rustdoc issue affecting the <code>Prerelease</code> and <code>BuildMetadata</code> docs (<a href="https://github-redirect.dependabot.com/dtolnay/semver/issues/281">#281</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/dtolnay/semver/commit/79e8f45ba989057fadfb5b2682e8e234c3127ade"><code>79e8f45</code></a> Release 1.0.10</li>
<li><a href="https://github.com/dtolnay/semver/commit/532f4257623094d6beea89d628c5cabb08e92d2b"><code>532f425</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/dtolnay/semver/issues/281">#281</a> from dtolnay/empty</li>
<li><a href="https://github.com/dtolnay/semver/commit/b351c2b41859af1881bdb29d87812dd8922d7faa"><code>b351c2b</code></a> Work around rustdoc showing private internals of associated const</li>
<li><a href="https://github.com/dtolnay/semver/commit/7a3e8f7239af4c955a4eaabf130e845ae8eeb74b"><code>7a3e8f7</code></a> Resolve get_first clippy lint</li>
<li><a href="https://github.com/dtolnay/semver/commit/eed5a41fbf3090d4f9ad20fb48a89bec0e9d80d7"><code>eed5a41</code></a> Check for outdated deps in fuzz target</li>
<li><a href="https://github.com/dtolnay/semver/commit/0eaad9d891326cb936de6512d6545ad612e59b43"><code>0eaad9d</code></a> Check all crates in workspace for outdated deps</li>
<li><a href="https://github.com/dtolnay/semver/commit/0080ed9bb561a9cb603f86aff5fe4ea8ce738814"><code>0080ed9</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/dtolnay/semver/issues/277">#277</a> from RalfJung/miri</li>
<li><a href="https://github.com/dtolnay/semver/commit/3888c08895dce46709bdd00e39bb1995b0578cb2"><code>3888c08</code></a> make Miri even more strict</li>
<li>See full diff in <a href="https://github.com/dtolnay/semver/compare/1.0.9...1.0.10">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=semver&package-manager=cargo&previous-version=1.0.9&new-version=1.0.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0dab0e4..2d06ca4d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -99,6 +99,8 @@
 
 ### Unreleased
 
+- Add `rawMetadata:String` field to configurable contract output 
+  [#1365](https://github.com/gakonst/ethers-rs/pull/1365)
 - Use relative source paths and `solc --base-path`
   [#1317](https://github.com/gakonst/ethers-rs/pull/1317)
 - Save cache entry objects with relative paths
diff --git a/Cargo.lock b/Cargo.lock
index 7b1c0c03..49fb4e00 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3317,9 +3317,9 @@ dependencies = [
 
 [[package]]
 name = "semver"
-version = "1.0.9"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
+checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c"
 dependencies = [
  "serde",
 ]
@@ -3573,9 +3573,9 @@ dependencies = [
 
 [[package]]
 name = "solang-parser"
-version = "0.1.13"
+version = "0.1.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "395b6e1ec5af117bd08f963c7cd80f8efd4eed51c5a332aed42b13e3f9bc860b"
+checksum = "4a62cd1bd34217d4ac27aa4fad0e868983583a0c54cacc3a8590ea7029b50c2b"
 dependencies = [
  "itertools",
  "lalrpop",
diff --git a/ethers-core/src/types/block.rs b/ethers-core/src/types/block.rs
index c516318c..cffee94a 100644
--- a/ethers-core/src/types/block.rs
+++ b/ethers-core/src/types/block.rs
@@ -533,6 +533,26 @@ impl BlockNumber {
             _ => None,
         }
     }
+
+    /// Returns `true` if a numeric block number is set
+    pub fn is_number(&self) -> bool {
+        matches!(self, BlockNumber::Number(_))
+    }
+
+    /// Returns `true` if it's "latest"
+    pub fn is_latest(&self) -> bool {
+        matches!(self, BlockNumber::Latest)
+    }
+
+    /// Returns `true` if it's "pending"
+    pub fn is_pending(&self) -> bool {
+        matches!(self, BlockNumber::Pending)
+    }
+
+    /// Returns `true` if it's "earliest"
+    pub fn is_earliest(&self) -> bool {
+        matches!(self, BlockNumber::Earliest)
+    }
 }
 
 impl<T: Into<U64>> From<T> for BlockNumber {
@@ -590,6 +610,12 @@ impl fmt::Display for BlockNumber {
     }
 }
 
+impl Default for BlockNumber {
+    fn default() -> Self {
+        BlockNumber::Latest
+    }
+}
+
 #[cfg(test)]
 #[cfg(not(feature = "celo"))]
 mod tests {
diff --git a/ethers-etherscan/Cargo.toml b/ethers-etherscan/Cargo.toml
index 26fca83b..bc98b6b7 100644
--- a/ethers-etherscan/Cargo.toml
+++ b/ethers-etherscan/Cargo.toml
@@ -21,7 +21,7 @@ serde_json = { version = "1.0.64", default-features = false }
 serde-aux = { version = "3.0.1", default-features = false }
 thiserror = "1.0"
 tracing = "0.1.35"
-semver = "1.0.9"
+semver = "1.0.10"
 
 [dev-dependencies]
 tempfile = "3.3.0"
diff --git a/ethers-signers/Cargo.toml b/ethers-signers/Cargo.toml
index 74268b4c..5aa9bd50 100644
--- a/ethers-signers/Cargo.toml
+++ b/ethers-signers/Cargo.toml
@@ -27,7 +27,7 @@ rand = { version = "0.8.5", default-features = false }
 yubihsm = { version = "0.40.0", features = ["secp256k1", "http", "usb"], optional = true }
 futures-util = { version = "^0.3", optional = true }
 futures-executor = { version = "^0.3", optional = true }
-semver = { version =  "1.0.9", optional = true }
+semver = { version =  "1.0.10", optional = true }
 trezor-client = { version = "0.0.5", optional = true, default-features = false, features = ["f_ethereum"] }
 
 # aws
diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml
index 42455906..47a2bc7f 100644
--- a/ethers-solc/Cargo.toml
+++ b/ethers-solc/Cargo.toml
@@ -17,7 +17,7 @@ keywords = ["ethereum", "web3", "solc", "solidity", "ethers"]
 ethers-core = { version = "^0.6.0", path = "../ethers-core", default-features = false }
 serde_json = "1.0.68"
 serde = { version = "1.0.130", features = ["derive"] }
-semver = { version = "1.0.9", features = ["serde"] }
+semver = { version = "1.0.10", features = ["serde"] }
 walkdir = "2.3.2"
 tokio = { version = "1.18", default-features = false, features = ["rt"] }
 futures-util = { version = "^0.3", optional = true }
@@ -35,7 +35,7 @@ tempfile = { version = "3.3.0", optional = true }
 fs_extra = { version = "1.2.0", optional = true }
 sha2 = { version = "0.9.8", default-features = false, optional = true }
 dunce = "1.0.2"
-solang-parser = { default-features = false, version = "=0.1.13" }
+solang-parser = { default-features = false, version = "=0.1.14" }
 rayon = "1.5.3"
 rand = { version = "0.8.5", optional = true }
 path-slash = "0.1.4"
diff --git a/ethers-solc/src/artifact_output/configurable.rs b/ethers-solc/src/artifact_output/configurable.rs
index 28120d7a..a964b20e 100644
--- a/ethers-solc/src/artifact_output/configurable.rs
+++ b/ethers-solc/src/artifact_output/configurable.rs
@@ -9,7 +9,8 @@ use crate::{
             EwasmOutputSelection,
         },
         Ast, CompactContractBytecodeCow, DevDoc, Evm, Ewasm, FunctionDebugData, GasEstimates,
-        GeneratedSource, LosslessAbi, Metadata, Offsets, Settings, StorageLayout, UserDoc,
+        GeneratedSource, LosslessAbi, LosslessMetadata, Metadata, Offsets, Settings, StorageLayout,
+        UserDoc,
     },
     sources::VersionedSourceFile,
     ArtifactOutput, SolcConfig, SolcError, SourceFile,
@@ -41,6 +42,8 @@ pub struct ConfigurableContractArtifact {
     #[serde(default, skip_serializing_if = "Option::is_none")]
     pub gas_estimates: Option<GasEstimates>,
     #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub raw_metadata: Option<String>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
     pub metadata: Option<Metadata>,
     #[serde(default, skip_serializing_if = "Option::is_none")]
     pub storage_layout: Option<StorageLayout>,
@@ -232,6 +235,7 @@ impl ArtifactOutput for ConfigurableArtifacts {
     ) -> Self::Artifact {
         let mut artifact_userdoc = None;
         let mut artifact_devdoc = None;
+        let mut artifact_raw_metadata = None;
         let mut artifact_metadata = None;
         let mut artifact_ir = None;
         let mut artifact_ir_optimized = None;
@@ -258,7 +262,10 @@ impl ArtifactOutput for ConfigurableArtifacts {
         } = contract;
 
         if self.additional_values.metadata {
-            artifact_metadata = metadata;
+            if let Some(LosslessMetadata { raw_metadata, metadata }) = metadata {
+                artifact_raw_metadata = Some(raw_metadata);
+                artifact_metadata = Some(metadata);
+            }
         }
         if self.additional_values.userdoc {
             artifact_userdoc = Some(userdoc);
@@ -318,6 +325,7 @@ impl ArtifactOutput for ConfigurableArtifacts {
             function_debug_data: artifact_function_debug_data,
             method_identifiers: artifact_method_identifiers,
             gas_estimates: artifact_gas_estimates,
+            raw_metadata: artifact_raw_metadata,
             metadata: artifact_metadata,
             storage_layout: artifact_storage_layout,
             userdoc: artifact_userdoc,
@@ -564,7 +572,7 @@ impl ExtraOutputFiles {
         if self.metadata {
             if let Some(ref metadata) = contract.metadata {
                 let file = file.with_extension("metadata.json");
-                fs::write(&file, serde_json::to_string_pretty(metadata)?)
+                fs::write(&file, serde_json::to_string_pretty(&metadata.raw_json()?)?)
                     .map_err(|err| SolcError::io(err, file))?
             }
         }
diff --git a/ethers-solc/src/artifacts/contract.rs b/ethers-solc/src/artifacts/contract.rs
index 2779e63e..f3a33434 100644
--- a/ethers-solc/src/artifacts/contract.rs
+++ b/ethers-solc/src/artifacts/contract.rs
@@ -4,7 +4,8 @@ use crate::artifacts::{
     bytecode::{
         Bytecode, BytecodeObject, CompactBytecode, CompactDeployedBytecode, DeployedBytecode,
     },
-    serde_helpers, DevDoc, Evm, Ewasm, LosslessAbi, Metadata, Offsets, StorageLayout, UserDoc,
+    serde_helpers, DevDoc, Evm, Ewasm, LosslessAbi, LosslessMetadata, Offsets, StorageLayout,
+    UserDoc,
 };
 use ethers_core::{abi::Contract as Abi, types::Bytes};
 use serde::{Deserialize, Serialize};
@@ -22,7 +23,7 @@ pub struct Contract {
         skip_serializing_if = "Option::is_none",
         with = "serde_helpers::json_string_opt"
     )]
-    pub metadata: Option<Metadata>,
+    pub metadata: Option<LosslessMetadata>,
     #[serde(default)]
     pub userdoc: UserDoc,
     #[serde(default)]
diff --git a/ethers-solc/src/artifacts/mod.rs b/ethers-solc/src/artifacts/mod.rs
index acc9229e..7e436b0c 100644
--- a/ethers-solc/src/artifacts/mod.rs
+++ b/ethers-solc/src/artifacts/mod.rs
@@ -856,6 +856,61 @@ pub struct Metadata {
     pub version: i64,
 }
 
+/// A helper type that ensures lossless (de)serialisation so we can preserve the exact String
+/// metadata value that's being hashed by solc
+#[derive(Clone, Debug, PartialEq)]
+pub struct LosslessMetadata {
+    /// The complete abi as json value
+    pub raw_metadata: String,
+    /// The deserialised metadata of `raw_metadata`
+    pub metadata: Metadata,
+}
+
+// === impl LosslessMetadata ===
+
+impl LosslessMetadata {
+    /// Returns the whole string raw metadata as `serde_json::Value`
+    pub fn raw_json(&self) -> serde_json::Result<serde_json::Value> {
+        serde_json::from_str(&self.raw_metadata)
+    }
+}
+
+impl Serialize for LosslessMetadata {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        self.raw_metadata.serialize(serializer)
+    }
+}
+
+impl<'de> Deserialize<'de> for LosslessMetadata {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        struct LosslessMetadataVisitor;
+
+        impl<'de> Visitor<'de> for LosslessMetadataVisitor {
+            type Value = LosslessMetadata;
+
+            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+                write!(formatter, "metadata string")
+            }
+
+            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
+            where
+                E: serde::de::Error,
+            {
+                let raw_metadata = value.to_string();
+                let metadata = serde_json::from_str(value).map_err(serde::de::Error::custom)?;
+                Ok(LosslessMetadata { raw_metadata, metadata })
+            }
+        }
+        deserializer.deserialize_str(LosslessMetadataVisitor)
+    }
+}
+
 /// Compiler settings
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct MetadataSettings {
diff --git a/ethers-solc/src/artifacts/serde_helpers.rs b/ethers-solc/src/artifacts/serde_helpers.rs
index 7e664df8..d935e1a9 100644
--- a/ethers-solc/src/artifacts/serde_helpers.rs
+++ b/ethers-solc/src/artifacts/serde_helpers.rs
@@ -66,8 +66,8 @@ pub mod json_string_opt {
             if s.is_empty() {
                 return Ok(None)
             }
-
-            serde_json::from_str(&s).map_err(de::Error::custom).map(Some)
+            let value = serde_json::Value::String(s);
+            serde_json::from_value(value).map_err(de::Error::custom).map(Some)
         } else {
             Ok(None)
         }


Test patch:
diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs
index 4600d09a..30309cc5 100644
--- a/ethers-solc/tests/project.rs
+++ b/ethers-solc/tests/project.rs
@@ -148,6 +148,7 @@ fn can_compile_configured() {
     let compiled = project.compile().unwrap();
     let artifact = compiled.find("Dapp").unwrap();
     assert!(artifact.metadata.is_some());
+    assert!(artifact.raw_metadata.is_some());
     assert!(artifact.ir.is_some());
     assert!(artifact.ir_optimized.is_some());
 }