feat(contract): add helper to query events w/ metadata (block num + tx hash) (#33)

* feat(contract): add helper to query events w/ metadata (block num + tx hash)
This commit is contained in:
Georgios Konstantopoulos 2020-06-22 01:08:40 +03:00 committed by GitHub
parent 833ca33f60
commit 7a17e54fbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 10 deletions

View File

@ -4,13 +4,14 @@ use ethers_providers::{FilterStream, JsonRpcClient, Provider};
use ethers_core::{ use ethers_core::{
abi::{Detokenize, Event as AbiEvent, RawLog}, abi::{Detokenize, Event as AbiEvent, RawLog},
types::{BlockNumber, Filter, Log, ValueOrArray, H256}, types::{BlockNumber, Filter, Log, TxHash, ValueOrArray, H256, U64},
}; };
use futures::stream::{Stream, StreamExt}; use futures::stream::{Stream, StreamExt};
use std::{collections::HashMap, marker::PhantomData}; use std::marker::PhantomData;
/// Helper for managing the event filter before querying or streaming its logs /// Helper for managing the event filter before querying or streaming its logs
#[derive(Debug)]
#[must_use = "event filters do nothing unless you `query` or `stream` them"] #[must_use = "event filters do nothing unless you `query` or `stream` them"]
pub struct Event<'a: 'b, 'b, P, D> { pub struct Event<'a: 'b, 'b, P, D> {
/// The event filter's state /// The event filter's state
@ -93,16 +94,16 @@ where
Ok(events) Ok(events)
} }
/// Queries the blockchain for the selected filter and returns a hashmap of /// Queries the blockchain for the selected filter and returns a vector of logs
/// txhash -> logs /// along with their metadata
pub async fn query_with_hashes(&self) -> Result<HashMap<H256, D>, ContractError> { pub async fn query_with_meta(&self) -> Result<Vec<(D, LogMeta)>, ContractError> {
let logs = self.provider.get_logs(&self.filter).await?; let logs = self.provider.get_logs(&self.filter).await?;
let events = logs let events = logs
.into_iter() .into_iter()
.map(|log| { .map(|log| {
let tx_hash = log.transaction_hash.expect("should have tx hash"); let meta = LogMeta::from(&log);
let event = self.parse_log(log)?; let event = self.parse_log(log)?;
Ok((tx_hash, event)) Ok((event, meta))
}) })
.collect::<Result<_, ContractError>>()?; .collect::<Result<_, ContractError>>()?;
Ok(events) Ok(events)
@ -125,3 +126,22 @@ where
Ok(D::from_tokens(tokens)?) Ok(D::from_tokens(tokens)?)
} }
} }
/// Metadata inside a log
#[derive(Clone, Debug, PartialEq)]
pub struct LogMeta {
/// The block in which the log was emitted
pub block_number: U64,
/// The transaction hash in which the log was emitted
pub transaction_hash: TxHash,
}
impl From<&Log> for LogMeta {
fn from(src: &Log) -> Self {
LogMeta {
block_number: src.block_number.expect("should have a block number"),
transaction_hash: src.transaction_hash.expect("should have a tx hash"),
}
}
}

View File

@ -2,7 +2,7 @@ use crate::{Contract, ContractError};
use ethers_core::{ use ethers_core::{
abi::{Abi, Tokenize}, abi::{Abi, Tokenize},
types::{Bytes, TransactionRequest}, types::{BlockNumber, Bytes, TransactionRequest},
}; };
use ethers_providers::JsonRpcClient; use ethers_providers::JsonRpcClient;
use ethers_signers::{Client, Signer}; use ethers_signers::{Client, Signer};
@ -15,6 +15,7 @@ pub struct Deployer<'a, P, S> {
abi: Abi, abi: Abi,
client: &'a Client<P, S>, client: &'a Client<P, S>,
confs: usize, confs: usize,
block: BlockNumber,
} }
impl<'a, P, S> Deployer<'a, P, S> impl<'a, P, S> Deployer<'a, P, S>
@ -28,11 +29,19 @@ where
self self
} }
pub fn block<T: Into<BlockNumber>>(mut self, block: T) -> Self {
self.block = block.into();
self
}
/// Broadcasts the contract deployment transaction and after waiting for it to /// Broadcasts the contract deployment transaction and after waiting for it to
/// be sufficiently confirmed (default: 1), it returns a [`Contract`](crate::Contract) /// be sufficiently confirmed (default: 1), it returns a [`Contract`](crate::Contract)
/// struct at the deployed contract's address. /// struct at the deployed contract's address.
pub async fn send(self) -> Result<Contract<'a, P, S>, ContractError> { pub async fn send(self) -> Result<Contract<'a, P, S>, ContractError> {
let pending_tx = self.client.send_transaction(self.tx, None).await?; let pending_tx = self
.client
.send_transaction(self.tx, Some(self.block))
.await?;
let receipt = pending_tx.confirmations(self.confs).await?; let receipt = pending_tx.confirmations(self.confs).await?;
@ -159,6 +168,7 @@ where
abi: self.abi, abi: self.abi,
tx, tx,
confs: 1, confs: 1,
block: BlockNumber::Latest,
}) })
} }
} }

View File

@ -176,6 +176,7 @@ mod celo_tests {
use ethers::{ use ethers::{
providers::{Http, Provider}, providers::{Http, Provider},
signers::Wallet, signers::Wallet,
types::BlockNumber,
}; };
use std::convert::TryFrom; use std::convert::TryFrom;
@ -195,7 +196,7 @@ mod celo_tests {
let factory = ContractFactory::new(abi, bytecode, &client); let factory = ContractFactory::new(abi, bytecode, &client);
let deployer = factory.deploy("initial value".to_string()).unwrap(); let deployer = factory.deploy("initial value".to_string()).unwrap();
let contract = deployer.send().await.unwrap(); let contract = deployer.block(BlockNumber::Pending).send().await.unwrap();
let value: String = contract let value: String = contract
.method("getValue", ()) .method("getValue", ())