Repository: gakonst/ethers-rs
Pull request #2264 (2023-03-15 09:47:02)

chore(clippy): make clippy happy
<!--
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

<!--
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
-   [ ] Breaking changes



Patch:
diff --git a/ethers-contract/ethers-contract-derive/src/event.rs b/ethers-contract/ethers-contract-derive/src/event.rs
index 844bf6a4..e6c8594b 100644
--- a/ethers-contract/ethers-contract-derive/src/event.rs
+++ b/ethers-contract/ethers-contract-derive/src/event.rs
@@ -1,6 +1,12 @@
 //! Helper functions for deriving `EthEvent`
 
+use crate::{abi_ty, utils};
 use ethers_contract_abigen::Source;
+use ethers_core::{
+    abi::{Event, EventExt, EventParam, HumanReadableParser},
+    macros::{ethers_contract_crate, ethers_core_crate},
+};
+use hex::FromHex;
 use proc_macro2::{Span, TokenStream};
 use quote::quote;
 use syn::{
@@ -8,14 +14,6 @@ use syn::{
     NestedMeta,
 };
 
-use ethers_core::{
-    abi::{Event, EventExt, EventParam, HumanReadableParser},
-    macros::{ethers_contract_crate, ethers_core_crate},
-};
-use hex::FromHex;
-
-use crate::{abi_ty, utils};
-
 /// Generates the `EthEvent` trait support
 pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> Result<TokenStream, Error> {
     let name = &input.ident;
@@ -124,10 +122,7 @@ impl EventField {
     }
 }
 
-fn derive_decode_from_log_impl(
-    input: &DeriveInput,
-    event: &Event,
-) -> Result<proc_macro2::TokenStream, Error> {
+fn derive_decode_from_log_impl(input: &DeriveInput, event: &Event) -> Result<TokenStream, Error> {
     let ethers_core = ethers_core_crate();
 
     let fields: Vec<_> = match input.data {
@@ -159,10 +154,8 @@ fn derive_decode_from_log_impl(
                 fields.unnamed.iter().collect()
             }
             Fields::Unit => {
-                return Err(Error::new(
-                    input.span(),
-                    "EthEvent cannot be derived for empty structs and unit",
-                ))
+                // Empty structs or unit, no fields
+                vec![]
             }
         },
         Data::Enum(_) => {
@@ -173,35 +166,6 @@ fn derive_decode_from_log_impl(
         }
     };
 
-    let mut event_fields = Vec::with_capacity(fields.len());
-    for (index, field) in fields.iter().enumerate() {
-        let mut param = event.inputs[index].clone();
-
-        let (topic_name, indexed) = parse_field_attributes(field)?;
-        if indexed {
-            param.indexed = true;
-        }
-        let topic_name =
-            param.indexed.then(|| topic_name.or_else(|| Some(param.name.clone()))).flatten();
-
-        event_fields.push(EventField { topic_name, index, param });
-    }
-
-    // convert fields to params list
-    let topic_types = event_fields
-        .iter()
-        .filter(|f| f.is_indexed())
-        .map(|f| utils::topic_param_type_quote(&f.param.kind));
-
-    let topic_types_init = quote! {let topic_types = ::std::vec![#( #topic_types ),*];};
-
-    let data_types = event_fields
-        .iter()
-        .filter(|f| !f.is_indexed())
-        .map(|f| utils::param_type_quote(&f.param.kind));
-
-    let data_types_init = quote! {let data_types = [#( #data_types ),*];};
-
     // decode
     let (signature_check, flat_topics_init, topic_tokens_len_check) = if event.anonymous {
         (
@@ -234,6 +198,51 @@ fn derive_decode_from_log_impl(
         )
     };
 
+    // Event with no fields, can skip decoding
+    if fields.is_empty() {
+        return Ok(quote! {
+
+            let #ethers_core::abi::RawLog {topics, data} = log;
+
+            #signature_check
+
+            if topics.len() != 1usize || !data.is_empty() {
+                return Err(::ethers_core::abi::Error::InvalidData);
+            }
+
+            #ethers_core::abi::Tokenizable::from_token(#ethers_core::abi::Token::Tuple(::std::vec::Vec::new())).map_err(|_|#ethers_core::abi::Error::InvalidData)
+        })
+    }
+
+    let mut event_fields = Vec::with_capacity(fields.len());
+    for (index, field) in fields.iter().enumerate() {
+        let mut param = event.inputs[index].clone();
+
+        let (topic_name, indexed) = parse_field_attributes(field)?;
+        if indexed {
+            param.indexed = true;
+        }
+        let topic_name =
+            param.indexed.then(|| topic_name.or_else(|| Some(param.name.clone()))).flatten();
+
+        event_fields.push(EventField { topic_name, index, param });
+    }
+
+    // convert fields to params list
+    let topic_types = event_fields
+        .iter()
+        .filter(|f| f.is_indexed())
+        .map(|f| utils::topic_param_type_quote(&f.param.kind));
+
+    let topic_types_init = quote! {let topic_types = ::std::vec![#( #topic_types ),*];};
+
+    let data_types = event_fields
+        .iter()
+        .filter(|f| !f.is_indexed())
+        .map(|f| utils::param_type_quote(&f.param.kind));
+
+    let data_types_init = quote! {let data_types = [#( #data_types ),*];};
+
     // check if indexed are sorted
     let tokens_init = if event_fields
         .iter()
diff --git a/ethers-contract/src/call.rs b/ethers-contract/src/call.rs
index 16b7b1b8..d3a26f64 100644
--- a/ethers-contract/src/call.rs
+++ b/ethers-contract/src/call.rs
@@ -360,6 +360,7 @@ where
     type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
 
     fn into_future(self) -> Self::IntoFuture {
+        #[allow(clippy::redundant_async_block)]
         Box::pin(async move { self.call().await })
     }
 }
diff --git a/ethers-signers/src/aws/mod.rs b/ethers-signers/src/aws/mod.rs
index 2d988252..fdbca554 100644
--- a/ethers-signers/src/aws/mod.rs
+++ b/ethers-signers/src/aws/mod.rs
@@ -253,7 +253,7 @@ impl super::Signer for AwsSigner {
             payload.encode_eip712().map_err(|e| Self::Error::Eip712Error(e.to_string()))?;
 
         let sig = self.sign_digest(digest).await?;
-        let sig = utils::sig_from_digest_bytes_trial_recovery(&sig, digest.into(), &self.pubkey);
+        let sig = utils::sig_from_digest_bytes_trial_recovery(&sig, digest, &self.pubkey);
 
         Ok(sig)
     }
diff --git a/ethers-signers/src/aws/utils.rs b/ethers-signers/src/aws/utils.rs
index ed45d8c1..481971df 100644
--- a/ethers-signers/src/aws/utils.rs
+++ b/ethers-signers/src/aws/utils.rs
@@ -40,9 +40,9 @@ pub(super) fn sig_from_digest_bytes_trial_recovery(
     let r = U256::from_big_endian(r_bytes.as_slice());
     let s = U256::from_big_endian(s_bytes.as_slice());
 
-    if check_candidate(&sig, RecoveryId::from_byte(0).unwrap(), digest, vk) {
+    if check_candidate(sig, RecoveryId::from_byte(0).unwrap(), digest, vk) {
         EthSig { r, s, v: 0 }
-    } else if check_candidate(&sig, RecoveryId::from_byte(1).unwrap(), digest, vk) {
+    } else if check_candidate(sig, RecoveryId::from_byte(1).unwrap(), digest, vk) {
         EthSig { r, s, v: 1 }
     } else {
         panic!("bad sig");


Test patch:
diff --git a/ethers-contract/tests/it/derive.rs b/ethers-contract/tests/it/derive.rs
index aa0bd1d0..41327bea 100644
--- a/ethers-contract/tests/it/derive.rs
+++ b/ethers-contract/tests/it/derive.rs
@@ -670,3 +670,31 @@ fn derives_abi_name() {
         "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef".parse().unwrap()
     );
 }
+
+// <https://github.com/gakonst/ethers-rs/issues/2261>
+#[test]
+fn derive_empty_events() {
+    #[derive(Debug, EthEvent)]
+    #[ethevent(abi = "EmptyEvent()")]
+    struct EmptyEvent;
+
+    let log = RawLog { topics: vec![EmptyEvent::signature()], data: vec![] };
+    let _event = <EmptyEvent as EthLogDecode>::decode_log(&log).unwrap();
+
+    let log = RawLog { topics: vec![EmptyEvent::signature()], data: vec![0] };
+    assert!(<EmptyEvent as EthLogDecode>::decode_log(&log).is_err());
+
+    let log = RawLog { topics: vec![EmptyEvent::signature(), H256::random()], data: vec![0] };
+    assert!(<EmptyEvent as EthLogDecode>::decode_log(&log).is_err());
+
+    assert_eq!(EmptyEvent::abi_signature(), "EmptyEvent()");
+
+    abigen!(
+        DummyContract,
+        r#"[
+           event EmptyEvent2()
+        ]"#,
+    );
+
+    assert_eq!(EmptyEvent2Filter::abi_signature(), "EmptyEvent2()");
+}