Skip to content

Commit

Permalink
Convert contract::deploy to async (#388)
Browse files Browse the repository at this point in the history
Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
  • Loading branch information
e00E and tomusdrw committed Oct 18, 2020
1 parent 720731f commit 3414c66
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 59 deletions.
1 change: 0 additions & 1 deletion examples/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ async fn main() -> web3::contract::Result<()> {
(U256::from(1_000_000_u64), "My Token".to_owned(), 3u64, "MT".to_owned()),
my_account,
)
.expect("Correct parameters are passed to the constructor.")
.await?;

let result = contract.query("balanceOf", (my_account,), None, Options::default(), None);
Expand Down
2 changes: 1 addition & 1 deletion examples/contract_log_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ async fn main() -> web3::contract::Result<()> {
.confirmations(1)
.poll_interval(time::Duration::from_secs(10))
.options(Options::with(|opt| opt.gas = Some(3_000_000.into())))
.execute(bytecode, (), accounts[0])?
.execute(bytecode, (), accounts[0])
.await?;

println!("contract deployed at: {}", contract.address());
Expand Down
2 changes: 1 addition & 1 deletion examples/contract_log_pubsub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ async fn main() -> web3::contract::Result<()> {
.confirmations(1)
.poll_interval(time::Duration::from_secs(10))
.options(Options::with(|opt| opt.gas = Some(3_000_000.into())))
.execute(bytecode, (), accounts[0])?;
.execute(bytecode, (), accounts[0]);
let contract = contract.await?;
println!("contract deployed at: {}", contract.address());

Expand Down
2 changes: 1 addition & 1 deletion examples/contract_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ async fn main() -> web3::contract::Result<()> {
.confirmations(1)
.poll_interval(time::Duration::from_secs(10))
.options(Options::with(|opt| opt.gas = Some(3_000_000.into())))
.execute(bytecode, (), accounts[0])?
.execute(bytecode, (), accounts[0])
.await?;

println!("Deployed at: {}", contract.address());
Expand Down
76 changes: 21 additions & 55 deletions src/contract/deploy.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
//! Contract deployment utilities

use ethabi;
use futures::{
task::{Context, Poll},
Future, FutureExt, TryFutureExt,
};
use futures::{Future, TryFutureExt};
use rustc_hex::{FromHex, ToHex};
use std::collections::HashMap;
use std::pin::Pin;
use std::time;

use crate::api::{Eth, Namespace};
Expand Down Expand Up @@ -51,7 +46,7 @@ impl<T: Transport> Builder<T> {
}

/// Execute deployment passing code and contructor parameters.
pub fn execute<P, V>(self, code: V, params: P, from: Address) -> Result<PendingContract<T>, ethabi::Error>
pub async fn execute<P, V>(self, code: V, params: P, from: Address) -> Result<Contract<T>, Error>
where
P: Tokenize,
V: AsRef<str>,
Expand All @@ -63,20 +58,21 @@ impl<T: Transport> Builder<T> {
self.do_execute(code, params, from, move |tx| {
confirm::send_transaction_with_confirmation(transport, tx, poll_interval, confirmations)
})
.await
}
/// Execute deployment passing code and contructor parameters.
/// Execute deployment passing code and constructor parameters.
///
/// Unlike the above `execute`, this method uses
/// `sign_raw_transaction_with_confirmation` instead of
/// `sign_transaction_with_confirmation`, which requires the account from
/// which the transaction is sent to be unlocked.
pub fn sign_and_execute<P, V>(
pub async fn sign_and_execute<P, V>(
self,
code: V,
params: P,
from: Address,
password: &str,
) -> Result<PendingContract<T, impl Future<Output = error::Result<TransactionReceipt>>>, ethabi::Error>
) -> Result<Contract<T>, Error>
where
P: Tokenize,
V: AsRef<str>,
Expand All @@ -97,15 +93,16 @@ impl<T: Transport> Builder<T> {
)
})
})
.await
}

fn do_execute<P, V, Ft>(
async fn do_execute<P, V, Ft>(
self,
code: V,
params: P,
from: Address,
send: impl FnOnce(TransactionRequest) -> Ft,
) -> Result<PendingContract<T, Ft>, ethabi::Error>
) -> Result<Contract<T>, Error>
where
P: Tokenize,
V: AsRef<str>,
Expand All @@ -119,9 +116,9 @@ impl<T: Transport> Builder<T> {

for (lib, address) in self.linker {
if lib.len() > 38 {
return Err(ethabi::Error::Other(
return Err(Error::Abi(ethabi::Error::Other(
"The library name should be under 39 characters.".into(),
));
)));
}
let replace = format!("__{:_<38}", lib); // This makes the required width 38 characters and will pad with `_` to match it.
let address: String = address.as_ref().to_hex();
Expand All @@ -133,7 +130,9 @@ impl<T: Transport> Builder<T> {
let params = params.into_tokens();
let data = match (abi.constructor(), params.is_empty()) {
(None, false) => {
return Err(ethabi::Error::Other("Constructor is not defined in the ABI.".into()));
return Err(Error::Abi(ethabi::Error::Other(
"Constructor is not defined in the ABI.".into(),
)));
}
(None, true) => code,
(Some(constructor), _) => constructor.encode_input(code, &params)?,
Expand All @@ -149,48 +148,16 @@ impl<T: Transport> Builder<T> {
data: Some(Bytes(data)),
condition: options.condition,
};

let waiting = send(tx);

Ok(PendingContract {
eth: Some(eth),
abi: Some(abi),
waiting,
})
}
}

/// Contract being deployed.
pub struct PendingContract<
T: Transport,
F: Future<Output = error::Result<TransactionReceipt>> = confirm::SendTransactionWithConfirmation<T>,
> {
eth: Option<Eth<T>>,
abi: Option<ethabi::Contract>,
waiting: F,
}

impl<T, F> Future for PendingContract<T, F>
where
F: Future<Output = error::Result<TransactionReceipt>> + Unpin,
T: Transport,
{
type Output = Result<Contract<T>, Error>;

fn poll(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
let receipt = ready!(self.waiting.poll_unpin(ctx))?;
let eth = self.eth.take().expect("future polled after ready; qed");
let abi = self.abi.take().expect("future polled after ready; qed");

Poll::Ready(match receipt.status {
let receipt = send(tx).await?;
match receipt.status {
Some(status) if status == 0.into() => Err(Error::ContractDeploymentFailure(receipt.transaction_hash)),
// If the `status` field is not present we use the presence of `contract_address` to
// determine if deployment was successfull.
_ => match receipt.contract_address {
Some(address) => Ok(Contract::new(eth, address, abi)),
None => Err(Error::ContractDeploymentFailure(receipt.transaction_hash)),
},
})
}
}
}

Expand Down Expand Up @@ -243,10 +210,9 @@ mod tests {
"0x01020304",
(U256::from(1_000_000), "My Token".to_owned(), 3u64, "MT".to_owned()),
Address::from_low_u64_be(5),
)
.unwrap(),
),
)
.unwrap();
.unwrap()
};

// then
Expand Down Expand Up @@ -303,7 +269,7 @@ mod tests {
let lib_address;
{
let builder = Contract::deploy(api::Eth::new(&transport), &lib_abi).unwrap();
lib_address = futures::executor::block_on(builder.execute(lib_code, (), Address::zero()).unwrap())
lib_address = futures::executor::block_on(builder.execute(lib_code, (), Address::zero()))
.unwrap()
.address();
}
Expand Down Expand Up @@ -331,7 +297,7 @@ mod tests {
linker
})
.unwrap();
let _ = futures::executor::block_on(builder.execute(main_code, (), Address::zero()).unwrap()).unwrap();
let _ = futures::executor::block_on(builder.execute(main_code, (), Address::zero())).unwrap();
}

transport.assert_request("eth_sendTransaction", &[
Expand Down
4 changes: 4 additions & 0 deletions src/contract/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ pub mod deploy {
/// Contract deployment error.
#[derive(Debug, Display, From)]
pub enum Error {
/// eth abi error
#[display(fmt = "Abi error: {}", _0)]
Abi(ethabi::Error),
/// Rpc error
#[display(fmt = "Api error: {}", _0)]
Api(ApiError),
Expand All @@ -52,6 +55,7 @@ pub mod deploy {
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
Error::Abi(ref e) => Some(e),
Error::Api(ref e) => Some(e),
Error::ContractDeploymentFailure(_) => None,
}
Expand Down

0 comments on commit 3414c66

Please sign in to comment.