Skip to content

Commit

Permalink
Use contract serialization instead of relying on source json (#554)
Browse files Browse the repository at this point in the history
Fix #170, part of #512.

Since we support multiple artifact formats now, contract generation should not rely on source json. This PR allows serializing contracts and makes generation code to use serialization.

Builder API still requires source JSON though. It will be fixed in #512 and #196.
  • Loading branch information
Tamika Nomara committed Jul 7, 2021
1 parent ac0fd44 commit 9384d5e
Show file tree
Hide file tree
Showing 7 changed files with 26 additions and 37 deletions.
2 changes: 1 addition & 1 deletion ethcontract-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
7 changes: 6 additions & 1 deletion ethcontract-common/src/artifact/truffle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -143,6 +143,11 @@ impl TruffleLoader {

Ok(contract)
}

/// Serialize a single contract.
pub fn save_to_string(contract: &Contract) -> Result<String, ArtifactError> {
to_string(contract).map_err(Into::into)
}
}

impl Default for TruffleLoader {
Expand Down
22 changes: 5 additions & 17 deletions ethcontract-common/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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: S) -> Result<Self, BytecodeError>
where
S: AsRef<str>,
{
let s = s.as_ref();
pub fn from_hex_str(s: &str) -> Result<Self, BytecodeError> {
if s.is_empty() {
// special case where we have an empty string byte code.
return Ok(Bytecode::default());
Expand Down Expand Up @@ -185,14 +181,6 @@ impl<'de> Visitor<'de> for BytecodeVisitor {
{
Bytecode::from_hex_str(v).map_err(E::custom)
}

fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: DeError,
{
// TODO(nlordell): try to reuse this allocation
self.visit_str(&v)
}
}

fn to_fixed_hex(address: &Address) -> String {
Expand Down Expand Up @@ -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
))
Expand All @@ -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
))
Expand Down
10 changes: 5 additions & 5 deletions ethcontract-common/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -78,15 +78,15 @@ 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<String>,
/// Contract method documentation.
pub methods: HashMap<String, DocEntry>,
}

#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
/// A documentation entry.
pub struct DocEntry {
/// The documentation details for this entry.
Expand Down
4 changes: 2 additions & 2 deletions ethcontract-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 4 additions & 10 deletions ethcontract-generate/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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
Expand All @@ -54,22 +52,20 @@ pub(crate) struct Context {
impl Context {
/// Create a context from the code generation arguments.
fn from_args(args: Args) -> Result<Self> {
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() {
Expand Down Expand Up @@ -119,7 +115,6 @@ impl Context {
.context("failed to parse event derives")?;

Ok(Context {
artifact_json,
contract,
runtime_crate,
visibility,
Expand All @@ -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,
Expand Down
4 changes: 3 additions & 1 deletion ethcontract-generate/src/contract/common.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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);
Expand Down

0 comments on commit 9384d5e

Please sign in to comment.