1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
use crate::{Contract, ContractError};
use ethers_core::{
abi::{Abi, Tokenize},
types::{transaction::eip2718::TypedTransaction, BlockNumber, Bytes, TransactionRequest},
};
use ethers_providers::Middleware;
#[cfg(not(feature = "legacy"))]
use ethers_core::types::Eip1559TransactionRequest;
use std::sync::Arc;
#[derive(Debug, Clone)]
/// Helper which manages the deployment transaction of a smart contract
pub struct Deployer<M> {
/// The deployer's transaction, exposed for overriding the defaults
pub tx: TypedTransaction,
abi: Abi,
client: Arc<M>,
confs: usize,
block: BlockNumber,
}
impl<M: Middleware> Deployer<M> {
/// Sets the number of confirmations to wait for the contract deployment transaction
pub fn confirmations<T: Into<usize>>(mut self, confirmations: T) -> Self {
self.confs = confirmations.into();
self
}
pub fn block<T: Into<BlockNumber>>(mut self, block: T) -> Self {
self.block = block.into();
self
}
/// Uses a Legacy transaction instead of an EIP-1559 one to do the deployment
pub fn legacy(mut self) -> Self {
self.tx = match self.tx {
TypedTransaction::Eip1559(inner) => {
let tx: TransactionRequest = inner.into();
TypedTransaction::Legacy(tx)
}
other => other,
};
self
}
/// 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<Contract<M>, ContractError<M>> {
let pending_tx = self
.client
.send_transaction(self.tx, Some(self.block.into()))
.await
.map_err(ContractError::MiddlewareError)?;
// TODO: Should this be calculated "optimistically" by address/nonce?
let receipt = pending_tx
.confirmations(self.confs)
.await
.map_err(|_| ContractError::ContractNotDeployed)?
.ok_or(ContractError::ContractNotDeployed)?;
let address = receipt
.contract_address
.ok_or(ContractError::ContractNotDeployed)?;
let contract = Contract::new(address, self.abi.clone(), self.client);
Ok(contract)
}
/// Returns a reference to the deployer's ABI
pub fn abi(&self) -> &Abi {
&self.abi
}
/// Returns a reference to the deployer's client
pub fn client(&self) -> &M {
&self.client
}
}
#[derive(Debug, Clone)]
/// To deploy a contract to the Ethereum network, a `ContractFactory` can be
/// created which manages the Contract bytecode and Application Binary Interface
/// (ABI), usually generated from the Solidity compiler.
///
/// Once the factory's deployment transaction is mined with sufficient confirmations,
/// the [`Contract`](crate::Contract) object is returned.
///
/// # Example
///
/// ```no_run
/// use ethers_core::utils::Solc;
/// use ethers_contract::ContractFactory;
/// use ethers_providers::{Provider, Http};
/// use ethers_signers::Wallet;
/// use std::convert::TryFrom;
///
/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
/// // first we'll compile the contract (you can alternatively compile it yourself
/// // and pass the ABI/Bytecode
/// let compiled = Solc::new("./tests/contract.sol").build().unwrap();
/// let contract = compiled
/// .get("SimpleStorage")
/// .expect("could not find contract");
///
/// // connect to the network
/// let client = Provider::<Http>::try_from("http://localhost:8545").unwrap();
/// let client = std::sync::Arc::new(client);
///
/// // create a factory which will be used to deploy instances of the contract
/// let factory = ContractFactory::new(contract.abi.clone(), contract.bytecode.clone(), client);
///
/// // The deployer created by the `deploy` call exposes a builder which gets consumed
/// // by the async `send` call
/// let contract = factory
/// .deploy("initial value".to_string())?
/// .confirmations(0usize)
/// .send()
/// .await?;
/// println!("{}", contract.address());
/// # Ok(())
/// # }
pub struct ContractFactory<M> {
client: Arc<M>,
abi: Abi,
bytecode: Bytes,
}
impl<M: Middleware> ContractFactory<M> {
/// 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<M>) -> Self {
Self {
client,
abi,
bytecode,
}
}
/// Constructs the deployment transaction based on the provided constructor
/// arguments and returns a `Deployer` instance. You must call `send()` in order
/// to actually deploy the contract.
///
/// Notes:
/// 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<T: Tokenize>(self, constructor_args: T) -> Result<Deployer<M>, ContractError<M>> {
// Encode the constructor args & concatenate with the bytecode if necessary
let params = constructor_args.into_tokens();
let data: Bytes = match (self.abi.constructor(), params.is_empty()) {
(None, false) => {
return Err(ContractError::ConstructorError);
}
(None, true) => self.bytecode.clone(),
(Some(constructor), _) => constructor
.encode_input(self.bytecode.to_vec(), ¶ms)?
.into(),
};
// create the tx object. Since we're deploying a contract, `to` is `None`
// We default to EIP-1559 transactions, but the sender can convert it back
// to a legacy one
#[cfg(feature = "legacy")]
let tx = TransactionRequest {
to: None,
data: Some(data),
..Default::default()
};
#[cfg(not(feature = "legacy"))]
let tx = Eip1559TransactionRequest {
to: None,
data: Some(data),
..Default::default()
};
let tx = tx.into();
Ok(Deployer {
client: Arc::clone(&self.client), // cheap clone behind the arc
abi: self.abi,
tx,
confs: 1,
block: BlockNumber::Latest,
})
}
}