From 0f12894765aa1d5686a3529aa43114f198272fd4 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Sun, 24 May 2020 19:29:04 +0300 Subject: [PATCH] abuse Deref for using provider methods in the Client --- examples/local_signer.rs | 8 +++----- src/providers/mod.rs | 10 +++++++--- src/signers/client.rs | 36 +++++++++++++++++++++++++----------- src/signers/mod.rs | 5 ++++- src/signers/wallet.rs | 4 ++++ 5 files changed, 43 insertions(+), 20 deletions(-) diff --git a/examples/local_signer.rs b/examples/local_signer.rs index f0d96ba4..21f1f5ab 100644 --- a/examples/local_signer.rs +++ b/examples/local_signer.rs @@ -13,10 +13,8 @@ async fn main() -> Result<(), failure::Error> { )? .connect(&provider); - // get the account's nonce - let nonce = provider - .get_transaction_count(client.signer.address, None) - .await?; + // get the account's nonce (we abuse the Deref to access the provider's functions) + let nonce = client.get_transaction_count(client.address(), None).await?; // craft the transaction let tx = UnsignedTransaction { @@ -29,7 +27,7 @@ async fn main() -> Result<(), failure::Error> { }; // send it! - let tx = client.send_transaction(tx).await?; + let tx = client.sign_and_send_transaction(tx).await?; // get the mined tx let tx = client.get_transaction(tx.hash).await?; diff --git a/src/providers/mod.rs b/src/providers/mod.rs index edab5267..a29e8312 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -7,7 +7,7 @@ mod http; use crate::{ signers::{Client, Signer}, - types::{Address, BlockNumber, Bytes, Transaction, TransactionRequest, TxHash, U256}, + types::{Address, BlockNumber, Transaction, TransactionRequest, TxHash, U256}, utils, }; @@ -46,10 +46,12 @@ impl Provider

{ } } + /// Gets the latest block number via the `eth_BlockNumber` API pub async fn get_block_number(&self) -> Result { self.0.request("eth_blockNumber", None::<()>).await } + /// Gets the transaction which matches the provided hash via the `eth_getTransactionByHash` API pub async fn get_transaction>( &self, hash: T, @@ -58,12 +60,14 @@ impl Provider

{ self.0.request("eth_getTransactionByHash", Some(hash)).await } + /// Broadcasts the transaction request via the `eth_sendTransaction` API pub async fn send_transaction(&self, tx: TransactionRequest) -> Result { self.0.request("eth_sendTransaction", Some(tx)).await } - pub async fn send_raw_transaction(&self, rlp: &Bytes) -> Result { - let rlp = utils::serialize(&rlp); + /// Broadcasts a raw RLP encoded transaction via the `eth_sendRawTransaction` API + pub async fn send_raw_transaction(&self, tx: &Transaction) -> Result { + let rlp = utils::serialize(&tx.rlp()); self.0.request("eth_sendRawTransaction", Some(rlp)).await } diff --git a/src/signers/client.rs b/src/signers/client.rs index dce3fd8f..ed1dbe21 100644 --- a/src/signers/client.rs +++ b/src/signers/client.rs @@ -1,31 +1,45 @@ use crate::{ providers::{JsonRpcClient, Provider}, signers::Signer, - types::{Transaction, TxHash, UnsignedTransaction}, + types::{Address, Transaction, UnsignedTransaction}, }; +use std::ops::Deref; + #[derive(Clone, Debug)] pub struct Client<'a, S, P> { - pub provider: &'a Provider

, - pub signer: S, + pub(crate) provider: &'a Provider

, + pub(crate) signer: S, } impl<'a, S: Signer, P: JsonRpcClient> Client<'a, S, P> { - pub async fn send_transaction(&self, tx: UnsignedTransaction) -> Result { + /// Signs the transaction and then broadcasts its RLP encoding via the `eth_sendRawTransaction` + /// API + pub async fn sign_and_send_transaction( + &self, + tx: UnsignedTransaction, + ) -> Result { // sign the transaction let signed_tx = self.signer.sign_transaction(tx.clone()); // broadcast it - self.provider.send_raw_transaction(&signed_tx.rlp()).await?; + self.provider.send_raw_transaction(&signed_tx).await?; Ok(signed_tx) } - // TODO: Forward all other calls to the provider - pub async fn get_transaction>( - &self, - hash: T, - ) -> Result { - self.provider.get_transaction(hash).await + pub fn address(&self) -> Address { + self.signer.address() + } +} + +// Abuse Deref to use the Provider's methods without re-writing everything. +// This is an anti-pattern and should not be encouraged, but this improves the UX while +// keeping the LoC low +impl<'a, S, P> Deref for Client<'a, S, P> { + type Target = &'a Provider

; + + fn deref(&self) -> &Self::Target { + &self.provider } } diff --git a/src/signers/mod.rs b/src/signers/mod.rs index abe38c97..5b2a67d8 100644 --- a/src/signers/mod.rs +++ b/src/signers/mod.rs @@ -9,7 +9,7 @@ pub use wallet::Wallet; mod client; pub(crate) use client::Client; -use crate::types::{Signature, Transaction, UnsignedTransaction}; +use crate::types::{Address, Signature, Transaction, UnsignedTransaction}; /// Trait for signing transactions and messages /// @@ -20,4 +20,7 @@ pub trait Signer { /// Signs the transaction fn sign_transaction(&self, message: UnsignedTransaction) -> Transaction; + + /// Returns the signer's Ethereum Address + fn address(&self) -> Address; } diff --git a/src/signers/wallet.rs b/src/signers/wallet.rs index b484709d..ccd107aa 100644 --- a/src/signers/wallet.rs +++ b/src/signers/wallet.rs @@ -24,6 +24,10 @@ impl<'a, N: Network> Signer for Wallet { fn sign_transaction(&self, tx: UnsignedTransaction) -> Transaction { self.private_key.sign_transaction(tx, N::CHAIN_ID) } + + fn address(&self) -> Address { + self.address + } } impl Wallet {