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 <sebastinez@me.com>

* chore: fmt

* fix: use legacy tx in unit test due to Ganache

Co-authored-by: Sebastian Martinez <sebastinez@me.com>
This commit is contained in:
Georgios Konstantopoulos 2021-08-12 19:19:24 +03:00 committed by GitHub
parent 99e9a687ca
commit 60ff4660df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 8 deletions

View File

@ -7,7 +7,7 @@ use crate::{
use ethers_core::{ use ethers_core::{
abi::{Abi, Detokenize, Error, EventExt, Function, Tokenize}, abi::{Abi, Detokenize, Error, EventExt, Function, Tokenize},
types::{Address, Filter, NameOrAddress, Selector}, types::{Address, Filter, NameOrAddress, Selector, ValueOrArray},
}; };
#[cfg(not(feature = "legacy"))] #[cfg(not(feature = "legacy"))]
@ -178,7 +178,7 @@ impl<M: Middleware> Contract<M> {
pub fn event_with_filter<D: EthLogDecode>(&self, filter: Filter) -> Event<M, D> { pub fn event_with_filter<D: EthLogDecode>(&self, filter: Filter) -> Event<M, D> {
Event { Event {
provider: &self.client, provider: &self.client,
filter: filter.address(self.address), filter: filter.address(ValueOrArray::Value(self.address)),
datatype: PhantomData, datatype: PhantomData,
} }
} }

View File

@ -1,4 +1,7 @@
use ethers::{contract::ContractFactory, types::H256}; use ethers::{
contract::ContractFactory,
types::{Filter, ValueOrArray, H256},
};
mod common; mod common;
pub use 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] #[tokio::test]
async fn signer_on_node() { async fn signer_on_node() {
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");

View File

@ -181,9 +181,7 @@ pub struct Filter {
pub block_option: FilterBlockOption, pub block_option: FilterBlockOption,
/// Address /// Address
// TODO: The spec says that this can also be an array, do we really want to address: Option<ValueOrArray<Address>>,
// monitor for the same event for multiple contracts?
address: Option<Address>,
/// Topics /// Topics
// TODO: We could improve the low level API here by using ethabi's RawTopicFilter // TODO: We could improve the low level API here by using ethabi's RawTopicFilter
@ -331,7 +329,7 @@ impl Filter {
self self
} }
pub fn address<T: Into<Address>>(mut self, address: T) -> Self { pub fn address<T: Into<ValueOrArray<Address>>>(mut self, address: T) -> Self {
self.address = Some(address.into()); self.address = Some(address.into());
self self
} }
@ -449,7 +447,7 @@ mod tests {
let ser = serialize(&filter); let ser = serialize(&filter);
assert_eq!(ser, json!({ "topics": [] })); assert_eq!(ser, json!({ "topics": [] }));
let filter = filter.address(addr); let filter = filter.address(ValueOrArray::Value(addr));
let ser = serialize(&filter); let ser = serialize(&filter);
assert_eq!(ser, json!({"address" : addr, "topics": []})); assert_eq!(ser, json!({"address" : addr, "topics": []}));