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:
parent
9a0c97286b
commit
833ca33f60
|
@ -148,6 +148,7 @@ where
|
||||||
|
|
||||||
// create the tx object. Since we're deploying a contract, `to` is `None`
|
// create the tx object. Since we're deploying a contract, `to` is `None`
|
||||||
let tx = TransactionRequest {
|
let tx = TransactionRequest {
|
||||||
|
from: Some(self.client.address()),
|
||||||
to: None,
|
to: None,
|
||||||
data: Some(data),
|
data: Some(data),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -6,8 +6,14 @@ pub use common::*;
|
||||||
#[cfg(not(feature = "celo"))]
|
#[cfg(not(feature = "celo"))]
|
||||||
mod eth_tests {
|
mod eth_tests {
|
||||||
use super::*;
|
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 serial_test::serial;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
|
@ -133,6 +139,35 @@ mod eth_tests {
|
||||||
assert_eq!(log.new_value, i.to_string());
|
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")]
|
#[cfg(feature = "celo")]
|
||||||
|
|
|
@ -71,7 +71,7 @@ use thiserror::Error;
|
||||||
/// [`Provider`]: ethers_providers::Provider
|
/// [`Provider`]: ethers_providers::Provider
|
||||||
pub struct Client<P, S> {
|
pub struct Client<P, S> {
|
||||||
pub(crate) provider: Provider<P>,
|
pub(crate) provider: Provider<P>,
|
||||||
pub(crate) signer: S,
|
pub(crate) signer: Option<S>,
|
||||||
pub(crate) address: Address,
|
pub(crate) address: Address,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ where
|
||||||
let address = signer.address();
|
let address = signer.address();
|
||||||
Client {
|
Client {
|
||||||
provider,
|
provider,
|
||||||
signer,
|
signer: Some(signer),
|
||||||
address,
|
address,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,11 @@ where
|
||||||
/// Signs a message with the internal signer, or if none is present it will make a call to
|
/// 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.
|
/// the connected node's `eth_call` API.
|
||||||
pub async fn sign_message<T: Into<Bytes>>(&self, msg: T) -> Result<Signature, ClientError> {
|
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
|
/// Signs and broadcasts the transaction. The optional parameter `block` can be passed so that
|
||||||
|
@ -131,11 +135,13 @@ where
|
||||||
// fill any missing fields
|
// fill any missing fields
|
||||||
self.fill_transaction(&mut tx, block).await?;
|
self.fill_transaction(&mut tx, block).await?;
|
||||||
|
|
||||||
// sign the transaction with the network
|
// sign the transaction and broadcast it
|
||||||
let signed_tx = self.signer.sign_transaction(tx).map_err(Into::into)?;
|
Ok(if let Some(ref signer) = self.signer {
|
||||||
|
let signed_tx = signer.sign_transaction(tx).map_err(Into::into)?;
|
||||||
// broadcast it
|
self.provider.send_raw_transaction(&signed_tx).await?
|
||||||
Ok(self.provider.send_raw_transaction(&signed_tx).await?)
|
} else {
|
||||||
|
self.provider.send_transaction(tx).await?
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fill_transaction(
|
async fn fill_transaction(
|
||||||
|
@ -166,7 +172,7 @@ where
|
||||||
|
|
||||||
/// Returns the client's address
|
/// Returns the client's address
|
||||||
pub fn address(&self) -> Address {
|
pub fn address(&self) -> Address {
|
||||||
self.signer.address()
|
self.address
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the client's provider
|
/// Returns a reference to the client's provider
|
||||||
|
@ -175,8 +181,8 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the client's signer
|
/// Returns a reference to the client's signer
|
||||||
pub fn signer(&self) -> &S {
|
pub fn signer(&self) -> Option<&S> {
|
||||||
&self.signer
|
self.signer.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the signer and returns a mutable reference to self so that it can be used in chained
|
/// 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,
|
P: Clone,
|
||||||
{
|
{
|
||||||
let mut this = self.clone();
|
let mut this = self.clone();
|
||||||
this.signer = signer;
|
this.address = signer.address();
|
||||||
|
this.signer = Some(signer);
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,6 +211,14 @@ where
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this
|
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`
|
/// 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
|
&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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ impl Wallet {
|
||||||
let address = self.address();
|
let address = self.address();
|
||||||
Client {
|
Client {
|
||||||
address,
|
address,
|
||||||
signer: self,
|
signer: Some(self),
|
||||||
provider,
|
provider,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue