diff --git a/ethcontract-common/Cargo.toml b/ethcontract-common/Cargo.toml index 33ec228e..d5ddfa15 100644 --- a/ethcontract-common/Cargo.toml +++ b/ethcontract-common/Cargo.toml @@ -12,7 +12,7 @@ Common types for ethcontract-rs runtime and proc macro. """ [dependencies] -ethabi = "14.0" +ethabi = { git = "https://github.com/taminomara/ethabi", branch = "serialize-contracts" } hex = "0.4" serde = "1.0" serde_derive = "1.0" diff --git a/ethcontract-common/src/artifact/truffle.rs b/ethcontract-common/src/artifact/truffle.rs index b963ba31..ce5aea5d 100644 --- a/ethcontract-common/src/artifact/truffle.rs +++ b/ethcontract-common/src/artifact/truffle.rs @@ -13,7 +13,7 @@ use crate::artifact::Artifact; use crate::errors::ArtifactError; use crate::Contract; -use serde_json::{from_reader, from_slice, from_str, from_value, Value}; +use serde_json::{from_reader, from_slice, from_str, from_value, to_string, Value}; use std::fs::File; use std::io::{BufReader, Read}; use std::path::Path; @@ -143,6 +143,11 @@ impl TruffleLoader { Ok(contract) } + + /// Serialize a single contract. + pub fn save_to_string(contract: &Contract) -> Result { + to_string(contract).map_err(Into::into) + } } impl Default for TruffleLoader { diff --git a/ethcontract-common/src/bytecode.rs b/ethcontract-common/src/bytecode.rs index e24c116a..676ec797 100644 --- a/ethcontract-common/src/bytecode.rs +++ b/ethcontract-common/src/bytecode.rs @@ -4,7 +4,7 @@ use crate::errors::{BytecodeError, LinkError}; use serde::de::{Error as DeError, Visitor}; -use serde::{Deserialize, Deserializer}; +use serde::{Deserialize, Deserializer, Serialize}; use std::collections::HashSet; use std::fmt::{Formatter, Result as FmtResult}; use std::mem; @@ -13,16 +13,12 @@ use web3::types::{Address, Bytes}; /// The string representation of the byte code. Note that this must be a /// `String` since `solc` linking requires string manipulation of the /// bytecode string representation. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize)] pub struct Bytecode(String); impl Bytecode { /// Read hex bytecode representation from a string slice. - pub fn from_hex_str(s: S) -> Result - where - S: AsRef, - { - let s = s.as_ref(); + pub fn from_hex_str(s: &str) -> Result { if s.is_empty() { // special case where we have an empty string byte code. return Ok(Bytecode::default()); @@ -185,14 +181,6 @@ impl<'de> Visitor<'de> for BytecodeVisitor { { Bytecode::from_hex_str(v).map_err(E::custom) } - - fn visit_string(self, v: String) -> Result - where - E: DeError, - { - // TODO(nlordell): try to reuse this allocation - self.visit_str(&v) - } } fn to_fixed_hex(address: &Address) -> String { @@ -245,7 +233,7 @@ mod tests { let address_encoded = [0u8; 20]; let name = "name"; let placeholder = format!("__{:_<38}", name); - let mut bytecode = Bytecode::from_hex_str(format!( + let mut bytecode = Bytecode::from_hex_str(&format!( "0x61{}{}61{}", placeholder, placeholder, placeholder )) @@ -265,7 +253,7 @@ mod tests { fn bytecode_link_fail() { let address = Address::zero(); let placeholder = format!("__{:_<38}", "name0"); - let mut bytecode = Bytecode::from_hex_str(format!( + let mut bytecode = Bytecode::from_hex_str(&format!( "0x61{}{}61{}", placeholder, placeholder, placeholder )) diff --git a/ethcontract-common/src/contract.rs b/ethcontract-common/src/contract.rs index 22f0b095..2d50699b 100644 --- a/ethcontract-common/src/contract.rs +++ b/ethcontract-common/src/contract.rs @@ -3,14 +3,14 @@ use crate::errors::ArtifactError; use crate::Abi; use crate::{bytecode::Bytecode, DeploymentInformation}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs::File; use std::path::Path; use web3::types::Address; /// Represents a contract data. -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(default = "Contract::empty")] pub struct Contract { /// The contract name. Unnamed contracts have an empty string as their name. @@ -68,7 +68,7 @@ impl Contract { } /// A contract's network configuration. -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Network { /// The address at which the contract is deployed on this network. pub address: Address, @@ -78,7 +78,7 @@ pub struct Network { } /// A contract's documentation. -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct Documentation { /// Contract documentation pub details: Option, @@ -86,7 +86,7 @@ pub struct Documentation { pub methods: HashMap, } -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] /// A documentation entry. pub struct DocEntry { /// The documentation details for this entry. diff --git a/ethcontract-common/src/lib.rs b/ethcontract-common/src/lib.rs index d0e41ee6..3b05abb3 100644 --- a/ethcontract-common/src/lib.rs +++ b/ethcontract-common/src/lib.rs @@ -14,12 +14,12 @@ pub use crate::abiext::FunctionExt; pub use crate::bytecode::Bytecode; pub use crate::contract::Contract; pub use ethabi::{self as abi, Contract as Abi}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; pub use web3::types::Address; pub use web3::types::H256 as TransactionHash; /// Information about when a contract instance was deployed -#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub enum DeploymentInformation { /// The block at which the contract was deployed diff --git a/ethcontract-generate/src/contract.rs b/ethcontract-generate/src/contract.rs index 8e35f4b9..2bf9707c 100644 --- a/ethcontract-generate/src/contract.rs +++ b/ethcontract-generate/src/contract.rs @@ -15,7 +15,7 @@ use crate::Args; use anyhow::{anyhow, Context as _, Result}; use ethcontract_common::{Address, Contract, DeploymentInformation}; use inflector::Inflector; -use proc_macro2::{Ident, Literal, TokenStream}; +use proc_macro2::{Ident, TokenStream}; use quote::quote; use std::collections::HashMap; use syn::{Path, Visibility}; @@ -27,8 +27,6 @@ pub(crate) struct Deployment { /// Internal shared context for generating smart contract bindings. pub(crate) struct Context { - /// The artifact JSON as string literal. - artifact_json: Literal, /// The parsed contract. contract: Contract, /// The identifier for the runtime crate. Usually this is `ethcontract` but @@ -54,22 +52,20 @@ pub(crate) struct Context { impl Context { /// Create a context from the code generation arguments. fn from_args(args: Args) -> Result { - let (artifact_json, contract) = { + let contract = { let artifact_json = args .artifact_source .artifact_json() .context("failed to get artifact JSON")?; - let contract = Contract::from_json(&artifact_json) + Contract::from_json(&artifact_json) .with_context(|| format!("invalid artifact JSON '{}'", artifact_json)) .with_context(|| { format!( "failed to parse artifact from source {:?}", args.artifact_source, ) - })?; - - (Literal::string(&artifact_json), contract) + })? }; let raw_contract_name = if let Some(name) = args.contract_name_override.as_ref() { @@ -119,7 +115,6 @@ impl Context { .context("failed to parse event derives")?; Ok(Context { - artifact_json, contract, runtime_crate, visibility, @@ -136,7 +131,6 @@ impl Context { impl Default for Context { fn default() -> Self { Context { - artifact_json: Literal::string("{}"), contract: Contract::empty(), runtime_crate: util::ident("ethcontract"), visibility: Visibility::Inherited, diff --git a/ethcontract-generate/src/contract/common.rs b/ethcontract-generate/src/contract/common.rs index 3a00cdb7..9cde822e 100644 --- a/ethcontract-generate/src/contract/common.rs +++ b/ethcontract-generate/src/contract/common.rs @@ -1,11 +1,11 @@ use crate::contract::Context; use crate::util::expand_doc; +use ethcontract_common::artifact::truffle::TruffleLoader; use ethcontract_common::{Address, DeploymentInformation}; use proc_macro2::{Literal, TokenStream}; use quote::quote; pub(crate) fn expand(cx: &Context) -> TokenStream { - let artifact_json = &cx.artifact_json; let contract_name = &cx.contract_name; let doc_str = cx @@ -16,6 +16,8 @@ pub(crate) fn expand(cx: &Context) -> TokenStream { .unwrap_or("Generated by `ethcontract`"); let doc = expand_doc(doc_str); + let artifact_json = TruffleLoader::save_to_string(&cx.contract).unwrap(); + let deployments = cx.deployments.iter().map(|(network_id, deployment)| { let network_id = Literal::string(&network_id.to_string()); let address = expand_address(deployment.address);