feat: stream_with_meta (#354)
This commit is contained in:
parent
c8d9f9ba85
commit
9fc142ca61
|
@ -1,8 +1,8 @@
|
||||||
use crate::{stream::EventStream, ContractError, EthLogDecode};
|
use crate::{log::LogMeta, stream::EventStream, ContractError, EthLogDecode};
|
||||||
|
|
||||||
use ethers_core::{
|
use ethers_core::{
|
||||||
abi::{Detokenize, RawLog},
|
abi::{Detokenize, RawLog},
|
||||||
types::{Address, BlockNumber, Filter, Log, TxHash, ValueOrArray, H256, U256, U64},
|
types::{BlockNumber, Filter, Log, ValueOrArray, H256},
|
||||||
};
|
};
|
||||||
use ethers_providers::{FilterWatcher, Middleware, PubsubClient, SubscriptionStream};
|
use ethers_providers::{FilterWatcher, Middleware, PubsubClient, SubscriptionStream};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
@ -214,38 +214,3 @@ where
|
||||||
.map_err(From::from)
|
.map_err(From::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Metadata inside a log
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct LogMeta {
|
|
||||||
/// Address from which this log originated
|
|
||||||
pub address: Address,
|
|
||||||
|
|
||||||
/// The block in which the log was emitted
|
|
||||||
pub block_number: U64,
|
|
||||||
|
|
||||||
/// The block hash in which the log was emitted
|
|
||||||
pub block_hash: H256,
|
|
||||||
|
|
||||||
/// The transaction hash in which the log was emitted
|
|
||||||
pub transaction_hash: TxHash,
|
|
||||||
|
|
||||||
/// Transactions index position log was created from
|
|
||||||
pub transaction_index: U64,
|
|
||||||
|
|
||||||
/// Log index position in the block
|
|
||||||
pub log_index: U256,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Log> for LogMeta {
|
|
||||||
fn from(src: &Log) -> Self {
|
|
||||||
LogMeta {
|
|
||||||
address: src.address,
|
|
||||||
block_number: src.block_number.expect("should have a block number"),
|
|
||||||
block_hash: src.block_hash.expect("should have a block hash"),
|
|
||||||
transaction_hash: src.transaction_hash.expect("should have a tx hash"),
|
|
||||||
transaction_index: src.transaction_index.expect("should have a tx index"),
|
|
||||||
log_index: src.log_index.expect("should have a log index"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,10 +26,10 @@ mod factory;
|
||||||
pub use factory::ContractFactory;
|
pub use factory::ContractFactory;
|
||||||
|
|
||||||
mod event;
|
mod event;
|
||||||
pub use event::{EthEvent, LogMeta};
|
pub use event::EthEvent;
|
||||||
|
|
||||||
mod log;
|
mod log;
|
||||||
pub use log::{decode_logs, EthLogDecode};
|
pub use log::{decode_logs, EthLogDecode, LogMeta};
|
||||||
|
|
||||||
mod stream;
|
mod stream;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//! Mod of types for ethereum logs
|
//! Mod of types for ethereum logs
|
||||||
use ethers_core::abi::Error;
|
use ethers_core::abi::Error;
|
||||||
use ethers_core::abi::RawLog;
|
use ethers_core::abi::RawLog;
|
||||||
|
use ethers_core::types::{Address, Log, TxHash, H256, U256, U64};
|
||||||
|
|
||||||
/// A trait for types (events) that can be decoded from a `RawLog`
|
/// A trait for types (events) that can be decoded from a `RawLog`
|
||||||
pub trait EthLogDecode: Send + Sync {
|
pub trait EthLogDecode: Send + Sync {
|
||||||
|
@ -14,3 +15,38 @@ pub trait EthLogDecode: Send + Sync {
|
||||||
pub fn decode_logs<T: EthLogDecode>(logs: &[RawLog]) -> Result<Vec<T>, Error> {
|
pub fn decode_logs<T: EthLogDecode>(logs: &[RawLog]) -> Result<Vec<T>, Error> {
|
||||||
logs.iter().map(T::decode_log).collect()
|
logs.iter().map(T::decode_log).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Metadata inside a log
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct LogMeta {
|
||||||
|
/// Address from which this log originated
|
||||||
|
pub address: Address,
|
||||||
|
|
||||||
|
/// The block in which the log was emitted
|
||||||
|
pub block_number: U64,
|
||||||
|
|
||||||
|
/// The block hash in which the log was emitted
|
||||||
|
pub block_hash: H256,
|
||||||
|
|
||||||
|
/// The transaction hash in which the log was emitted
|
||||||
|
pub transaction_hash: TxHash,
|
||||||
|
|
||||||
|
/// Transactions index position log was created from
|
||||||
|
pub transaction_index: U64,
|
||||||
|
|
||||||
|
/// Log index position in the block
|
||||||
|
pub log_index: U256,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Log> for LogMeta {
|
||||||
|
fn from(src: &Log) -> Self {
|
||||||
|
LogMeta {
|
||||||
|
address: src.address,
|
||||||
|
block_number: src.block_number.expect("should have a block number"),
|
||||||
|
block_hash: src.block_hash.expect("should have a block hash"),
|
||||||
|
transaction_hash: src.transaction_hash.expect("should have a tx hash"),
|
||||||
|
transaction_index: src.transaction_index.expect("should have a tx index"),
|
||||||
|
log_index: src.log_index.expect("should have a log index"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::LogMeta;
|
||||||
use ethers_core::types::{Log, U256};
|
use ethers_core::types::{Log, U256};
|
||||||
use futures_util::stream::{Stream, StreamExt};
|
use futures_util::stream::{Stream, StreamExt};
|
||||||
use pin_project::pin_project;
|
use pin_project::pin_project;
|
||||||
|
@ -19,6 +20,12 @@ pub struct EventStream<'a, T, R, E> {
|
||||||
parse: MapEvent<'a, R, E>,
|
parse: MapEvent<'a, R, E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, T, R, E> EventStream<'a, T, R, E> {
|
||||||
|
pub fn with_meta(self) -> EventStreamMeta<'a, T, R, E> {
|
||||||
|
EventStreamMeta(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, T, R, E> EventStream<'a, T, R, E> {
|
impl<'a, T, R, E> EventStream<'a, T, R, E> {
|
||||||
pub fn new(id: U256, stream: T, parse: MapEvent<'a, R, E>) -> Self {
|
pub fn new(id: U256, stream: T, parse: MapEvent<'a, R, E>) -> Self {
|
||||||
Self { id, stream, parse }
|
Self { id, stream, parse }
|
||||||
|
@ -39,3 +46,26 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pin_project]
|
||||||
|
pub struct EventStreamMeta<'a, T, R, E>(pub EventStream<'a, T, R, E>);
|
||||||
|
|
||||||
|
impl<'a, T, R, E> Stream for EventStreamMeta<'a, T, R, E>
|
||||||
|
where
|
||||||
|
T: Stream<Item = Log> + Unpin,
|
||||||
|
{
|
||||||
|
type Item = Result<(R, LogMeta), E>;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Option<Self::Item>> {
|
||||||
|
let this = self.project();
|
||||||
|
match futures_util::ready!(this.0.stream.poll_next_unpin(ctx)) {
|
||||||
|
Some(item) => {
|
||||||
|
let meta = LogMeta::from(&item);
|
||||||
|
let res = (this.0.parse)(item);
|
||||||
|
let res = res.map(|inner| (inner, meta));
|
||||||
|
Poll::Ready(Some(res))
|
||||||
|
}
|
||||||
|
None => Poll::Pending,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -278,7 +278,7 @@ mod eth_tests {
|
||||||
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
|
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
|
||||||
let ganache = Ganache::new().spawn();
|
let ganache = Ganache::new().spawn();
|
||||||
let client = connect(&ganache, 0);
|
let client = connect(&ganache, 0);
|
||||||
let contract = deploy(client, abi.clone(), bytecode).await;
|
let contract = deploy(client.clone(), abi.clone(), bytecode).await;
|
||||||
|
|
||||||
// We spawn the event listener:
|
// We spawn the event listener:
|
||||||
let event = contract.event::<ValueChanged>();
|
let event = contract.event::<ValueChanged>();
|
||||||
|
@ -292,9 +292,13 @@ mod eth_tests {
|
||||||
let mut subscription = event2.subscribe().await.unwrap();
|
let mut subscription = event2.subscribe().await.unwrap();
|
||||||
assert_eq!(subscription.id, 2.into());
|
assert_eq!(subscription.id, 2.into());
|
||||||
|
|
||||||
|
let mut subscription_meta = event2.subscribe().await.unwrap().with_meta();
|
||||||
|
assert_eq!(subscription_meta.0.id, 3.into());
|
||||||
|
|
||||||
let num_calls = 3u64;
|
let num_calls = 3u64;
|
||||||
|
|
||||||
// and we make a few calls
|
// and we make a few calls
|
||||||
|
let num = client.get_block_number().await.unwrap();
|
||||||
for i in 0..num_calls {
|
for i in 0..num_calls {
|
||||||
let call = contract
|
let call = contract
|
||||||
.method::<_, H256>("setValue", i.to_string())
|
.method::<_, H256>("setValue", i.to_string())
|
||||||
|
@ -307,8 +311,19 @@ mod eth_tests {
|
||||||
// unwrap the option of the stream, then unwrap the decoding result
|
// unwrap the option of the stream, then unwrap the decoding result
|
||||||
let log = stream.next().await.unwrap().unwrap();
|
let log = stream.next().await.unwrap().unwrap();
|
||||||
let log2 = subscription.next().await.unwrap().unwrap();
|
let log2 = subscription.next().await.unwrap().unwrap();
|
||||||
|
let (log3, meta) = subscription_meta.next().await.unwrap().unwrap();
|
||||||
|
assert_eq!(log.new_value, log3.new_value);
|
||||||
assert_eq!(log.new_value, log2.new_value);
|
assert_eq!(log.new_value, log2.new_value);
|
||||||
assert_eq!(log.new_value, i.to_string());
|
assert_eq!(log.new_value, i.to_string());
|
||||||
|
assert_eq!(meta.block_number, num + i + 1);
|
||||||
|
let hash = client
|
||||||
|
.get_block(num + i + 1)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.hash
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(meta.block_hash, hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue