Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extends logs with extra metadata and query params #651

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
82 changes: 75 additions & 7 deletions src/contract/mod.rs
Expand Up @@ -6,8 +6,8 @@ use crate::{
contract::tokens::{Detokenize, Tokenize},
futures::Future,
types::{
AccessList, Address, BlockId, Bytes, CallRequest, FilterBuilder, TransactionCondition, TransactionReceipt,
TransactionRequest, H256, U256, U64,
AccessList, Address, BlockId, BlockNumber, Bytes, CallRequest, FilterBuilder, LogWithMeta,
TransactionCondition, TransactionReceipt, TransactionRequest, H256, U256, U64,
},
Transport,
};
Expand Down Expand Up @@ -279,7 +279,16 @@ impl<T: Transport> Contract<T> {
}

/// Find events matching the topics.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation should be updated.

pub async fn events<A, B, C, R>(&self, event: &str, topic0: A, topic1: B, topic2: C) -> Result<Vec<R>>
pub async fn events<A, B, C, R>(
&self,
event: &str,
from_block: Option<BlockNumber>,
to_block: Option<BlockNumber>,
block_hash: Option<H256>,
Comment on lines +285 to +287
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having 7 parameters, and 3 of them being optional is a bit too much. I'd like to propose a helper struct with a builder to amalgamate them.

pub struct BlockRange {
  from_block: Option<BlockNumber>,
  to_block: Option<BlockNumber>,
  block_hash: Option<H256>,
}

impl BlockRange {
  // set both `from_block` and `to_block`
  pub fn range(from: BlockNumber, to: BlockNumber) -> Self;
  // set just `from_block`
  pub fn from(BlockNumber) -> Self;
  // set just `to_block`
  pub fn to(BlockNumber) -> Self;
  // set just `block_hash`
  pub fn hash(H256) -> Self;
  // look for one exact block (set both `from/to` and `hash`).
  pub fn exact(BlockNumber, H256) -> Self;

  // applies the parameters to given filter
  pub (crate) fn apply_to(&self, b: FilterBuilder) -> FilterBuilder;
}

topic0: A,
topic1: B,
topic2: C,
) -> Result<Vec<LogWithMeta<R>>>
where
A: Tokenize,
B: Tokenize,
Expand Down Expand Up @@ -310,7 +319,15 @@ impl<T: Transport> Contract<T> {

let logs = self
.eth
.logs(FilterBuilder::default().topic_filter(filter).build())
.logs(
FilterBuilder::default()
.address(vec![self.address])
.topic_filter(filter)
.from_block(from_block)
.to_block(to_block)
.block_hash(block_hash)
.build(),
)
.await?;
logs.into_iter()
.map(move |l| {
Expand All @@ -319,9 +336,14 @@ impl<T: Transport> Contract<T> {
data: l.data.0,
})?;

R::from_tokens(log.params.into_iter().map(|x| x.value).collect::<Vec<_>>())
let event_data = R::from_tokens(log.params.into_iter().map(|x| x.value).collect::<Vec<_>>())?;

Ok(LogWithMeta {
transaction_hash: l.transaction_hash,
event_data,
})
})
.collect::<Result<Vec<R>>>()
.collect::<Result<Vec<LogWithMeta<R>>>>()
}
}

Expand Down Expand Up @@ -349,6 +371,15 @@ mod contract_signing {
// TODO [ToDr] SendTransactionWithConfirmation should support custom error type (so that we can return
// `contract::Error` instead of more generic `Error`.
.map_err(|err| crate::error::Error::Decoder(format!("{:?}", err)))?;
self.sign_raw(fn_data, options, key).await
}

async fn sign_raw(
&self,
fn_data: Vec<u8>,
options: Options,
key: impl signing::Key,
) -> crate::Result<SignedTransaction> {
let accounts = Accounts::new(self.eth.transport().clone());
let mut tx = TransactionParameters {
nonce: options.nonce,
Expand Down Expand Up @@ -385,10 +416,24 @@ mod contract_signing {
self.eth.send_raw_transaction(signed.raw_transaction).await
}

/// Submit contract call transaction to the transaction pool.
///
/// Note this function DOES NOT wait for any confirmations, so there is no guarantees that the call is actually executed.
/// If you'd rather wait for block inclusion, please use [`signed_call_raw_with_confirmations`] instead.
pub async fn signed_call_raw(
&self,
fn_data: Vec<u8>,
options: Options,
key: impl signing::Key,
) -> crate::Result<H256> {
let signed = self.sign_raw(fn_data, options, key).await?;
self.eth.send_raw_transaction(signed.raw_transaction).await
}

/// Submit contract call transaction to the transaction pool and wait for the transaction to be included in a block.
///
/// This function will wait for block inclusion of the transaction before returning.
// If you'd rather just submit transaction and receive it's hash, please use [`signed_call`] instead.
/// If you'd rather just submit transaction and receive it's hash, please use [`signed_call`] instead.
pub async fn signed_call_with_confirmations(
&self,
func: &str,
Expand All @@ -408,6 +453,29 @@ mod contract_signing {
)
.await
}

/// Submit contract call transaction to the transaction pool and wait for the transaction to be included in a block.
///
/// This function will wait for block inclusion of the transaction before returning.
/// If you'd rather just submit transaction and receive it's hash, please use [`signed_call`] instead.
pub async fn signed_call_raw_with_confirmations(
&self,
fn_data: Vec<u8>,
options: Options,
confirmations: usize,
key: impl signing::Key,
) -> crate::Result<TransactionReceipt> {
let poll_interval = time::Duration::from_secs(1);
let signed = self.sign_raw(fn_data, options, key).await?;

confirm::send_raw_transaction_with_confirmation(
self.eth.transport().clone(),
signed.raw_transaction,
poll_interval,
confirmations,
)
.await
}
}
}

Expand Down
42 changes: 31 additions & 11 deletions src/types/log.rs
@@ -1,4 +1,7 @@
use crate::types::{BlockNumber, Bytes, Index, H160, H256, U256, U64};
use crate::{
contract::tokens::Detokenize,
types::{BlockNumber, Bytes, Index, H160, H256, U256, U64},
};
use serde::{Deserialize, Serialize, Serializer};

/// A log produced by a transaction.
Expand Down Expand Up @@ -54,6 +57,17 @@ impl Log {
}
}

/// A log produced when a specific contract event was emitted.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LogWithMeta<R: Detokenize> {
/// Transaction hash necessary to retrieve `calldata`.
#[serde(rename = "transactionHash")]
pub transaction_hash: Option<H256>,

/// A tuple from the event signature (e.g. `(uint256, bytes)`).
pub event_data: R,
}

#[derive(Default, Debug, PartialEq, Clone)]
struct ValueOrArray<T>(Vec<T>);

Expand Down Expand Up @@ -106,27 +120,33 @@ impl FilterBuilder {
/// Sets `from_block`. The fields `from_block` and `block_hash` are
/// mutually exclusive. Setting `from_block` will clear a previously set
/// `block_hash`.
pub fn from_block(mut self, block: BlockNumber) -> Self {
self.filter.block_hash = None;
self.filter.from_block = Some(block);
pub fn from_block(mut self, block: Option<BlockNumber>) -> Self {
if let Some(block) = block {
self.filter.block_hash = None;
self.filter.from_block = Some(block);
}
self
Comment on lines +123 to +127
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should not be changed, the builder should only take values that are defined, not Options. None case should be handled at the call site.

}

/// Sets `to_block`. The fields `to_block` and `block_hash` are mutually
/// exclusive. Setting `to_block` will clear a previously set `block_hash`.
pub fn to_block(mut self, block: BlockNumber) -> Self {
self.filter.block_hash = None;
self.filter.to_block = Some(block);
pub fn to_block(mut self, block: Option<BlockNumber>) -> Self {
if let Some(block) = block {
self.filter.block_hash = None;
self.filter.to_block = Some(block);
}
self
Comment on lines +133 to +137
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

}

/// Sets `block_hash`. The field `block_hash` and the pair `from_block` and
/// `to_block` are mutually exclusive. Setting `block_hash` will clear a
/// previously set `from_block` and `to_block`.
pub fn block_hash(mut self, hash: H256) -> Self {
self.filter.from_block = None;
self.filter.to_block = None;
self.filter.block_hash = Some(hash);
pub fn block_hash(mut self, hash: Option<H256>) -> Self {
if let Some(_block_hash) = hash {
self.filter.from_block = None;
self.filter.to_block = None;
self.filter.block_hash = hash;
}
self
Comment on lines +144 to +149
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto.

}

Expand Down
2 changes: 1 addition & 1 deletion src/types/mod.rs
Expand Up @@ -25,7 +25,7 @@ pub use self::{
bytes::Bytes,
bytes_array::BytesArray,
fee_history::FeeHistory,
log::{Filter, FilterBuilder, Log},
log::{Filter, FilterBuilder, Log, LogWithMeta},
parity_peers::{
EthProtocolInfo, ParityPeerInfo, ParityPeerType, PeerNetworkInfo, PeerProtocolsInfo, PipProtocolInfo,
},
Expand Down