From 82d7523c88860870214f68b23e7b843f96b10da4 Mon Sep 17 00:00:00 2001 From: Nicholas Rodrigues Lordello Date: Thu, 9 Jan 2020 09:54:12 +0100 Subject: [PATCH 1/9] refactor a bit to uncouple contract from truffle artifacts --- common/src/{truffle => }/bytecode.rs | 0 common/src/lib.rs | 5 +++++ common/src/truffle.rs | 7 ++----- generate/src/contract/deployment.rs | 4 ++-- generate/src/contract/methods.rs | 2 +- generate/src/contract/types.rs | 2 +- src/contract.rs | 4 ++-- src/contract/deploy.rs | 7 ++++--- src/contract/method.rs | 4 ++-- src/errors.rs | 2 +- src/lib.rs | 3 ++- 11 files changed, 22 insertions(+), 18 deletions(-) rename common/src/{truffle => }/bytecode.rs (100%) diff --git a/common/src/truffle/bytecode.rs b/common/src/bytecode.rs similarity index 100% rename from common/src/truffle/bytecode.rs rename to common/src/bytecode.rs diff --git a/common/src/lib.rs b/common/src/lib.rs index 9eba4e27..b3b7b024 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,6 +1,11 @@ //! Crate for common times shared between the `ethcontract` runtime crate as and //! the `ethcontract-derive` crate. +pub mod bytecode; pub mod errors; mod str; pub mod truffle; + +pub use crate::bytecode::Bytecode; +pub use crate::truffle::Artifact; +pub use ethabi::{self as abi, Contract as Abi}; diff --git a/common/src/truffle.rs b/common/src/truffle.rs index dd427ef7..d999312f 100644 --- a/common/src/truffle.rs +++ b/common/src/truffle.rs @@ -1,17 +1,14 @@ //! Module for reading and examining data produced by truffle. -mod bytecode; - +use crate::bytecode::Bytecode; use crate::errors::ArtifactError; +use ethabi::Contract as Abi; use serde::Deserialize; use std::collections::HashMap; use std::fs::File; use std::path::Path; use web3::types::Address; -pub use self::bytecode::Bytecode; -pub use ethabi::{self as abi, Contract as Abi}; - /// Represents a truffle artifact. #[derive(Clone, Debug, Deserialize)] pub struct Artifact { diff --git a/generate/src/contract/deployment.rs b/generate/src/contract/deployment.rs index cdfed937..0d17b1e5 100644 --- a/generate/src/contract/deployment.rs +++ b/generate/src/contract/deployment.rs @@ -1,7 +1,7 @@ use crate::contract::{methods, Context}; use crate::util; use anyhow::Result; -use ethcontract_common::truffle::abi::{Param, ParamType}; +use ethcontract_common::abi::{Param, ParamType}; use inflector::Inflector; use proc_macro2::{Literal, TokenStream}; use quote::quote; @@ -22,7 +22,7 @@ pub(crate) fn expand(cx: &Context) -> Result { impl #ethcontract::contract::Deploy<#ethcontract::transport::DynTransport> for #contract_name { fn deployed_at( web3: #ethcontract::web3::api::Web3<#ethcontract::transport::DynTransport>, - abi: #ethcontract::truffle::Abi, + abi: #ethcontract::common::Abi, at: #ethcontract::Address, ) -> Self { use #ethcontract::Instance; diff --git a/generate/src/contract/methods.rs b/generate/src/contract/methods.rs index f172c0e9..e484e8c0 100644 --- a/generate/src/contract/methods.rs +++ b/generate/src/contract/methods.rs @@ -1,7 +1,7 @@ use crate::contract::{types, Context}; use crate::util; use anyhow::Result; -use ethcontract_common::truffle::abi::{Function, Param}; +use ethcontract_common::abi::{Function, Param}; use inflector::Inflector; use proc_macro2::{Literal, TokenStream}; use quote::quote; diff --git a/generate/src/contract/types.rs b/generate/src/contract/types.rs index c8c65dbb..7bdd76f1 100644 --- a/generate/src/contract/types.rs +++ b/generate/src/contract/types.rs @@ -1,6 +1,6 @@ use crate::contract::Context; use anyhow::{anyhow, Result}; -use ethcontract_common::truffle::abi::ParamType; +use ethcontract_common::abi::ParamType; use proc_macro2::{Literal, TokenStream}; use quote::quote; diff --git a/src/contract.rs b/src/contract.rs index e14b65cd..29f4a68f 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -6,8 +6,8 @@ mod deploy; mod method; use crate::errors::{DeployError, LinkError}; -use crate::truffle::abi::Result as AbiResult; -use crate::truffle::{Abi, Artifact, Bytecode}; +use ethcontract_common::abi::Result as AbiResult; +use ethcontract_common::{Abi, Artifact, Bytecode}; use web3::api::Web3; use web3::contract::tokens::{Detokenize, Tokenize}; use web3::types::{Address, Bytes}; diff --git a/src/contract/deploy.rs b/src/contract/deploy.rs index a8a6c42b..8a60319d 100644 --- a/src/contract/deploy.rs +++ b/src/contract/deploy.rs @@ -5,8 +5,8 @@ use crate::contract::Instance; use crate::errors::{DeployError, ExecutionError}; use crate::future::{CompatCallFuture, Web3Unpin}; use crate::transaction::{Account, SendFuture, TransactionBuilder, TransactionResult}; -use crate::truffle::abi::ErrorKind as AbiErrorKind; -use crate::truffle::{Abi, Artifact}; +use ethcontract_common::abi::ErrorKind as AbiErrorKind; +use ethcontract_common::{Abi, Artifact}; use futures::compat::Future01CompatExt; use futures::ready; use std::future::Future; @@ -274,7 +274,8 @@ mod tests { use super::*; use crate::contract::Instance; use crate::test::prelude::*; - use crate::truffle::{Artifact, Bytecode, Network}; + use ethcontract_common::Bytecode; + use ethcontract_common::truffle::Network; #[test] fn deployed() { diff --git a/src/contract/method.rs b/src/contract/method.rs index 16cec904..60c243f0 100644 --- a/src/contract/method.rs +++ b/src/contract/method.rs @@ -6,7 +6,7 @@ use crate::errors::{ExecutionError, MethodError}; use crate::future::CompatCallFuture; use crate::hash; use crate::transaction::{Account, SendFuture, TransactionBuilder}; -use crate::truffle::abi::{self, Function, ParamType}; +use ethcontract_common::abi::{self, Function, ParamType}; use futures::compat::Future01CompatExt; use futures::ready; use lazy_static::lazy_static; @@ -328,7 +328,7 @@ impl Future for CallFuture { mod tests { use super::*; use crate::test::prelude::*; - use crate::truffle::abi::{Param, ParamType}; + use ethcontract_common::abi::Param; fn test_abi_function() -> (Function, Bytes) { let function = Function { diff --git a/src/errors.rs b/src/errors.rs index 33909a11..42b7983e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,6 +1,6 @@ //! Module with common error types. -use crate::truffle::abi::{Error as AbiError, ErrorKind as AbiErrorKind, Function}; +use ethcontract_common::abi::{Error as AbiError, ErrorKind as AbiErrorKind, Function}; use ethsign::Error as SignError; use jsonrpc_core::Error as JsonrpcError; use std::num::ParseIntError; diff --git a/src/lib.rs b/src/lib.rs index 99927ae7..06f9b31d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,8 @@ pub use crate::contract::Instance; use crate::contract::{DeployBuilder, MethodBuilder, ViewMethodBuilder}; pub use crate::transaction::Account; pub use crate::transport::DynTransport; -pub use ethcontract_common::truffle::{self, Artifact}; +pub use ethcontract_common as common; +pub use ethcontract_common::truffle::Artifact; pub use ethcontract_derive::contract; pub use ethsign::{self, Protected, SecretKey}; pub use serde_json as json; From 456c1276bce41f1082556104606d82ed06ddcb34 Mon Sep 17 00:00:00 2001 From: Nicholas Rodrigues Lordello Date: Thu, 9 Jan 2020 16:23:19 +0100 Subject: [PATCH 2/9] abstracted away need for truffle artifact for deployments --- common/src/bytecode.rs | 2 +- src/contract.rs | 66 +++++++++++++--- src/contract/deploy.rs | 175 +++++++++++++++++++++-------------------- 3 files changed, 143 insertions(+), 100 deletions(-) diff --git a/common/src/bytecode.rs b/common/src/bytecode.rs index 82bba58d..a490101b 100644 --- a/common/src/bytecode.rs +++ b/common/src/bytecode.rs @@ -81,7 +81,7 @@ impl Bytecode { } /// Convert a bytecode into its byte representation. - pub fn into_bytes(self) -> Result { + pub fn to_bytes(&self) -> Result { match self.undefined_libraries().next() { Some(library) => Err(LinkError::UndefinedLibrary(library.to_string())), None => Ok(Bytes(hex::decode(&self.0).expect("valid hex"))), diff --git a/src/contract.rs b/src/contract.rs index 29f4a68f..03bf59c8 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -7,13 +7,15 @@ mod method; use crate::errors::{DeployError, LinkError}; use ethcontract_common::abi::Result as AbiResult; +use ethcontract_common::truffle::Network; use ethcontract_common::{Abi, Artifact, Bytecode}; +use std::collections::HashMap; use web3::api::Web3; use web3::contract::tokens::{Detokenize, Tokenize}; use web3::types::{Address, Bytes}; use web3::Transport; -pub use self::deploy::{Deploy, DeployBuilder, DeployFuture, DeployedFuture}; +pub use self::deploy::{DeployBuilder, DeployFuture, DeployedFuture, Deployments, Factory}; pub use self::method::{ CallFuture, MethodBuilder, MethodDefaults, MethodFuture, MethodSendFuture, ViewMethodBuilder, }; @@ -49,8 +51,8 @@ impl Instance { /// /// Note that this does not verify that a contract with a matchin `Abi` is /// actually deployed at the given address. - pub fn deployed(web3: Web3, artifact: Artifact) -> DeployedFuture { - DeployedFuture::from_args(web3, artifact) + pub fn deployed(web3: Web3, artifact: Artifact) -> DeployedFuture { + DeployedFuture::new(web3, Networks::new(artifact)) } /// Creates a contract builder with the specified `web3` provider and the @@ -60,11 +62,11 @@ impl Instance { web3: Web3, artifact: Artifact, params: P, - ) -> Result, DeployError> + ) -> Result, DeployError> where P: Tokenize, { - DeployBuilder::new(web3, artifact, params) + Linker::new(artifact).deploy(web3, params) } /// Deploys a contract with the specified `web3` provider with the given @@ -74,7 +76,7 @@ impl Instance { artifact: Artifact, params: P, libraries: I, - ) -> Result, DeployError> + ) -> Result, DeployError> where P: Tokenize, I: Iterator, @@ -138,6 +140,33 @@ impl Instance { } } +/// `Deployments` implementation for an `Instance`. This type is not intended to +/// be used directly but rather through the `Instance::deployed` API. +#[derive(Debug, Clone)] +pub struct Networks { + abi: Abi, + networks: HashMap, +} + +impl Networks { + /// Create a new `Networks` instanced for a contract artifact. + pub fn new(artifact: Artifact) -> Self { + Networks { + abi: artifact.abi, + networks: artifact.networks, + } + } +} + +impl Deployments for Networks { + type Instance = Instance; + + fn from_network(self, web3: Web3, network_id: &str) -> Option { + let address = self.networks.get(network_id)?.address; + Some(Instance::at(web3, self.abi, address)) + } +} + /// Builder for specifying linking options for a contract. #[derive(Debug, Clone)] pub struct Linker { @@ -178,17 +207,28 @@ impl Linker { self, web3: Web3, params: P, - ) -> Result>, DeployError> + ) -> Result, DeployError> where T: Transport, P: Tokenize, { - let artifact = Artifact { - abi: self.abi, - bytecode: self.bytecode, - ..Artifact::empty() - }; + DeployBuilder::new(web3, self, params) + } +} + +impl Factory for Linker { + type Instance = Instance; - DeployBuilder::new(web3, artifact, params) + fn abi(&self) -> &Abi { + &self.abi + } + + fn bytecode(&self) -> &Bytecode { + &self.bytecode + } + + fn at_address(self, web3: Web3, address: Address) -> Self::Instance { + Instance::at(web3, self.abi, address) } } + diff --git a/src/contract/deploy.rs b/src/contract/deploy.rs index 8a60319d..7676934c 100644 --- a/src/contract/deploy.rs +++ b/src/contract/deploy.rs @@ -1,16 +1,14 @@ //! Implementation for creating instances for deployed contracts and deploying //! new contracts. -use crate::contract::Instance; use crate::errors::{DeployError, ExecutionError}; use crate::future::{CompatCallFuture, Web3Unpin}; use crate::transaction::{Account, SendFuture, TransactionBuilder, TransactionResult}; use ethcontract_common::abi::ErrorKind as AbiErrorKind; -use ethcontract_common::{Abi, Artifact}; +use ethcontract_common::{Abi, Bytecode}; use futures::compat::Future01CompatExt; use futures::ready; use std::future::Future; -use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; use web3::api::Web3; @@ -18,18 +16,20 @@ use web3::contract::tokens::Tokenize; use web3::types::{Address, Bytes, U256}; use web3::Transport; -/// A trait for deployable contract types. This allows generated types to be -/// deployable without having to create new future types. -pub trait Deploy { - /// Construct a contract type deployed at an address. - fn deployed_at(web3: Web3, abi: Abi, at: Address) -> Self; -} - -impl Deploy for Instance { - #[inline(always)] - fn deployed_at(web3: Web3, abi: Abi, at: Address) -> Self { - Instance::at(web3, abi, at) - } +/// a factory trait for deployable contract instances. this traits provides +/// functionality for creating instances of a contract type for a given network +/// ID. +/// +/// this allows generated contracts to be deployable without having to create +/// new builder and future types. +pub trait Deployments: Unpin { + /// The type of the contract instance being created. + type Instance; + + /// Create a contract instance for the specified network. This method should + /// return `None` when no deployment can be found for the specified network + /// ID. + fn from_network(self, web3: Web3, network_id: &str) -> Option; } /// Future for creating a deployed contract instance. @@ -37,95 +37,97 @@ impl Deploy for Instance { pub struct DeployedFuture where T: Transport, - D: Deploy, + D: Deployments, { - /// Deployed arguments: `web3` provider and artifact. - args: Option<(Web3Unpin, Artifact)>, + /// The deployment arguments. + args: Option<(Web3Unpin, D)>, + /// The factory used to locate the contract address from a netowkr ID. /// Underlying future for retrieving the network ID. network_id: CompatCallFuture, - _deploy: PhantomData>, } impl DeployedFuture where T: Transport, - D: Deploy, + D: Deployments, { /// Construct a new future that resolves when a deployed contract is located /// from a `web3` provider and artifact data. - pub fn from_args(web3: Web3, artifact: Artifact) -> Self { + pub fn new(web3: Web3, deployments: D) -> Self { let net = web3.net(); DeployedFuture { - args: Some((web3.into(), artifact)), + args: Some((web3.into(), deployments)), network_id: net.version().compat(), - _deploy: Default::default(), } } - - /// Take value of our passed in `web3` provider. - fn args(self: Pin<&mut Self>) -> (Web3, Artifact) { - let (web3, artifact) = self - .get_mut() - .args - .take() - .expect("should be called only once"); - (web3.into(), artifact) - } - - /// Get a pinned reference to the inner `CallFuture` for retrieving the - /// current network ID. - fn network_id(self: Pin<&mut Self>) -> Pin<&mut CompatCallFuture> { - Pin::new(&mut self.get_mut().network_id) - } } impl Future for DeployedFuture where T: Transport, - D: Deploy, + D: Deployments, { - type Output = Result; + type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - self.as_mut().network_id().poll(cx).map(|network_id| { - let network_id = network_id?; - let (web3, artifact) = self.args(); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let unpinned = self.get_mut(); + Pin::new(&mut unpinned.network_id) + .poll(cx) + .map(|network_id| { + let network_id = network_id?; + let (web3, deployments) = unpinned.args.take().expect("called more than once"); + deployments + .from_network(web3.into(), &network_id) + .ok_or(DeployError::NotFound(network_id)) + }) + } +} - let address = match artifact.networks.get(&network_id) { - Some(network) => network.address, - None => return Err(DeployError::NotFound(network_id)), - }; +/// a factory trait for deployable contract instances. this traits provides +/// functionality for building a deployment and creating instances of a +/// contract type at a given address. +/// +/// this allows generated contracts to be deployable without having to create +/// new builder and future types. +pub trait Factory: Unpin { + /// The type of the contract instance being created. + type Instance; - Ok(D::deployed_at(web3, artifact.abi, address)) - }) - } + /// Gets a reference to the contract bytecode. + fn bytecode(&self) -> &Bytecode; + + /// Gets a reference the contract ABI. + fn abi(&self) -> &Abi; + + /// Create a contract instance from the specified address. + fn at_address(self, web3: Web3, address: Address) -> Self::Instance; } /// Builder for specifying options for deploying a linked contract. #[derive(Debug, Clone)] #[must_use = "deploy builers do nothing unless you `.deploy()` them"] -pub struct DeployBuilder +pub struct DeployBuilder where T: Transport, - D: Deploy, + F: Factory, { /// The underlying `web3` provider. web3: Web3, - /// The ABI for the contract that is to be deployed. - abi: Abi, + /// The instance factory to use once the contract is deployed and its + /// address is retrieved. + factory: F, /// The underlying transaction used t tx: TransactionBuilder, - _deploy: PhantomData>, } -impl DeployBuilder +impl DeployBuilder where T: Transport, - D: Deploy, + F: Factory, { /// Create a new deploy builder from a `web3` provider, artifact data and /// deployment (constructor) parameters. - pub fn new

(web3: Web3, artifact: Artifact, params: P) -> Result + pub fn new

(web3: Web3, factory: F, params: P) -> Result where P: Tokenize, { @@ -133,13 +135,13 @@ where // `rust-web3` code so that we can add things like signing support; // luckily most of complicated bits can be reused from the tx code - if artifact.bytecode.is_empty() { + if factory.bytecode().is_empty() { return Err(DeployError::EmptyBytecode); } - let code = artifact.bytecode.into_bytes()?; + let code = factory.bytecode().to_bytes()?; let params = params.into_tokens(); - let data = match (artifact.abi.constructor(), params.is_empty()) { + let data = match (factory.abi().constructor(), params.is_empty()) { (None, false) => return Err(AbiErrorKind::InvalidData.into()), (None, true) => code, (Some(ctor), _) => Bytes(ctor.encode_input(code.0, ¶ms)?), @@ -147,9 +149,8 @@ where Ok(DeployBuilder { web3: web3.clone(), - abi: artifact.abi, + factory, tx: TransactionBuilder::new(web3).data(data).confirmations(0), - _deploy: Default::default(), }) } @@ -204,46 +205,45 @@ where /// Sign (if required) and execute the transaction. Returns the transaction /// hash that can be used to retrieve transaction information. - pub fn deploy(self) -> DeployFuture { + pub fn deploy(self) -> DeployFuture { DeployFuture::from_builder(self) } } /// Future for deploying a contract instance. #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct DeployFuture +pub struct DeployFuture where T: Transport, - D: Deploy, + F: Factory, { + /// /// The deployment args - args: Option<(Web3Unpin, Abi)>, + args: Option<(Web3Unpin, F)>, /// The future resolved when the deploy transaction is complete. send: SendFuture, - _deploy: PhantomData>, } -impl DeployFuture +impl DeployFuture where T: Transport, - D: Deploy, + F: Factory, { /// Create an instance from a `DeployBuilder`. - pub fn from_builder(builder: DeployBuilder) -> Self { + pub fn from_builder(builder: DeployBuilder) -> Self { DeployFuture { - args: Some((builder.web3.into(), builder.abi)), + args: Some((builder.web3.into(), builder.factory)), send: builder.tx.send(), - _deploy: Default::default(), } } } -impl Future for DeployFuture +impl Future for DeployFuture where T: Transport, - D: Deploy, + F: Factory, { - type Output = Result; + type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { let unpinned = self.get_mut(); @@ -263,19 +263,19 @@ where } }; - let (web3, abi) = unpinned.args.take().expect("called more than once"); + let (web3, factory) = unpinned.args.take().expect("called more than once"); - Poll::Ready(Ok(D::deployed_at(web3.into(), abi, address))) + Poll::Ready(Ok(factory.at_address(web3.into(), address))) } } #[cfg(test)] mod tests { use super::*; - use crate::contract::Instance; + use crate::contract::{Networks, Linker}; use crate::test::prelude::*; - use ethcontract_common::Bytecode; use ethcontract_common::truffle::Network; + use ethcontract_common::{Artifact, Bytecode}; #[test] fn deployed() { @@ -293,7 +293,8 @@ mod tests { }; transport.add_response(json!(network_id)); // estimate gas response - let instance: Instance<_> = DeployedFuture::from_args(web3, artifact) + let networks = Networks::new(artifact); + let instance = DeployedFuture::new(web3, networks) .wait() .expect("successful deployment"); @@ -314,7 +315,8 @@ mod tests { bytecode: bytecode.clone(), ..Artifact::empty() }; - let tx = DeployBuilder::<_, Instance<_>>::new(web3, artifact, ()) + let linker = Linker::new(artifact); + let tx = DeployBuilder::new(web3, linker, ()) .expect("error creating deploy builder") .from(Account::Local(from, None)) .gas(1.into()) @@ -328,7 +330,7 @@ mod tests { assert_eq!(tx.gas, Some(1.into())); assert_eq!(tx.gas_price, Some(2.into())); assert_eq!(tx.value, Some(28.into())); - assert_eq!(tx.data, Some(bytecode.into_bytes().unwrap())); + assert_eq!(tx.data, Some(bytecode.to_bytes().unwrap())); assert_eq!(tx.nonce, Some(42.into())); transport.assert_no_more_requests(); } @@ -345,7 +347,8 @@ mod tests { let web3 = Web3::new(transport.clone()); let artifact = Artifact::empty(); - let error = DeployBuilder::<_, Instance<_>>::new(web3, artifact, ()) + let linker = Linker::new(artifact); + let error = DeployBuilder::new(web3, linker, ()) .err() .unwrap(); From 8014cf9e099ca008316f31c8242000d354dfe8ba Mon Sep 17 00:00:00 2001 From: Nicholas Rodrigues Lordello Date: Thu, 9 Jan 2020 17:02:42 +0100 Subject: [PATCH 3/9] rename private module --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 06f9b31d..ac7f85db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,7 +106,7 @@ pub use web3::types::{Address, BlockNumber, TransactionCondition, H256, U256}; /// Type alias for a contract `Instance` with an underyling `DynTransport`. pub type DynInstance = Instance; -/// Type alias for a `MethodBuilder` with an underlying `DynTransport`. +/// Type alias for a `DeployBuilder` with an underlying `DynTransport`. pub type DynDeployBuilder = DeployBuilder; /// Type alias for a `MethodBuilder` with an underlying `DynTransport`. @@ -116,8 +116,8 @@ pub type DynMethodBuilder = MethodBuilder; pub type DynViewMethodBuilder = ViewMethodBuilder; #[doc(hidden)] -pub mod foreign { - //! Foreign types that we re-export to be used internally by the procedural +pub mod private { + //! Private types that we export to be used internally by the procedural //! macro but do not appear on public interfaces. pub use lazy_static::lazy_static; From 03c5ec92654fe7f828e9bedb68228892f63b6502 Mon Sep 17 00:00:00 2001 From: Nicholas Rodrigues Lordello Date: Mon, 13 Jan 2020 13:16:21 +0100 Subject: [PATCH 4/9] deployed is now generated with new traits --- generate/src/contract/common.rs | 2 +- generate/src/contract/deployment.rs | 83 +++++++++++++++++------------ src/contract.rs | 1 - src/contract/deploy.rs | 6 +-- src/internal.rs | 65 ++++++++++++++++++++++ src/lib.rs | 5 ++ 6 files changed, 121 insertions(+), 41 deletions(-) create mode 100644 src/internal.rs diff --git a/generate/src/contract/common.rs b/generate/src/contract/common.rs index 1a6fa619..40a147b6 100644 --- a/generate/src/contract/common.rs +++ b/generate/src/contract/common.rs @@ -30,7 +30,7 @@ pub(crate) fn expand(cx: &Context) -> TokenStream { /// Retrieves the truffle artifact used to generate the type safe API /// for this contract. pub fn artifact() -> &'static #ethcontract::Artifact { - use #ethcontract::foreign::lazy_static; + use #ethcontract::private::lazy_static; use #ethcontract::Artifact; lazy_static! { diff --git a/generate/src/contract/deployment.rs b/generate/src/contract/deployment.rs index 0d17b1e5..691e5eca 100644 --- a/generate/src/contract/deployment.rs +++ b/generate/src/contract/deployment.rs @@ -7,17 +7,15 @@ use proc_macro2::{Literal, TokenStream}; use quote::quote; pub(crate) fn expand(cx: &Context) -> Result { - let ethcontract = &cx.runtime_crate; - let contract_name = &cx.contract_name; - let deployed = expand_deployed(&cx); let deploy = expand_deploy(&cx)?; Ok(quote! { - impl #contract_name { - #deployed - #deploy - } + #deployed + }) + + /* + #deploy impl #ethcontract::contract::Deploy<#ethcontract::transport::DynTransport> for #contract_name { fn deployed_at( @@ -36,7 +34,7 @@ pub(crate) fn expand(cx: &Context) -> Result { } } } - }) + */ } fn expand_deployed(cx: &Context) -> TokenStream { @@ -45,37 +43,52 @@ fn expand_deployed(cx: &Context) -> TokenStream { } let ethcontract = &cx.runtime_crate; + let contract_name = &cx.contract_name; quote! { - /// Locates a deployed contract based on the current network ID - /// reported by the `web3` provider. - /// - /// Note that this does not verify that a contract with a maching - /// `Abi` is actually deployed at the given address. - pub fn deployed( - web3: &#ethcontract::web3::api::Web3, - ) -> #ethcontract::contract::DeployedFuture<#ethcontract::transport::DynTransport, Self> - where - F: #ethcontract::web3::futures::Future + Send + 'static, - T: #ethcontract::web3::Transport + 'static, - { - use #ethcontract::Artifact; - use #ethcontract::contract::DeployedFuture; - use #ethcontract::transport::DynTransport; - use #ethcontract::web3::api::Web3; + impl #contract_name { + /// Locates a deployed contract based on the current network ID + /// reported by the `web3` provider. + /// + /// Note that this does not verify that a contract with a maching + /// `Abi` is actually deployed at the given address. + pub fn deployed( + web3: &#ethcontract::web3::api::Web3, + ) -> #ethcontract::contract::DeployedFuture< + #ethcontract::transport::DynTransport, + #ethcontract::internal::Contract, + > + where + F: #ethcontract::web3::futures::Future< + Item = #ethcontract::json::Value, + Error = #ethcontract::web3::Error + > + Send + 'static, + T: #ethcontract::web3::Transport + 'static, + { + use #ethcontract::Artifact; + use #ethcontract::contract::DeployedFuture; + use #ethcontract::transport::DynTransport; + use #ethcontract::web3::api::Web3; + + let transport = DynTransport::new(web3.transport().clone()); + let web3 = Web3::new(transport); + + DeployedFuture::new(web3, Default::default()) + } + } - let transport = DynTransport::new(web3.transport().clone()); - let web3 = Web3::new(transport); - let artifact = { // only clone the pieces we need - let artifact = Self::artifact(); - Artifact { - abi: artifact.abi.clone(), - networks: artifact.networks.clone(), - ..Artifact::empty() - } - }; + impl #ethcontract::internal::ContractDeployments for #contract_name { + fn from_network(web3: #ethcontract::DynWeb3, network_id: &str) -> Option { + use #ethcontract::Instance; - DeployedFuture::from_args(web3, artifact) + let artifact = Self::artifact(); + artifact + .networks + .get(network_id) + .map(move |network| #contract_name { + instance: Instance::at(web3, artifact.abi.clone(), network.address), + }) + } } } } diff --git a/src/contract.rs b/src/contract.rs index 03bf59c8..71c653a9 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -231,4 +231,3 @@ impl Factory for Linker { Instance::at(web3, self.abi, address) } } - diff --git a/src/contract/deploy.rs b/src/contract/deploy.rs index 7676934c..e4bb0ef4 100644 --- a/src/contract/deploy.rs +++ b/src/contract/deploy.rs @@ -272,7 +272,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::contract::{Networks, Linker}; + use crate::contract::{Linker, Networks}; use crate::test::prelude::*; use ethcontract_common::truffle::Network; use ethcontract_common::{Artifact, Bytecode}; @@ -348,9 +348,7 @@ mod tests { let artifact = Artifact::empty(); let linker = Linker::new(artifact); - let error = DeployBuilder::new(web3, linker, ()) - .err() - .unwrap(); + let error = DeployBuilder::new(web3, linker, ()).err().unwrap(); assert_eq!(error.to_string(), DeployError::EmptyBytecode.to_string()); transport.assert_no_more_requests(); diff --git a/src/internal.rs b/src/internal.rs new file mode 100644 index 00000000..f848b20f --- /dev/null +++ b/src/internal.rs @@ -0,0 +1,65 @@ +//! Module provides internal functionality specific to generated contracts that +//! isn't intended to be used directly but rather from generated code. + +use crate::DynWeb3; +use crate::contract::{Deployments, Factory}; +use crate::transport::DynTransport; +use ethcontract_common::{Abi, Bytecode}; +use std::marker::PhantomData; +use web3::types::Address; + +/// A struct for wrapping generated contract types. This allows these contract +/// types to implement traits that require instances without them. +pub struct Contract(PhantomData>); + +impl Default for Contract { + fn default() -> Self { + Contract(PhantomData) + } +} + +/// Analogous to the `ethcontract::contract::Deployments` trait except with +/// associated functions instead of methods meaning that no `self` instance is +/// required. +pub trait ContractDeployments: Sized { + /// See `ethcontract::contract::Deployments::from_network`. + fn from_network(web3: DynWeb3, network_id: &str) -> Option; +} + +impl Deployments for Contract { + type Instance = C; + + fn from_network(self, web3: DynWeb3, network_id: &str) -> Option { + C::from_network(web3, network_id) + } +} + +/// Analogous to the `ethcontract::contract::Factory` trait except with +/// associated functions instead of methods meaning that no `self` instance is +/// required. +pub trait ContractFactory: Sized { + /// See `ethcontract::contract::Factory::bytecode`. + fn bytecode() -> &'static Bytecode; + + /// See `ethcontract::contract::Factory::abi`. + fn abi() -> &'static Abi; + + /// See `ethcontract::contract::Factory::at_address`. + fn at_address(web3: DynWeb3, address: Address) -> Self; +} + +impl Factory for Contract { + type Instance = C; + + fn bytecode(&self) -> &Bytecode { + C::bytecode() + } + + fn abi(&self) -> &Abi { + C::abi() + } + + fn at_address(self, web3: DynWeb3, address: Address) -> Self::Instance { + C::at_address(web3, address) + } +} diff --git a/src/lib.rs b/src/lib.rs index ac7f85db..fb6476bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,6 +87,7 @@ pub mod contract; pub mod errors; mod future; mod hash; +pub mod internal; pub mod sign; pub mod transaction; pub mod transport; @@ -101,8 +102,12 @@ pub use ethcontract_derive::contract; pub use ethsign::{self, Protected, SecretKey}; pub use serde_json as json; pub use web3; +use web3::api::Web3; pub use web3::types::{Address, BlockNumber, TransactionCondition, H256, U256}; +/// Type alias for a `Web3` provider with an underlying `DynTransport`. +pub type DynWeb3 = Web3; + /// Type alias for a contract `Instance` with an underyling `DynTransport`. pub type DynInstance = Instance; From 3f2fb133e9281ac98fcf32a4a1cf019c20a2f34b Mon Sep 17 00:00:00 2001 From: Nicholas Rodrigues Lordello Date: Mon, 13 Jan 2020 15:00:48 +0100 Subject: [PATCH 5/9] refactor deployment traits --- generate/src/contract/deployment.rs | 13 ++-- src/contract.rs | 36 +++++----- src/contract/deploy.rs | 101 +++++++++++++++------------- src/internal.rs | 65 ------------------ src/lib.rs | 1 - 5 files changed, 77 insertions(+), 139 deletions(-) delete mode 100644 src/internal.rs diff --git a/generate/src/contract/deployment.rs b/generate/src/contract/deployment.rs index 691e5eca..fc432b46 100644 --- a/generate/src/contract/deployment.rs +++ b/generate/src/contract/deployment.rs @@ -54,10 +54,7 @@ fn expand_deployed(cx: &Context) -> TokenStream { /// `Abi` is actually deployed at the given address. pub fn deployed( web3: &#ethcontract::web3::api::Web3, - ) -> #ethcontract::contract::DeployedFuture< - #ethcontract::transport::DynTransport, - #ethcontract::internal::Contract, - > + ) -> #ethcontract::contract::DeployedFuture<#ethcontract::transport::DynTransport, Self> where F: #ethcontract::web3::futures::Future< Item = #ethcontract::json::Value, @@ -77,8 +74,10 @@ fn expand_deployed(cx: &Context) -> TokenStream { } } - impl #ethcontract::internal::ContractDeployments for #contract_name { - fn from_network(web3: #ethcontract::DynWeb3, network_id: &str) -> Option { + impl #ethcontract::contract::FromNetwork<#ethcontract::DynTransport> for #contract_name { + type Context = (); + + fn from_network(web3: #ethcontract::DynWeb3, network_id: &str, _: Self::Context) -> Option { use #ethcontract::Instance; let artifact = Self::artifact(); @@ -173,7 +172,7 @@ fn expand_deploy(cx: &Context) -> Result { }; #link - DeployBuilder::new(web3, artifact, #arg).expect("valid deployment args") + DeployBuilder::new(web3, Default::default(), #arg).expect("valid deployment args") } }) } diff --git a/src/contract.rs b/src/contract.rs index 71c653a9..57568424 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -15,7 +15,7 @@ use web3::contract::tokens::{Detokenize, Tokenize}; use web3::types::{Address, Bytes}; use web3::Transport; -pub use self::deploy::{DeployBuilder, DeployFuture, DeployedFuture, Deployments, Factory}; +pub use self::deploy::{DeployBuilder, DeployFuture, DeployedFuture, FromNetwork, Deploy}; pub use self::method::{ CallFuture, MethodBuilder, MethodDefaults, MethodFuture, MethodSendFuture, ViewMethodBuilder, }; @@ -51,7 +51,7 @@ impl Instance { /// /// Note that this does not verify that a contract with a matchin `Abi` is /// actually deployed at the given address. - pub fn deployed(web3: Web3, artifact: Artifact) -> DeployedFuture { + pub fn deployed(web3: Web3, artifact: Artifact) -> DeployedFuture> { DeployedFuture::new(web3, Networks::new(artifact)) } @@ -62,7 +62,7 @@ impl Instance { web3: Web3, artifact: Artifact, params: P, - ) -> Result, DeployError> + ) -> Result>, DeployError> where P: Tokenize, { @@ -76,7 +76,7 @@ impl Instance { artifact: Artifact, params: P, libraries: I, - ) -> Result, DeployError> + ) -> Result>, DeployError> where P: Tokenize, I: Iterator, @@ -158,12 +158,12 @@ impl Networks { } } -impl Deployments for Networks { - type Instance = Instance; +impl FromNetwork for Instance { + type Context = Networks; - fn from_network(self, web3: Web3, network_id: &str) -> Option { - let address = self.networks.get(network_id)?.address; - Some(Instance::at(web3, self.abi, address)) + fn from_network(web3: Web3, network_id: &str, cx: Self::Context) -> Option { + let address = cx.networks.get(network_id)?.address; + Some(Instance::at(web3, cx.abi, address)) } } @@ -207,7 +207,7 @@ impl Linker { self, web3: Web3, params: P, - ) -> Result, DeployError> + ) -> Result>, DeployError> where T: Transport, P: Tokenize, @@ -216,18 +216,18 @@ impl Linker { } } -impl Factory for Linker { - type Instance = Instance; +impl Deploy for Instance { + type Context = Linker; - fn abi(&self) -> &Abi { - &self.abi + fn abi(cx: &Self::Context) -> &Abi { + &cx.abi } - fn bytecode(&self) -> &Bytecode { - &self.bytecode + fn bytecode(cx: &Self::Context) -> &Bytecode { + &cx.bytecode } - fn at_address(self, web3: Web3, address: Address) -> Self::Instance { - Instance::at(web3, self.abi, address) + fn at_address(web3: Web3, address: Address, cx: Self::Context) -> Self { + Instance::at(web3, cx.abi, address) } } diff --git a/src/contract/deploy.rs b/src/contract/deploy.rs index e4bb0ef4..e983178e 100644 --- a/src/contract/deploy.rs +++ b/src/contract/deploy.rs @@ -9,6 +9,7 @@ use ethcontract_common::{Abi, Bytecode}; use futures::compat::Future01CompatExt; use futures::ready; use std::future::Future; +use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; use web3::api::Web3; @@ -22,52 +23,54 @@ use web3::Transport; /// /// this allows generated contracts to be deployable without having to create /// new builder and future types. -pub trait Deployments: Unpin { - /// The type of the contract instance being created. - type Instance; +pub trait FromNetwork: Sized { + /// Context passed to the `Deployments`. + type Context: Unpin; /// Create a contract instance for the specified network. This method should /// return `None` when no deployment can be found for the specified network /// ID. - fn from_network(self, web3: Web3, network_id: &str) -> Option; + fn from_network(web3: Web3, network_id: &str, cx: Self::Context) -> Option; } /// Future for creating a deployed contract instance. #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct DeployedFuture +pub struct DeployedFuture where T: Transport, - D: Deployments, + I: FromNetwork, { /// The deployment arguments. - args: Option<(Web3Unpin, D)>, + args: Option<(Web3Unpin, I::Context)>, /// The factory used to locate the contract address from a netowkr ID. /// Underlying future for retrieving the network ID. network_id: CompatCallFuture, + _instance: PhantomData>, } -impl DeployedFuture +impl DeployedFuture where T: Transport, - D: Deployments, + I: FromNetwork, { /// Construct a new future that resolves when a deployed contract is located /// from a `web3` provider and artifact data. - pub fn new(web3: Web3, deployments: D) -> Self { + pub fn new(web3: Web3, context: I::Context) -> Self { let net = web3.net(); DeployedFuture { - args: Some((web3.into(), deployments)), + args: Some((web3.into(), context)), network_id: net.version().compat(), + _instance: PhantomData, } } } -impl Future for DeployedFuture +impl Future for DeployedFuture where T: Transport, - D: Deployments, + I: FromNetwork, { - type Output = Result; + type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { let unpinned = self.get_mut(); @@ -75,9 +78,8 @@ where .poll(cx) .map(|network_id| { let network_id = network_id?; - let (web3, deployments) = unpinned.args.take().expect("called more than once"); - deployments - .from_network(web3.into(), &network_id) + let (web3, context) = unpinned.args.take().expect("called more than once"); + I::from_network(web3.into(), &network_id, context) .ok_or(DeployError::NotFound(network_id)) }) } @@ -89,45 +91,45 @@ where /// /// this allows generated contracts to be deployable without having to create /// new builder and future types. -pub trait Factory: Unpin { +pub trait Deploy: Sized { /// The type of the contract instance being created. - type Instance; + type Context: Unpin; /// Gets a reference to the contract bytecode. - fn bytecode(&self) -> &Bytecode; + fn bytecode(cx: &Self::Context) -> &Bytecode; /// Gets a reference the contract ABI. - fn abi(&self) -> &Abi; + fn abi(cx: &Self::Context) -> &Abi; /// Create a contract instance from the specified address. - fn at_address(self, web3: Web3, address: Address) -> Self::Instance; + fn at_address(web3: Web3, address: Address, cx: Self::Context) -> Self; } /// Builder for specifying options for deploying a linked contract. #[derive(Debug, Clone)] #[must_use = "deploy builers do nothing unless you `.deploy()` them"] -pub struct DeployBuilder +pub struct DeployBuilder where T: Transport, - F: Factory, + I: Deploy, { /// The underlying `web3` provider. web3: Web3, - /// The instance factory to use once the contract is deployed and its - /// address is retrieved. - factory: F, + /// The factory context. + context: I::Context, /// The underlying transaction used t tx: TransactionBuilder, + _instance: PhantomData, } -impl DeployBuilder +impl DeployBuilder where T: Transport, - F: Factory, + I: Deploy, { /// Create a new deploy builder from a `web3` provider, artifact data and /// deployment (constructor) parameters. - pub fn new

(web3: Web3, factory: F, params: P) -> Result + pub fn new

(web3: Web3, context: I::Context, params: P) -> Result where P: Tokenize, { @@ -135,13 +137,14 @@ where // `rust-web3` code so that we can add things like signing support; // luckily most of complicated bits can be reused from the tx code - if factory.bytecode().is_empty() { + let bytecode = I::bytecode(&context); + if bytecode.is_empty() { return Err(DeployError::EmptyBytecode); } - let code = factory.bytecode().to_bytes()?; + let code = bytecode.to_bytes()?; let params = params.into_tokens(); - let data = match (factory.abi().constructor(), params.is_empty()) { + let data = match (I::abi(&context).constructor(), params.is_empty()) { (None, false) => return Err(AbiErrorKind::InvalidData.into()), (None, true) => code, (Some(ctor), _) => Bytes(ctor.encode_input(code.0, ¶ms)?), @@ -149,8 +152,9 @@ where Ok(DeployBuilder { web3: web3.clone(), - factory, + context, tx: TransactionBuilder::new(web3).data(data).confirmations(0), + _instance: PhantomData, }) } @@ -205,45 +209,46 @@ where /// Sign (if required) and execute the transaction. Returns the transaction /// hash that can be used to retrieve transaction information. - pub fn deploy(self) -> DeployFuture { + pub fn deploy(self) -> DeployFuture { DeployFuture::from_builder(self) } } /// Future for deploying a contract instance. #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct DeployFuture +pub struct DeployFuture where T: Transport, - F: Factory, + I: Deploy, { - /// /// The deployment args - args: Option<(Web3Unpin, F)>, + args: Option<(Web3Unpin, I::Context)>, /// The future resolved when the deploy transaction is complete. send: SendFuture, + _instance: PhantomData>, } -impl DeployFuture +impl DeployFuture where T: Transport, - F: Factory, + I: Deploy, { /// Create an instance from a `DeployBuilder`. - pub fn from_builder(builder: DeployBuilder) -> Self { + pub fn from_builder(builder: DeployBuilder) -> Self { DeployFuture { - args: Some((builder.web3.into(), builder.factory)), + args: Some((builder.web3.into(), builder.context)), send: builder.tx.send(), + _instance: PhantomData, } } } -impl Future for DeployFuture +impl Future for DeployFuture where T: Transport, - F: Factory, + I: Deploy, { - type Output = Result; + type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { let unpinned = self.get_mut(); @@ -263,9 +268,9 @@ where } }; - let (web3, factory) = unpinned.args.take().expect("called more than once"); + let (web3, context) = unpinned.args.take().expect("called more than once"); - Poll::Ready(Ok(factory.at_address(web3.into(), address))) + Poll::Ready(Ok(I::at_address(web3.into(), address, context))) } } diff --git a/src/internal.rs b/src/internal.rs deleted file mode 100644 index f848b20f..00000000 --- a/src/internal.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! Module provides internal functionality specific to generated contracts that -//! isn't intended to be used directly but rather from generated code. - -use crate::DynWeb3; -use crate::contract::{Deployments, Factory}; -use crate::transport::DynTransport; -use ethcontract_common::{Abi, Bytecode}; -use std::marker::PhantomData; -use web3::types::Address; - -/// A struct for wrapping generated contract types. This allows these contract -/// types to implement traits that require instances without them. -pub struct Contract(PhantomData>); - -impl Default for Contract { - fn default() -> Self { - Contract(PhantomData) - } -} - -/// Analogous to the `ethcontract::contract::Deployments` trait except with -/// associated functions instead of methods meaning that no `self` instance is -/// required. -pub trait ContractDeployments: Sized { - /// See `ethcontract::contract::Deployments::from_network`. - fn from_network(web3: DynWeb3, network_id: &str) -> Option; -} - -impl Deployments for Contract { - type Instance = C; - - fn from_network(self, web3: DynWeb3, network_id: &str) -> Option { - C::from_network(web3, network_id) - } -} - -/// Analogous to the `ethcontract::contract::Factory` trait except with -/// associated functions instead of methods meaning that no `self` instance is -/// required. -pub trait ContractFactory: Sized { - /// See `ethcontract::contract::Factory::bytecode`. - fn bytecode() -> &'static Bytecode; - - /// See `ethcontract::contract::Factory::abi`. - fn abi() -> &'static Abi; - - /// See `ethcontract::contract::Factory::at_address`. - fn at_address(web3: DynWeb3, address: Address) -> Self; -} - -impl Factory for Contract { - type Instance = C; - - fn bytecode(&self) -> &Bytecode { - C::bytecode() - } - - fn abi(&self) -> &Abi { - C::abi() - } - - fn at_address(self, web3: DynWeb3, address: Address) -> Self::Instance { - C::at_address(web3, address) - } -} diff --git a/src/lib.rs b/src/lib.rs index fb6476bb..bf499f9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,7 +87,6 @@ pub mod contract; pub mod errors; mod future; mod hash; -pub mod internal; pub mod sign; pub mod transaction; pub mod transport; From 8ec1bb370ed9b2c9d223403eb1e00f89add173f5 Mon Sep 17 00:00:00 2001 From: Nicholas Rodrigues Lordello Date: Mon, 13 Jan 2020 15:43:56 +0100 Subject: [PATCH 6/9] fix unit tests and deploy code --- generate/src/contract/deployment.rs | 96 ++++++++++++++--------------- src/contract/deploy.rs | 12 ++-- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/generate/src/contract/deployment.rs b/generate/src/contract/deployment.rs index fc432b46..5c283c1b 100644 --- a/generate/src/contract/deployment.rs +++ b/generate/src/contract/deployment.rs @@ -12,29 +12,8 @@ pub(crate) fn expand(cx: &Context) -> Result { Ok(quote! { #deployed - }) - - /* #deploy - - impl #ethcontract::contract::Deploy<#ethcontract::transport::DynTransport> for #contract_name { - fn deployed_at( - web3: #ethcontract::web3::api::Web3<#ethcontract::transport::DynTransport>, - abi: #ethcontract::common::Abi, - at: #ethcontract::Address, - ) -> Self { - use #ethcontract::Instance; - - // NOTE: we need to make sure that we were deployed with the - // correct ABI; luckily Abi implementes PartialEq - debug_assert_eq!(abi, Self::artifact().abi); - - Self { - instance: Instance::at(web3, abi, at), - } - } - } - */ + }) } fn expand_deployed(cx: &Context) -> TokenStream { @@ -84,7 +63,7 @@ fn expand_deployed(cx: &Context) -> TokenStream { artifact .networks .get(network_id) - .map(move |network| #contract_name { + .map(move |network| Self { instance: Instance::at(web3, artifact.abi.clone(), network.address), }) } @@ -99,6 +78,7 @@ fn expand_deploy(cx: &Context) -> Result { } let ethcontract = &cx.runtime_crate; + let contract_name = &cx.contract_name; // TODO(nlordell): not sure how contructor documentation get generated as I // can't seem to get truffle to output it @@ -134,12 +114,12 @@ fn expand_deploy(cx: &Context) -> Result { let address = util::ident(&lib_param.name); quote! { - artifact.bytecode.link(#name, #address).expect("valid library"); + bytecode.link(#name, #address).expect("valid library"); } }); quote! { - let mut artifact = artifact; + let mut bytecode = bytecode; #( #link_libraries )* } } else { @@ -147,32 +127,48 @@ fn expand_deploy(cx: &Context) -> Result { }; Ok(quote! { - #doc - pub fn builder( - web3: &#ethcontract::web3::api::Web3 #lib_input #input , - ) -> #ethcontract::DynDeployBuilder - where - F: #ethcontract::web3::futures::Future + Send + 'static, - T: #ethcontract::web3::Transport + 'static, - { - use #ethcontract::{Artifact, DynTransport}; - use #ethcontract::contract::DeployBuilder; - use #ethcontract::web3::api::Web3; - - let transport = DynTransport::new(web3.transport().clone()); - let web3 = Web3::new(transport); - - let artifact = { // only clone the pieces we need - let artifact = Self::artifact(); - Artifact { - abi: artifact.abi.clone(), - bytecode: artifact.bytecode.clone(), - ..Artifact::empty() - } - }; - #link + impl #contract_name { + #doc + pub fn builder( + web3: &#ethcontract::web3::api::Web3 #lib_input #input , + ) -> #ethcontract::DynDeployBuilder + where + F: #ethcontract::web3::futures::Future + Send + 'static, + T: #ethcontract::web3::Transport + 'static, + { + use #ethcontract::{Artifact, DynTransport}; + use #ethcontract::contract::DeployBuilder; + use #ethcontract::web3::api::Web3; + + let transport = DynTransport::new(web3.transport().clone()); + let web3 = Web3::new(transport); + + let bytecode = Self::artifact().bytecode.clone(); + #link + + DeployBuilder::new(web3, bytecode, #arg).expect("valid deployment args") + } + } + + impl #ethcontract::contract::Deploy<#ethcontract::DynTransport> for #contract_name { + type Context = #ethcontract::common::Bytecode; - DeployBuilder::new(web3, Default::default(), #arg).expect("valid deployment args") + fn bytecode(cx: &Self::Context) -> &#ethcontract::common::Bytecode { + cx + } + + fn abi(_: &Self::Context) -> &#ethcontract::common::Abi { + &Self::artifact().abi + } + + fn at_address(web3: #ethcontract::DynWeb3, address: #ethcontract::Address, _: Self::Context) -> Self { + use #ethcontract::Instance; + + let abi = Self::artifact().abi.clone(); + Self { + instance: Instance::at(web3, abi, address), + } + } } }) } diff --git a/src/contract/deploy.rs b/src/contract/deploy.rs index e983178e..cc756336 100644 --- a/src/contract/deploy.rs +++ b/src/contract/deploy.rs @@ -277,11 +277,15 @@ where #[cfg(test)] mod tests { use super::*; - use crate::contract::{Linker, Networks}; + use crate::contract::{Instance, Linker, Networks}; use crate::test::prelude::*; use ethcontract_common::truffle::Network; use ethcontract_common::{Artifact, Bytecode}; + type InstanceDeployedFuture = DeployedFuture>; + + type InstanceDeployBuilder = DeployBuilder>; + #[test] fn deployed() { let mut transport = TestTransport::new(); @@ -299,7 +303,7 @@ mod tests { transport.add_response(json!(network_id)); // estimate gas response let networks = Networks::new(artifact); - let instance = DeployedFuture::new(web3, networks) + let instance = InstanceDeployedFuture::new(web3, networks) .wait() .expect("successful deployment"); @@ -321,7 +325,7 @@ mod tests { ..Artifact::empty() }; let linker = Linker::new(artifact); - let tx = DeployBuilder::new(web3, linker, ()) + let tx = InstanceDeployBuilder::new(web3, linker, ()) .expect("error creating deploy builder") .from(Account::Local(from, None)) .gas(1.into()) @@ -353,7 +357,7 @@ mod tests { let artifact = Artifact::empty(); let linker = Linker::new(artifact); - let error = DeployBuilder::new(web3, linker, ()).err().unwrap(); + let error = InstanceDeployBuilder::new(web3, linker, ()).err().unwrap(); assert_eq!(error.to_string(), DeployError::EmptyBytecode.to_string()); transport.assert_no_more_requests(); From d6f87e6f56113f9e77035352c299895715f03607 Mon Sep 17 00:00:00 2001 From: Nicholas Rodrigues Lordello Date: Mon, 13 Jan 2020 16:23:03 +0100 Subject: [PATCH 7/9] cargo clippy+fmt --- generate/src/contract/deployment.rs | 5 ++--- src/contract.rs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/generate/src/contract/deployment.rs b/generate/src/contract/deployment.rs index 5c283c1b..95b25018 100644 --- a/generate/src/contract/deployment.rs +++ b/generate/src/contract/deployment.rs @@ -41,7 +41,6 @@ fn expand_deployed(cx: &Context) -> TokenStream { > + Send + 'static, T: #ethcontract::web3::Transport + 'static, { - use #ethcontract::Artifact; use #ethcontract::contract::DeployedFuture; use #ethcontract::transport::DynTransport; use #ethcontract::web3::api::Web3; @@ -49,7 +48,7 @@ fn expand_deployed(cx: &Context) -> TokenStream { let transport = DynTransport::new(web3.transport().clone()); let web3 = Web3::new(transport); - DeployedFuture::new(web3, Default::default()) + DeployedFuture::new(web3, ()) } } @@ -136,7 +135,7 @@ fn expand_deploy(cx: &Context) -> Result { F: #ethcontract::web3::futures::Future + Send + 'static, T: #ethcontract::web3::Transport + 'static, { - use #ethcontract::{Artifact, DynTransport}; + use #ethcontract::DynTransport; use #ethcontract::contract::DeployBuilder; use #ethcontract::web3::api::Web3; diff --git a/src/contract.rs b/src/contract.rs index 57568424..4db9e317 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -15,7 +15,7 @@ use web3::contract::tokens::{Detokenize, Tokenize}; use web3::types::{Address, Bytes}; use web3::Transport; -pub use self::deploy::{DeployBuilder, DeployFuture, DeployedFuture, FromNetwork, Deploy}; +pub use self::deploy::{Deploy, DeployBuilder, DeployFuture, DeployedFuture, FromNetwork}; pub use self::method::{ CallFuture, MethodBuilder, MethodDefaults, MethodFuture, MethodSendFuture, ViewMethodBuilder, }; From dfb941a4b99e19ab0279c9fea20751190afda78b Mon Sep 17 00:00:00 2001 From: Nicholas Rodrigues Lordello Date: Mon, 13 Jan 2020 18:51:53 +0100 Subject: [PATCH 8/9] use Self where it makes sense --- src/contract.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index 4db9e317..1fe2e617 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -51,7 +51,7 @@ impl Instance { /// /// Note that this does not verify that a contract with a matchin `Abi` is /// actually deployed at the given address. - pub fn deployed(web3: Web3, artifact: Artifact) -> DeployedFuture> { + pub fn deployed(web3: Web3, artifact: Artifact) -> DeployedFuture { DeployedFuture::new(web3, Networks::new(artifact)) } @@ -62,7 +62,7 @@ impl Instance { web3: Web3, artifact: Artifact, params: P, - ) -> Result>, DeployError> + ) -> Result, DeployError> where P: Tokenize, { @@ -76,7 +76,7 @@ impl Instance { artifact: Artifact, params: P, libraries: I, - ) -> Result>, DeployError> + ) -> Result, DeployError> where P: Tokenize, I: Iterator, From bad95ec61bae053274c8ada06a4a9098c3402118 Mon Sep 17 00:00:00 2001 From: Nicholas Rodrigues Lordello Date: Tue, 14 Jan 2020 12:16:55 +0100 Subject: [PATCH 9/9] Networks -> Deployments --- src/contract.rs | 15 ++++++++------- src/contract/deploy.rs | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index 1fe2e617..8387efd1 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -52,7 +52,7 @@ impl Instance { /// Note that this does not verify that a contract with a matchin `Abi` is /// actually deployed at the given address. pub fn deployed(web3: Web3, artifact: Artifact) -> DeployedFuture { - DeployedFuture::new(web3, Networks::new(artifact)) + DeployedFuture::new(web3, Deployments::new(artifact)) } /// Creates a contract builder with the specified `web3` provider and the @@ -140,18 +140,19 @@ impl Instance { } } -/// `Deployments` implementation for an `Instance`. This type is not intended to +/// Deployment information for for an `Instance`. This includes the contract ABI +/// and the known addresses of contracts for network IDs. /// be used directly but rather through the `Instance::deployed` API. #[derive(Debug, Clone)] -pub struct Networks { +pub struct Deployments { abi: Abi, networks: HashMap, } -impl Networks { - /// Create a new `Networks` instanced for a contract artifact. +impl Deployments { + /// Create a new `Deployments` instanced for a contract artifact. pub fn new(artifact: Artifact) -> Self { - Networks { + Deployments { abi: artifact.abi, networks: artifact.networks, } @@ -159,7 +160,7 @@ impl Networks { } impl FromNetwork for Instance { - type Context = Networks; + type Context = Deployments; fn from_network(web3: Web3, network_id: &str, cx: Self::Context) -> Option { let address = cx.networks.get(network_id)?.address; diff --git a/src/contract/deploy.rs b/src/contract/deploy.rs index cc756336..6390c2d1 100644 --- a/src/contract/deploy.rs +++ b/src/contract/deploy.rs @@ -277,7 +277,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::contract::{Instance, Linker, Networks}; + use crate::contract::{Deployments, Instance, Linker}; use crate::test::prelude::*; use ethcontract_common::truffle::Network; use ethcontract_common::{Artifact, Bytecode}; @@ -302,7 +302,7 @@ mod tests { }; transport.add_response(json!(network_id)); // estimate gas response - let networks = Networks::new(artifact); + let networks = Deployments::new(artifact); let instance = InstanceDeployedFuture::new(web3, networks) .wait() .expect("successful deployment");