Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert contract::deploy to async #388

Merged
merged 3 commits into from
Oct 18, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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