refactor(ethers-contract): split to modules
This commit is contained in:
parent
099fa5d7ef
commit
f42aaf2588
|
@ -272,6 +272,7 @@ dependencies = [
|
||||||
"once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -19,3 +19,4 @@ syn = "1.0.12"
|
||||||
url = "2.1"
|
url = "2.1"
|
||||||
serde_json = "1.0.53"
|
serde_json = "1.0.53"
|
||||||
once_cell = "1.4.0"
|
once_cell = "1.4.0"
|
||||||
|
rustc-hex = { version = "2.1.0", default-features = false }
|
||||||
|
|
|
@ -6,12 +6,13 @@ use quote::quote;
|
||||||
|
|
||||||
pub(crate) fn imports() -> TokenStream {
|
pub(crate) fn imports() -> TokenStream {
|
||||||
quote! {
|
quote! {
|
||||||
|
// TODO: Can we make this context aware so that it imports either ethers_contract
|
||||||
|
// or ethers::contract?
|
||||||
use ethers_contract::{
|
use ethers_contract::{
|
||||||
Sender, Event,
|
|
||||||
abi::{Abi, Token, Detokenize, InvalidOutputType, Tokenizable},
|
abi::{Abi, Token, Detokenize, InvalidOutputType, Tokenizable},
|
||||||
Contract, Lazy,
|
Contract, ContractCall, Event, Lazy,
|
||||||
|
signers::{Client, Signer},
|
||||||
types::*, // import all the types so that we can codegen for everything
|
types::*, // import all the types so that we can codegen for everything
|
||||||
signers::{Signer, Client},
|
|
||||||
providers::JsonRpcClient,
|
providers::JsonRpcClient,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ fn expand_filter(event: &Event) -> Result<TokenStream> {
|
||||||
let ev_name = Literal::string(&event.name);
|
let ev_name = Literal::string(&event.name);
|
||||||
let result = util::ident(&event.name.to_pascal_case());
|
let result = util::ident(&event.name.to_pascal_case());
|
||||||
|
|
||||||
let doc = util::expand_doc(&format!("Gets the {} event", event.name));
|
let doc = util::expand_doc(&format!("Gets the contract's `{}` event", event.name));
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
|
|
||||||
#doc
|
#doc
|
||||||
|
|
|
@ -7,6 +7,7 @@ use anyhow::{anyhow, Context as _, Result};
|
||||||
use inflector::Inflector;
|
use inflector::Inflector;
|
||||||
use proc_macro2::{Literal, TokenStream};
|
use proc_macro2::{Literal, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use rustc_hex::ToHex;
|
||||||
use syn::Ident;
|
use syn::Ident;
|
||||||
|
|
||||||
/// Expands a context into a method struct containing all the generated bindings
|
/// Expands a context into a method struct containing all the generated bindings
|
||||||
|
@ -39,13 +40,17 @@ fn expand_function(function: &Function, alias: Option<Ident>) -> Result<TokenStr
|
||||||
let outputs = expand_fn_outputs(&function.outputs)?;
|
let outputs = expand_fn_outputs(&function.outputs)?;
|
||||||
|
|
||||||
let result = if function.constant {
|
let result = if function.constant {
|
||||||
quote! { Sender<'a, S, P, #outputs> }
|
quote! { ContractCall<'a, S, P, #outputs> }
|
||||||
} else {
|
} else {
|
||||||
quote! { Sender<'a, S, P, H256> }
|
quote! { ContractCall<'a, S, P, H256> }
|
||||||
};
|
};
|
||||||
|
|
||||||
let arg = expand_inputs_call_arg(&function.inputs);
|
let arg = expand_inputs_call_arg(&function.inputs);
|
||||||
let doc = util::expand_doc(&format!("Calls the contract's {} function", function.name));
|
let doc = util::expand_doc(&format!(
|
||||||
|
"Calls the contract's `{}` (0x{}) function",
|
||||||
|
function.name,
|
||||||
|
function.selector().to_hex::<String>()
|
||||||
|
));
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
|
|
||||||
#doc
|
#doc
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
use ethers_abi::{Detokenize, Function};
|
||||||
|
use ethers_providers::JsonRpcClient;
|
||||||
|
use ethers_signers::{Client, Signer};
|
||||||
|
use ethers_types::{Address, BlockNumber, TransactionRequest, H256, U256};
|
||||||
|
|
||||||
|
use std::{fmt::Debug, marker::PhantomData};
|
||||||
|
|
||||||
|
use thiserror::Error as ThisError;
|
||||||
|
|
||||||
|
pub struct ContractCall<'a, S, P, D> {
|
||||||
|
pub(crate) tx: TransactionRequest,
|
||||||
|
pub(crate) function: Function,
|
||||||
|
pub(crate) client: &'a Client<'a, S, P>,
|
||||||
|
pub(crate) block: Option<BlockNumber>,
|
||||||
|
pub(crate) datatype: PhantomData<D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S, P, D: Detokenize> ContractCall<'a, S, P, D> {
|
||||||
|
/// Sets the `from` field in the transaction to the provided value
|
||||||
|
pub fn from<T: Into<Address>>(mut self, from: T) -> Self {
|
||||||
|
self.tx.from = Some(from.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the `gas` field in the transaction to the provided value
|
||||||
|
pub fn gas<T: Into<U256>>(mut self, gas: T) -> Self {
|
||||||
|
self.tx.gas = Some(gas.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the `gas_price` field in the transaction to the provided value
|
||||||
|
pub fn gas_price<T: Into<U256>>(mut self, gas_price: T) -> Self {
|
||||||
|
self.tx.gas_price = Some(gas_price.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the `value` field in the transaction to the provided value
|
||||||
|
pub fn value<T: Into<U256>>(mut self, value: T) -> Self {
|
||||||
|
self.tx.value = Some(value.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ThisError, Debug)]
|
||||||
|
// TODO: Can we get rid of this static?
|
||||||
|
pub enum ContractError<P: JsonRpcClient>
|
||||||
|
where
|
||||||
|
P::Error: 'static,
|
||||||
|
{
|
||||||
|
#[error(transparent)]
|
||||||
|
DecodingError(#[from] ethers_abi::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
DetokenizationError(#[from] ethers_abi::InvalidOutputType),
|
||||||
|
#[error(transparent)]
|
||||||
|
CallError(P::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S: Signer, P: JsonRpcClient, D: Detokenize> ContractCall<'a, S, P, D>
|
||||||
|
where
|
||||||
|
P::Error: 'static,
|
||||||
|
{
|
||||||
|
/// Queries the blockchain via an `eth_call` for the provided transaction.
|
||||||
|
///
|
||||||
|
/// If executed on a non-state mutating smart contract function (i.e. `view`, `pure`)
|
||||||
|
/// then it will return the raw data from the chain.
|
||||||
|
///
|
||||||
|
/// If executed on a mutating smart contract function, it will do a "dry run" of the call
|
||||||
|
/// and return the return type of the transaction without mutating the state
|
||||||
|
///
|
||||||
|
/// Note: this function _does not_ send a transaction from your account
|
||||||
|
pub async fn call(self) -> Result<D, ContractError<P>> {
|
||||||
|
let bytes = self
|
||||||
|
.client
|
||||||
|
.call(self.tx, self.block)
|
||||||
|
.await
|
||||||
|
.map_err(ContractError::CallError)?;
|
||||||
|
|
||||||
|
let tokens = self.function.decode_output(&bytes.0)?;
|
||||||
|
|
||||||
|
let data = D::from_tokens(tokens)?;
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signs and broadcasts the provided transaction
|
||||||
|
pub async fn send(self) -> Result<H256, P::Error> {
|
||||||
|
self.client.send_transaction(self.tx, self.block).await
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,17 @@
|
||||||
use ethers_abi::{
|
use crate::{ContractCall, Event};
|
||||||
Abi, Detokenize, Error, Event as AbiEvent, EventExt, Function, FunctionExt, RawLog, Tokenize,
|
|
||||||
};
|
use ethers_abi::{Abi, Detokenize, Error, EventExt, Function, FunctionExt, Tokenize};
|
||||||
use ethers_providers::{JsonRpcClient, Provider};
|
use ethers_providers::JsonRpcClient;
|
||||||
use ethers_signers::{Client, Signer};
|
use ethers_signers::{Client, Signer};
|
||||||
use ethers_types::{
|
use ethers_types::{Address, Filter, Selector, TransactionRequest};
|
||||||
Address, BlockNumber, Filter, Selector, TransactionRequest, ValueOrArray, H256, U256,
|
|
||||||
};
|
|
||||||
|
|
||||||
use rustc_hex::ToHex;
|
use rustc_hex::ToHex;
|
||||||
use std::{collections::HashMap, fmt::Debug, hash::Hash, marker::PhantomData};
|
use std::{collections::HashMap, fmt::Debug, hash::Hash, marker::PhantomData};
|
||||||
|
|
||||||
use thiserror::Error as ThisError;
|
|
||||||
|
|
||||||
/// Represents a contract instance at an address. Provides methods for
|
/// Represents a contract instance at an address. Provides methods for
|
||||||
/// contract interaction.
|
/// contract interaction.
|
||||||
|
// TODO: Should we separate the lifetimes for the two references?
|
||||||
|
// https://stackoverflow.com/a/29862184
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Contract<'a, S, P> {
|
pub struct Contract<'a, S, P> {
|
||||||
client: &'a Client<'a, S, P>,
|
client: &'a Client<'a, S, P>,
|
||||||
|
@ -40,7 +38,7 @@ impl<'a, S: Signer, P: JsonRpcClient> Contract<'a, S, P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a transaction builder for the provided function name. If there are
|
/// Returns an `Event` builder for the provided event name. If there are
|
||||||
/// multiple functions with the same name due to overloading, consider using
|
/// multiple functions with the same name due to overloading, consider using
|
||||||
/// the `method_hash` method instead, since this will use the first match.
|
/// the `method_hash` method instead, since this will use the first match.
|
||||||
pub fn event<'b, D: Detokenize>(&'a self, name: &str) -> Result<Event<'a, 'b, P, D>, Error>
|
pub fn event<'b, D: Detokenize>(&'a self, name: &str) -> Result<Event<'a, 'b, P, D>, Error>
|
||||||
|
@ -64,7 +62,7 @@ impl<'a, S: Signer, P: JsonRpcClient> Contract<'a, S, P> {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
args: T,
|
args: T,
|
||||||
) -> Result<Sender<'a, S, P, D>, Error> {
|
) -> Result<ContractCall<'a, S, P, D>, Error> {
|
||||||
// get the function
|
// get the function
|
||||||
let function = self.abi.function(name)?;
|
let function = self.abi.function(name)?;
|
||||||
self.method_func(function, args)
|
self.method_func(function, args)
|
||||||
|
@ -76,7 +74,7 @@ impl<'a, S: Signer, P: JsonRpcClient> Contract<'a, S, P> {
|
||||||
&self,
|
&self,
|
||||||
signature: Selector,
|
signature: Selector,
|
||||||
args: T,
|
args: T,
|
||||||
) -> Result<Sender<'a, S, P, D>, Error> {
|
) -> Result<ContractCall<'a, S, P, D>, Error> {
|
||||||
let function = self
|
let function = self
|
||||||
.methods
|
.methods
|
||||||
.get(&signature)
|
.get(&signature)
|
||||||
|
@ -89,7 +87,7 @@ impl<'a, S: Signer, P: JsonRpcClient> Contract<'a, S, P> {
|
||||||
&self,
|
&self,
|
||||||
function: &Function,
|
function: &Function,
|
||||||
args: T,
|
args: T,
|
||||||
) -> Result<Sender<'a, S, P, D>, Error> {
|
) -> Result<ContractCall<'a, S, P, D>, Error> {
|
||||||
// create the calldata
|
// create the calldata
|
||||||
let data = function.encode_input(&args.into_tokens())?;
|
let data = function.encode_input(&args.into_tokens())?;
|
||||||
|
|
||||||
|
@ -100,7 +98,7 @@ impl<'a, S: Signer, P: JsonRpcClient> Contract<'a, S, P> {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Sender {
|
Ok(ContractCall {
|
||||||
tx,
|
tx,
|
||||||
client: self.client,
|
client: self.client,
|
||||||
block: None,
|
block: None,
|
||||||
|
@ -118,144 +116,6 @@ impl<'a, S: Signer, P: JsonRpcClient> Contract<'a, S, P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Sender<'a, S, P, D> {
|
|
||||||
tx: TransactionRequest,
|
|
||||||
function: Function,
|
|
||||||
client: &'a Client<'a, S, P>,
|
|
||||||
block: Option<BlockNumber>,
|
|
||||||
datatype: PhantomData<D>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, S, P, D: Detokenize> Sender<'a, S, P, D> {
|
|
||||||
/// Sets the `from` field in the transaction to the provided value
|
|
||||||
pub fn from<T: Into<Address>>(mut self, from: T) -> Self {
|
|
||||||
self.tx.from = Some(from.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `gas` field in the transaction to the provided value
|
|
||||||
pub fn gas<T: Into<U256>>(mut self, gas: T) -> Self {
|
|
||||||
self.tx.gas = Some(gas.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `gas_price` field in the transaction to the provided value
|
|
||||||
pub fn gas_price<T: Into<U256>>(mut self, gas_price: T) -> Self {
|
|
||||||
self.tx.gas_price = Some(gas_price.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `value` field in the transaction to the provided value
|
|
||||||
pub fn value<T: Into<U256>>(mut self, value: T) -> Self {
|
|
||||||
self.tx.value = Some(value.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ThisError, Debug)]
|
|
||||||
// TODO: Can we get rid of this static?
|
|
||||||
pub enum ContractError<P: JsonRpcClient>
|
|
||||||
where
|
|
||||||
P::Error: 'static,
|
|
||||||
{
|
|
||||||
#[error(transparent)]
|
|
||||||
DecodingError(#[from] ethers_abi::Error),
|
|
||||||
#[error(transparent)]
|
|
||||||
DetokenizationError(#[from] ethers_abi::InvalidOutputType),
|
|
||||||
#[error(transparent)]
|
|
||||||
CallError(P::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, S: Signer, P: JsonRpcClient, D: Detokenize> Sender<'a, S, P, D>
|
|
||||||
where
|
|
||||||
P::Error: 'static,
|
|
||||||
{
|
|
||||||
pub async fn call(self) -> Result<D, ContractError<P>> {
|
|
||||||
let bytes = self
|
|
||||||
.client
|
|
||||||
.call(self.tx, self.block)
|
|
||||||
.await
|
|
||||||
.map_err(ContractError::CallError)?;
|
|
||||||
|
|
||||||
let tokens = self.function.decode_output(&bytes.0)?;
|
|
||||||
|
|
||||||
let data = D::from_tokens(tokens)?;
|
|
||||||
|
|
||||||
Ok(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send(self) -> Result<H256, P::Error> {
|
|
||||||
self.client.send_transaction(self.tx, self.block).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Event<'a, 'b, P, D> {
|
|
||||||
pub filter: Filter,
|
|
||||||
provider: &'a Provider<P>,
|
|
||||||
event: &'b AbiEvent,
|
|
||||||
datatype: PhantomData<D>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, P, D: Detokenize> Event<'a, 'b, P, D> {
|
|
||||||
pub fn from_block<T: Into<BlockNumber>>(mut self, block: T) -> Self {
|
|
||||||
self.filter.from_block = Some(block.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_block<T: Into<BlockNumber>>(mut self, block: T) -> Self {
|
|
||||||
self.filter.to_block = Some(block.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn topic0<T: Into<ValueOrArray<H256>>>(mut self, topic: T) -> Self {
|
|
||||||
self.filter.topics.push(topic.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn topics(mut self, topics: &[ValueOrArray<H256>]) -> Self {
|
|
||||||
self.filter.topics.extend_from_slice(topics);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Can we get rid of the static?
|
|
||||||
impl<'a, 'b, P: JsonRpcClient, D: Detokenize> Event<'a, 'b, P, D>
|
|
||||||
where
|
|
||||||
P::Error: 'static,
|
|
||||||
{
|
|
||||||
pub async fn query(self) -> Result<Vec<D>, ContractError<P>> {
|
|
||||||
// get the logs
|
|
||||||
let logs = self
|
|
||||||
.provider
|
|
||||||
.get_logs(&self.filter)
|
|
||||||
.await
|
|
||||||
.map_err(ContractError::CallError)?;
|
|
||||||
|
|
||||||
let events = logs
|
|
||||||
.into_iter()
|
|
||||||
.map(|log| {
|
|
||||||
// ethabi parses the unindexed and indexed logs together to a
|
|
||||||
// vector of tokens
|
|
||||||
let tokens = self
|
|
||||||
.event
|
|
||||||
.parse_log(RawLog {
|
|
||||||
topics: log.topics,
|
|
||||||
data: log.data.0,
|
|
||||||
})?
|
|
||||||
.params
|
|
||||||
.into_iter()
|
|
||||||
.map(|param| param.value)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// convert the tokens to the requested datatype
|
|
||||||
Ok::<_, ContractError<P>>(D::from_tokens(tokens)?)
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
Ok(events)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Utility function for creating a mapping between a unique signature and a
|
/// Utility function for creating a mapping between a unique signature and a
|
||||||
/// name-index pair for accessing contract ABI items.
|
/// name-index pair for accessing contract ABI items.
|
||||||
fn create_mapping<T, S, F>(
|
fn create_mapping<T, S, F>(
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
use crate::ContractError;
|
||||||
|
|
||||||
|
use ethers_abi::{Detokenize, Event as AbiEvent, RawLog};
|
||||||
|
use ethers_providers::{JsonRpcClient, Provider};
|
||||||
|
|
||||||
|
use ethers_types::{BlockNumber, Filter, ValueOrArray, H256};
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
pub struct Event<'a, 'b, P, D> {
|
||||||
|
pub filter: Filter,
|
||||||
|
pub(crate) provider: &'a Provider<P>,
|
||||||
|
pub(crate) event: &'b AbiEvent,
|
||||||
|
pub(crate) datatype: PhantomData<D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Improve these functions
|
||||||
|
impl<'a, 'b, P, D: Detokenize> Event<'a, 'b, P, D> {
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
pub fn from_block<T: Into<BlockNumber>>(mut self, block: T) -> Self {
|
||||||
|
self.filter.from_block = Some(block.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
pub fn to_block<T: Into<BlockNumber>>(mut self, block: T) -> Self {
|
||||||
|
self.filter.to_block = Some(block.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn topic0<T: Into<ValueOrArray<H256>>>(mut self, topic: T) -> Self {
|
||||||
|
self.filter.topics.push(topic.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn topics(mut self, topics: &[ValueOrArray<H256>]) -> Self {
|
||||||
|
self.filter.topics.extend_from_slice(topics);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Can we get rid of the static?
|
||||||
|
impl<'a, 'b, P: JsonRpcClient, D: Detokenize> Event<'a, 'b, P, D>
|
||||||
|
where
|
||||||
|
P::Error: 'static,
|
||||||
|
{
|
||||||
|
/// Queries the blockchain for the selected filter and returns a vector of matching
|
||||||
|
/// event logs
|
||||||
|
pub async fn query(self) -> Result<Vec<D>, ContractError<P>> {
|
||||||
|
// get the logs
|
||||||
|
let logs = self
|
||||||
|
.provider
|
||||||
|
.get_logs(&self.filter)
|
||||||
|
.await
|
||||||
|
.map_err(ContractError::CallError)?;
|
||||||
|
|
||||||
|
let events = logs
|
||||||
|
.into_iter()
|
||||||
|
.map(|log| {
|
||||||
|
// ethabi parses the unindexed and indexed logs together to a
|
||||||
|
// vector of tokens
|
||||||
|
let tokens = self
|
||||||
|
.event
|
||||||
|
.parse_log(RawLog {
|
||||||
|
topics: log.topics,
|
||||||
|
data: log.data.0,
|
||||||
|
})?
|
||||||
|
.params
|
||||||
|
.into_iter()
|
||||||
|
.map(|param| param.value)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// convert the tokens to the requested datatype
|
||||||
|
Ok::<_, ContractError<P>>(D::from_tokens(tokens)?)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok(events)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add filter watchers
|
||||||
|
}
|
|
@ -1,5 +1,11 @@
|
||||||
mod contract;
|
mod contract;
|
||||||
pub use contract::*;
|
pub use contract::Contract;
|
||||||
|
|
||||||
|
mod event;
|
||||||
|
pub use event::Event;
|
||||||
|
|
||||||
|
mod call;
|
||||||
|
pub use call::{ContractCall, ContractError};
|
||||||
|
|
||||||
#[cfg(feature = "abigen")]
|
#[cfg(feature = "abigen")]
|
||||||
pub use ethers_contract_abigen::Builder;
|
pub use ethers_contract_abigen::Builder;
|
||||||
|
|
Loading…
Reference in New Issue