abuse Deref for using provider methods in the Client

This commit is contained in:
Georgios Konstantopoulos 2020-05-24 19:29:04 +03:00
parent 8a8e33c9c8
commit 0f12894765
No known key found for this signature in database
GPG Key ID: FA607837CD26EDBC
5 changed files with 43 additions and 20 deletions

View File

@ -13,10 +13,8 @@ async fn main() -> Result<(), failure::Error> {
)? )?
.connect(&provider); .connect(&provider);
// get the account's nonce // get the account's nonce (we abuse the Deref to access the provider's functions)
let nonce = provider let nonce = client.get_transaction_count(client.address(), None).await?;
.get_transaction_count(client.signer.address, None)
.await?;
// craft the transaction // craft the transaction
let tx = UnsignedTransaction { let tx = UnsignedTransaction {
@ -29,7 +27,7 @@ async fn main() -> Result<(), failure::Error> {
}; };
// send it! // send it!
let tx = client.send_transaction(tx).await?; let tx = client.sign_and_send_transaction(tx).await?;
// get the mined tx // get the mined tx
let tx = client.get_transaction(tx.hash).await?; let tx = client.get_transaction(tx.hash).await?;

View File

@ -7,7 +7,7 @@ mod http;
use crate::{ use crate::{
signers::{Client, Signer}, signers::{Client, Signer},
types::{Address, BlockNumber, Bytes, Transaction, TransactionRequest, TxHash, U256}, types::{Address, BlockNumber, Transaction, TransactionRequest, TxHash, U256},
utils, utils,
}; };
@ -46,10 +46,12 @@ impl<P: JsonRpcClient> Provider<P> {
} }
} }
/// Gets the latest block number via the `eth_BlockNumber` API
pub async fn get_block_number(&self) -> Result<U256, P::Error> { pub async fn get_block_number(&self) -> Result<U256, P::Error> {
self.0.request("eth_blockNumber", None::<()>).await 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<T: Send + Sync + Into<TxHash>>( pub async fn get_transaction<T: Send + Sync + Into<TxHash>>(
&self, &self,
hash: T, hash: T,
@ -58,12 +60,14 @@ impl<P: JsonRpcClient> Provider<P> {
self.0.request("eth_getTransactionByHash", Some(hash)).await 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<TxHash, P::Error> { pub async fn send_transaction(&self, tx: TransactionRequest) -> Result<TxHash, P::Error> {
self.0.request("eth_sendTransaction", Some(tx)).await self.0.request("eth_sendTransaction", Some(tx)).await
} }
pub async fn send_raw_transaction(&self, rlp: &Bytes) -> Result<TxHash, P::Error> { /// Broadcasts a raw RLP encoded transaction via the `eth_sendRawTransaction` API
let rlp = utils::serialize(&rlp); pub async fn send_raw_transaction(&self, tx: &Transaction) -> Result<TxHash, P::Error> {
let rlp = utils::serialize(&tx.rlp());
self.0.request("eth_sendRawTransaction", Some(rlp)).await self.0.request("eth_sendRawTransaction", Some(rlp)).await
} }

View File

@ -1,31 +1,45 @@
use crate::{ use crate::{
providers::{JsonRpcClient, Provider}, providers::{JsonRpcClient, Provider},
signers::Signer, signers::Signer,
types::{Transaction, TxHash, UnsignedTransaction}, types::{Address, Transaction, UnsignedTransaction},
}; };
use std::ops::Deref;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Client<'a, S, P> { pub struct Client<'a, S, P> {
pub provider: &'a Provider<P>, pub(crate) provider: &'a Provider<P>,
pub signer: S, pub(crate) signer: S,
} }
impl<'a, S: Signer, P: JsonRpcClient> Client<'a, S, P> { impl<'a, S: Signer, P: JsonRpcClient> Client<'a, S, P> {
pub async fn send_transaction(&self, tx: UnsignedTransaction) -> Result<Transaction, P::Error> { /// 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<Transaction, P::Error> {
// sign the transaction // sign the transaction
let signed_tx = self.signer.sign_transaction(tx.clone()); let signed_tx = self.signer.sign_transaction(tx.clone());
// broadcast it // broadcast it
self.provider.send_raw_transaction(&signed_tx.rlp()).await?; self.provider.send_raw_transaction(&signed_tx).await?;
Ok(signed_tx) Ok(signed_tx)
} }
// TODO: Forward all other calls to the provider pub fn address(&self) -> Address {
pub async fn get_transaction<T: Send + Sync + Into<TxHash>>( self.signer.address()
&self, }
hash: T, }
) -> Result<Transaction, P::Error> {
self.provider.get_transaction(hash).await // 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<P>;
fn deref(&self) -> &Self::Target {
&self.provider
} }
} }

View File

@ -9,7 +9,7 @@ pub use wallet::Wallet;
mod client; mod client;
pub(crate) use client::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 /// Trait for signing transactions and messages
/// ///
@ -20,4 +20,7 @@ pub trait Signer {
/// Signs the transaction /// Signs the transaction
fn sign_transaction(&self, message: UnsignedTransaction) -> Transaction; fn sign_transaction(&self, message: UnsignedTransaction) -> Transaction;
/// Returns the signer's Ethereum Address
fn address(&self) -> Address;
} }

View File

@ -24,6 +24,10 @@ impl<'a, N: Network> Signer for Wallet<N> {
fn sign_transaction(&self, tx: UnsignedTransaction) -> Transaction { fn sign_transaction(&self, tx: UnsignedTransaction) -> Transaction {
self.private_key.sign_transaction(tx, N::CHAIN_ID) self.private_key.sign_transaction(tx, N::CHAIN_ID)
} }
fn address(&self) -> Address {
self.address
}
} }
impl<N: Network> Wallet<N> { impl<N: Network> Wallet<N> {