feat(signers): make Signer on Client optional (#34)

This allows using Contract either in read-only mode (e.g. for fetching data/events)
or for using the accounts on the node as senders
This commit is contained in:
Georgios Konstantopoulos 2020-06-21 21:19:59 +03:00 committed by GitHub
parent 9a0c97286b
commit 833ca33f60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 14 deletions

View File

@ -148,6 +148,7 @@ where
// create the tx object. Since we're deploying a contract, `to` is `None`
let tx = TransactionRequest {
from: Some(self.client.address()),
to: None,
data: Some(data),
..Default::default()

View File

@ -6,8 +6,14 @@ pub use common::*;
#[cfg(not(feature = "celo"))]
mod eth_tests {
use super::*;
use ethers::{providers::StreamExt, types::Address, utils::Ganache};
use ethers::{
providers::{Http, Provider, StreamExt},
signers::Client,
types::Address,
utils::Ganache,
};
use serial_test::serial;
use std::convert::TryFrom;
#[tokio::test]
#[serial]
@ -133,6 +139,35 @@ mod eth_tests {
assert_eq!(log.new_value, i.to_string());
}
}
#[tokio::test]
#[serial]
async fn signer_on_node() {
let (abi, bytecode) = compile();
let provider = Provider::<Http>::try_from("http://localhost:8545").unwrap();
let deployer = "3cDB3d9e1B74692Bb1E3bb5fc81938151cA64b02"
.parse::<Address>()
.unwrap();
let client = Client::from(provider).with_sender(deployer);
let (_ganache, contract) = deploy(&client, abi, bytecode).await;
// make a call without the signer
let _tx = contract
.method::<_, H256>("setValue", "hi".to_owned())
.unwrap()
.send()
.await
.unwrap()
.await
.unwrap();
let value: String = contract
.method::<_, String>("getValue", ())
.unwrap()
.call()
.await
.unwrap();
assert_eq!(value, "hi");
}
}
#[cfg(feature = "celo")]

View File

@ -71,7 +71,7 @@ use thiserror::Error;
/// [`Provider`]: ethers_providers::Provider
pub struct Client<P, S> {
pub(crate) provider: Provider<P>,
pub(crate) signer: S,
pub(crate) signer: Option<S>,
pub(crate) address: Address,
}
@ -102,7 +102,7 @@ where
let address = signer.address();
Client {
provider,
signer,
signer: Some(signer),
address,
}
}
@ -110,7 +110,11 @@ where
/// 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> {
Ok(self.signer.sign_message(msg.into()))
Ok(if let Some(ref signer) = self.signer {
signer.sign_message(msg.into())
} else {
self.provider.sign(msg, &self.address()).await?
})
}
/// Signs and broadcasts the transaction. The optional parameter `block` can be passed so that
@ -131,11 +135,13 @@ where
// fill any missing fields
self.fill_transaction(&mut tx, block).await?;
// sign the transaction with the network
let signed_tx = self.signer.sign_transaction(tx).map_err(Into::into)?;
// broadcast it
Ok(self.provider.send_raw_transaction(&signed_tx).await?)
// sign the transaction and broadcast it
Ok(if let Some(ref signer) = self.signer {
let signed_tx = signer.sign_transaction(tx).map_err(Into::into)?;
self.provider.send_raw_transaction(&signed_tx).await?
} else {
self.provider.send_transaction(tx).await?
})
}
async fn fill_transaction(
@ -166,7 +172,7 @@ where
/// Returns the client's address
pub fn address(&self) -> Address {
self.signer.address()
self.address
}
/// Returns a reference to the client's provider
@ -175,8 +181,8 @@ where
}
/// Returns a reference to the client's signer
pub fn signer(&self) -> &S {
&self.signer
pub fn signer(&self) -> Option<&S> {
self.signer.as_ref()
}
/// Sets the signer and returns a mutable reference to self so that it can be used in chained
@ -188,7 +194,8 @@ where
P: Clone,
{
let mut this = self.clone();
this.signer = signer;
this.address = signer.address();
this.signer = Some(signer);
this
}
@ -204,6 +211,14 @@ where
this.provider = provider;
this
}
/// Sets the address which will be used for interacting with the blockchain.
/// Useful if no signer is set and you want to specify a default sender for
/// your transactions
pub fn with_sender<T: Into<Address>>(mut self, address: T) -> Self {
self.address = address.into();
self
}
}
/// Calls the future if `item` is None, otherwise returns a `futures::ok`
@ -228,3 +243,13 @@ impl<P, S> Deref for Client<P, S> {
&self.provider
}
}
impl<P: JsonRpcClient, S> From<Provider<P>> for Client<P, S> {
fn from(provider: Provider<P>) -> Self {
Self {
provider,
signer: None,
address: Address::zero(),
}
}
}

View File

@ -119,7 +119,7 @@ impl Wallet {
let address = self.address();
Client {
address,
signer: self,
signer: Some(self),
provider,
}
}