Repository: gakonst/ethers-rs
Pull request #1661 (2022-09-04 00:21:39)

fix: incorrect encoding on TransactionReceipt
<!--
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
RLP encoding for `TransactionReceipt` is broken, since it directly encoded the `status` field, which is an optional.

## Solution
Use `rlp_opt` instead of `rlp_append` for the `status` field.

<!--
Summarize the solution and provide any necessary context needed to understand
the code change.
-->

## PR Checklist

- [x] Added Tests
- [ ] Added Documentation
- [x] Updated the changelog



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30ed61aa..fa26373d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Unreleased
 
+- Fix RLP encoding of `TransactionReceipt`
 - Add `Unit8` helper type [#1639](https://github.com/gakonst/ethers-rs/pull/1639)
 - Add `evm.deployedBytecode.immutableReferences` output selector [#1523](https://github.com/gakonst/ethers-rs/pull/1523)
 - Added `get_erc1155_token_transfer_events` function for etherscan client [#1503](https://github.com/gakonst/ethers-rs/pull/1503)
@@ -85,6 +86,7 @@
 - [#1535](https://github.com/gakonst/ethers-rs/pull/1535) Add support to Aurora and Aurora testnet networks.
 - [#1632](https://github.com/gakonst/ethers-rs/pull/1632) Re-export `H32` from `ethabi`.
 - [#1634](https://github.com/gakonst/ethers-rs/pull/1634) Derive missing `Clone`, `Copy` and `Debug` impls in ethers-etherscan.
+- Bytes debug format now displays hex literals [#1658](https://github.com/gakonst/ethers-rs/pull/1658)
 
 ## ethers-contract-abigen
 
diff --git a/ethers-core/src/types/bytes.rs b/ethers-core/src/types/bytes.rs
index a23f0521..4b1ffb64 100644
--- a/ethers-core/src/types/bytes.rs
+++ b/ethers-core/src/types/bytes.rs
@@ -10,7 +10,7 @@ use std::{
 use thiserror::Error;
 
 /// Wrapper type around Bytes to deserialize/serialize "0x" prefixed ethereum hex strings
-#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize, Ord, PartialOrd)]
+#[derive(Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize, Ord, PartialOrd)]
 pub struct Bytes(
     #[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")]
     pub  bytes::Bytes,
@@ -20,6 +20,12 @@ fn bytes_to_hex(b: &Bytes) -> String {
     hex::encode(b.0.as_ref())
 }
 
+impl Debug for Bytes {
+    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+        write!(f, "Bytes(0x{})", bytes_to_hex(self))
+    }
+}
+
 impl Display for Bytes {
     fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
         write!(f, "0x{}", bytes_to_hex(self))
@@ -157,7 +163,7 @@ impl FromStr for Bytes {
         if let Some(value) = value.strip_prefix("0x") {
             hex::decode(value)
         } else {
-            hex::decode(&value)
+            hex::decode(value)
         }
         .map(Into::into)
         .map_err(|e| ParseBytesError(format!("Invalid hex: {}", e)))
@@ -209,4 +215,11 @@ mod tests {
         let b = b.unwrap();
         assert_eq!(b.as_ref(), hex::decode("1213").unwrap());
     }
+
+    #[test]
+    fn test_debug_formatting() {
+        let b = Bytes::from(vec![1, 35, 69, 103, 137, 171, 205, 239]);
+        assert_eq!(format!("{:?}", b), "Bytes(0x0123456789abcdef)");
+        assert_eq!(format!("{:#?}", b), "Bytes(0x0123456789abcdef)");
+    }
 }
diff --git a/ethers-core/src/types/chain.rs b/ethers-core/src/types/chain.rs
index 88ff947b..b1be25e5 100644
--- a/ethers-core/src/types/chain.rs
+++ b/ethers-core/src/types/chain.rs
@@ -353,7 +353,7 @@ impl FromStr for Chain {
             "kovan" => Chain::Kovan,
             "xdai" => Chain::XDai,
             "polygon" => Chain::Polygon,
-            "polygon-mumbai" => Chain::PolygonMumbai,
+            "mumbai" | "polygon-mumbai" => Chain::PolygonMumbai,
             "avalanche" => Chain::Avalanche,
             "avalanche-fuji" => Chain::AvalancheFuji,
             "sepolia" => Chain::Sepolia,
diff --git a/ethers-core/src/types/transaction/eip2718.rs b/ethers-core/src/types/transaction/eip2718.rs
index df147ebf..62134380 100644
--- a/ethers-core/src/types/transaction/eip2718.rs
+++ b/ethers-core/src/types/transaction/eip2718.rs
@@ -311,7 +311,7 @@ impl TypedTransaction {
 
     /// Hashes the transaction's data with the included signature.
     pub fn hash(&self, signature: &Signature) -> H256 {
-        keccak256(&self.rlp_signed(signature).as_ref()).into()
+        keccak256(self.rlp_signed(signature).as_ref()).into()
     }
 
     /// Decodes a signed TypedTransaction from a rlp encoded byte stream
diff --git a/ethers-core/src/types/transaction/response.rs b/ethers-core/src/types/transaction/response.rs
index 0f76c138..b127bbef 100644
--- a/ethers-core/src/types/transaction/response.rs
+++ b/ethers-core/src/types/transaction/response.rs
@@ -21,18 +21,15 @@ pub struct Transaction {
     pub nonce: U256,
 
     /// Block hash. None when pending.
-    #[serde(rename = "blockHash")]
-    #[serde(skip_serializing_if = "Option::is_none")]
+    #[serde(default, rename = "blockHash")]
     pub block_hash: Option<H256>,
 
     /// Block number. None when pending.
-    #[serde(rename = "blockNumber")]
-    #[serde(skip_serializing_if = "Option::is_none")]
+    #[serde(default, rename = "blockNumber")]
     pub block_number: Option<U64>,
 
     /// Transaction Index. None when pending.
-    #[serde(rename = "transactionIndex")]
-    #[serde(skip_serializing_if = "Option::is_none")]
+    #[serde(default, rename = "transactionIndex")]
     pub transaction_index: Option<U64>,
 
     /// Sender
@@ -133,7 +130,7 @@ impl Transaction {
     }
 
     pub fn hash(&self) -> H256 {
-        keccak256(&self.rlp().as_ref()).into()
+        keccak256(self.rlp().as_ref()).into()
     }
 
     pub fn rlp(&self) -> Bytes {
@@ -428,7 +425,7 @@ pub struct TransactionReceipt {
 impl rlp::Encodable for TransactionReceipt {
     fn rlp_append(&self, s: &mut RlpStream) {
         s.begin_list(4);
-        s.append(&self.status);
+        rlp_opt(s, &self.status);
         s.append(&self.cumulative_gas_used);
         s.append(&self.logs_bloom);
         s.append_list(&self.logs);
@@ -460,6 +457,8 @@ impl PartialOrd<Self> for TransactionReceipt {
 #[cfg(test)]
 #[cfg(not(feature = "celo"))]
 mod tests {
+    use rlp::Encodable;
+
     use crate::types::transaction::eip2930::AccessListItem;
 
     use super::*;
@@ -910,6 +909,17 @@ mod tests {
         assert_eq!(v, receipt);
     }
 
+    #[test]
+    fn rlp_encode_receipt() {
+        let receipt = TransactionReceipt { status: Some(1u64.into()), ..Default::default() };
+        let encoded = receipt.rlp_bytes();
+
+        assert_eq!(
+            encoded,
+            hex::decode("f901060180b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0").unwrap(),
+        );
+    }
+
     #[test]
     fn can_sort_receipts() {
         let mut a = TransactionReceipt { block_number: Some(0u64.into()), ..Default::default() };
diff --git a/ethers-etherscan/src/source_tree.rs b/ethers-etherscan/src/source_tree.rs
index 015bb84a..4ec55ad7 100644
--- a/ethers-etherscan/src/source_tree.rs
+++ b/ethers-etherscan/src/source_tree.rs
@@ -19,7 +19,7 @@ impl SourceTree {
     /// Expand the source tree into the provided directory.  This method sanitizes paths to ensure
     /// that no directory traversal happens.
     pub fn write_to(&self, dir: &Path) -> Result<()> {
-        create_dir_all(&dir)?;
+        create_dir_all(dir)?;
         for entry in &self.entries {
             let mut sanitized_path = sanitize_path(&entry.path);
             if sanitized_path.extension().is_none() {
diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs
index 7d36041b..0aec2f7b 100644
--- a/ethers-providers/src/provider.rs
+++ b/ethers-providers/src/provider.rs
@@ -936,8 +936,7 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
             .map_err(|e| ProviderError::CustomError(format!("Invalid metadata url: {}", e)))?;
 
         if token.type_ == erc::ERCNFTType::ERC1155 {
-            metadata_url
-                .set_path(&metadata_url.path().replace("%7Bid%7D", &hex::encode(&token.id)));
+            metadata_url.set_path(&metadata_url.path().replace("%7Bid%7D", &hex::encode(token.id)));
         }
         if metadata_url.scheme() == "ipfs" {
             metadata_url = erc::http_link_ipfs(metadata_url).map_err(ProviderError::CustomError)?;
@@ -1265,7 +1264,7 @@ impl<P: JsonRpcClient> Provider<P> {
                 "`{}` resolver ({:?}) does not support selector {}.",
                 ens_name,
                 resolver_address,
-                hex::encode(&selector)
+                hex::encode(selector)
             )))
         }
 
diff --git a/ethers-signers/src/aws/mod.rs b/ethers-signers/src/aws/mod.rs
index d489bfe7..bfca3ec4 100644
--- a/ethers-signers/src/aws/mod.rs
+++ b/ethers-signers/src/aws/mod.rs
@@ -123,7 +123,7 @@ where
     resp
 }
 
-#[instrument(err, skip(kms, digest, key_id), fields(digest = %hex::encode(&digest), key_id = %key_id.as_ref()))]
+#[instrument(err, skip(kms, digest, key_id), fields(digest = %hex::encode(digest), key_id = %key_id.as_ref()))]
 async fn request_sign_digest<T>(
     kms: &KmsClient,
     key_id: T,
@@ -165,8 +165,8 @@ impl<'a> AwsSigner<'a> {
 
         debug!(
             "Instantiated AWS signer with pubkey 0x{} and address 0x{}",
-            hex::encode(&pubkey.to_bytes()),
-            hex::encode(&address)
+            hex::encode(pubkey.to_bytes()),
+            hex::encode(address)
         );
 
         Ok(Self { kms, chain_id, key_id: key_id.as_ref().to_owned(), pubkey, address })
@@ -204,7 +204,7 @@ impl<'a> AwsSigner<'a> {
 
     /// Sign a digest with this signer's key and add the eip155 `v` value
     /// corresponding to the input chain_id
-    #[instrument(err, skip(digest), fields(digest = %hex::encode(&digest)))]
+    #[instrument(err, skip(digest), fields(digest = %hex::encode(digest)))]
     async fn sign_digest_with_eip155(
         &self,
         digest: H256,
diff --git a/ethers-signers/src/ledger/app.rs b/ethers-signers/src/ledger/app.rs
index 59d7bef1..3db050f3 100644
--- a/ethers-signers/src/ledger/app.rs
+++ b/ethers-signers/src/ledger/app.rs
@@ -243,7 +243,7 @@ impl LedgerEthereum {
                 index |= 0x80000000;
             }
 
-            bytes.extend(&index.to_be_bytes());
+            bytes.extend(index.to_be_bytes());
         }
 
         bytes
diff --git a/ethers-solc/src/artifacts/mod.rs b/ethers-solc/src/artifacts/mod.rs
index 766b47c7..e132e32f 100644
--- a/ethers-solc/src/artifacts/mod.rs
+++ b/ethers-solc/src/artifacts/mod.rs
@@ -947,6 +947,8 @@ pub struct MetadataSettings {
     /// since metadata is per file
     #[serde(default)]
     pub libraries: BTreeMap<String, String>,
+    #[serde(rename = "viaIR")]
+    pub via_ir: bool,
 }
 
 /// Compilation source files/source units, keys are file names
diff --git a/ethers-solc/src/cache.rs b/ethers-solc/src/cache.rs
index 2329a5a5..4943b69f 100644
--- a/ethers-solc/src/cache.rs
+++ b/ethers-solc/src/cache.rs
@@ -607,7 +607,7 @@ impl<'a, T: ArtifactOutput> ArtifactsCacheInner<'a, T> {
             .collect();
 
         let entry = CacheEntry {
-            last_modification_date: CacheEntry::read_last_modification_date(&file)
+            last_modification_date: CacheEntry::read_last_modification_date(file)
                 .unwrap_or_default(),
             content_hash: source.content_hash(),
             source_name: utils::source_name(file, self.project.root()).into(),
@@ -721,7 +721,7 @@ impl<'a, T: ArtifactOutput> ArtifactsCacheInner<'a, T> {
     /// returns `false` if the corresponding cache entry remained unchanged otherwise `true`
     fn is_dirty(&self, file: &Path, version: &Version) -> bool {
         if let Some(hash) = self.content_hashes.get(file) {
-            if let Some(entry) = self.cache.entry(&file) {
+            if let Some(entry) = self.cache.entry(file) {
                 if entry.content_hash.as_bytes() != hash.as_bytes() {
                     tracing::trace!("changed content hash for source file \"{}\"", file.display());
                     return true
diff --git a/ethers-solc/src/compile/mod.rs b/ethers-solc/src/compile/mod.rs
index 46ebed6c..22ca3520 100644
--- a/ethers-solc/src/compile/mod.rs
+++ b/ethers-solc/src/compile/mod.rs
@@ -465,7 +465,7 @@ impl Solc {
         if checksum_calc == checksum_found {
             Ok(())
         } else {
-            let expected = hex::encode(&checksum_found);
+            let expected = hex::encode(checksum_found);
             let detected = hex::encode(checksum_calc);
             tracing:: warn!(target : "solc", "checksum mismatch for {:?}, expected {}, but found {} for file {:?}", version, expected, detected, version_path);
             Err(SolcError::ChecksumMismatch { version, expected, detected, file: version_path })
diff --git a/ethers-solc/src/config.rs b/ethers-solc/src/config.rs
index 41a52ad3..9195cd6a 100644
--- a/ethers-solc/src/config.rs
+++ b/ethers-solc/src/config.rs
@@ -923,13 +923,13 @@ mod tests {
         std::fs::File::create(&contracts).unwrap();
         assert_eq!(ProjectPathsConfig::find_source_dir(root), contracts,);
         assert_eq!(
-            ProjectPathsConfig::builder().build_with_root(&root).sources,
+            ProjectPathsConfig::builder().build_with_root(root).sources,
             utils::canonicalized(contracts),
         );
         std::fs::File::create(&src).unwrap();
         assert_eq!(ProjectPathsConfig::find_source_dir(root), src,);
         assert_eq!(
-            ProjectPathsConfig::builder().build_with_root(&root).sources,
+            ProjectPathsConfig::builder().build_with_root(root).sources,
             utils::canonicalized(src),
         );
 
@@ -937,18 +937,18 @@ mod tests {
         std::fs::File::create(&artifacts).unwrap();
         assert_eq!(ProjectPathsConfig::find_artifacts_dir(root), artifacts,);
         assert_eq!(
-            ProjectPathsConfig::builder().build_with_root(&root).artifacts,
+            ProjectPathsConfig::builder().build_with_root(root).artifacts,
             utils::canonicalized(artifacts),
         );
         assert_eq!(
-            ProjectPathsConfig::builder().build_with_root(&root).build_infos,
+            ProjectPathsConfig::builder().build_with_root(root).build_infos,
             utils::canonicalized(build_infos)
         );
 
         std::fs::File::create(&out).unwrap();
         assert_eq!(ProjectPathsConfig::find_artifacts_dir(root), out,);
         assert_eq!(
-            ProjectPathsConfig::builder().build_with_root(&root).artifacts,
+            ProjectPathsConfig::builder().build_with_root(root).artifacts,
             utils::canonicalized(out),
         );
 
@@ -956,13 +956,13 @@ mod tests {
         std::fs::File::create(&node_modules).unwrap();
         assert_eq!(ProjectPathsConfig::find_libs(root), vec![node_modules.clone()],);
         assert_eq!(
-            ProjectPathsConfig::builder().build_with_root(&root).libraries,
+            ProjectPathsConfig::builder().build_with_root(root).libraries,
             vec![utils::canonicalized(node_modules)],
         );
         std::fs::File::create(&lib).unwrap();
         assert_eq!(ProjectPathsConfig::find_libs(root), vec![lib.clone()],);
         assert_eq!(
-            ProjectPathsConfig::builder().build_with_root(&root).libraries,
+            ProjectPathsConfig::builder().build_with_root(root).libraries,
             vec![utils::canonicalized(lib)],
         );
     }
@@ -975,7 +975,7 @@ mod tests {
 
         // Set the artifacts directory without setting the
         // build info directory
-        let project = ProjectPathsConfig::builder().artifacts(&artifacts).build_with_root(&root);
+        let project = ProjectPathsConfig::builder().artifacts(&artifacts).build_with_root(root);
 
         // The artifacts should be set correctly based on the configured value
         assert_eq!(project.artifacts, utils::canonicalized(artifacts));
diff --git a/ethers-solc/src/utils.rs b/ethers-solc/src/utils.rs
index 89636366..ce3d1c26 100644
--- a/ethers-solc/src/utils.rs
+++ b/ethers-solc/src/utils.rs
@@ -564,7 +564,7 @@ pragma solidity ^0.8.0;
         let a = Path::new("/foo/bar/bar/test.txt");
         let b = Path::new("/foo/bar/foo/example/constract.sol");
         let expected = Path::new("/foo/bar");
-        assert_eq!(common_ancestor(&a, &b).unwrap(), expected.to_path_buf())
+        assert_eq!(common_ancestor(a, b).unwrap(), expected.to_path_buf())
     }
 
     #[test]


Test patch:
diff --git a/ethers-contract/tests/it/common/derive.rs b/ethers-contract/tests/it/common/derive.rs
index 6aad8e1b..aeaff216 100644
--- a/ethers-contract/tests/it/common/derive.rs
+++ b/ethers-contract/tests/it/common/derive.rs
@@ -387,9 +387,9 @@ fn eth_display_works() {
 
     let val = format!(
         "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, 50, 100, 0x{}, {}, 0x{}, {:?}, 0x{}",
-        hex::encode(&item.h),
+        hex::encode(item.h),
         item.i,
-        hex::encode(&item.arr_u8),
+        hex::encode(item.arr_u8),
         item.arr_u16,
         hex::encode(&item.v),
     );
diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs
index 41fcb6f6..fa7de6d9 100644
--- a/ethers-solc/tests/project.rs
+++ b/ethers-solc/tests/project.rs
@@ -385,7 +385,7 @@ fn can_compile_dapp_sample_with_cache() {
     assert!(compiled.is_unchanged());
 
     // deleted artifacts cause recompile even with cache
-    std::fs::remove_dir_all(&project.artifacts_path()).unwrap();
+    std::fs::remove_dir_all(project.artifacts_path()).unwrap();
     let compiled = project.compile().unwrap();
     assert!(compiled.find_first("Dapp").is_some());
     assert!(!compiled.is_unchanged());