ethers-rs/ethers-contract/src/factory.rs

125 lines
3.4 KiB
Rust
Raw Normal View History

2020-05-30 20:04:08 +00:00
use crate::{Contract, ContractError};
2020-05-30 14:24:50 +00:00
2020-05-31 16:01:34 +00:00
use ethers_core::{
2020-05-30 14:24:50 +00:00
abi::{Abi, Tokenize},
2020-05-31 16:01:34 +00:00
types::{Bytes, TransactionRequest},
2020-05-30 14:24:50 +00:00
};
2020-05-31 16:01:34 +00:00
use ethers_providers::{networks::Network, JsonRpcClient};
use ethers_signers::{Client, Signer};
2020-05-30 14:24:50 +00:00
2020-05-30 20:04:08 +00:00
use std::time::Duration;
use tokio::time;
/// Poll for tx confirmation once every 7 seconds.
/// TODO: Can this be improved by replacing polling with an "on new block" subscription?
const POLL_INTERVAL: u64 = 7000;
#[derive(Debug, Clone)]
pub struct Deployer<'a, P, N, S> {
client: &'a Client<'a, P, N, S>,
abi: &'a Abi,
tx: TransactionRequest,
confs: usize,
poll_interval: Duration,
}
impl<'a, P, N, S> Deployer<'a, P, N, S>
where
S: Signer,
P: JsonRpcClient,
P::Error: 'static,
N: Network,
{
pub fn poll_interval<T: Into<Duration>>(mut self, interval: T) -> Self {
self.poll_interval = interval.into();
self
}
pub fn confirmations<T: Into<usize>>(mut self, confirmations: T) -> Self {
self.confs = confirmations.into();
self
}
pub async fn send(self) -> Result<Contract<'a, P, N, S>, ContractError<P>> {
let tx_hash = self
.client
.send_transaction(self.tx, None)
.await
.map_err(ContractError::CallError)?;
// poll for the receipt
let address;
loop {
if let Ok(receipt) = self.client.get_transaction_receipt(tx_hash).await {
address = receipt
.contract_address
.ok_or(ContractError::ContractNotDeployed)?;
break;
}
time::delay_for(Duration::from_millis(POLL_INTERVAL)).await;
}
let contract = Contract::new(self.client, self.abi, address);
Ok(contract)
}
}
2020-05-30 14:24:50 +00:00
#[derive(Debug, Clone)]
pub struct ContractFactory<'a, P, N, S> {
client: &'a Client<'a, P, N, S>,
abi: &'a Abi,
bytecode: &'a Bytes,
}
2020-05-30 20:04:08 +00:00
impl<'a, P, N, S> ContractFactory<'a, P, N, S>
where
S: Signer,
P: JsonRpcClient,
P::Error: 'static,
N: Network,
{
2020-05-30 14:24:50 +00:00
/// Instantiate a new contract factory
pub fn new(client: &'a Client<'a, P, N, S>, abi: &'a Abi, bytecode: &'a Bytes) -> Self {
Self {
client,
abi,
bytecode,
}
}
/// Deploys an instance of the contract with the provider constructor arguments
/// and returns the contract's instance
2020-05-31 14:50:00 +00:00
pub fn deploy<T: Tokenize>(
2020-05-30 20:04:08 +00:00
&self,
2020-05-30 14:24:50 +00:00
constructor_args: T,
2020-05-30 20:04:08 +00:00
) -> Result<Deployer<'a, P, N, S>, ContractError<P>> {
// 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), _) => {
Bytes(constructor.encode_input(self.bytecode.0.clone(), &params)?)
}
};
// create the tx object. Since we're deploying a contract, `to` is `None`
let tx = TransactionRequest {
to: None,
data: Some(data),
..Default::default()
};
Ok(Deployer {
client: self.client,
abi: self.abi,
tx,
confs: 1,
poll_interval: Duration::from_millis(POLL_INTERVAL),
})
2020-05-30 14:24:50 +00:00
}
}