From 2607c67ca04008f1a91a09ae32d245a6111e39db Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Tue, 7 Feb 2023 13:51:34 -0500 Subject: [PATCH] Refactor factories to use `Borrow` (#2103) * refactor: abstract factories over Borrow * chore: changelog * fix: docs for factory and type aliases --- CHANGELOG.md | 2 + ethers-contract/src/contract.rs | 2 +- ethers-contract/src/factory.rs | 126 +++++++++++++++++++++++--------- ethers-contract/src/lib.rs | 2 +- 4 files changed, 95 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d794156d..5510068b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -299,6 +299,8 @@ ### Unreleased +- Make `Factory` objects generic over the borrow trait, to allow non-arc mware + [#2103](https://github.com/gakonst/ethers-rs/pull/2103) - Make `Contract` objects generic over the borrow trait, to allow non-arc mware [#2082](https://github.com/gakonst/ethers-rs/pull/2082) - Return pending transaction from `Multicall::send` diff --git a/ethers-contract/src/contract.rs b/ethers-contract/src/contract.rs index c54418a9..b842b120 100644 --- a/ethers-contract/src/contract.rs +++ b/ethers-contract/src/contract.rs @@ -243,7 +243,7 @@ where /// Returns an [`Event`](crate::builders::Event) builder for the provided event. /// This function operates in a static context, then it does not require a `self` /// to reference to instantiate an [`Event`](crate::builders::Event) builder. - pub fn event_of_type(client: &Arc) -> Event { + pub fn event_of_type(client: &M) -> Event { Event { provider: client, filter: Filter::new().event(&D::abi_signature()), diff --git a/ethers-contract/src/factory.rs b/ethers-contract/src/factory.rs index bffb9045..d3248150 100644 --- a/ethers-contract/src/factory.rs +++ b/ethers-contract/src/factory.rs @@ -1,4 +1,4 @@ -use crate::{Contract, ContractError}; +use crate::{ContractError, ContractInstance}; use ethers_core::{ abi::{Abi, Token, Tokenize}, @@ -15,33 +15,62 @@ use ethers_providers::{ #[cfg(not(feature = "legacy"))] use ethers_core::types::Eip1559TransactionRequest; -use std::{marker::PhantomData, sync::Arc}; +use std::{borrow::Borrow, marker::PhantomData, sync::Arc}; -/// Helper which manages the deployment transaction of a smart contract. +/// `ContractDeployer` is a [`ContractDeploymentTx`] object with an +/// [`Arc`] middleware. This type alias exists to preserve backwards +/// compatibility with less-abstract Contracts. /// -/// This is just a wrapper type for [Deployer] with an additional type to convert the [Contract] -/// that the deployer returns when sending the transaction. +/// For full usage docs, see [`ContractDeploymentTx`]. +pub type ContractDeployer = ContractDeploymentTx, M, C>; + +/// `ContractFactory` is a [`DeploymentTxFactory`] object with an +/// [`Arc`] middleware. This type alias exists to preserve backwards +/// compatibility with less-abstract Contracts. +/// +/// For full usage docs, see [`DeploymentTxFactory`]. +pub type ContractFactory = DeploymentTxFactory, M>; + +/// Helper which manages the deployment transaction of a smart contract. It +/// wraps a deployment transaction, and retrieves the contract address output +/// by it. +/// +/// Currently, we recommend using the [`ContractDeployer`] type alias. #[derive(Debug)] -#[must_use = "Deployer does nothing unless you `send` it"] -pub struct ContractDeployer { +#[must_use = "DeploymentTx does nothing unless you `send` it"] +pub struct ContractDeploymentTx { /// the actual deployer, exposed for overriding the defaults - pub deployer: Deployer, + pub deployer: Deployer, /// marker for the `Contract` type to create afterwards /// /// this type will be used to construct it via `From::from(Contract)` _contract: PhantomData, } -impl Clone for ContractDeployer { +impl Clone for ContractDeploymentTx +where + B: Clone, +{ fn clone(&self) -> Self { - ContractDeployer { deployer: self.deployer.clone(), _contract: self._contract } + ContractDeploymentTx { deployer: self.deployer.clone(), _contract: self._contract } } } -impl>> ContractDeployer { - /// Create a new instance of this [ContractDeployer] - pub fn new(deployer: Deployer) -> Self { - Self { deployer, _contract: Default::default() } +impl From> for ContractDeploymentTx { + fn from(deployer: Deployer) -> Self { + Self { deployer, _contract: PhantomData } + } +} + +impl ContractDeploymentTx +where + B: Borrow + Clone, + M: Middleware, + C: From>, +{ + /// Create a new instance of this from a deployer. + pub fn new(deployer: Deployer) -> Self { + Self { deployer, _contract: PhantomData } } /// Sets the number of confirmations to wait for the contract deployment transaction @@ -148,7 +177,7 @@ impl>> ContractDeployer { } /// Returns a pointer to the deployer's client - pub fn client(&self) -> Arc { + pub fn client(&self) -> &M { self.deployer.client() } } @@ -156,16 +185,20 @@ impl>> ContractDeployer { /// Helper which manages the deployment transaction of a smart contract #[derive(Debug)] #[must_use = "Deployer does nothing unless you `send` it"] -pub struct Deployer { +pub struct Deployer { /// The deployer's transaction, exposed for overriding the defaults pub tx: TypedTransaction, abi: Abi, - client: Arc, + client: B, confs: usize, block: BlockNumber, + _m: PhantomData, } -impl Clone for Deployer { +impl Clone for Deployer +where + B: Clone, +{ fn clone(&self) -> Self { Deployer { tx: self.tx.clone(), @@ -173,11 +206,16 @@ impl Clone for Deployer { client: self.client.clone(), confs: self.confs, block: self.block, + _m: PhantomData, } } } -impl Deployer { +impl Deployer +where + B: Borrow + Clone, + M: Middleware, +{ /// Sets the number of confirmations to wait for the contract deployment transaction pub fn confirmations>(mut self, confirmations: T) -> Self { self.confs = confirmations.into(); @@ -206,6 +244,7 @@ impl Deployer { /// Note: this function _does not_ send a transaction from your account pub async fn call(&self) -> Result<(), ContractError> { self.client + .borrow() .call(&self.tx, Some(self.block.into())) .await .map_err(ContractError::MiddlewareError)?; @@ -220,13 +259,13 @@ impl Deployer { /// /// Note: this function _does not_ send a transaction from your account pub fn call_raw(&self) -> CallBuilder<'_, M::Provider> { - self.client.provider().call_raw(&self.tx).block(self.block.into()) + self.client.borrow().provider().call_raw(&self.tx).block(self.block.into()) } /// Broadcasts the contract deployment transaction and after waiting for it to /// be sufficiently confirmed (default: 1), it returns a [`Contract`](crate::Contract) /// struct at the deployed contract's address. - pub async fn send(self) -> Result, ContractError> { + pub async fn send(self) -> Result, ContractError> { let (contract, _) = self.send_with_receipt().await?; Ok(contract) } @@ -237,9 +276,10 @@ impl Deployer { /// and the corresponding [`TransactionReceipt`](ethers_core::types::TransactionReceipt). pub async fn send_with_receipt( self, - ) -> Result<(Contract, TransactionReceipt), ContractError> { + ) -> Result<(ContractInstance, TransactionReceipt), ContractError> { let pending_tx = self .client + .borrow() .send_transaction(self.tx, Some(self.block.into())) .await .map_err(ContractError::MiddlewareError)?; @@ -252,7 +292,7 @@ impl Deployer { .ok_or(ContractError::ContractNotDeployed)?; let address = receipt.contract_address.ok_or(ContractError::ContractNotDeployed)?; - let contract = Contract::new(address, self.abi.clone(), self.client); + let contract = ContractInstance::new(address, self.abi.clone(), self.client.clone()); Ok((contract, receipt)) } @@ -262,8 +302,8 @@ impl Deployer { } /// Returns a pointer to the deployer's client - pub fn client(&self) -> Arc { - self.client.clone() + pub fn client(&self) -> &M { + self.client.borrow() } } @@ -309,31 +349,43 @@ impl Deployer { /// # Ok(()) /// # } #[derive(Debug)] -pub struct ContractFactory { - client: Arc, +pub struct DeploymentTxFactory { + client: B, abi: Abi, bytecode: Bytes, + _m: PhantomData, } -impl Clone for ContractFactory { +impl Clone for DeploymentTxFactory +where + B: Clone, +{ fn clone(&self) -> Self { - ContractFactory { + DeploymentTxFactory { client: self.client.clone(), abi: self.abi.clone(), bytecode: self.bytecode.clone(), + _m: PhantomData, } } } -impl ContractFactory { +impl DeploymentTxFactory +where + B: Borrow + Clone, + M: Middleware, +{ /// Creates a factory for deployment of the Contract with bytecode, and the /// constructor defined in the abi. The client will be used to send any deployment /// transaction. - pub fn new(abi: Abi, bytecode: Bytes, client: Arc) -> Self { - Self { client, abi, bytecode } + pub fn new(abi: Abi, bytecode: Bytes, client: B) -> Self { + Self { client, abi, bytecode, _m: PhantomData } } - pub fn deploy_tokens(self, params: Vec) -> Result, ContractError> { + pub fn deploy_tokens(self, params: Vec) -> Result, ContractError> + where + B: Clone, + { // Encode the constructor args & concatenate with the bytecode if necessary let data: Bytes = match (self.abi.constructor(), params.is_empty()) { (None, false) => return Err(ContractError::ConstructorError), @@ -353,11 +405,12 @@ impl ContractFactory { let tx = tx.into(); Ok(Deployer { - client: Arc::clone(&self.client), // cheap clone behind the arc + client: self.client.clone(), abi: self.abi, tx, confs: 1, block: BlockNumber::Latest, + _m: PhantomData, }) } @@ -369,7 +422,10 @@ impl ContractFactory { /// 1. If there are no constructor arguments, you should pass `()` as the argument. /// 1. The default poll duration is 7 seconds. /// 1. The default number of confirmations is 1 block. - pub fn deploy(self, constructor_args: T) -> Result, ContractError> { + pub fn deploy( + self, + constructor_args: T, + ) -> Result, ContractError> { self.deploy_tokens(constructor_args.into_tokens()) } } diff --git a/ethers-contract/src/lib.rs b/ethers-contract/src/lib.rs index 2d4fc645..4a14760b 100644 --- a/ethers-contract/src/lib.rs +++ b/ethers-contract/src/lib.rs @@ -15,7 +15,7 @@ mod error; pub use error::EthError; mod factory; -pub use factory::{ContractDeployer, ContractFactory}; +pub use factory::{ContractDeployer, ContractDeploymentTx, ContractFactory, DeploymentTxFactory}; mod event; pub use event::{EthEvent, Event};