From e51fbd06b0225fa427aa253c6a7fd1a5cfae9cf5 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Thu, 28 May 2020 17:53:22 +0300 Subject: [PATCH] ethers-types docs --- crates/ethers-contract/src/event.rs | 7 +- crates/ethers-types/src/abi/mod.rs | 2 +- crates/ethers-types/src/abi/tokens.rs | 2 +- crates/ethers-types/src/chainstate/log.rs | 93 ++++++++++++++++--- crates/ethers-types/src/chainstate/mod.rs | 5 +- .../src/chainstate/transaction.rs | 25 ----- crates/ethers-types/src/crypto/keys.rs | 5 + crates/ethers-types/src/lib.rs | 42 ++++++++- 8 files changed, 134 insertions(+), 47 deletions(-) diff --git a/crates/ethers-contract/src/event.rs b/crates/ethers-contract/src/event.rs index 691c5668..303935cd 100644 --- a/crates/ethers-contract/src/event.rs +++ b/crates/ethers-contract/src/event.rs @@ -31,12 +31,7 @@ impl<'a, 'b, P, N, D: Detokenize> Event<'a, 'b, P, N, D> { } pub fn topic0>>(mut self, topic: T) -> Self { - self.filter.topics.push(topic.into()); - self - } - - pub fn topics(mut self, topics: &[ValueOrArray]) -> Self { - self.filter.topics.extend_from_slice(topics); + self.filter.topics[0] = topic.into(); self } } diff --git a/crates/ethers-types/src/abi/mod.rs b/crates/ethers-types/src/abi/mod.rs index eae95c54..91c7ab26 100644 --- a/crates/ethers-types/src/abi/mod.rs +++ b/crates/ethers-types/src/abi/mod.rs @@ -1,5 +1,5 @@ //! This module implements extensions to the `ethabi` API. -//! Taken from: https://github.com/gnosis/ethcontract-rs/blob/master/common/src/abiext.rs +// Adapted from [Gnosis' ethcontract](https://github.com/gnosis/ethcontract-rs/blob/master/common/src/abiext.rs) use crate::Selector; use ethers_utils::id; diff --git a/crates/ethers-types/src/abi/tokens.rs b/crates/ethers-types/src/abi/tokens.rs index 35861522..5fbb44fa 100644 --- a/crates/ethers-types/src/abi/tokens.rs +++ b/crates/ethers-types/src/abi/tokens.rs @@ -1,5 +1,5 @@ //! Contract Functions Output types. -//! Adapted from: https://github.com/tomusdrw/rust-web3/blob/master/src/contract/tokens.rs +// Adapted from: [rust-web3](https://github.com/tomusdrw/rust-web3/blob/master/src/contract/tokens.rs) #![allow(clippy::all)] use crate::{abi::Token, Address, Bytes, H256, U128, U256}; diff --git a/crates/ethers-types/src/chainstate/log.rs b/crates/ethers-types/src/chainstate/log.rs index 4e3b573b..2ddd3b79 100644 --- a/crates/ethers-types/src/chainstate/log.rs +++ b/crates/ethers-types/src/chainstate/log.rs @@ -1,6 +1,7 @@ +// Adapted from https://github.com/tomusdrw/rust-web3/blob/master/src/types/log.rs use crate::{Address, BlockNumber, Bytes, H256, U256, U64}; use ethers_utils::keccak256; -use serde::{Deserialize, Serialize}; +use serde::{ser::SerializeSeq, Deserialize, Serialize, Serializer}; use std::str::FromStr; /// A log produced by a transaction. @@ -60,7 +61,7 @@ pub struct Log { pub removed: Option, } -/// Filter +/// Filter for #[derive(Default, Debug, PartialEq, Clone, Serialize)] pub struct Filter { /// From Block @@ -78,9 +79,10 @@ pub struct Filter { address: Option
, /// Topics - #[serde(skip_serializing_if = "Vec::is_empty")] - // TODO: Split in an event name + 3 topics - pub topics: Vec>, + // TODO: We could improve the low level API here by using ethabi's RawTopicFilter + // and/or TopicFilter + #[serde(serialize_with = "skip_nones")] + pub topics: [Option>; 4], /// Limit #[serde(skip_serializing_if = "Option::is_none")] @@ -117,16 +119,30 @@ impl Filter { /// given the event in string form, it hashes it and adds it to the topics to monitor pub fn event(self, event_name: &str) -> Self { let hash = H256::from(keccak256(event_name.as_bytes())); - self.topic(hash) + self.topic0(hash) } - pub fn topic>>(mut self, topic: T) -> Self { - self.topics.push(topic.into()); + /// Sets topic0 (the event name for non-anonymous events) + pub fn topic0>>(mut self, topic: T) -> Self { + self.topics[0] = Some(topic.into()); self } - pub fn topics(mut self, topics: &[ValueOrArray]) -> Self { - self.topics.extend_from_slice(topics); + /// Sets the 1st indexed topic + pub fn topic1>>(mut self, topic: T) -> Self { + self.topics[1] = Some(topic.into()); + self + } + + /// Sets the 2nd indexed topic + pub fn topic2>>(mut self, topic: T) -> Self { + self.topics[2] = Some(topic.into()); + self + } + + /// Sets the 3rd indexed topic + pub fn topic3>>(mut self, topic: T) -> Self { + self.topics[3] = Some(topic.into()); self } @@ -136,12 +152,17 @@ impl Filter { } } +/// Union type for representing a single value or a vector of values inside a filter #[derive(Debug, PartialEq, Clone)] pub enum ValueOrArray { + /// A single value Value(T), + /// A vector of values Array(Vec), } +// TODO: Implement more common types - or adjust this to work with all Tokenizable items + impl From for ValueOrArray { fn from(src: H256) -> Self { ValueOrArray::Value(src) @@ -156,13 +177,21 @@ impl From
for ValueOrArray { } } +impl From for ValueOrArray { + fn from(src: U256) -> Self { + let mut bytes = [0; 32]; + src.to_big_endian(&mut bytes); + ValueOrArray::Value(H256::from(bytes)) + } +} + impl Serialize for ValueOrArray where T: Serialize, { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { match self { ValueOrArray::Value(inner) => inner.serialize(serializer), @@ -170,3 +199,45 @@ where } } } + +// adapted from https://github.com/serde-rs/serde/issues/550#issuecomment-246746639 +fn skip_nones(elements: &[Option], serializer: S) -> Result +where + T: Serialize, + S: Serializer, +{ + // get number of Some elements + let len = elements.iter().filter(|opt| opt.is_some()).count(); + + let mut seq = serializer.serialize_seq(Some(len))?; + for elem in elements { + if elem.is_some() { + seq.serialize_element(elem)?; + } + } + seq.end() +} + +#[cfg(test)] +mod tests { + use super::*; + use ethers_utils::serialize; + + #[test] + fn filter_serialization_test() { + let t1 = "9729a6fbefefc8f6005933898b13dc45c3a2c8b7" + .parse::
() + .unwrap(); + let t3 = U256::from(123); + let filter = Filter::new() + .address_str("f817796F60D268A36a57b8D2dF1B97B14C0D0E1d") + .unwrap() + .event("ValueChanged(address,string,string)") // event name + .topic1(t1) + .topic2(t3); + + dbg!(&filter); + let ser = serialize(&filter).to_string(); + assert_eq!(ser, "{\"address\":\"0xf817796f60d268a36a57b8d2df1b97b14c0d0e1d\",\"topics\":[\"0xe826f71647b8486f2bae59832124c70792fba044036720a54ec8dacdd5df4fcb\",\"0x0000000000000000000000009729a6fbefefc8f6005933898b13dc45c3a2c8b7\",\"0x000000000000000000000000000000000000000000000000000000000000007b\"]}"); + } +} diff --git a/crates/ethers-types/src/chainstate/mod.rs b/crates/ethers-types/src/chainstate/mod.rs index 2933bdf4..11d287ed 100644 --- a/crates/ethers-types/src/chainstate/mod.rs +++ b/crates/ethers-types/src/chainstate/mod.rs @@ -1,11 +1,14 @@ pub type Selector = [u8; 4]; // Re-export common ethereum datatypes with more specific names + +/// A transaction Hash pub use ethereum_types::H256 as TxHash; + pub use ethereum_types::{Address, Bloom, H160, H256, U128, U256, U64}; mod transaction; -pub use transaction::{Overrides, Transaction, TransactionReceipt, TransactionRequest}; +pub use transaction::{Transaction, TransactionReceipt, TransactionRequest}; mod bytes; pub use bytes::Bytes; diff --git a/crates/ethers-types/src/chainstate/transaction.rs b/crates/ethers-types/src/chainstate/transaction.rs index 63acbf9c..b150bb20 100644 --- a/crates/ethers-types/src/chainstate/transaction.rs +++ b/crates/ethers-types/src/chainstate/transaction.rs @@ -6,31 +6,6 @@ use rlp::RlpStream; use serde::{Deserialize, Serialize}; use std::str::FromStr; -/// Override params for interacting with a contract -#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Debug)] -pub struct Overrides { - /// Sender address or ENS name - #[serde(skip_serializing_if = "Option::is_none")] - pub from: Option
, - - /// Supplied gas (None for sensible default) - #[serde(skip_serializing_if = "Option::is_none")] - pub gas: Option, - - /// Gas price (None for sensible default) - #[serde(rename = "gasPrice")] - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_price: Option, - - /// Transfered value (None for no transfer) - #[serde(skip_serializing_if = "Option::is_none")] - pub value: Option, - - /// Transaction nonce (None for next available nonce) - #[serde(skip_serializing_if = "Option::is_none")] - pub nonce: Option, -} - /// Parameters for sending a transaction #[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Debug)] pub struct TransactionRequest { diff --git a/crates/ethers-types/src/crypto/keys.rs b/crates/ethers-types/src/crypto/keys.rs index 8398bc48..c021426a 100644 --- a/crates/ethers-types/src/crypto/keys.rs +++ b/crates/ethers-types/src/crypto/keys.rs @@ -24,12 +24,17 @@ impl FromStr for PrivateKey { } } +/// An error which may be thrown when attempting to sign a transaction with +/// missing fields #[derive(Clone, Debug, Error)] pub enum TxError { + /// Thrown if the `nonce` field is missing #[error("no nonce was specified")] NonceMissing, + /// Thrown if the `gas_price` field is missing #[error("no gas price was specified")] GasPriceMissing, + /// Thrown if the `gas` field is missing #[error("no gas was specified")] GasMissing, } diff --git a/crates/ethers-types/src/lib.rs b/crates/ethers-types/src/lib.rs index 864931c4..02754663 100644 --- a/crates/ethers-types/src/lib.rs +++ b/crates/ethers-types/src/lib.rs @@ -1,5 +1,40 @@ -//! Various Ethereum Related Datatypes - +//! # Ethereum Related DataTypes +//! +//! This library provides type definitions for Ethereum's main datatypes +//! +//! # Signing an ethereum-prefixed message +//! +//! Signing in Ethereum is done by first prefixing the message with +//! `"\x19Ethereum Signed Message:\n" + message.length`, and then +//! signing the hash of the result. +//! +//! ```rust +//! use ethers_types::{PrivateKey, Address}; +//! +//! let message = "Some data"; +//! let key = PrivateKey::new(&mut rand::thread_rng()); +//! let address = Address::from(key); +//! +//! // Sign the message +//! let signature = key.sign(message); +//! +//! // Recover the signer from the message +//! let recovered = signature.recover(message).unwrap(); +//! +//! assert_eq!(recovered, address); +//! ``` +//! +//! # ABI Encoding and Decoding +//! +//! This crate re-exports the [`ethabi`](http://docs.rs/ethabi) crate's functions +//! under the `abi` module +//! +//! # A note about `secp256k1` and `rand` +//! +//! The version of `rand` used in the `secp256k1` crate is not compatible with the +//! latest one in crates at the time of writing (rand version 0.5.1, secp256k1 version 0.17.1) +//! As a result, the RNGs used for generating private keys must use a compatible rand crate +//! version. For convenience, we re-export it so that consumers can use it as `ethers_types::rand`. mod crypto; pub use crypto::*; @@ -8,3 +43,6 @@ pub use chainstate::*; #[cfg(feature = "abi")] pub mod abi; + +// Convenience re-export +pub use ethers_utils as utils;