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

Truffle Decoupling #117

Merged
merged 11 commits into from
Jan 14, 2020
2 changes: 1 addition & 1 deletion common/src/truffle/bytecode.rs → common/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl Bytecode {
}

/// Convert a bytecode into its byte representation.
pub fn into_bytes(self) -> Result<Bytes, LinkError> {
pub fn to_bytes(&self) -> Result<Bytes, LinkError> {
match self.undefined_libraries().next() {
Some(library) => Err(LinkError::UndefinedLibrary(library.to_string())),
None => Ok(Bytes(hex::decode(&self.0).expect("valid hex"))),
Expand Down
5 changes: 5 additions & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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};
7 changes: 2 additions & 5 deletions common/src/truffle.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion generate/src/contract/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand Down
169 changes: 88 additions & 81 deletions generate/src/contract/deployment.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,19 @@
use crate::contract::{methods, Context};
use crate::util;
use anyhow::{Context as _, 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;

pub(crate) fn expand(cx: &Context) -> Result<TokenStream> {
let ethcontract = &cx.runtime_crate;
let contract_name = &cx.contract_name;

let deployed = expand_deployed(&cx);
let deploy =
expand_deploy(&cx).context("error generating contract `deploy` associated function")?;

Ok(quote! {
impl #contract_name {
#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::truffle::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),
}
}
}
#deployed
#deploy
})
}

Expand All @@ -46,37 +23,50 @@ 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<F, T>(
web3: &#ethcontract::web3::api::Web3<T>,
) -> #ethcontract::contract::DeployedFuture<#ethcontract::transport::DynTransport, Self>
where
F: #ethcontract::web3::futures::Future<Item = #ethcontract::json::Value, Error = #ethcontract::web3::Error> + Send + 'static,
T: #ethcontract::web3::Transport<Out = F> + '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);
let artifact = { // only clone the pieces we need
let artifact = Self::artifact();
Artifact {
abi: artifact.abi.clone(),
networks: artifact.networks.clone(),
..Artifact::empty()
}
};
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<F, T>(
web3: &#ethcontract::web3::api::Web3<T>,
) -> #ethcontract::contract::DeployedFuture<#ethcontract::transport::DynTransport, Self>
where
F: #ethcontract::web3::futures::Future<
Item = #ethcontract::json::Value,
Error = #ethcontract::web3::Error
> + Send + 'static,
T: #ethcontract::web3::Transport<Out = F> + 'static,
{
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, ())
}
}

impl #ethcontract::contract::FromNetwork<#ethcontract::DynTransport> for #contract_name {
type Context = ();

DeployedFuture::from_args(web3, artifact)
fn from_network(web3: #ethcontract::DynWeb3, network_id: &str, _: Self::Context) -> Option<Self> {
use #ethcontract::Instance;

let artifact = Self::artifact();
artifact
.networks
.get(network_id)
.map(move |network| Self {
instance: Instance::at(web3, artifact.abi.clone(), network.address),
})
}
}
}
}
Expand All @@ -88,6 +78,7 @@ fn expand_deploy(cx: &Context) -> Result<TokenStream> {
}

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
Expand Down Expand Up @@ -123,45 +114,61 @@ fn expand_deploy(cx: &Context) -> Result<TokenStream> {
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 {
quote! {}
};

Ok(quote! {
#doc
pub fn builder<F, T>(
web3: &#ethcontract::web3::api::Web3<T> #lib_input #input ,
) -> #ethcontract::DynDeployBuilder<Self>
where
F: #ethcontract::web3::futures::Future<Item = #ethcontract::json::Value, Error = #ethcontract::web3::Error> + Send + 'static,
T: #ethcontract::web3::Transport<Out = F> + '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<F, T>(
web3: &#ethcontract::web3::api::Web3<T> #lib_input #input ,
) -> #ethcontract::DynDeployBuilder<Self>
where
F: #ethcontract::web3::futures::Future<Item = #ethcontract::json::Value, Error = #ethcontract::web3::Error> + Send + 'static,
T: #ethcontract::web3::Transport<Out = F> + 'static,
{
use #ethcontract::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")
}
}

DeployBuilder::new(web3, artifact, #arg).expect("valid deployment args")
impl #ethcontract::contract::Deploy<#ethcontract::DynTransport> for #contract_name {
type Context = #ethcontract::common::Bytecode;

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),
}
}
}
})
}
2 changes: 1 addition & 1 deletion generate/src/contract/methods.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::contract::{types, Context};
use crate::util;
use anyhow::{Context as _, 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;
Expand Down
2 changes: 1 addition & 1 deletion generate/src/contract/types.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
62 changes: 51 additions & 11 deletions src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ 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::truffle::Network;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this still truffle specific or should this also be lifted into common?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is truffle specific.

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::{Deploy, DeployBuilder, DeployFuture, DeployedFuture, FromNetwork};
pub use self::method::{
CallFuture, MethodBuilder, MethodDefaults, MethodFuture, MethodSendFuture, ViewMethodBuilder,
};
Expand Down Expand Up @@ -50,7 +52,7 @@ impl<T: Transport> Instance<T> {
/// 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<T>, artifact: Artifact) -> DeployedFuture<T, Self> {
DeployedFuture::from_args(web3, artifact)
DeployedFuture::new(web3, Deployments::new(artifact))
}

/// Creates a contract builder with the specified `web3` provider and the
Expand All @@ -64,7 +66,7 @@ impl<T: Transport> Instance<T> {
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
Expand Down Expand Up @@ -138,6 +140,34 @@ impl<T: Transport> Instance<T> {
}
}

/// 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 Deployments {
abi: Abi,
networks: HashMap<String, Network>,
}

impl Deployments {
/// Create a new `Deployments` instanced for a contract artifact.
pub fn new(artifact: Artifact) -> Self {
Deployments {
abi: artifact.abi,
networks: artifact.networks,
}
}
}

impl<T: Transport> FromNetwork<T> for Instance<T> {
type Context = Deployments;

fn from_network(web3: Web3<T>, network_id: &str, cx: Self::Context) -> Option<Self> {
let address = cx.networks.get(network_id)?.address;
Some(Instance::at(web3, cx.abi, address))
}
}

/// Builder for specifying linking options for a contract.
#[derive(Debug, Clone)]
pub struct Linker {
Expand Down Expand Up @@ -183,12 +213,22 @@ impl Linker {
T: Transport,
P: Tokenize,
{
let artifact = Artifact {
abi: self.abi,
bytecode: self.bytecode,
..Artifact::empty()
};
DeployBuilder::new(web3, self, params)
}
}

impl<T: Transport> Deploy<T> for Instance<T> {
type Context = Linker;

fn abi(cx: &Self::Context) -> &Abi {
&cx.abi
}

fn bytecode(cx: &Self::Context) -> &Bytecode {
&cx.bytecode
}

DeployBuilder::new(web3, artifact, params)
fn at_address(web3: Web3<T>, address: Address, cx: Self::Context) -> Self {
Instance::at(web3, cx.abi, address)
}
}
Loading