fix: serialize null filters
This commit is contained in:
parent
701e442f94
commit
b0feff2432
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue