Repository: gakonst/ethers-rs
Pull request #1059 (2022-03-17 21:35:42)

feat(codec): add hex encode and decode functions
<!--
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
Add codec helper functions to encode/decode hex
<!--
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
- [ ] Added Documentation
- [ ] Updated the changelog



Patch:
diff --git a/ethers-core/src/abi/codec.rs b/ethers-core/src/abi/codec.rs
index fef30f18..39cf4031 100644
--- a/ethers-core/src/abi/codec.rs
+++ b/ethers-core/src/abi/codec.rs
@@ -9,12 +9,28 @@ use crate::{
 pub trait AbiEncode {
     /// ABI encode the type
     fn encode(self) -> Vec<u8>;
+
+    /// Returns the encoded value as hex string, _with_ a `0x` prefix
+    fn encode_hex(self) -> String
+    where
+        Self: Sized,
+    {
+        format!("0x{}", hex::encode(self.encode()))
+    }
 }
 
 /// Trait for ABI decoding
 pub trait AbiDecode: Sized {
     /// Decodes the ABI encoded data
     fn decode(bytes: impl AsRef<[u8]>) -> Result<Self, AbiError>;
+
+    /// Decode hex encoded ABI encoded data
+    ///
+    /// Expects a hex encoded string, with optional `0x` prefix
+    fn decode_hex(data: impl AsRef<str>) -> Result<Self, AbiError> {
+        let bytes: Bytes = data.as_ref().parse()?;
+        Self::decode(bytes)
+    }
 }
 
 macro_rules! impl_abi_codec {
diff --git a/ethers-core/src/abi/error.rs b/ethers-core/src/abi/error.rs
index d9a1ae98..48a3dcd2 100644
--- a/ethers-core/src/abi/error.rs
+++ b/ethers-core/src/abi/error.rs
@@ -24,6 +24,7 @@ pub(crate) use _format_err as format_err;
 macro_rules! _bail {
     ($($tt:tt)*) => { return Err($crate::abi::error::format_err!($($tt)*)) };
 }
+use crate::types::ParseBytesError;
 pub(crate) use _bail as bail;
 
 /// ABI codec related errors
@@ -39,4 +40,7 @@ pub enum AbiError {
 
     #[error("missing or wrong function selector")]
     WrongSelector,
+
+    #[error(transparent)]
+    ParseBytesError(#[from] ParseBytesError),
 }
diff --git a/ethers-core/src/macros/ethers_crate.rs b/ethers-core/src/macros/ethers_crate.rs
index 70497494..bdc3be12 100644
--- a/ethers-core/src/macros/ethers_crate.rs
+++ b/ethers-core/src/macros/ethers_crate.rs
@@ -69,9 +69,18 @@ pub fn determine_ethers_crates() -> (&'static str, &'static str, &'static str) {
         .and_then(|metadata| {
             metadata.root_package().and_then(|pkg| {
                 let sub_crates = Some(("ethers_core", "ethers_contract", "ethers_providers"));
-                if pkg.name == "ethers-contract" {
-                    // Note(mattsse): this is super hacky but required in order to compile the tests
-                    // in the `ethers-contract` crate
+
+                // Note(mattsse): this is super hacky but required in order to compile and test
+                // ethers' internal crates
+                if [
+                    "ethers-contract",
+                    "ethers-derive-eip712",
+                    "ethers-signers",
+                    "ethers-middleware",
+                    "ethers-solc",
+                ]
+                .contains(&pkg.name.as_str())
+                {
                     return sub_crates
                 }
 
diff --git a/ethers-core/src/types/mod.rs b/ethers-core/src/types/mod.rs
index 6e821163..5083fbb4 100644
--- a/ethers-core/src/types/mod.rs
+++ b/ethers-core/src/types/mod.rs
@@ -25,7 +25,7 @@ mod i256;
 pub use i256::{Sign, I256};
 
 mod bytes;
-pub use self::bytes::Bytes;
+pub use self::bytes::{Bytes, ParseBytesError};
 
 mod block;
 pub use block::{Block, BlockId, BlockNumber};
diff --git a/ethers-etherscan/src/account.rs b/ethers-etherscan/src/account.rs
index 5aa9932a..47cecfd9 100644
--- a/ethers-etherscan/src/account.rs
+++ b/ethers-etherscan/src/account.rs
@@ -601,7 +601,7 @@ mod tests {
 
             let balances = client
                 .get_ether_balance_multi(
-                    &vec![&"0x58eB28A67731c570Ef827C365c89B5751F9E6b0a".parse().unwrap()],
+                    &[&"0x58eB28A67731c570Ef827C365c89B5751F9E6b0a".parse().unwrap()],
                     None,
                 )
                 .await;
diff --git a/ethers-etherscan/src/source_tree.rs b/ethers-etherscan/src/source_tree.rs
index 5e0ef18a..49becbe2 100644
--- a/ethers-etherscan/src/source_tree.rs
+++ b/ethers-etherscan/src/source_tree.rs
@@ -57,7 +57,7 @@ mod tests {
                 },
             ],
         };
-        st.write_to(&tempdir.path()).unwrap();
+        st.write_to(tempdir.path()).unwrap();
         let written_paths = read_dir(tempdir.path()).unwrap();
         let paths: Vec<PathBuf> =
             written_paths.into_iter().filter_map(|x| x.ok()).map(|x| x.path()).collect();
@@ -83,7 +83,7 @@ mod tests {
                 },
             ],
         };
-        st.write_to(&tempdir.path()).unwrap();
+        st.write_to(tempdir.path()).unwrap();
         let written_paths = read_dir(tempdir.path()).unwrap();
         let paths: Vec<PathBuf> =
             written_paths.into_iter().filter_map(|x| x.ok()).map(|x| x.path()).collect();
diff --git a/examples/watch_blocks.rs b/examples/watch_blocks.rs
index 7e075857..53895540 100644
--- a/examples/watch_blocks.rs
+++ b/examples/watch_blocks.rs
@@ -8,7 +8,8 @@ async fn main() -> eyre::Result<()> {
     let provider = Provider::new(ws).interval(Duration::from_millis(2000));
     let mut stream = provider.watch_blocks().await?.take(5);
     while let Some(block) = stream.next().await {
-        dbg!(block);
+        let block = provider.get_block(block).await?.unwrap();
+        println!("Ts: {:?}, block number: {} -> {:?}",block.timestamp,block.number.unwrap(),block.hash.unwrap());
     }
 
     Ok(())


Test patch:
diff --git a/ethers-contract/tests/abigen.rs b/ethers-contract/tests/abigen.rs
index 80f5616c..9017633d 100644
--- a/ethers-contract/tests/abigen.rs
+++ b/ethers-contract/tests/abigen.rs
@@ -382,7 +382,7 @@ fn can_handle_underscore_numeric() {
             _100pct(string)
         ]"#
     );
-    let call = _100PctCall("message".to_string());
+    let _call = _100PctCall("message".to_string());
 
     let provider = Arc::new(Provider::new(MockProvider::new()));
     let contract = Test::new(Address::default(), Arc::clone(&provider));
diff --git a/ethers-providers/tests/provider.rs b/ethers-providers/tests/provider.rs
index 54129843..a4123055 100644
--- a/ethers-providers/tests/provider.rs
+++ b/ethers-providers/tests/provider.rs
@@ -1,5 +1,5 @@
 #![cfg(not(target_arch = "wasm32"))]
-use ethers_providers::{Http, Middleware, Provider, RINKEBY};
+use ethers_providers::{Http, Middleware, Provider};
 use std::{convert::TryFrom, time::Duration};
 
 #[cfg(not(feature = "celo"))]
@@ -9,6 +9,7 @@ mod eth_tests {
         types::{Address, BlockId, TransactionRequest, H256},
         utils::Ganache,
     };
+    use ethers_providers::RINKEBY;
 
     #[tokio::test]
     async fn non_existing_data_works() {