From 60ff4660dfc1485a0a70c93e28868175fe462642 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Thu, 12 Aug 2021 19:19:24 +0300 Subject: [PATCH] feat: multiple addresses on Filter (#375) * feat: support multiple addresses in filters To support filtering of 1..n addresses we had to add the ValueOrArray enum to the log types and refactor some ethers-contract code Fixes: #240 Signed-off-by: Sebastian Martinez * chore: fmt * fix: use legacy tx in unit test due to Ganache Co-authored-by: Sebastian Martinez --- ethers-contract/src/contract.rs | 4 +-- ethers-contract/tests/contract.rs | 42 ++++++++++++++++++++++++++++++- ethers-core/src/types/log.rs | 8 +++--- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/ethers-contract/src/contract.rs b/ethers-contract/src/contract.rs index 6ede8aa2..a1b56d9c 100644 --- a/ethers-contract/src/contract.rs +++ b/ethers-contract/src/contract.rs @@ -7,7 +7,7 @@ use crate::{ use ethers_core::{ abi::{Abi, Detokenize, Error, EventExt, Function, Tokenize}, - types::{Address, Filter, NameOrAddress, Selector}, + types::{Address, Filter, NameOrAddress, Selector, ValueOrArray}, }; #[cfg(not(feature = "legacy"))] @@ -178,7 +178,7 @@ impl Contract { pub fn event_with_filter(&self, filter: Filter) -> Event { Event { provider: &self.client, - filter: filter.address(self.address), + filter: filter.address(ValueOrArray::Value(self.address)), datatype: PhantomData, } } diff --git a/ethers-contract/tests/contract.rs b/ethers-contract/tests/contract.rs index 67f04835..75a4afac 100644 --- a/ethers-contract/tests/contract.rs +++ b/ethers-contract/tests/contract.rs @@ -1,4 +1,7 @@ -use ethers::{contract::ContractFactory, types::H256}; +use ethers::{ + contract::ContractFactory, + types::{Filter, ValueOrArray, H256}, +}; mod common; pub use common::*; @@ -338,6 +341,43 @@ mod eth_tests { } } + #[tokio::test] + async fn watch_subscription_events_multiple_addresses() { + let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); + let ganache = Ganache::new().spawn(); + let client = connect(&ganache, 0); + let contract_1 = deploy(client.clone(), abi.clone(), bytecode.clone()).await; + let contract_2 = deploy(client.clone(), abi.clone(), bytecode).await; + + let ws = Provider::connect(ganache.ws_endpoint()).await.unwrap(); + let filter = Filter::new().address(ValueOrArray::Array(vec![ + contract_1.address(), + contract_2.address(), + ])); + let mut stream = ws.subscribe_logs(&filter).await.unwrap(); + + // and we make a few calls + let call = contract_1 + .method::<_, H256>("setValue", "1".to_string()) + .unwrap() + .legacy(); + let pending_tx = call.send().await.unwrap(); + let _receipt = pending_tx.await.unwrap(); + + let call = contract_2 + .method::<_, H256>("setValue", "2".to_string()) + .unwrap() + .legacy(); + let pending_tx = call.send().await.unwrap(); + let _receipt = pending_tx.await.unwrap(); + + // unwrap the option of the stream, then unwrap the decoding result + let log_1 = stream.next().await.unwrap(); + let log_2 = stream.next().await.unwrap(); + assert_eq!(log_1.address, contract_1.address()); + assert_eq!(log_2.address, contract_2.address()); + } + #[tokio::test] async fn signer_on_node() { let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); diff --git a/ethers-core/src/types/log.rs b/ethers-core/src/types/log.rs index ea18595b..06705d16 100644 --- a/ethers-core/src/types/log.rs +++ b/ethers-core/src/types/log.rs @@ -181,9 +181,7 @@ pub struct Filter { pub block_option: FilterBlockOption, /// Address - // TODO: The spec says that this can also be an array, do we really want to - // monitor for the same event for multiple contracts? - address: Option
, + address: Option>, /// Topics // TODO: We could improve the low level API here by using ethabi's RawTopicFilter @@ -331,7 +329,7 @@ impl Filter { self } - pub fn address>(mut self, address: T) -> Self { + pub fn address>>(mut self, address: T) -> Self { self.address = Some(address.into()); self } @@ -449,7 +447,7 @@ mod tests { let ser = serialize(&filter); assert_eq!(ser, json!({ "topics": [] })); - let filter = filter.address(addr); + let filter = filter.address(ValueOrArray::Value(addr)); let ser = serialize(&filter); assert_eq!(ser, json!({"address" : addr, "topics": []}));