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()
|
cx.contract_name.to_string().to_lowercase()
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let abi_name = super::util::safe_ident(&format!("{}_ABI", name.to_string().to_uppercase()));
|
||||||
|
|
||||||
// 0. Imports
|
// 0. Imports
|
||||||
let imports = common::imports();
|
let imports = common::imports();
|
||||||
|
|
||||||
// 1. Declare Contract struct
|
// 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
|
// 2. Declare events structs & impl FromTokens for each event
|
||||||
let events_decl = cx.events_declaration()?;
|
let events_decl = cx.events_declaration()?;
|
||||||
|
@ -86,7 +88,7 @@ impl Context {
|
||||||
/// client at the given `Address`. The contract derefs to a `ethers::Contract`
|
/// client at the given `Address`. The contract derefs to a `ethers::Contract`
|
||||||
/// object
|
/// object
|
||||||
pub fn new<T: Into<Address>>(address: T, client: &'a Client<P, S>) -> Self {
|
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)
|
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 name = &cx.contract_name;
|
||||||
let abi = &cx.abi_str;
|
let abi = &cx.abi_str;
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
// Inline ABI declaration
|
// 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"));
|
.expect("invalid abi"));
|
||||||
|
|
||||||
// Struct declaration
|
// Struct declaration
|
||||||
|
|
|
@ -102,12 +102,13 @@ async fn deploy_and_call_contract() {
|
||||||
.event("ValueChanged")
|
.event("ValueChanged")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.from_block(0u64)
|
.from_block(0u64)
|
||||||
|
.topic1(client.address()) // Corresponds to the first indexed parameter
|
||||||
.query()
|
.query()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(logs[0].new_value, "initial value");
|
assert_eq!(logs[0].new_value, "initial value");
|
||||||
assert_eq!(logs[1].new_value, "hi");
|
assert_eq!(logs[1].new_value, "hi2");
|
||||||
assert_eq!(logs[2].new_value, "hi2");
|
assert_eq!(logs.len(), 2);
|
||||||
|
|
||||||
let logs: Vec<ValueChanged> = contract2
|
let logs: Vec<ValueChanged> = contract2
|
||||||
.event("ValueChanged")
|
.event("ValueChanged")
|
||||||
|
@ -123,19 +124,22 @@ async fn deploy_and_call_contract() {
|
||||||
// Note: We also provide the `abigen` macro for generating these bindings automatically
|
// Note: We also provide the `abigen` macro for generating these bindings automatically
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct ValueChanged {
|
struct ValueChanged {
|
||||||
author: Address,
|
old_author: Address,
|
||||||
|
new_author: Address,
|
||||||
old_value: String,
|
old_value: String,
|
||||||
new_value: String,
|
new_value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Detokenize for ValueChanged {
|
impl Detokenize for ValueChanged {
|
||||||
fn from_tokens(tokens: Vec<Token>) -> Result<ValueChanged, InvalidOutputType> {
|
fn from_tokens(tokens: Vec<Token>) -> Result<ValueChanged, InvalidOutputType> {
|
||||||
let author: Address = tokens[0].clone().to_address().unwrap();
|
let old_author: Address = tokens[1].clone().to_address().unwrap();
|
||||||
let old_value = tokens[1].clone().to_string().unwrap();
|
let new_author: Address = tokens[1].clone().to_address().unwrap();
|
||||||
let new_value = tokens[2].clone().to_string().unwrap();
|
let old_value = tokens[2].clone().to_string().unwrap();
|
||||||
|
let new_value = tokens[3].clone().to_string().unwrap();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
author,
|
old_author,
|
||||||
|
new_author,
|
||||||
old_value,
|
old_value,
|
||||||
new_value,
|
new_value,
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,13 +2,13 @@ pragma solidity >=0.4.24;
|
||||||
|
|
||||||
contract SimpleStorage {
|
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;
|
address public lastSender;
|
||||||
string _value;
|
string _value;
|
||||||
|
|
||||||
constructor(string memory value) public {
|
constructor(string memory value) public {
|
||||||
emit ValueChanged(msg.sender, _value, value);
|
emit ValueChanged(msg.sender, address(0), _value, value);
|
||||||
_value = value;
|
_value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ contract SimpleStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setValue(string memory value) public {
|
function setValue(string memory value) public {
|
||||||
emit ValueChanged(msg.sender, _value, value);
|
emit ValueChanged(msg.sender, lastSender, _value, value);
|
||||||
_value = value;
|
_value = value;
|
||||||
lastSender = msg.sender;
|
lastSender = msg.sender;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
types::{Address, BlockNumber, Bytes, H256, U256, U64},
|
types::{Address, BlockNumber, Bytes, H256, U256, U64},
|
||||||
utils::keccak256,
|
utils::keccak256,
|
||||||
};
|
};
|
||||||
use serde::{ser::SerializeSeq, Deserialize, Serialize, Serializer};
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// A log produced by a transaction.
|
/// A log produced by a transaction.
|
||||||
|
@ -83,7 +83,6 @@ pub struct Filter {
|
||||||
/// Topics
|
/// Topics
|
||||||
// TODO: We could improve the low level API here by using ethabi's RawTopicFilter
|
// TODO: We could improve the low level API here by using ethabi's RawTopicFilter
|
||||||
// and/or TopicFilter
|
// and/or TopicFilter
|
||||||
#[serde(serialize_with = "skip_nones")]
|
|
||||||
pub topics: [Option<ValueOrArray<H256>>; 4],
|
pub topics: [Option<ValueOrArray<H256>>; 4],
|
||||||
|
|
||||||
/// Limit
|
/// 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ethers_core::{
|
||||||
abi::{self, Detokenize, ParamType},
|
abi::{self, Detokenize, ParamType},
|
||||||
types::{
|
types::{
|
||||||
Address, Block, BlockId, BlockNumber, Bytes, Filter, Log, NameOrAddress, Selector,
|
Address, Block, BlockId, BlockNumber, Bytes, Filter, Log, NameOrAddress, Selector,
|
||||||
Transaction, TransactionReceipt, TransactionRequest, TxHash, U256,
|
Signature, Transaction, TransactionReceipt, TransactionRequest, TxHash, U256, U64,
|
||||||
},
|
},
|
||||||
utils,
|
utils,
|
||||||
};
|
};
|
||||||
|
@ -40,7 +40,7 @@ impl<P: JsonRpcClient> Provider<P> {
|
||||||
// Functions for querying the state of the blockchain
|
// Functions for querying the state of the blockchain
|
||||||
|
|
||||||
/// Gets the latest block number via the `eth_BlockNumber` API
|
/// 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
|
Ok(self
|
||||||
.0
|
.0
|
||||||
.request("eth_blockNumber", None::<()>)
|
.request("eth_blockNumber", None::<()>)
|
||||||
|
@ -258,6 +258,17 @@ impl<P: JsonRpcClient> Provider<P> {
|
||||||
.map_err(Into::into)?)
|
.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
|
////// Contract state
|
||||||
|
|
||||||
/// Returns an array (possibly empty) of logs that match the filter
|
/// Returns an array (possibly empty) of logs that match the filter
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::Signer;
|
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 ethers_providers::{JsonRpcClient, Provider, ProviderError};
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -13,6 +15,7 @@ use thiserror::Error;
|
||||||
pub struct Client<P, S> {
|
pub struct Client<P, S> {
|
||||||
pub(crate) provider: Provider<P>,
|
pub(crate) provider: Provider<P>,
|
||||||
pub(crate) signer: Option<S>,
|
pub(crate) signer: Option<S>,
|
||||||
|
pub(crate) address: Address,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, S> From<Provider<P>> for Client<P, S> {
|
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 {
|
Client {
|
||||||
provider,
|
provider,
|
||||||
signer: None,
|
signer: None,
|
||||||
|
address: Address::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,11 +40,23 @@ pub enum ClientError {
|
||||||
EnsError(String),
|
EnsError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper functions for locally signing transactions
|
||||||
impl<P, S> Client<P, S>
|
impl<P, S> Client<P, S>
|
||||||
where
|
where
|
||||||
S: Signer,
|
S: Signer,
|
||||||
P: JsonRpcClient,
|
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
|
/// Signs and broadcasts the transaction
|
||||||
pub async fn send_transaction(
|
pub async fn send_transaction(
|
||||||
&self,
|
&self,
|
||||||
|
@ -124,10 +140,23 @@ where
|
||||||
self.signer.as_ref().expect("no signer is configured")
|
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.signer = Some(signer);
|
||||||
self
|
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.
|
// 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
|
/// Connects to a provider and returns a client
|
||||||
pub fn connect<P: JsonRpcClient>(self, provider: Provider<P>) -> Client<P, Wallet> {
|
pub fn connect<P: JsonRpcClient>(self, provider: Provider<P>) -> Client<P, Wallet> {
|
||||||
|
let address = self.address();
|
||||||
Client {
|
Client {
|
||||||
|
address,
|
||||||
signer: Some(self),
|
signer: Some(self),
|
||||||
provider,
|
provider,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue