add deployer
This commit is contained in:
parent
e96446388f
commit
bdda7d0883
|
@ -253,6 +253,7 @@ dependencies = [
|
|||
"rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thiserror 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -16,6 +16,7 @@ serde = { version = "1.0.110", default-features = false }
|
|||
rustc-hex = { version = "2.1.0", default-features = false }
|
||||
thiserror = { version = "1.0.19", default-features = false }
|
||||
once_cell = "1.4.0"
|
||||
tokio = { version = "0.2.21", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["abigen"]
|
||||
|
|
|
@ -55,6 +55,10 @@ where
|
|||
DetokenizationError(#[from] InvalidOutputType),
|
||||
#[error(transparent)]
|
||||
CallError(P::Error),
|
||||
#[error("constructor is not defined in the ABI")]
|
||||
ConstructorError,
|
||||
#[error("Contract was not deployed")]
|
||||
ContractNotDeployed,
|
||||
}
|
||||
|
||||
impl<'a, P, N, S, D> ContractCall<'a, P, N, S, D>
|
||||
|
|
|
@ -1,12 +1,70 @@
|
|||
use crate::Contract;
|
||||
use crate::{Contract, ContractError};
|
||||
|
||||
use ethers_providers::{networks::Network, JsonRpcClient};
|
||||
use ethers_signers::{Client, Signer};
|
||||
use ethers_types::{
|
||||
abi::{Abi, Tokenize},
|
||||
Bytes,
|
||||
Bytes, TransactionRequest,
|
||||
};
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ContractFactory<'a, P, N, S> {
|
||||
client: &'a Client<'a, P, N, S>,
|
||||
|
@ -14,7 +72,13 @@ pub struct ContractFactory<'a, P, N, S> {
|
|||
bytecode: &'a Bytes,
|
||||
}
|
||||
|
||||
impl<'a, P: JsonRpcClient, N: Network, S: Signer> ContractFactory<'a, P, N, S> {
|
||||
impl<'a, P, N, S> ContractFactory<'a, P, N, S>
|
||||
where
|
||||
S: Signer,
|
||||
P: JsonRpcClient,
|
||||
P::Error: 'static,
|
||||
N: Network,
|
||||
{
|
||||
/// Instantiate a new contract factory
|
||||
pub fn new(client: &'a Client<'a, P, N, S>, abi: &'a Abi, bytecode: &'a Bytes) -> Self {
|
||||
Self {
|
||||
|
@ -27,17 +91,34 @@ impl<'a, P: JsonRpcClient, N: Network, S: Signer> ContractFactory<'a, P, N, S> {
|
|||
/// Deploys an instance of the contract with the provider constructor arguments
|
||||
/// and returns the contract's instance
|
||||
pub async fn deploy<T: Tokenize>(
|
||||
&self,
|
||||
constructor_args: T,
|
||||
) -> Result<Contract<'a, P, N, S>, P::Error> {
|
||||
// 1. Encode the constructor args
|
||||
//
|
||||
// 2. Create the runtime bytecode by concatenating the bytecode with the constructor
|
||||
// arguments (?)
|
||||
//
|
||||
// 3. Call `client.send_transaction()` to deploy
|
||||
//
|
||||
// 4. Get the address of the contract from the transaction receipt
|
||||
//
|
||||
// 5. Instantiate & return the contract object
|
||||
) -> 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(), ¶ms)?)
|
||||
}
|
||||
};
|
||||
|
||||
// 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),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue