feat: Instantiate an event builder without a contract instance (#1882)
* Build an `Event` without requiring a contract instance * Contract unit test * Function to set Event address * Example * Typo to improve readability * CHANGELOG * cargo +nightly fmt * FIX conflict * review: applied Address alias Co-authored-by: Andrea Simeoni <> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
parent
f4b6178332
commit
845aa4920f
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
### Unreleased
|
### Unreleased
|
||||||
|
|
||||||
|
- An `Event` builder can be instantiated specifying the event filter type, without the need to instantiate a contract.
|
||||||
- Add 'ethers_core::types::OpCode' and use in 'ethers_core::types::VMOperation' [1857](https://github.com/gakonst/ethers-rs/issues/1857)
|
- Add 'ethers_core::types::OpCode' and use in 'ethers_core::types::VMOperation' [1857](https://github.com/gakonst/ethers-rs/issues/1857)
|
||||||
- Remove rust_decimals dependency for ethers-core
|
- Remove rust_decimals dependency for ethers-core
|
||||||
- Add support for numbers greater than 2^96 for `ethers_core::utils::parse_units` [#1822](https://github.com/gakonst/ethers-rs/issues/1822)
|
- Add support for numbers greater than 2^96 for `ethers_core::utils::parse_units` [#1822](https://github.com/gakonst/ethers-rs/issues/1822)
|
||||||
|
|
|
@ -191,6 +191,19 @@ impl<M> Contract<M> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<M: Middleware> Contract<M> {
|
||||||
|
/// Returns an [`Event`](crate::builders::Event) builder for the provided event.
|
||||||
|
/// This function operates in a static context, then it does not require a `self`
|
||||||
|
/// to reference to instantiate an [`Event`](crate::builders::Event) builder.
|
||||||
|
pub fn event_of_type<'a, D: EthEvent>(client: &'a Arc<M>) -> Event<'a, M, D> {
|
||||||
|
Event {
|
||||||
|
provider: client,
|
||||||
|
filter: Filter::new().event(&D::abi_signature()),
|
||||||
|
datatype: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<M: Middleware> Contract<M> {
|
impl<M: Middleware> Contract<M> {
|
||||||
/// Creates a new contract from the provided client, abi and address
|
/// Creates a new contract from the provided client, abi and address
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
use crate::{log::LogMeta, stream::EventStream, ContractError, EthLogDecode};
|
use crate::{log::LogMeta, stream::EventStream, ContractError, EthLogDecode};
|
||||||
use ethers_core::{
|
use ethers_core::{
|
||||||
abi::{Detokenize, RawLog},
|
abi::{Address, Detokenize, RawLog},
|
||||||
types::{BlockNumber, Filter, Log, Topic, H256},
|
types::{BlockNumber, Filter, Log, Topic, ValueOrArray, H256},
|
||||||
};
|
};
|
||||||
use ethers_providers::{FilterWatcher, Middleware, PubsubClient, SubscriptionStream};
|
use ethers_providers::{FilterWatcher, Middleware, PubsubClient, SubscriptionStream};
|
||||||
use std::{borrow::Cow, marker::PhantomData};
|
use std::{borrow::Cow, marker::PhantomData};
|
||||||
|
@ -108,6 +108,12 @@ impl<M, D: EthLogDecode> Event<'_, M, D> {
|
||||||
self.filter.topics[3] = Some(topic.into());
|
self.filter.topics[3] = Some(topic.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the filter's address.
|
||||||
|
pub fn address(mut self, address: ValueOrArray<Address>) -> Self {
|
||||||
|
self.filter = self.filter.address(address);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, M, D> Event<'a, M, D>
|
impl<'a, M, D> Event<'a, M, D>
|
||||||
|
|
|
@ -6,7 +6,7 @@ use ethers_core::types::{Filter, ValueOrArray, H256};
|
||||||
#[cfg(not(feature = "celo"))]
|
#[cfg(not(feature = "celo"))]
|
||||||
mod eth_tests {
|
mod eth_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ethers_contract::{LogMeta, Multicall, MulticallVersion};
|
use ethers_contract::{EthEvent, LogMeta, Multicall, MulticallVersion};
|
||||||
use ethers_core::{
|
use ethers_core::{
|
||||||
abi::{encode, Detokenize, Token, Tokenizable},
|
abi::{encode, Detokenize, Token, Tokenizable},
|
||||||
types::{transaction::eip712::Eip712, Address, BlockId, Bytes, I256, U256},
|
types::{transaction::eip712::Eip712, Address, BlockId, Bytes, I256, U256},
|
||||||
|
@ -324,6 +324,21 @@ mod eth_tests {
|
||||||
assert_eq!(log_2.address, contract_2.address());
|
assert_eq!(log_2.address, contract_2.address());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn build_event_of_type() {
|
||||||
|
abigen!(
|
||||||
|
AggregatorInterface,
|
||||||
|
r#"[
|
||||||
|
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt)
|
||||||
|
]"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
let anvil = Anvil::new().spawn();
|
||||||
|
let client = connect(&anvil, 0);
|
||||||
|
let event = ethers_contract::Contract::event_of_type::<AnswerUpdatedFilter>(&client);
|
||||||
|
assert_eq!(event.filter, Filter::new().event(&AnswerUpdatedFilter::abi_signature()));
|
||||||
|
}
|
||||||
|
|
||||||
#[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");
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
use ethers::{contract::Contract, prelude::*};
|
||||||
|
use std::{error::Error, sync::Arc};
|
||||||
|
abigen!(
|
||||||
|
AggregatorInterface,
|
||||||
|
r#"[
|
||||||
|
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt)
|
||||||
|
]"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
const PRICE_FEED_1: &str = "0x7de93682b9b5d80d45cd371f7a14f74d49b0914c";
|
||||||
|
const PRICE_FEED_2: &str = "0x0f00392fcb466c0e4e4310d81b941e07b4d5a079";
|
||||||
|
const PRICE_FEED_3: &str = "0xebf67ab8cff336d3f609127e8bbf8bd6dd93cd81";
|
||||||
|
|
||||||
|
/// Subscribe to a typed event stream without requiring a `Contract` instance.
|
||||||
|
/// In this example we subscribe Chainlink price feeds and filter out them
|
||||||
|
/// by address.
|
||||||
|
/// -------------------------------------------------------------------------------
|
||||||
|
/// In order to run this example you need to include Ws and TLS features
|
||||||
|
/// Run this example with
|
||||||
|
/// `cargo run -p ethers --example subscribe_events_by_type --features="ws","rustls"`
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let client = get_client().await;
|
||||||
|
let client = Arc::new(client);
|
||||||
|
|
||||||
|
// Build an Event by type. We are not tied to a contract instance. We use builder functions to
|
||||||
|
// refine the event filter
|
||||||
|
let event = Contract::event_of_type::<AnswerUpdatedFilter>(&client)
|
||||||
|
.from_block(16022082)
|
||||||
|
.to_block(16022282)
|
||||||
|
.address(ValueOrArray::Array(vec![
|
||||||
|
PRICE_FEED_1.parse()?,
|
||||||
|
PRICE_FEED_2.parse()?,
|
||||||
|
PRICE_FEED_3.parse()?,
|
||||||
|
]));
|
||||||
|
|
||||||
|
let mut stream = event.subscribe_with_meta().await?;
|
||||||
|
|
||||||
|
// Note that `log` has type AnswerUpdatedFilter
|
||||||
|
while let Some(Ok((log, meta))) = stream.next().await {
|
||||||
|
println!("{:?}", log);
|
||||||
|
println!("{:?}", meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_client() -> Provider<Ws> {
|
||||||
|
Provider::<Ws>::connect("wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27")
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
Loading…
Reference in New Issue