fix: serialize null filters

This commit is contained in:
Georgios Konstantopoulos 2020-06-03 00:10:46 +03:00
parent 701e442f94
commit b0feff2432
No known key found for this signature in database
GPG Key ID: FA607837CD26EDBC
8 changed files with 67 additions and 38 deletions

View File

@ -57,11 +57,13 @@ impl Context {
cx.contract_name.to_string().to_lowercase()
));
let abi_name = super::util::safe_ident(&format!("{}_ABI", name.to_string().to_uppercase()));
// 0. Imports
let imports = common::imports();
// 1. Declare Contract struct
let struct_decl = common::struct_declaration(&cx);
let struct_decl = common::struct_declaration(&cx, &abi_name);
// 2. Declare events structs & impl FromTokens for each event
let events_decl = cx.events_declaration()?;
@ -86,7 +88,7 @@ impl Context {
/// client at the given `Address`. The contract derefs to a `ethers::Contract`
/// object
pub fn new<T: Into<Address>>(address: T, client: &'a Client<P, S>) -> Self {
let contract = Contract::new(address.into(), &ABI, client);
let contract = Contract::new(address.into(), &#abi_name, client);
Self(contract)
}

View File

@ -20,13 +20,13 @@ pub(crate) fn imports() -> TokenStream {
}
}
pub(crate) fn struct_declaration(cx: &Context) -> TokenStream {
pub(crate) fn struct_declaration(cx: &Context, abi_name: &proc_macro2::Ident) -> TokenStream {
let name = &cx.contract_name;
let abi = &cx.abi_str;
quote! {
// Inline ABI declaration
static ABI: Lazy<Abi> = Lazy::new(|| serde_json::from_str(#abi)
pub static #abi_name: Lazy<Abi> = Lazy::new(|| serde_json::from_str(#abi)
.expect("invalid abi"));
// Struct declaration

View File

@ -102,12 +102,13 @@ async fn deploy_and_call_contract() {
.event("ValueChanged")
.unwrap()
.from_block(0u64)
.topic1(client.address()) // Corresponds to the first indexed parameter
.query()
.await
.unwrap();
assert_eq!(logs[0].new_value, "initial value");
assert_eq!(logs[1].new_value, "hi");
assert_eq!(logs[2].new_value, "hi2");
assert_eq!(logs[1].new_value, "hi2");
assert_eq!(logs.len(), 2);
let logs: Vec<ValueChanged> = contract2
.event("ValueChanged")
@ -123,19 +124,22 @@ async fn deploy_and_call_contract() {
// Note: We also provide the `abigen` macro for generating these bindings automatically
#[derive(Clone, Debug)]
struct ValueChanged {
author: Address,
old_author: Address,
new_author: Address,
old_value: String,
new_value: String,
}
impl Detokenize for ValueChanged {
fn from_tokens(tokens: Vec<Token>) -> Result<ValueChanged, InvalidOutputType> {
let author: Address = tokens[0].clone().to_address().unwrap();
let old_value = tokens[1].clone().to_string().unwrap();
let new_value = tokens[2].clone().to_string().unwrap();
let old_author: Address = tokens[1].clone().to_address().unwrap();
let new_author: Address = tokens[1].clone().to_address().unwrap();
let old_value = tokens[2].clone().to_string().unwrap();
let new_value = tokens[3].clone().to_string().unwrap();
Ok(Self {
author,
old_author,
new_author,
old_value,
new_value,
})

View File

@ -2,13 +2,13 @@ pragma solidity >=0.4.24;
contract SimpleStorage {
event ValueChanged(address indexed author, string oldValue, string newValue);
event ValueChanged(address indexed author, address indexed oldAuthor, string oldValue, string newValue);
address public lastSender;
string _value;
constructor(string memory value) public {
emit ValueChanged(msg.sender, _value, value);
emit ValueChanged(msg.sender, address(0), _value, value);
_value = value;
}
@ -17,7 +17,7 @@ contract SimpleStorage {
}
function setValue(string memory value) public {
emit ValueChanged(msg.sender, _value, value);
emit ValueChanged(msg.sender, lastSender, _value, value);
_value = value;
lastSender = msg.sender;
}

View File

@ -3,7 +3,7 @@ use crate::{
types::{Address, BlockNumber, Bytes, H256, U256, U64},
utils::keccak256,
};
use serde::{ser::SerializeSeq, Deserialize, Serialize, Serializer};
use serde::{Deserialize, Serialize, Serializer};
use std::str::FromStr;
/// A log produced by a transaction.
@ -83,7 +83,6 @@ pub struct Filter {
/// Topics
// 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<ValueOrArray<H256>>; 4],
/// Limit
@ -202,24 +201,6 @@ where
}
}
// adapted from https://github.com/serde-rs/serde/issues/550#issuecomment-246746639
fn skip_nones<T, S>(elements: &[Option<T>], serializer: S) -> Result<S::Ok, S::Error>
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::*;

View File

@ -4,7 +4,7 @@ use ethers_core::{
abi::{self, Detokenize, ParamType},
types::{
Address, Block, BlockId, BlockNumber, Bytes, Filter, Log, NameOrAddress, Selector,
Transaction, TransactionReceipt, TransactionRequest, TxHash, U256,
Signature, Transaction, TransactionReceipt, TransactionRequest, TxHash, U256, U64,
},
utils,
};
@ -40,7 +40,7 @@ impl<P: JsonRpcClient> Provider<P> {
// Functions for querying the state of the blockchain
/// Gets the latest block number via the `eth_BlockNumber` API
pub async fn get_block_number(&self) -> Result<U256, ProviderError> {
pub async fn get_block_number(&self) -> Result<U64, ProviderError> {
Ok(self
.0
.request("eth_blockNumber", None::<()>)
@ -258,6 +258,17 @@ impl<P: JsonRpcClient> Provider<P> {
.map_err(Into::into)?)
}
/// Signs data using a specific account. This account needs to be unlocked.
pub async fn sign(&self, data: &Bytes, from: &Address) -> Result<Signature, ProviderError> {
let data = utils::serialize(data);
let from = utils::serialize(from);
Ok(self
.0
.request("eth_sign", Some(vec![from, data]))
.await
.map_err(Into::into)?)
}
////// Contract state
/// Returns an array (possibly empty) of logs that match the filter

View File

@ -1,6 +1,8 @@
use crate::Signer;
use ethers_core::types::{Address, BlockNumber, NameOrAddress, TransactionRequest, TxHash};
use ethers_core::types::{
Address, BlockNumber, Bytes, NameOrAddress, Signature, TransactionRequest, TxHash,
};
use ethers_providers::{JsonRpcClient, Provider, ProviderError};
use std::ops::Deref;
@ -13,6 +15,7 @@ use thiserror::Error;
pub struct Client<P, S> {
pub(crate) provider: Provider<P>,
pub(crate) signer: Option<S>,
pub(crate) address: Address,
}
impl<P, S> From<Provider<P>> for Client<P, S> {
@ -20,6 +23,7 @@ impl<P, S> From<Provider<P>> for Client<P, S> {
Client {
provider,
signer: None,
address: Address::zero(),
}
}
}
@ -36,11 +40,23 @@ pub enum ClientError {
EnsError(String),
}
// Helper functions for locally signing transactions
impl<P, S> Client<P, S>
where
S: Signer,
P: JsonRpcClient,
{
/// Signs a message with the internal signer, or if none is present it will make a call to
/// the connected node's `eth_call` API.
pub async fn sign_message<T: Into<Bytes>>(&self, msg: T) -> Result<Signature, ClientError> {
let msg = msg.into();
Ok(if let Some(ref signer) = self.signer {
signer.sign_message(msg)
} else {
self.provider.sign(&msg, &self.address).await?
})
}
/// Signs and broadcasts the transaction
pub async fn send_transaction(
&self,
@ -124,10 +140,23 @@ where
self.signer.as_ref().expect("no signer is configured")
}
pub fn with_signer(mut self, signer: S) -> Self {
/// Sets the signer
pub fn with_signer(&mut self, signer: S) -> &mut Self {
self.signer = Some(signer);
self
}
/// Sets the provider
pub fn with_provider(&mut self, provider: Provider<P>) -> &mut Self {
self.provider = provider;
self
}
/// Sets the account to be used with the `eth_sign` API calls
pub fn from(&mut self, address: Address) -> &mut Self {
self.address = address;
self
}
}
// Abuse Deref to use the Provider's methods without re-writing everything.

View File

@ -64,7 +64,9 @@ impl Wallet {
/// Connects to a provider and returns a client
pub fn connect<P: JsonRpcClient>(self, provider: Provider<P>) -> Client<P, Wallet> {
let address = self.address();
Client {
address,
signer: Some(self),
provider,
}