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

Confirmations #105

Merged
merged 18 commits into from
Jan 8, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
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: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ ethcontract-common = { version = "0.2.0", path = "./common" }
ethcontract-derive = { version = "0.2.0", path = "./derive" }
ethsign = "0.7"
futures = { version = "0.3", features = ["compat"] }
futures-timer = "2.0"
jsonrpc-core = "11.0"
lazy_static = "1.4"
rlp = "0.4"
Expand Down
1 change: 0 additions & 1 deletion examples/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ async fn run() {

let instance = RustCoin::builder(&web3)
.gas(4_712_388.into())
.confirmations(0)
.deploy()
.await
.expect("deployment failed");
Expand Down
1 change: 0 additions & 1 deletion examples/generate/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ async fn run() {

let instance = RustCoin::builder(&web3)
.gas(4_712_388.into())
.confirmations(0)
.deploy()
.await
.expect("deployment failed");
Expand Down
2 changes: 0 additions & 2 deletions examples/linked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@ async fn run() {

let library = SimpleLibrary::builder(&web3)
.gas(4_712_388.into())
.confirmations(0)
.deploy()
.await
.expect("library deployment failure");
let instance = LinkedContract::builder(&web3, library.address(), 1337.into())
.gas(4_712_388.into())
.confirmations(0)
.deploy()
.await
.expect("contract deployment failure");
Expand Down
8 changes: 5 additions & 3 deletions examples/rinkeby.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use ethcontract::web3::api::Web3;
use ethcontract::web3::transports::WebSocket;
use ethcontract::{Account, SecretKey, H256};
use std::env;
use std::time::Duration;

ethcontract::contract!("examples/truffle/build/contracts/DeployedContract.json");

Expand All @@ -24,7 +23,9 @@ async fn run() {
format!("wss://rinkeby.infura.io/ws/v3/{}", project_id)
};

// use a WebSocket transport to support confirmations
// NOTE: Use a WebSocket transport for `eth_newBlockFilter` support on
// Infura, filters are disabled over HTTPS. Filters are needed for
// confirmation support.
let (eloop, ws) = WebSocket::new(&infura_url).expect("transport failed");
eloop.into_remote();
let web3 = Web3::new(ws);
Expand All @@ -46,7 +47,8 @@ async fn run() {
println!(" incrementing (this may take a while)...");
instance
.increment()
.send_and_confirm(Duration::new(5, 0), 1)
.confirmations(1) // wait for 1 block confirmation
.send()
.await
.expect("increment failed");
println!(
Expand Down
3 changes: 1 addition & 2 deletions src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ use web3::Transport;

pub use self::deploy::{Deploy, DeployBuilder, DeployFuture, DeployedFuture};
pub use self::method::{
CallFuture, MethodBuilder, MethodDefaults, MethodFuture, MethodSendAndConfirmFuture,
MethodSendFuture, ViewMethodBuilder,
CallFuture, MethodBuilder, MethodDefaults, MethodFuture, MethodSendFuture, ViewMethodBuilder,
};

/// Represents a contract instance at an address. Provides methods for
Expand Down
76 changes: 29 additions & 47 deletions src/contract/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
//! new contracts.

use crate::contract::Instance;
use crate::errors::DeployError;
use crate::errors::{DeployError, ExecutionError};
use crate::future::{CompatCallFuture, Web3Unpin};
use crate::transaction::{Account, SendAndConfirmFuture, TransactionBuilder};
use crate::transaction::{Account, SendFuture, TransactionBuilder, TransactionResult};
use crate::truffle::abi::ErrorKind as AbiErrorKind;
use crate::truffle::{Abi, Artifact};
use futures::compat::Future01CompatExt;
Expand All @@ -13,7 +13,6 @@ use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use web3::api::Web3;
use web3::contract::tokens::Tokenize;
use web3::types::{Address, Bytes, U256};
Expand Down Expand Up @@ -116,10 +115,6 @@ where
abi: Abi,
/// The underlying transaction used t
tx: TransactionBuilder<T>,
/// The poll interval for confirming the contract deployed.
pub poll_interval: Option<Duration>,
/// The number of confirmations to wait for.
pub confirmations: Option<usize>,
_deploy: PhantomData<Box<D>>,
}

Expand Down Expand Up @@ -157,9 +152,7 @@ where
Ok(DeployBuilder {
web3: web3.clone(),
abi: artifact.abi,
tx: TransactionBuilder::new(web3).data(data),
poll_interval: None,
confirmations: None,
tx: TransactionBuilder::new(web3).data(data).confirmations(0),
_deploy: Default::default(),
})
}
Expand All @@ -181,35 +174,29 @@ where
/// Specify the gas price to use, if not specified then the estimated gas
/// price will be used.
pub fn gas_price(mut self, value: U256) -> DeployBuilder<T, D> {
self.tx = self.tx.gas(value);
self.tx = self.tx.gas_price(value);
self
}

/// Specify what how much ETH to transfer with the transaction, if not
/// specified then no ETH will be sent.
pub fn value(mut self, value: U256) -> DeployBuilder<T, D> {
self.tx = self.tx.gas(value);
self.tx = self.tx.value(value);
self
}

/// Specify the nonce for the transation, if not specified will use the
/// current transaction count for the signing account.
pub fn nonce(mut self, value: U256) -> DeployBuilder<T, D> {
self.tx = self.tx.gas(value);
self
}

/// Specify the poll interval to use for confirming the deployment, if not
/// specified will use a period of 7 seconds.
pub fn poll_interval(mut self, value: Duration) -> DeployBuilder<T, D> {
self.poll_interval = Some(value);
self.tx = self.tx.nonce(value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these copy pasta errors that are being fixed here 🙈

Copy link
Contributor Author

@nlordell nlordell Jan 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was addressed in #108, it’s just the github GUI showing it.

self
}

/// Specify the number of confirmations to wait for when confirming the
/// transaction, if not specified will wait for 1 confirmation.
/// transaction, if not specified will wait for the transaction to be mined
/// without any extra confirmations.
pub fn confirmations(mut self, value: usize) -> DeployBuilder<T, D> {
self.confirmations = Some(value);
self.tx = self.tx.confirmations(value);
self
}

Expand All @@ -236,7 +223,7 @@ where
/// The deployment args
args: Option<(Web3Unpin<T>, Abi)>,
/// The future resolved when the deploy transaction is complete.
tx: Result<SendAndConfirmFuture<T>, Option<DeployError>>,
send: SendFuture<T>,
_deploy: PhantomData<Box<D>>,
}

Expand All @@ -247,13 +234,9 @@ where
{
/// Create an instance from a `DeployBuilder`.
pub fn from_builder(builder: DeployBuilder<T, D>) -> DeployFuture<T, D> {
// NOTE(nlordell): arbitrary default values taken from `rust-web3`
let poll_interval = builder.poll_interval.unwrap_or(Duration::from_secs(7));
let confirmations = builder.confirmations.unwrap_or(1);

DeployFuture {
args: Some((builder.web3.into(), builder.abi)),
tx: Ok(builder.tx.send_and_confirm(poll_interval, confirmations)),
send: builder.tx.send(),
_deploy: Default::default(),
}
}
Expand All @@ -268,26 +251,25 @@ where

fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let unpinned = self.get_mut();
match unpinned.tx {
Ok(ref mut tx) => {
let tx = ready!(Pin::new(tx).poll(cx).map_err(DeployError::from));
let tx = match tx {
Ok(tx) => tx,
Err(err) => return Poll::Ready(Err(err)),
};
let address = match tx.contract_address {
Some(address) => address,
None => {
return Poll::Ready(Err(DeployError::Failure(tx.transaction_hash)));
}
};

let (web3, abi) = unpinned.args.take().expect("called once");

Poll::Ready(Ok(D::deployed_at(web3.into(), abi, address)))

let tx = match ready!(Pin::new(&mut unpinned.send).poll(cx)) {
Ok(TransactionResult::Receipt(tx)) => tx,
Ok(TransactionResult::Hash(tx)) => return Poll::Ready(Err(DeployError::Pending(tx))),
Err(err) => return Poll::Ready(Err(err.into())),
};

let address = match tx.contract_address {
Some(address) => address,
None => {
return Poll::Ready(Err(DeployError::Tx(ExecutionError::Failure(
tx.transaction_hash,
))));
}
Err(ref mut err) => Poll::Ready(Err(err.take().expect("called once"))),
}
};

let (web3, abi) = unpinned.args.take().expect("called more than once");

Poll::Ready(Ok(D::deployed_at(web3.into(), abi, address)))
}
}

Expand Down
27 changes: 9 additions & 18 deletions src/contract/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use crate::errors::{ExecutionError, MethodError};
use crate::future::CompatCallFuture;
use crate::hash;
use crate::transaction::{Account, SendAndConfirmFuture, SendFuture, TransactionBuilder};
use crate::transaction::{Account, SendFuture, TransactionBuilder};
use crate::truffle::abi::{self, Function, ParamType};
use futures::compat::Future01CompatExt;
use futures::ready;
Expand All @@ -14,7 +14,6 @@ use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use web3::api::Web3;
use web3::contract::tokens::Detokenize;
use web3::types::{Address, BlockNumber, Bytes, CallRequest, U256};
Expand Down Expand Up @@ -103,6 +102,14 @@ impl<T: Transport, R> MethodBuilder<T, R> {
self
}

/// Specify the number of confirmations to wait for when confirming the
/// transaction, if not specified will wait for the transaction to be mined
/// without any extra confirmations.
pub fn confirmations(mut self, value: usize) -> MethodBuilder<T, R> {
self.tx = self.tx.confirmations(value);
self
}

/// Extract inner `TransactionBuilder` from this `SendBuilder`. This exposes
/// `TransactionBuilder` only APIs.
pub fn into_inner(self) -> TransactionBuilder<T> {
Expand All @@ -113,19 +120,6 @@ impl<T: Transport, R> MethodBuilder<T, R> {
pub fn send(self) -> MethodSendFuture<T> {
MethodFuture::new(self.function, self.tx.send())
}

/// Send a transaction for the method call and wait for confirmation.
/// Returns the transaction receipt for inspection.
pub fn send_and_confirm(
self,
poll_interval: Duration,
confirmations: usize,
) -> MethodSendAndConfirmFuture<T> {
MethodFuture::new(
self.function,
self.tx.send_and_confirm(poll_interval, confirmations),
)
}
}

/// Future that wraps an inner transaction execution future to add method
Expand Down Expand Up @@ -161,9 +155,6 @@ where
/// A type alias for a `MethodFuture` wrapped `SendFuture`.
pub type MethodSendFuture<T> = MethodFuture<SendFuture<T>>;

/// A type alias for a `MethodFuture` wrapped `SendAndConfirmFuture`.
pub type MethodSendAndConfirmFuture<T> = MethodFuture<SendAndConfirmFuture<T>>;

impl<T: Transport, R: Detokenize> MethodBuilder<T, R> {
/// Demotes a `MethodBuilder` into a `ViewMethodBuilder` which has a more
/// restricted API and cannot actually send transactions.
Expand Down
15 changes: 12 additions & 3 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ pub enum DeployError {
#[error("error executing contract deployment transaction: {0}")]
Tx(#[from] ExecutionError),

/// Transaction failure (e.g. out of gas).
#[error("contract deployment transaction failed: {0}")]
Failure(H256),
/// Transaction was unable to confirm and is still pending. The contract
/// address cannot be determined.
#[error("contract deployment transaction pending: {0}")]
Pending(H256),
}

impl From<AbiErrorKind> for DeployError {
Expand Down Expand Up @@ -78,6 +79,14 @@ pub enum ExecutionError {
/// A contract call executed an invalid opcode.
#[error("contract call executed an invalid opcode")]
InvalidOpcode,

/// A contract transaction failed to confirm within the block timeout limit.
#[error("transaction timed-out")]
ConfirmTimeout,

/// Transaction failure (e.g. out of gas or revert).
#[error("transaction failed: {0}")]
Failure(H256),
}

impl From<Web3Error> for ExecutionError {
Expand Down
4 changes: 0 additions & 4 deletions src/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use std::ops::Deref;
use std::pin::Pin;
use std::task::{Context, Poll};
use web3::api::Web3;
use web3::confirm::SendTransactionWithConfirmation;
use web3::helpers::CallFuture;
use web3::Transport;

Expand Down Expand Up @@ -69,6 +68,3 @@ impl<T: Transport> Unpin for Web3Unpin<T> {}

/// Type alias for Compat01As03<CallFuture<...>> since it is used a lot.
pub type CompatCallFuture<T, R> = Compat01As03<CallFuture<R, <T as Transport>::Out>>;

/// Type alias for Compat01As03<SendTransactionWithConfirmation<...>>.
pub type CompatSendTxWithConfirmation<T> = Compat01As03<SendTransactionWithConfirmation<T>>;
Loading