feat(solc): add configurable Artifact type (#907)
* refactor: make artifacts a sub mod * feat: more options for output selection * feat(solc): make ArtifactOutput struct functions * fix: migrate all features * feat: add configurable artifacts type * refactor: move configurable to separate file * feat: impl ArtifactOutput * refactor: write extras * simplify write extra * feat: more helper functions * feat: implement delegate * fix: failing doc test * fix: rustfmt * chore: update CHANGELOG * feat: add helper functions * refactor: remove flatten * feat: add link function * feat: replace default type * fix: doc tests * feat: more utility functions * fix: failing tests * chore: rename types * chore: bump ethers-solc 0.3.0 * fix: set metadata file extension properly
This commit is contained in:
parent
8ce58bfcf3
commit
19d2fd1155
|
@ -54,6 +54,8 @@
|
||||||
|
|
||||||
### Unreleased
|
### Unreleased
|
||||||
|
|
||||||
|
- Let `Project` take ownership of `ArtifactOutput` and change trait interface
|
||||||
|
[#907](https://github.com/gakonst/ethers-rs/pull/907)
|
||||||
- Total revamp of the `Project::compile` pipeline
|
- Total revamp of the `Project::compile` pipeline
|
||||||
[#802](https://github.com/gakonst/ethers-rs/pull/802)
|
[#802](https://github.com/gakonst/ethers-rs/pull/802)
|
||||||
- Support multiple versions of compiled contracts
|
- Support multiple versions of compiled contracts
|
||||||
|
|
|
@ -1375,7 +1375,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethers-solc"
|
name = "ethers-solc"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"colored",
|
"colored",
|
||||||
"criterion",
|
"criterion",
|
||||||
|
|
|
@ -88,7 +88,7 @@ ethers-core = { version = "^0.6.0", default-features = false, path = "./ethers-c
|
||||||
ethers-providers = { version = "^0.6.0", default-features = false, path = "./ethers-providers" }
|
ethers-providers = { version = "^0.6.0", default-features = false, path = "./ethers-providers" }
|
||||||
ethers-signers = { version = "^0.6.0", default-features = false, path = "./ethers-signers" }
|
ethers-signers = { version = "^0.6.0", default-features = false, path = "./ethers-signers" }
|
||||||
ethers-middleware = { version = "^0.6.0", default-features = false, path = "./ethers-middleware" }
|
ethers-middleware = { version = "^0.6.0", default-features = false, path = "./ethers-middleware" }
|
||||||
ethers-solc = { version = "^0.2.0", default-features = false, path = "./ethers-solc" }
|
ethers-solc = { version = "^0.3.0", default-features = false, path = "./ethers-solc" }
|
||||||
ethers-etherscan = { version = "^0.2.0", default-features = false, path = "./ethers-etherscan" }
|
ethers-etherscan = { version = "^0.2.0", default-features = false, path = "./ethers-etherscan" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -32,7 +32,7 @@ ethers-contract-abigen = { version = "^0.6.0", path = "ethers-contract-abigen" }
|
||||||
ethers-contract-derive = { version = "^0.6.0", path = "ethers-contract-derive" }
|
ethers-contract-derive = { version = "^0.6.0", path = "ethers-contract-derive" }
|
||||||
ethers-core = { version = "^0.6.0", path = "../ethers-core", default-features = false, features = ["eip712"]}
|
ethers-core = { version = "^0.6.0", path = "../ethers-core", default-features = false, features = ["eip712"]}
|
||||||
ethers-derive-eip712 = { version = "^0.2.0", path = "../ethers-core/ethers-derive-eip712"}
|
ethers-derive-eip712 = { version = "^0.2.0", path = "../ethers-core/ethers-derive-eip712"}
|
||||||
ethers-solc = { version = "^0.2.0", path = "../ethers-solc", default-features = false }
|
ethers-solc = { version = "^0.3.0", path = "../ethers-solc", default-features = false }
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||||
tokio = { version = "1.5", default-features = false, features = ["macros"] }
|
tokio = { version = "1.5", default-features = false, features = ["macros"] }
|
||||||
|
|
|
@ -15,7 +15,7 @@ keywords = ["ethereum", "web3", "etherscan", "ethers"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ethers-core = { version = "^0.6.0", path = "../ethers-core", default-features = false }
|
ethers-core = { version = "^0.6.0", path = "../ethers-core", default-features = false }
|
||||||
ethers-solc = { version = "^0.2.0", path = "../ethers-solc", default-features = false }
|
ethers-solc = { version = "^0.3.0", path = "../ethers-solc", default-features = false }
|
||||||
reqwest = { version = "0.11.9", default-features = false, features = ["json"] }
|
reqwest = { version = "0.11.9", default-features = false, features = ["json"] }
|
||||||
serde = { version = "1.0.124", default-features = false, features = ["derive"] }
|
serde = { version = "1.0.124", default-features = false, features = ["derive"] }
|
||||||
serde_json = { version = "1.0.64", default-features = false }
|
serde_json = { version = "1.0.64", default-features = false }
|
||||||
|
|
|
@ -311,7 +311,7 @@ mod tests {
|
||||||
.sources(&root)
|
.sources(&root)
|
||||||
.build()
|
.build()
|
||||||
.expect("failed to resolve project paths");
|
.expect("failed to resolve project paths");
|
||||||
let project = Project::<MinimalCombinedArtifacts>::builder()
|
let project = Project::builder()
|
||||||
.paths(paths)
|
.paths(paths)
|
||||||
.build()
|
.build()
|
||||||
.expect("failed to build the project");
|
.expect("failed to build the project");
|
||||||
|
|
|
@ -42,7 +42,7 @@ hex = { version = "0.4.3", default-features = false, features = ["std"] }
|
||||||
rand = { version = "0.8.5", default-features = false }
|
rand = { version = "0.8.5", default-features = false }
|
||||||
ethers-providers = { version = "^0.6.0", path = "../ethers-providers", default-features = false, features = ["ws", "rustls"] }
|
ethers-providers = { version = "^0.6.0", path = "../ethers-providers", default-features = false, features = ["ws", "rustls"] }
|
||||||
once_cell = "1.8.0"
|
once_cell = "1.8.0"
|
||||||
ethers-solc = { version = "^0.2.0", path = "../ethers-solc", default-features = false }
|
ethers-solc = { version = "^0.3.0", path = "../ethers-solc", default-features = false }
|
||||||
serial_test = "0.5.1"
|
serial_test = "0.5.1"
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ethers-solc"
|
name = "ethers-solc"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
authors = ["Matthias Seitz <matthias.seitz@outlook.de>", "Georgios Konstantopoulos <me@gakonst.com>"]
|
authors = ["Matthias Seitz <matthias.seitz@outlook.de>", "Georgios Konstantopoulos <me@gakonst.com>"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
|
@ -0,0 +1,486 @@
|
||||||
|
//! A configurable artifacts handler implementation
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
artifacts::{
|
||||||
|
output_selection::{ContractOutputSelection, EvmOutputSelection, EwasmOutputSelection},
|
||||||
|
CompactBytecode, CompactContract, CompactContractBytecode, CompactDeployedBytecode,
|
||||||
|
CompactEvm, DevDoc, Ewasm, GasEstimates, Metadata, Offsets, Settings, StorageLayout,
|
||||||
|
UserDoc,
|
||||||
|
},
|
||||||
|
ArtifactOutput, Contract, SolcConfig, SolcError,
|
||||||
|
};
|
||||||
|
use ethers_core::abi::Abi;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{collections::BTreeMap, fs, path::Path};
|
||||||
|
|
||||||
|
/// Represents the `Artifact` that `ConfigurableArtifacts` emits.
|
||||||
|
///
|
||||||
|
/// This is essentially a superset of [`CompactContractBytecode`].
|
||||||
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ConfigurableContractArtifact {
|
||||||
|
/// The Ethereum Contract ABI. If empty, it is represented as an empty
|
||||||
|
/// array. See https://docs.soliditylang.org/en/develop/abi-spec.html
|
||||||
|
pub abi: Option<Abi>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub bytecode: Option<CompactBytecode>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub deployed_bytecode: Option<CompactDeployedBytecode>,
|
||||||
|
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub assembly: Option<String>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub method_identifiers: Option<BTreeMap<String, String>>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub gas_estimates: Option<GasEstimates>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub metadata: Option<Metadata>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub storage_layout: Option<StorageLayout>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub userdoc: Option<UserDoc>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub devdoc: Option<DevDoc>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ir: Option<String>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ir_optimized: Option<String>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ewasm: Option<Ewasm>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigurableContractArtifact {
|
||||||
|
/// Returns the inner element that contains the core bytecode related information
|
||||||
|
pub fn into_contract_bytecode(self) -> CompactContractBytecode {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Looks for all link references in deployment and runtime bytecodes
|
||||||
|
pub fn all_link_references(&self) -> BTreeMap<String, BTreeMap<String, Vec<Offsets>>> {
|
||||||
|
let mut links = BTreeMap::new();
|
||||||
|
if let Some(bcode) = &self.bytecode {
|
||||||
|
links.extend(bcode.link_references.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(d_bcode) = &self.deployed_bytecode {
|
||||||
|
if let Some(bcode) = &d_bcode.bytecode {
|
||||||
|
links.extend(bcode.link_references.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
links
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConfigurableContractArtifact> for CompactContractBytecode {
|
||||||
|
fn from(artifact: ConfigurableContractArtifact) -> Self {
|
||||||
|
CompactContractBytecode {
|
||||||
|
abi: artifact.abi,
|
||||||
|
bytecode: artifact.bytecode,
|
||||||
|
deployed_bytecode: artifact.deployed_bytecode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConfigurableContractArtifact> for CompactContract {
|
||||||
|
fn from(artifact: ConfigurableContractArtifact) -> Self {
|
||||||
|
CompactContractBytecode::from(artifact).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An `Artifact` implementation that can be configured to include additional content and emit
|
||||||
|
/// additional files
|
||||||
|
///
|
||||||
|
/// Creates a single json artifact with
|
||||||
|
/// ```json
|
||||||
|
/// {
|
||||||
|
/// "abi": [],
|
||||||
|
/// "bytecode": {...},
|
||||||
|
/// "deployedBytecode": {...}
|
||||||
|
/// // additional values
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||||
|
pub struct ConfigurableArtifacts {
|
||||||
|
/// A set of additional values to include in the contract's artifact file
|
||||||
|
pub additional_values: ExtraOutputValues,
|
||||||
|
|
||||||
|
/// A set of values that should be written to a separate file
|
||||||
|
pub additional_files: ExtraOutputFiles,
|
||||||
|
|
||||||
|
/// PRIVATE: This structure may grow, As such, constructing this structure should
|
||||||
|
/// _always_ be done using a public constructor or update syntax:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
///
|
||||||
|
/// use ethers_solc::{ExtraOutputFiles, ConfigurableArtifacts};
|
||||||
|
/// let config = ConfigurableArtifacts {
|
||||||
|
/// additional_files: ExtraOutputFiles { metadata: true, ..Default::default() },
|
||||||
|
/// ..Default::default()
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub __non_exhaustive: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigurableArtifacts {
|
||||||
|
pub fn new(
|
||||||
|
extra_values: impl IntoIterator<Item = ContractOutputSelection>,
|
||||||
|
extra_files: impl IntoIterator<Item = ContractOutputSelection>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
additional_values: ExtraOutputValues::from_output_selection(extra_values),
|
||||||
|
additional_files: ExtraOutputFiles::from_output_selection(extra_files),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `Settings` this configuration corresponds to
|
||||||
|
pub fn settings(&self) -> Settings {
|
||||||
|
SolcConfig::builder().additional_outputs(self.output_selection()).build().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the output selection corresponding to this configuration
|
||||||
|
pub fn output_selection(&self) -> Vec<ContractOutputSelection> {
|
||||||
|
let mut selection = ContractOutputSelection::basic();
|
||||||
|
if self.additional_values.ir {
|
||||||
|
selection.push(ContractOutputSelection::Ir);
|
||||||
|
}
|
||||||
|
if self.additional_values.ir_optimized || self.additional_files.ir_optimized {
|
||||||
|
selection.push(ContractOutputSelection::IrOptimized);
|
||||||
|
}
|
||||||
|
if self.additional_values.metadata || self.additional_files.metadata {
|
||||||
|
selection.push(ContractOutputSelection::Metadata);
|
||||||
|
}
|
||||||
|
if self.additional_values.storage_layout {
|
||||||
|
selection.push(ContractOutputSelection::StorageLayout);
|
||||||
|
}
|
||||||
|
if self.additional_values.devdoc {
|
||||||
|
selection.push(ContractOutputSelection::DevDoc);
|
||||||
|
}
|
||||||
|
if self.additional_values.userdoc {
|
||||||
|
selection.push(ContractOutputSelection::UserDoc);
|
||||||
|
}
|
||||||
|
if self.additional_values.gas_estimates {
|
||||||
|
selection.push(EvmOutputSelection::GasEstimates.into());
|
||||||
|
}
|
||||||
|
if self.additional_values.assembly || self.additional_files.assembly {
|
||||||
|
selection.push(EvmOutputSelection::Assembly.into());
|
||||||
|
}
|
||||||
|
if self.additional_values.ewasm || self.additional_files.ewasm {
|
||||||
|
selection.push(EwasmOutputSelection::All.into());
|
||||||
|
}
|
||||||
|
selection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArtifactOutput for ConfigurableArtifacts {
|
||||||
|
type Artifact = ConfigurableContractArtifact;
|
||||||
|
|
||||||
|
fn write_contract_extras(&self, contract: &Contract, file: &Path) -> Result<(), SolcError> {
|
||||||
|
self.additional_files.write_extras(contract, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contract_to_artifact(&self, _file: &str, _name: &str, contract: Contract) -> Self::Artifact {
|
||||||
|
let mut artifact_userdoc = None;
|
||||||
|
let mut artifact_devdoc = None;
|
||||||
|
let mut artifact_metadata = None;
|
||||||
|
let mut artifact_ir = None;
|
||||||
|
let mut artifact_ir_optimized = None;
|
||||||
|
let mut artifact_ewasm = None;
|
||||||
|
let mut artifact_bytecode = None;
|
||||||
|
let mut artifact_deployed_bytecode = None;
|
||||||
|
let mut artifact_gas_estimates = None;
|
||||||
|
let mut artifact_method_identifiers = None;
|
||||||
|
let mut artifact_assembly = None;
|
||||||
|
let mut artifact_storage_layout = None;
|
||||||
|
|
||||||
|
let Contract {
|
||||||
|
abi,
|
||||||
|
metadata,
|
||||||
|
userdoc,
|
||||||
|
devdoc,
|
||||||
|
ir,
|
||||||
|
storage_layout,
|
||||||
|
evm,
|
||||||
|
ewasm,
|
||||||
|
ir_optimized,
|
||||||
|
} = contract;
|
||||||
|
|
||||||
|
if self.additional_values.metadata {
|
||||||
|
artifact_metadata = metadata;
|
||||||
|
}
|
||||||
|
if self.additional_values.userdoc {
|
||||||
|
artifact_userdoc = Some(userdoc);
|
||||||
|
}
|
||||||
|
if self.additional_values.devdoc {
|
||||||
|
artifact_devdoc = Some(devdoc);
|
||||||
|
}
|
||||||
|
if self.additional_values.ewasm {
|
||||||
|
artifact_ewasm = ewasm;
|
||||||
|
}
|
||||||
|
if self.additional_values.ir {
|
||||||
|
artifact_ir = ir;
|
||||||
|
}
|
||||||
|
if self.additional_values.ir_optimized {
|
||||||
|
artifact_ir_optimized = ir_optimized;
|
||||||
|
}
|
||||||
|
if self.additional_values.storage_layout {
|
||||||
|
artifact_storage_layout = Some(storage_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(evm) = evm {
|
||||||
|
let CompactEvm {
|
||||||
|
assembly,
|
||||||
|
bytecode,
|
||||||
|
deployed_bytecode,
|
||||||
|
method_identifiers,
|
||||||
|
gas_estimates,
|
||||||
|
..
|
||||||
|
} = evm.into_compact();
|
||||||
|
|
||||||
|
artifact_bytecode = bytecode;
|
||||||
|
artifact_deployed_bytecode = deployed_bytecode;
|
||||||
|
|
||||||
|
if self.additional_values.gas_estimates {
|
||||||
|
artifact_gas_estimates = gas_estimates;
|
||||||
|
}
|
||||||
|
if self.additional_values.method_identifiers {
|
||||||
|
artifact_method_identifiers = Some(method_identifiers);
|
||||||
|
}
|
||||||
|
if self.additional_values.assembly {
|
||||||
|
artifact_assembly = assembly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigurableContractArtifact {
|
||||||
|
abi,
|
||||||
|
bytecode: artifact_bytecode,
|
||||||
|
deployed_bytecode: artifact_deployed_bytecode,
|
||||||
|
assembly: artifact_assembly,
|
||||||
|
method_identifiers: artifact_method_identifiers,
|
||||||
|
gas_estimates: artifact_gas_estimates,
|
||||||
|
metadata: artifact_metadata,
|
||||||
|
storage_layout: artifact_storage_layout,
|
||||||
|
userdoc: artifact_userdoc,
|
||||||
|
devdoc: artifact_devdoc,
|
||||||
|
ir: artifact_ir,
|
||||||
|
ir_optimized: artifact_ir_optimized,
|
||||||
|
ewasm: artifact_ewasm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines the additional values to include in the contract's artifact file
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||||
|
pub struct ExtraOutputValues {
|
||||||
|
pub ast: bool,
|
||||||
|
pub userdoc: bool,
|
||||||
|
pub devdoc: bool,
|
||||||
|
pub method_identifiers: bool,
|
||||||
|
pub storage_layout: bool,
|
||||||
|
pub assembly: bool,
|
||||||
|
pub gas_estimates: bool,
|
||||||
|
pub compact_format: bool,
|
||||||
|
pub metadata: bool,
|
||||||
|
pub ir: bool,
|
||||||
|
pub ir_optimized: bool,
|
||||||
|
pub ewasm: bool,
|
||||||
|
|
||||||
|
/// PRIVATE: This structure may grow, As such, constructing this structure should
|
||||||
|
/// _always_ be done using a public constructor or update syntax:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
///
|
||||||
|
/// use ethers_solc::ExtraOutputValues;
|
||||||
|
/// let config = ExtraOutputValues {
|
||||||
|
/// ir: true,
|
||||||
|
/// ..Default::default()
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub __non_exhaustive: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtraOutputValues {
|
||||||
|
/// Returns an instance where all values are set to `true`
|
||||||
|
pub fn all() -> Self {
|
||||||
|
Self {
|
||||||
|
ast: true,
|
||||||
|
userdoc: true,
|
||||||
|
devdoc: true,
|
||||||
|
method_identifiers: true,
|
||||||
|
storage_layout: true,
|
||||||
|
assembly: true,
|
||||||
|
gas_estimates: true,
|
||||||
|
compact_format: true,
|
||||||
|
metadata: true,
|
||||||
|
ir: true,
|
||||||
|
ir_optimized: true,
|
||||||
|
ewasm: true,
|
||||||
|
__non_exhaustive: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the values based on a set of `ContractOutputSelection`
|
||||||
|
pub fn from_output_selection(
|
||||||
|
settings: impl IntoIterator<Item = ContractOutputSelection>,
|
||||||
|
) -> Self {
|
||||||
|
let mut config = Self::default();
|
||||||
|
for value in settings.into_iter() {
|
||||||
|
match value {
|
||||||
|
ContractOutputSelection::DevDoc => {
|
||||||
|
config.devdoc = true;
|
||||||
|
}
|
||||||
|
ContractOutputSelection::UserDoc => {
|
||||||
|
config.userdoc = true;
|
||||||
|
}
|
||||||
|
ContractOutputSelection::Metadata => {
|
||||||
|
config.metadata = true;
|
||||||
|
}
|
||||||
|
ContractOutputSelection::Ir => {
|
||||||
|
config.ir = true;
|
||||||
|
}
|
||||||
|
ContractOutputSelection::IrOptimized => {
|
||||||
|
config.ir_optimized = true;
|
||||||
|
}
|
||||||
|
ContractOutputSelection::StorageLayout => {
|
||||||
|
config.storage_layout = true;
|
||||||
|
}
|
||||||
|
ContractOutputSelection::Evm(evm) => match evm {
|
||||||
|
EvmOutputSelection::All => {
|
||||||
|
config.assembly = true;
|
||||||
|
config.gas_estimates = true;
|
||||||
|
config.method_identifiers = true;
|
||||||
|
}
|
||||||
|
EvmOutputSelection::Assembly => {
|
||||||
|
config.assembly = true;
|
||||||
|
}
|
||||||
|
EvmOutputSelection::MethodIdentifiers => {
|
||||||
|
config.method_identifiers = true;
|
||||||
|
}
|
||||||
|
EvmOutputSelection::GasEstimates => {
|
||||||
|
config.gas_estimates = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
ContractOutputSelection::Ewasm(_) => {
|
||||||
|
config.ewasm = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines what to emit as additional file
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||||
|
pub struct ExtraOutputFiles {
|
||||||
|
pub metadata: bool,
|
||||||
|
pub ir_optimized: bool,
|
||||||
|
pub ewasm: bool,
|
||||||
|
pub assembly: bool,
|
||||||
|
|
||||||
|
/// PRIVATE: This structure may grow, As such, constructing this structure should
|
||||||
|
/// _always_ be done using a public constructor or update syntax:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
///
|
||||||
|
/// use ethers_solc::ExtraOutputFiles;
|
||||||
|
/// let config = ExtraOutputFiles {
|
||||||
|
/// metadata: true,
|
||||||
|
/// ..Default::default()
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub __non_exhaustive: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtraOutputFiles {
|
||||||
|
/// Returns an instance where all values are set to `true`
|
||||||
|
pub fn all() -> Self {
|
||||||
|
Self {
|
||||||
|
metadata: true,
|
||||||
|
ir_optimized: true,
|
||||||
|
ewasm: true,
|
||||||
|
assembly: true,
|
||||||
|
__non_exhaustive: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the values based on a set of `ContractOutputSelection`
|
||||||
|
pub fn from_output_selection(
|
||||||
|
settings: impl IntoIterator<Item = ContractOutputSelection>,
|
||||||
|
) -> Self {
|
||||||
|
let mut config = Self::default();
|
||||||
|
for value in settings.into_iter() {
|
||||||
|
match value {
|
||||||
|
ContractOutputSelection::Metadata => {
|
||||||
|
config.metadata = true;
|
||||||
|
}
|
||||||
|
ContractOutputSelection::IrOptimized => {
|
||||||
|
config.ir_optimized = true;
|
||||||
|
}
|
||||||
|
ContractOutputSelection::Evm(evm) => match evm {
|
||||||
|
EvmOutputSelection::All => {
|
||||||
|
config.assembly = true;
|
||||||
|
}
|
||||||
|
EvmOutputSelection::Assembly => {
|
||||||
|
config.assembly = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
ContractOutputSelection::Ewasm(_) => {
|
||||||
|
config.ewasm = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the set values as separate files
|
||||||
|
pub fn write_extras(&self, contract: &Contract, file: &Path) -> Result<(), SolcError> {
|
||||||
|
if self.metadata {
|
||||||
|
if let Some(ref metadata) = contract.metadata {
|
||||||
|
let file = file.with_extension("metadata.json");
|
||||||
|
fs::write(&file, serde_json::to_string_pretty(metadata)?)
|
||||||
|
.map_err(|err| SolcError::io(err, file))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.ir_optimized {
|
||||||
|
if let Some(ref iropt) = contract.ir_optimized {
|
||||||
|
let file = file.with_extension("iropt");
|
||||||
|
fs::write(&file, iropt).map_err(|err| SolcError::io(err, file))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.ewasm {
|
||||||
|
if let Some(ref ir) = contract.ir {
|
||||||
|
let file = file.with_extension("ir");
|
||||||
|
fs::write(&file, ir).map_err(|err| SolcError::io(err, file))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.ewasm {
|
||||||
|
if let Some(ref ewasm) = contract.ewasm {
|
||||||
|
let file = file.with_extension("ewasm");
|
||||||
|
fs::write(&file, serde_json::to_vec_pretty(ewasm)?)
|
||||||
|
.map_err(|err| SolcError::io(err, file))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.assembly {
|
||||||
|
if let Some(ref evm) = contract.evm {
|
||||||
|
if let Some(ref asm) = evm.assembly {
|
||||||
|
let file = file.with_extension("asm");
|
||||||
|
fs::write(&file, asm).map_err(|err| SolcError::io(err, file))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,9 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod configurable;
|
||||||
|
pub use configurable::*;
|
||||||
|
|
||||||
/// Represents an artifact file representing a [`crate::Contract`]
|
/// Represents an artifact file representing a [`crate::Contract`]
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ArtifactFile<T> {
|
pub struct ArtifactFile<T> {
|
||||||
|
@ -314,18 +317,24 @@ pub trait ArtifactOutput {
|
||||||
/// This will be invoked with all aggregated contracts from (multiple) solc `CompilerOutput`.
|
/// This will be invoked with all aggregated contracts from (multiple) solc `CompilerOutput`.
|
||||||
/// See [`crate::AggregatedCompilerOutput`]
|
/// See [`crate::AggregatedCompilerOutput`]
|
||||||
fn on_output(
|
fn on_output(
|
||||||
|
&self,
|
||||||
contracts: &VersionedContracts,
|
contracts: &VersionedContracts,
|
||||||
layout: &ProjectPathsConfig,
|
layout: &ProjectPathsConfig,
|
||||||
) -> Result<Artifacts<Self::Artifact>> {
|
) -> Result<Artifacts<Self::Artifact>> {
|
||||||
let mut artifacts = Self::output_to_artifacts(contracts);
|
let mut artifacts = self.output_to_artifacts(contracts);
|
||||||
artifacts.join_all(&layout.artifacts);
|
artifacts.join_all(&layout.artifacts);
|
||||||
artifacts.write_all()?;
|
artifacts.write_all()?;
|
||||||
|
|
||||||
Self::write_extras(contracts, layout)?;
|
self.write_extras(contracts, layout)?;
|
||||||
|
|
||||||
Ok(artifacts)
|
Ok(artifacts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write additional files for the contract
|
||||||
|
fn write_contract_extras(&self, contract: &Contract, file: &Path) -> Result<()> {
|
||||||
|
ExtraOutputFiles::all().write_extras(contract, file)
|
||||||
|
}
|
||||||
|
|
||||||
/// Writes additional files for the contracts if the included in the `Contract`, such as `ir`,
|
/// Writes additional files for the contracts if the included in the `Contract`, such as `ir`,
|
||||||
/// `ewasm`, `iropt`.
|
/// `ewasm`, `iropt`.
|
||||||
///
|
///
|
||||||
|
@ -334,7 +343,11 @@ pub trait ArtifactOutput {
|
||||||
/// [`Contract`] will `None`. If they'll be manually added to the `output_selection`, then
|
/// [`Contract`] will `None`. If they'll be manually added to the `output_selection`, then
|
||||||
/// we're also creating individual files for this output, such as `Greeter.iropt`,
|
/// we're also creating individual files for this output, such as `Greeter.iropt`,
|
||||||
/// `Gretter.ewasm`
|
/// `Gretter.ewasm`
|
||||||
fn write_extras(contracts: &VersionedContracts, layout: &ProjectPathsConfig) -> Result<()> {
|
fn write_extras(
|
||||||
|
&self,
|
||||||
|
contracts: &VersionedContracts,
|
||||||
|
layout: &ProjectPathsConfig,
|
||||||
|
) -> Result<()> {
|
||||||
for (file, contracts) in contracts.as_ref().iter() {
|
for (file, contracts) in contracts.as_ref().iter() {
|
||||||
for (name, versioned_contracts) in contracts {
|
for (name, versioned_contracts) in contracts {
|
||||||
for c in versioned_contracts {
|
for c in versioned_contracts {
|
||||||
|
@ -347,30 +360,7 @@ pub trait ArtifactOutput {
|
||||||
let file = layout.artifacts.join(artifact_path);
|
let file = layout.artifacts.join(artifact_path);
|
||||||
utils::create_parent_dir_all(&file)?;
|
utils::create_parent_dir_all(&file)?;
|
||||||
|
|
||||||
if let Some(iropt) = &c.contract.ir_optimized {
|
self.write_contract_extras(&c.contract, &file)?;
|
||||||
fs::write(&file.with_extension("iropt"), iropt)
|
|
||||||
.map_err(|err| SolcError::io(err, file.with_extension("iropt")))?
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ir) = &c.contract.ir {
|
|
||||||
fs::write(&file.with_extension("ir"), ir)
|
|
||||||
.map_err(|err| SolcError::io(err, file.with_extension("ir")))?
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ewasm) = &c.contract.ewasm {
|
|
||||||
fs::write(
|
|
||||||
&file.with_extension("ewasm"),
|
|
||||||
serde_json::to_vec_pretty(&ewasm)?,
|
|
||||||
)
|
|
||||||
.map_err(|err| SolcError::io(err, file.with_extension("ewasm")))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(evm) = &c.contract.evm {
|
|
||||||
if let Some(asm) = &evm.assembly {
|
|
||||||
fs::write(&file.with_extension("asm"), asm)
|
|
||||||
.map_err(|err| SolcError::io(err, file.with_extension("asm")))?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -474,13 +464,13 @@ pub trait ArtifactOutput {
|
||||||
///
|
///
|
||||||
/// This is the core conversion function that takes care of converting a `Contract` into the
|
/// This is the core conversion function that takes care of converting a `Contract` into the
|
||||||
/// associated `Artifact` type
|
/// associated `Artifact` type
|
||||||
fn contract_to_artifact(_file: &str, _name: &str, contract: Contract) -> Self::Artifact;
|
fn contract_to_artifact(&self, _file: &str, _name: &str, contract: Contract) -> Self::Artifact;
|
||||||
|
|
||||||
/// Convert the compiler output into a set of artifacts
|
/// Convert the compiler output into a set of artifacts
|
||||||
///
|
///
|
||||||
/// **Note:** This does only convert, but _NOT_ write the artifacts to disk, See
|
/// **Note:** This does only convert, but _NOT_ write the artifacts to disk, See
|
||||||
/// [`Self::on_output()`]
|
/// [`Self::on_output()`]
|
||||||
fn output_to_artifacts(contracts: &VersionedContracts) -> Artifacts<Self::Artifact> {
|
fn output_to_artifacts(&self, contracts: &VersionedContracts) -> Artifacts<Self::Artifact> {
|
||||||
let mut artifacts = ArtifactsMap::new();
|
let mut artifacts = ArtifactsMap::new();
|
||||||
for (file, contracts) in contracts.as_ref().iter() {
|
for (file, contracts) in contracts.as_ref().iter() {
|
||||||
let mut entries = BTreeMap::new();
|
let mut entries = BTreeMap::new();
|
||||||
|
@ -493,8 +483,7 @@ pub trait ArtifactOutput {
|
||||||
} else {
|
} else {
|
||||||
Self::output_file(file, name)
|
Self::output_file(file, name)
|
||||||
};
|
};
|
||||||
let artifact =
|
let artifact = self.contract_to_artifact(file, name, contract.contract.clone());
|
||||||
Self::contract_to_artifact(file, name, contract.contract.clone());
|
|
||||||
|
|
||||||
contracts.push(ArtifactFile {
|
contracts.push(ArtifactFile {
|
||||||
artifact,
|
artifact,
|
||||||
|
@ -511,40 +500,45 @@ pub trait ArtifactOutput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An Artifacts implementation that uses a compact representation
|
/// An `Artifact` implementation that uses a compact representation
|
||||||
///
|
///
|
||||||
/// Creates a single json artifact with
|
/// Creates a single json artifact with
|
||||||
/// ```json
|
/// ```json
|
||||||
/// {
|
/// {
|
||||||
/// "abi": [],
|
/// "abi": [],
|
||||||
/// "bin": "...",
|
/// "bytecode": {...},
|
||||||
/// "runtime-bin": "..."
|
/// "deployedBytecode": {...}
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||||
pub struct MinimalCombinedArtifacts;
|
pub struct MinimalCombinedArtifacts {
|
||||||
|
_priv: (),
|
||||||
|
}
|
||||||
|
|
||||||
impl ArtifactOutput for MinimalCombinedArtifacts {
|
impl ArtifactOutput for MinimalCombinedArtifacts {
|
||||||
type Artifact = CompactContractBytecode;
|
type Artifact = CompactContractBytecode;
|
||||||
|
|
||||||
fn contract_to_artifact(_file: &str, _name: &str, contract: Contract) -> Self::Artifact {
|
fn contract_to_artifact(&self, _file: &str, _name: &str, contract: Contract) -> Self::Artifact {
|
||||||
Self::Artifact::from(contract)
|
Self::Artifact::from(contract)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An Artifacts handler implementation that works the same as `MinimalCombinedArtifacts` but also
|
/// An Artifacts handler implementation that works the same as `MinimalCombinedArtifacts` but also
|
||||||
/// supports reading hardhat artifacts if an initial attempt to deserialize an artifact failed
|
/// supports reading hardhat artifacts if an initial attempt to deserialize an artifact failed
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||||
pub struct MinimalCombinedArtifactsHardhatFallback;
|
pub struct MinimalCombinedArtifactsHardhatFallback {
|
||||||
|
_priv: (),
|
||||||
|
}
|
||||||
|
|
||||||
impl ArtifactOutput for MinimalCombinedArtifactsHardhatFallback {
|
impl ArtifactOutput for MinimalCombinedArtifactsHardhatFallback {
|
||||||
type Artifact = CompactContractBytecode;
|
type Artifact = CompactContractBytecode;
|
||||||
|
|
||||||
fn on_output(
|
fn on_output(
|
||||||
|
&self,
|
||||||
output: &VersionedContracts,
|
output: &VersionedContracts,
|
||||||
layout: &ProjectPathsConfig,
|
layout: &ProjectPathsConfig,
|
||||||
) -> Result<Artifacts<Self::Artifact>> {
|
) -> Result<Artifacts<Self::Artifact>> {
|
||||||
MinimalCombinedArtifacts::on_output(output, layout)
|
MinimalCombinedArtifacts::default().on_output(output, layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_cached_artifact(path: impl AsRef<Path>) -> Result<Self::Artifact> {
|
fn read_cached_artifact(path: impl AsRef<Path>) -> Result<Self::Artifact> {
|
||||||
|
@ -561,8 +555,8 @@ impl ArtifactOutput for MinimalCombinedArtifactsHardhatFallback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contract_to_artifact(file: &str, name: &str, contract: Contract) -> Self::Artifact {
|
fn contract_to_artifact(&self, file: &str, name: &str, contract: Contract) -> Self::Artifact {
|
||||||
MinimalCombinedArtifacts::contract_to_artifact(file, name, contract)
|
MinimalCombinedArtifacts::default().contract_to_artifact(file, name, contract)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
pub mod output_selection;
|
pub mod output_selection;
|
||||||
pub mod serde_helpers;
|
pub mod serde_helpers;
|
||||||
|
use crate::artifacts::output_selection::ContractOutputSelection;
|
||||||
pub use serde_helpers::{deserialize_bytes, deserialize_opt_bytes};
|
pub use serde_helpers::{deserialize_bytes, deserialize_opt_bytes};
|
||||||
|
|
||||||
/// Solidity files are made up of multiple `source units`, a solidity contract is such a `source
|
/// Solidity files are made up of multiple `source units`, a solidity contract is such a `source
|
||||||
|
@ -229,6 +230,25 @@ impl Settings {
|
||||||
)])
|
)])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inserts a set of `ContractOutputSelection`
|
||||||
|
pub fn push_all(&mut self, settings: impl IntoIterator<Item = ContractOutputSelection>) {
|
||||||
|
for value in settings {
|
||||||
|
self.push_output_selection(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a set of `ContractOutputSelection`
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_extra_output(
|
||||||
|
mut self,
|
||||||
|
settings: impl IntoIterator<Item = ContractOutputSelection>,
|
||||||
|
) -> Self {
|
||||||
|
for value in settings {
|
||||||
|
self.push_output_selection(value)
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Inserts the value for all files and contracts
|
/// Inserts the value for all files and contracts
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -922,6 +942,7 @@ impl From<Contract> for ContractBytecode {
|
||||||
/// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole
|
/// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole
|
||||||
/// `Bytecode` object.
|
/// `Bytecode` object.
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CompactContractBytecode {
|
pub struct CompactContractBytecode {
|
||||||
/// The Ethereum Contract ABI. If empty, it is represented as an empty
|
/// The Ethereum Contract ABI. If empty, it is represented as an empty
|
||||||
/// array. See https://docs.soliditylang.org/en/develop/abi-spec.html
|
/// array. See https://docs.soliditylang.org/en/develop/abi-spec.html
|
||||||
|
@ -952,13 +973,8 @@ impl CompactContractBytecode {
|
||||||
impl From<Contract> for CompactContractBytecode {
|
impl From<Contract> for CompactContractBytecode {
|
||||||
fn from(c: Contract) -> Self {
|
fn from(c: Contract) -> Self {
|
||||||
let (bytecode, deployed_bytecode) = if let Some(evm) = c.evm {
|
let (bytecode, deployed_bytecode) = if let Some(evm) = c.evm {
|
||||||
let (maybe_bcode, maybe_runtime) = match (evm.bytecode, evm.deployed_bytecode) {
|
let evm = evm.into_compact();
|
||||||
(Some(bcode), Some(dbcode)) => (Some(bcode.into()), Some(dbcode.into())),
|
(evm.bytecode, evm.deployed_bytecode)
|
||||||
(None, Some(dbcode)) => (None, Some(dbcode.into())),
|
|
||||||
(Some(bcode), None) => (Some(bcode.into()), None),
|
|
||||||
(None, None) => (None, None),
|
|
||||||
};
|
|
||||||
(maybe_bcode, maybe_runtime)
|
|
||||||
} else {
|
} else {
|
||||||
(None, None)
|
(None, None)
|
||||||
};
|
};
|
||||||
|
@ -1336,6 +1352,55 @@ pub struct Evm {
|
||||||
pub gas_estimates: Option<GasEstimates>,
|
pub gas_estimates: Option<GasEstimates>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Evm {
|
||||||
|
/// Crate internal helper do transform the underlying bytecode artifacts into a more convenient
|
||||||
|
/// structure
|
||||||
|
pub(crate) fn into_compact(self) -> CompactEvm {
|
||||||
|
let Evm {
|
||||||
|
assembly,
|
||||||
|
legacy_assembly,
|
||||||
|
bytecode,
|
||||||
|
deployed_bytecode,
|
||||||
|
method_identifiers,
|
||||||
|
gas_estimates,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let (bytecode, deployed_bytecode) = match (bytecode, deployed_bytecode) {
|
||||||
|
(Some(bcode), Some(dbcode)) => (Some(bcode.into()), Some(dbcode.into())),
|
||||||
|
(None, Some(dbcode)) => (None, Some(dbcode.into())),
|
||||||
|
(Some(bcode), None) => (Some(bcode.into()), None),
|
||||||
|
(None, None) => (None, None),
|
||||||
|
};
|
||||||
|
|
||||||
|
CompactEvm {
|
||||||
|
assembly,
|
||||||
|
legacy_assembly,
|
||||||
|
bytecode,
|
||||||
|
deployed_bytecode,
|
||||||
|
method_identifiers,
|
||||||
|
gas_estimates,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub(crate) struct CompactEvm {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub assembly: Option<String>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub legacy_assembly: Option<serde_json::Value>,
|
||||||
|
pub bytecode: Option<CompactBytecode>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub deployed_bytecode: Option<CompactDeployedBytecode>,
|
||||||
|
/// The list of function hashes
|
||||||
|
#[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")]
|
||||||
|
pub method_identifiers: BTreeMap<String, String>,
|
||||||
|
/// Function gas estimates
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub gas_estimates: Option<GasEstimates>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Bytecode {
|
pub struct Bytecode {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use std::{fmt, str::FromStr};
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
/// Contract level output selection
|
/// Contract level output selection
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub enum ContractOutputSelection {
|
pub enum ContractOutputSelection {
|
||||||
Abi,
|
Abi,
|
||||||
DevDoc,
|
DevDoc,
|
||||||
|
@ -17,6 +17,23 @@ pub enum ContractOutputSelection {
|
||||||
Ewasm(EwasmOutputSelection),
|
Ewasm(EwasmOutputSelection),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ContractOutputSelection {
|
||||||
|
/// Returns the basic set of contract level settings that should be included in the `Contract`
|
||||||
|
/// that solc emits:
|
||||||
|
/// - "abi"
|
||||||
|
/// - "evm.bytecode"
|
||||||
|
/// - "evm.deployedBytecode"
|
||||||
|
/// - "evm.methodIdentifiers"
|
||||||
|
pub fn basic() -> Vec<ContractOutputSelection> {
|
||||||
|
vec![
|
||||||
|
ContractOutputSelection::Abi,
|
||||||
|
BytecodeOutputSelection::All.into(),
|
||||||
|
DeployedBytecodeOutputSelection::All.into(),
|
||||||
|
EvmOutputSelection::MethodIdentifiers.into(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Serialize for ContractOutputSelection {
|
impl Serialize for ContractOutputSelection {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
|
@ -61,8 +78,12 @@ impl FromStr for ContractOutputSelection {
|
||||||
"userdoc" => Ok(ContractOutputSelection::UserDoc),
|
"userdoc" => Ok(ContractOutputSelection::UserDoc),
|
||||||
"metadata" => Ok(ContractOutputSelection::Metadata),
|
"metadata" => Ok(ContractOutputSelection::Metadata),
|
||||||
"ir" => Ok(ContractOutputSelection::Ir),
|
"ir" => Ok(ContractOutputSelection::Ir),
|
||||||
"irOptimized" => Ok(ContractOutputSelection::IrOptimized),
|
"ir-optimized" | "irOptimized" | "iroptimized" => {
|
||||||
"storageLayout" => Ok(ContractOutputSelection::StorageLayout),
|
Ok(ContractOutputSelection::IrOptimized)
|
||||||
|
}
|
||||||
|
"storage-layout" | "storagelayout" | "storageLayout" => {
|
||||||
|
Ok(ContractOutputSelection::StorageLayout)
|
||||||
|
}
|
||||||
s => EvmOutputSelection::from_str(s)
|
s => EvmOutputSelection::from_str(s)
|
||||||
.map(ContractOutputSelection::Evm)
|
.map(ContractOutputSelection::Evm)
|
||||||
.or_else(|_| EwasmOutputSelection::from_str(s).map(ContractOutputSelection::Ewasm))
|
.or_else(|_| EwasmOutputSelection::from_str(s).map(ContractOutputSelection::Ewasm))
|
||||||
|
@ -71,8 +92,20 @@ impl FromStr for ContractOutputSelection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Into<EvmOutputSelection>> From<T> for ContractOutputSelection {
|
||||||
|
fn from(evm: T) -> Self {
|
||||||
|
ContractOutputSelection::Evm(evm.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<EwasmOutputSelection> for ContractOutputSelection {
|
||||||
|
fn from(ewasm: EwasmOutputSelection) -> Self {
|
||||||
|
ContractOutputSelection::Ewasm(ewasm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Contract level output selection for `evm`
|
/// Contract level output selection for `evm`
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub enum EvmOutputSelection {
|
pub enum EvmOutputSelection {
|
||||||
All,
|
All,
|
||||||
Assembly,
|
Assembly,
|
||||||
|
@ -83,6 +116,18 @@ pub enum EvmOutputSelection {
|
||||||
DeployedByteCode(DeployedBytecodeOutputSelection),
|
DeployedByteCode(DeployedBytecodeOutputSelection),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<BytecodeOutputSelection> for EvmOutputSelection {
|
||||||
|
fn from(b: BytecodeOutputSelection) -> Self {
|
||||||
|
EvmOutputSelection::ByteCode(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DeployedBytecodeOutputSelection> for EvmOutputSelection {
|
||||||
|
fn from(b: DeployedBytecodeOutputSelection) -> Self {
|
||||||
|
EvmOutputSelection::DeployedByteCode(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Serialize for EvmOutputSelection {
|
impl Serialize for EvmOutputSelection {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
|
@ -121,10 +166,12 @@ impl FromStr for EvmOutputSelection {
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
"evm" => Ok(EvmOutputSelection::All),
|
"evm" => Ok(EvmOutputSelection::All),
|
||||||
"evm.assembly" => Ok(EvmOutputSelection::Assembly),
|
"asm" | "evm.assembly" => Ok(EvmOutputSelection::Assembly),
|
||||||
"evm.legacyAssembly" => Ok(EvmOutputSelection::LegacyAssembly),
|
"evm.legacyAssembly" => Ok(EvmOutputSelection::LegacyAssembly),
|
||||||
"evm.methodIdentifiers" => Ok(EvmOutputSelection::MethodIdentifiers),
|
"methodidentifiers" | "evm.methodIdentifiers" | "evm.methodidentifiers" => {
|
||||||
"evm.gasEstimates" => Ok(EvmOutputSelection::GasEstimates),
|
Ok(EvmOutputSelection::MethodIdentifiers)
|
||||||
|
}
|
||||||
|
"gas" | "evm.gasEstimates" | "evm.gasestimates" => Ok(EvmOutputSelection::GasEstimates),
|
||||||
s => BytecodeOutputSelection::from_str(s)
|
s => BytecodeOutputSelection::from_str(s)
|
||||||
.map(EvmOutputSelection::ByteCode)
|
.map(EvmOutputSelection::ByteCode)
|
||||||
.or_else(|_| {
|
.or_else(|_| {
|
||||||
|
@ -137,7 +184,7 @@ impl FromStr for EvmOutputSelection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contract level output selection for `evm.bytecode`
|
/// Contract level output selection for `evm.bytecode`
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub enum BytecodeOutputSelection {
|
pub enum BytecodeOutputSelection {
|
||||||
All,
|
All,
|
||||||
FunctionDebugData,
|
FunctionDebugData,
|
||||||
|
@ -202,7 +249,7 @@ impl FromStr for BytecodeOutputSelection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contract level output selection for `evm.deployedBytecode`
|
/// Contract level output selection for `evm.deployedBytecode`
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub enum DeployedBytecodeOutputSelection {
|
pub enum DeployedBytecodeOutputSelection {
|
||||||
All,
|
All,
|
||||||
FunctionDebugData,
|
FunctionDebugData,
|
||||||
|
@ -277,7 +324,7 @@ impl FromStr for DeployedBytecodeOutputSelection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contract level output selection for `evm.ewasm`
|
/// Contract level output selection for `evm.ewasm`
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub enum EwasmOutputSelection {
|
pub enum EwasmOutputSelection {
|
||||||
All,
|
All,
|
||||||
Wast,
|
Wast,
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
//! The output of a compiled project
|
//! The output of a compiled project
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
artifacts::{CompactContractRef, Contract, Error, SourceFile, SourceFiles},
|
artifacts::{
|
||||||
|
CompactContractBytecode, CompactContractRef, Contract, Error, SourceFile, SourceFiles,
|
||||||
|
},
|
||||||
contracts::{VersionedContract, VersionedContracts},
|
contracts::{VersionedContract, VersionedContracts},
|
||||||
ArtifactOutput, Artifacts, CompilerOutput,
|
ArtifactOutput, Artifacts, CompilerOutput, ConfigurableArtifacts,
|
||||||
};
|
};
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use std::{collections::BTreeMap, fmt, path::Path};
|
use std::{collections::BTreeMap, fmt, path::Path};
|
||||||
|
@ -11,7 +13,7 @@ use std::{collections::BTreeMap, fmt, path::Path};
|
||||||
/// Contains a mixture of already compiled/cached artifacts and the input set of sources that still
|
/// Contains a mixture of already compiled/cached artifacts and the input set of sources that still
|
||||||
/// need to be compiled.
|
/// need to be compiled.
|
||||||
#[derive(Debug, Clone, PartialEq, Default)]
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
pub struct ProjectCompileOutput<T: ArtifactOutput> {
|
pub struct ProjectCompileOutput<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||||
/// contains the aggregated `CompilerOutput`
|
/// contains the aggregated `CompilerOutput`
|
||||||
///
|
///
|
||||||
/// See [`CompilerSources::compile`]
|
/// See [`CompilerSources::compile`]
|
||||||
|
@ -33,11 +35,11 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use std::collections::btree_map::BTreeMap;
|
/// use std::collections::btree_map::BTreeMap;
|
||||||
/// use ethers_solc::artifacts::CompactContractBytecode;
|
/// use ethers_solc::ConfigurableContractArtifact;
|
||||||
/// use ethers_solc::Project;
|
/// use ethers_solc::Project;
|
||||||
///
|
///
|
||||||
/// let project = Project::builder().build().unwrap();
|
/// let project = Project::builder().build().unwrap();
|
||||||
/// let contracts: BTreeMap<String, CompactContractBytecode> = project.compile().unwrap().into_artifacts().collect();
|
/// let contracts: BTreeMap<String, ConfigurableContractArtifact> = project.compile().unwrap().into_artifacts().collect();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn into_artifacts(self) -> impl Iterator<Item = (String, T::Artifact)> {
|
pub fn into_artifacts(self) -> impl Iterator<Item = (String, T::Artifact)> {
|
||||||
let Self { cached_artifacts, compiled_artifacts, .. } = self;
|
let Self { cached_artifacts, compiled_artifacts, .. } = self;
|
||||||
|
@ -53,11 +55,10 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use std::collections::btree_map::BTreeMap;
|
/// use std::collections::btree_map::BTreeMap;
|
||||||
/// use ethers_solc::artifacts::CompactContractBytecode;
|
/// use ethers_solc::{ConfigurableContractArtifact, Project};
|
||||||
/// use ethers_solc::Project;
|
|
||||||
///
|
///
|
||||||
/// let project = Project::builder().build().unwrap();
|
/// let project = Project::builder().build().unwrap();
|
||||||
/// let contracts: Vec<(String, String, CompactContractBytecode)> = project.compile().unwrap().into_artifacts_with_files().collect();
|
/// let contracts: Vec<(String, String, ConfigurableContractArtifact)> = project.compile().unwrap().into_artifacts_with_files().collect();
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// **NOTE** the `file` will be returned as is, see also [`Self::with_stripped_file_prefixes()`]
|
/// **NOTE** the `file` will be returned as is, see also [`Self::with_stripped_file_prefixes()`]
|
||||||
|
@ -173,6 +174,27 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ProjectCompileOutput<ConfigurableArtifacts> {
|
||||||
|
/// A helper functions that extracts the underlying [`CompactContractBytecode`] from the
|
||||||
|
/// [`ConfigurableContractArtifact`]
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::collections::btree_map::BTreeMap;
|
||||||
|
/// use ethers_solc::artifacts::CompactContractBytecode;
|
||||||
|
/// use ethers_solc::Project;
|
||||||
|
///
|
||||||
|
/// let project = Project::builder().build().unwrap();
|
||||||
|
/// let contracts: BTreeMap<String, CompactContractBytecode> = project.compile().unwrap().into_contract_bytecodes().collect();
|
||||||
|
/// ```
|
||||||
|
pub fn into_contract_bytecodes(
|
||||||
|
self,
|
||||||
|
) -> impl Iterator<Item = (String, CompactContractBytecode)> {
|
||||||
|
self.into_artifacts().map(|(name, artifact)| (name, artifact.into_contract_bytecode()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: ArtifactOutput> fmt::Display for ProjectCompileOutput<T> {
|
impl<T: ArtifactOutput> fmt::Display for ProjectCompileOutput<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if self.compiler_output.is_unchanged() {
|
if self.compiler_output.is_unchanged() {
|
||||||
|
|
|
@ -219,11 +219,15 @@ impl<'a, T: ArtifactOutput> CompiledState<'a, T> {
|
||||||
/// Writes all output contracts to disk if enabled in the `Project`
|
/// Writes all output contracts to disk if enabled in the `Project`
|
||||||
fn write_artifacts(self) -> Result<ArtifactsState<'a, T>> {
|
fn write_artifacts(self) -> Result<ArtifactsState<'a, T>> {
|
||||||
let CompiledState { output, cache } = self;
|
let CompiledState { output, cache } = self;
|
||||||
|
|
||||||
// write all artifacts
|
// write all artifacts
|
||||||
let compiled_artifacts = if !cache.project().no_artifacts {
|
let compiled_artifacts = if !cache.project().no_artifacts {
|
||||||
T::on_output(&output.contracts, &cache.project().paths)?
|
cache
|
||||||
|
.project()
|
||||||
|
.artifacts_handler()
|
||||||
|
.on_output(&output.contracts, &cache.project().paths)?
|
||||||
} else {
|
} else {
|
||||||
T::output_to_artifacts(&output.contracts)
|
cache.project().artifacts_handler().output_to_artifacts(&output.contracts)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ArtifactsState { output, cache, compiled_artifacts })
|
Ok(ArtifactsState { output, cache, compiled_artifacts })
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::{
|
||||||
utils, Source, Sources,
|
utils, Source, Sources,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::artifacts::output_selection::ContractOutputSelection;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Formatter},
|
fmt::{self, Formatter},
|
||||||
|
@ -465,9 +466,18 @@ impl SolcConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<SolcConfig> for Settings {
|
||||||
|
fn from(config: SolcConfig) -> Self {
|
||||||
|
config.settings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SolcConfigBuilder {
|
pub struct SolcConfigBuilder {
|
||||||
settings: Option<Settings>,
|
settings: Option<Settings>,
|
||||||
|
|
||||||
|
/// additionally selected outputs that should be included in the `Contract` that `solc´ creates
|
||||||
|
output_selection: Vec<ContractOutputSelection>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SolcConfigBuilder {
|
impl SolcConfigBuilder {
|
||||||
|
@ -476,12 +486,34 @@ impl SolcConfigBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds another `ContractOutputSelection` to the set
|
||||||
|
#[must_use]
|
||||||
|
pub fn additional_output(mut self, output: impl Into<ContractOutputSelection>) -> Self {
|
||||||
|
self.output_selection.push(output.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds multiple `ContractOutputSelection` to the set
|
||||||
|
#[must_use]
|
||||||
|
pub fn additional_outputs<I, S>(mut self, outputs: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: Into<ContractOutputSelection>,
|
||||||
|
{
|
||||||
|
for out in outputs {
|
||||||
|
self = self.additional_output(out);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates the solc config
|
/// Creates the solc config
|
||||||
///
|
///
|
||||||
/// If no solc version is configured then it will be determined by calling `solc --version`.
|
/// If no solc version is configured then it will be determined by calling `solc --version`.
|
||||||
pub fn build(self) -> SolcConfig {
|
pub fn build(self) -> SolcConfig {
|
||||||
let Self { settings } = self;
|
let Self { settings, output_selection } = self;
|
||||||
SolcConfig { settings: settings.unwrap_or_default() }
|
let mut settings = settings.unwrap_or_default();
|
||||||
|
settings.push_all(output_selection);
|
||||||
|
SolcConfig { settings }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,13 +78,15 @@ impl From<HardhatArtifact> for CompactContractBytecode {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hardhat style artifacts handler
|
/// Hardhat style artifacts handler
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||||
pub struct HardhatArtifacts;
|
pub struct HardhatArtifacts {
|
||||||
|
_priv: (),
|
||||||
|
}
|
||||||
|
|
||||||
impl ArtifactOutput for HardhatArtifacts {
|
impl ArtifactOutput for HardhatArtifacts {
|
||||||
type Artifact = HardhatArtifact;
|
type Artifact = HardhatArtifact;
|
||||||
|
|
||||||
fn contract_to_artifact(file: &str, name: &str, contract: Contract) -> Self::Artifact {
|
fn contract_to_artifact(&self, file: &str, name: &str, contract: Contract) -> Self::Artifact {
|
||||||
let (bytecode, link_references, deployed_bytecode, deployed_link_references) =
|
let (bytecode, link_references, deployed_bytecode, deployed_link_references) =
|
||||||
if let Some(evm) = contract.evm {
|
if let Some(evm) = contract.evm {
|
||||||
let (deployed_bytecode, deployed_link_references) =
|
let (deployed_bytecode, deployed_link_references) =
|
||||||
|
|
|
@ -2,6 +2,7 @@ pub mod artifacts;
|
||||||
pub mod sourcemap;
|
pub mod sourcemap;
|
||||||
|
|
||||||
pub use artifacts::{CompilerInput, CompilerOutput, EvmVersion};
|
pub use artifacts::{CompilerInput, CompilerOutput, EvmVersion};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
mod artifact_output;
|
mod artifact_output;
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
|
@ -30,13 +31,12 @@ pub mod utils;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
artifacts::{Contract, Sources},
|
artifacts::{Contract, Sources},
|
||||||
|
contracts::VersionedContracts,
|
||||||
error::{SolcError, SolcIoError},
|
error::{SolcError, SolcIoError},
|
||||||
};
|
};
|
||||||
use error::Result;
|
use error::Result;
|
||||||
use std::{
|
use semver::Version;
|
||||||
marker::PhantomData,
|
use std::path::{Path, PathBuf};
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Utilities for creating, mocking and testing of (temporary) projects
|
/// Utilities for creating, mocking and testing of (temporary) projects
|
||||||
#[cfg(feature = "project-util")]
|
#[cfg(feature = "project-util")]
|
||||||
|
@ -44,7 +44,7 @@ pub mod project_util;
|
||||||
|
|
||||||
/// Represents a project workspace and handles `solc` compiling of all contracts in that workspace.
|
/// Represents a project workspace and handles `solc` compiling of all contracts in that workspace.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Project<Artifacts: ArtifactOutput = MinimalCombinedArtifacts> {
|
pub struct Project<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||||
/// The layout of the
|
/// The layout of the
|
||||||
pub paths: ProjectPathsConfig,
|
pub paths: ProjectPathsConfig,
|
||||||
/// Where to find solc
|
/// Where to find solc
|
||||||
|
@ -57,8 +57,8 @@ pub struct Project<Artifacts: ArtifactOutput = MinimalCombinedArtifacts> {
|
||||||
pub no_artifacts: bool,
|
pub no_artifacts: bool,
|
||||||
/// Whether writing artifacts to disk is enabled
|
/// Whether writing artifacts to disk is enabled
|
||||||
pub auto_detect: bool,
|
pub auto_detect: bool,
|
||||||
/// How to handle compiler output
|
/// Handles all artifacts related tasks, reading and writing from the artifact dir.
|
||||||
pub artifacts: PhantomData<Artifacts>,
|
pub artifacts: T,
|
||||||
/// Errors/Warnings which match these error codes are not going to be logged
|
/// Errors/Warnings which match these error codes are not going to be logged
|
||||||
pub ignored_error_codes: Vec<u64>,
|
pub ignored_error_codes: Vec<u64>,
|
||||||
/// The paths which will be allowed for library inclusion
|
/// The paths which will be allowed for library inclusion
|
||||||
|
@ -74,7 +74,7 @@ impl Project {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// Configure with `MinimalCombinedArtifacts` artifacts output
|
/// Configure with `ConfigurableArtifacts` artifacts output
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ethers_solc::Project;
|
/// use ethers_solc::Project;
|
||||||
|
@ -91,15 +91,15 @@ impl Project {
|
||||||
/// or use the builder directly
|
/// or use the builder directly
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ethers_solc::{MinimalCombinedArtifacts, ProjectBuilder};
|
/// use ethers_solc::{ConfigurableArtifacts, ProjectBuilder};
|
||||||
/// let config = ProjectBuilder::<MinimalCombinedArtifacts>::default().build().unwrap();
|
/// let config = ProjectBuilder::<ConfigurableArtifacts>::default().build().unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn builder() -> ProjectBuilder {
|
pub fn builder() -> ProjectBuilder {
|
||||||
ProjectBuilder::default()
|
ProjectBuilder::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Artifacts: ArtifactOutput> Project<Artifacts> {
|
impl<T: ArtifactOutput> Project<T> {
|
||||||
/// Returns the path to the artifacts directory
|
/// Returns the path to the artifacts directory
|
||||||
pub fn artifacts_path(&self) -> &PathBuf {
|
pub fn artifacts_path(&self) -> &PathBuf {
|
||||||
&self.paths.artifacts
|
&self.paths.artifacts
|
||||||
|
@ -120,6 +120,11 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
|
||||||
&self.paths.root
|
&self.paths.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the handler that takes care of processing all artifacts
|
||||||
|
pub fn artifacts_handler(&self) -> &T {
|
||||||
|
&self.artifacts
|
||||||
|
}
|
||||||
|
|
||||||
/// Applies the configured settings to the given `Solc`
|
/// Applies the configured settings to the given `Solc`
|
||||||
fn configure_solc(&self, mut solc: Solc) -> Solc {
|
fn configure_solc(&self, mut solc: Solc) -> Solc {
|
||||||
if self.allowed_lib_paths.0.is_empty() {
|
if self.allowed_lib_paths.0.is_empty() {
|
||||||
|
@ -187,7 +192,7 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[tracing::instrument(skip_all, name = "compile")]
|
#[tracing::instrument(skip_all, name = "compile")]
|
||||||
pub fn compile(&self) -> Result<ProjectCompileOutput<Artifacts>> {
|
pub fn compile(&self) -> Result<ProjectCompileOutput<T>> {
|
||||||
let sources = self.paths.read_input_files()?;
|
let sources = self.paths.read_input_files()?;
|
||||||
tracing::trace!("found {} sources to compile: {:?}", sources.len(), sources.keys());
|
tracing::trace!("found {} sources to compile: {:?}", sources.len(), sources.keys());
|
||||||
|
|
||||||
|
@ -226,7 +231,7 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(all(feature = "svm", feature = "async"))]
|
#[cfg(all(feature = "svm", feature = "async"))]
|
||||||
pub fn svm_compile(&self, sources: Sources) -> Result<ProjectCompileOutput<Artifacts>> {
|
pub fn svm_compile(&self, sources: Sources) -> Result<ProjectCompileOutput<T>> {
|
||||||
project::ProjectCompiler::with_sources(self, sources)?.compile()
|
project::ProjectCompiler::with_sources(self, sources)?.compile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +262,7 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
|
||||||
&self,
|
&self,
|
||||||
solc: &Solc,
|
solc: &Solc,
|
||||||
sources: Sources,
|
sources: Sources,
|
||||||
) -> Result<ProjectCompileOutput<Artifacts>> {
|
) -> Result<ProjectCompileOutput<T>> {
|
||||||
project::ProjectCompiler::with_sources_and_solc(
|
project::ProjectCompiler::with_sources_and_solc(
|
||||||
self,
|
self,
|
||||||
sources,
|
sources,
|
||||||
|
@ -325,7 +330,7 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProjectBuilder<Artifacts: ArtifactOutput = MinimalCombinedArtifacts> {
|
pub struct ProjectBuilder<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||||
/// The layout of the
|
/// The layout of the
|
||||||
paths: Option<ProjectPathsConfig>,
|
paths: Option<ProjectPathsConfig>,
|
||||||
/// Where to find solc
|
/// Where to find solc
|
||||||
|
@ -340,7 +345,8 @@ pub struct ProjectBuilder<Artifacts: ArtifactOutput = MinimalCombinedArtifacts>
|
||||||
auto_detect: bool,
|
auto_detect: bool,
|
||||||
/// Use offline mode
|
/// Use offline mode
|
||||||
offline: bool,
|
offline: bool,
|
||||||
artifacts: PhantomData<Artifacts>,
|
/// handles all artifacts related tasks
|
||||||
|
artifacts: T,
|
||||||
/// Which error codes to ignore
|
/// Which error codes to ignore
|
||||||
pub ignored_error_codes: Vec<u64>,
|
pub ignored_error_codes: Vec<u64>,
|
||||||
/// All allowed paths
|
/// All allowed paths
|
||||||
|
@ -348,7 +354,24 @@ pub struct ProjectBuilder<Artifacts: ArtifactOutput = MinimalCombinedArtifacts>
|
||||||
solc_jobs: Option<usize>,
|
solc_jobs: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Artifacts: ArtifactOutput> ProjectBuilder<Artifacts> {
|
impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||||
|
/// Create a new builder with the given artifacts handler
|
||||||
|
pub fn new(artifacts: T) -> Self {
|
||||||
|
Self {
|
||||||
|
paths: None,
|
||||||
|
solc: None,
|
||||||
|
solc_config: None,
|
||||||
|
cached: true,
|
||||||
|
no_artifacts: false,
|
||||||
|
auto_detect: true,
|
||||||
|
offline: false,
|
||||||
|
artifacts,
|
||||||
|
ignored_error_codes: Vec::new(),
|
||||||
|
allowed_paths: vec![],
|
||||||
|
solc_jobs: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn paths(mut self, paths: ProjectPathsConfig) -> Self {
|
pub fn paths(mut self, paths: ProjectPathsConfig) -> Self {
|
||||||
self.paths = Some(paths);
|
self.paths = Some(paths);
|
||||||
|
@ -454,7 +477,7 @@ impl<Artifacts: ArtifactOutput> ProjectBuilder<Artifacts> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set arbitrary `ArtifactOutputHandler`
|
/// Set arbitrary `ArtifactOutputHandler`
|
||||||
pub fn artifacts<A: ArtifactOutput>(self) -> ProjectBuilder<A> {
|
pub fn artifacts<A: ArtifactOutput>(self, artifacts: A) -> ProjectBuilder<A> {
|
||||||
let ProjectBuilder {
|
let ProjectBuilder {
|
||||||
paths,
|
paths,
|
||||||
solc,
|
solc,
|
||||||
|
@ -476,7 +499,7 @@ impl<Artifacts: ArtifactOutput> ProjectBuilder<Artifacts> {
|
||||||
no_artifacts,
|
no_artifacts,
|
||||||
auto_detect,
|
auto_detect,
|
||||||
offline,
|
offline,
|
||||||
artifacts: PhantomData::default(),
|
artifacts,
|
||||||
ignored_error_codes,
|
ignored_error_codes,
|
||||||
allowed_paths,
|
allowed_paths,
|
||||||
solc_jobs,
|
solc_jobs,
|
||||||
|
@ -485,7 +508,7 @@ impl<Artifacts: ArtifactOutput> ProjectBuilder<Artifacts> {
|
||||||
|
|
||||||
/// Adds an allowed-path to the solc executable
|
/// Adds an allowed-path to the solc executable
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn allowed_path<T: Into<PathBuf>>(mut self, path: T) -> Self {
|
pub fn allowed_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
|
||||||
self.allowed_paths.push(path.into());
|
self.allowed_paths.push(path.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -503,7 +526,7 @@ impl<Artifacts: ArtifactOutput> ProjectBuilder<Artifacts> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Result<Project<Artifacts>> {
|
pub fn build(self) -> Result<Project<T>> {
|
||||||
let Self {
|
let Self {
|
||||||
paths,
|
paths,
|
||||||
solc,
|
solc,
|
||||||
|
@ -544,29 +567,85 @@ impl<Artifacts: ArtifactOutput> ProjectBuilder<Artifacts> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Artifacts: ArtifactOutput> Default for ProjectBuilder<Artifacts> {
|
impl<T: ArtifactOutput + Default> Default for ProjectBuilder<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self::new(T::default())
|
||||||
paths: None,
|
|
||||||
solc: None,
|
|
||||||
solc_config: None,
|
|
||||||
cached: true,
|
|
||||||
no_artifacts: false,
|
|
||||||
auto_detect: true,
|
|
||||||
offline: false,
|
|
||||||
artifacts: PhantomData::default(),
|
|
||||||
ignored_error_codes: Vec::new(),
|
|
||||||
allowed_paths: vec![],
|
|
||||||
solc_jobs: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Artifacts: ArtifactOutput> ArtifactOutput for Project<Artifacts> {
|
impl<T: ArtifactOutput> ArtifactOutput for Project<T> {
|
||||||
type Artifact = Artifacts::Artifact;
|
type Artifact = T::Artifact;
|
||||||
|
|
||||||
fn contract_to_artifact(file: &str, name: &str, contract: Contract) -> Self::Artifact {
|
fn on_output(
|
||||||
Artifacts::contract_to_artifact(file, name, contract)
|
&self,
|
||||||
|
contracts: &VersionedContracts,
|
||||||
|
layout: &ProjectPathsConfig,
|
||||||
|
) -> Result<Artifacts<Self::Artifact>> {
|
||||||
|
self.artifacts_handler().on_output(contracts, layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_contract_extras(&self, contract: &Contract, file: &Path) -> Result<()> {
|
||||||
|
self.artifacts_handler().write_contract_extras(contract, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_extras(
|
||||||
|
&self,
|
||||||
|
contracts: &VersionedContracts,
|
||||||
|
layout: &ProjectPathsConfig,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.artifacts_handler().write_extras(contracts, layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_file_name(name: impl AsRef<str>) -> PathBuf {
|
||||||
|
T::output_file_name(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_file_name_versioned(name: impl AsRef<str>, version: &Version) -> PathBuf {
|
||||||
|
T::output_file_name_versioned(name, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_file(contract_file: impl AsRef<Path>, name: impl AsRef<str>) -> PathBuf {
|
||||||
|
T::output_file(contract_file, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_file_versioned(
|
||||||
|
contract_file: impl AsRef<Path>,
|
||||||
|
name: impl AsRef<str>,
|
||||||
|
version: &Version,
|
||||||
|
) -> PathBuf {
|
||||||
|
T::output_file_versioned(contract_file, name, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contract_name(file: impl AsRef<Path>) -> Option<String> {
|
||||||
|
T::contract_name(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_exists(
|
||||||
|
contract_file: impl AsRef<Path>,
|
||||||
|
name: impl AsRef<str>,
|
||||||
|
root: impl AsRef<Path>,
|
||||||
|
) -> bool {
|
||||||
|
T::output_exists(contract_file, name, root)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_cached_artifact(path: impl AsRef<Path>) -> Result<Self::Artifact> {
|
||||||
|
T::read_cached_artifact(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_cached_artifacts<P, I>(files: I) -> Result<BTreeMap<PathBuf, Self::Artifact>>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = P>,
|
||||||
|
P: Into<PathBuf>,
|
||||||
|
{
|
||||||
|
T::read_cached_artifacts(files)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contract_to_artifact(&self, file: &str, name: &str, contract: Contract) -> Self::Artifact {
|
||||||
|
self.artifacts_handler().contract_to_artifact(file, name, contract)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_to_artifacts(&self, contracts: &VersionedContracts) -> Artifacts<Self::Artifact> {
|
||||||
|
self.artifacts_handler().output_to_artifacts(contracts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
//! Utilities for mocking project workspaces
|
//! Utilities for mocking project workspaces
|
||||||
use crate::{
|
use crate::{
|
||||||
|
artifacts::Settings,
|
||||||
config::ProjectPathsConfigBuilder,
|
config::ProjectPathsConfigBuilder,
|
||||||
error::{Result, SolcError},
|
error::{Result, SolcError},
|
||||||
hh::HardhatArtifacts,
|
hh::HardhatArtifacts,
|
||||||
utils::tempdir,
|
utils::tempdir,
|
||||||
ArtifactOutput, MinimalCombinedArtifacts, PathStyle, Project, ProjectCompileOutput,
|
ArtifactOutput, ConfigurableArtifacts, PathStyle, Project, ProjectCompileOutput,
|
||||||
ProjectPathsConfig, SolcIoError,
|
ProjectPathsConfig, SolcIoError,
|
||||||
};
|
};
|
||||||
use fs_extra::{dir, file};
|
use fs_extra::{dir, file};
|
||||||
|
@ -17,7 +18,7 @@ use tempfile::TempDir;
|
||||||
/// A [`Project`] wrapper that lives in a new temporary directory
|
/// A [`Project`] wrapper that lives in a new temporary directory
|
||||||
///
|
///
|
||||||
/// Once `TempProject` is dropped, the temp dir is automatically removed, see [`TempDir::drop()`]
|
/// Once `TempProject` is dropped, the temp dir is automatically removed, see [`TempDir::drop()`]
|
||||||
pub struct TempProject<T: ArtifactOutput = MinimalCombinedArtifacts> {
|
pub struct TempProject<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||||
/// temporary workspace root
|
/// temporary workspace root
|
||||||
_root: TempDir,
|
_root: TempDir,
|
||||||
/// actual project workspace with the `root` tempdir as its root
|
/// actual project workspace with the `root` tempdir as its root
|
||||||
|
@ -32,26 +33,29 @@ impl<T: ArtifactOutput> TempProject<T> {
|
||||||
Ok(project)
|
Ok(project)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new temp project inside a tempdir with a prefixed directory
|
/// Creates a new temp project using the provided paths and artifacts handler.
|
||||||
pub fn prefixed(prefix: &str, paths: ProjectPathsConfigBuilder) -> Result<Self> {
|
/// sets the project root to a temp dir
|
||||||
|
pub fn with_artifacts(paths: ProjectPathsConfigBuilder, artifacts: T) -> Result<Self> {
|
||||||
|
Self::prefixed_with_artifacts("temp-project", paths, artifacts)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new temp project inside a tempdir with a prefixed directory and the given
|
||||||
|
/// artifacts handler
|
||||||
|
pub fn prefixed_with_artifacts(
|
||||||
|
prefix: &str,
|
||||||
|
paths: ProjectPathsConfigBuilder,
|
||||||
|
artifacts: T,
|
||||||
|
) -> Result<Self> {
|
||||||
let tmp_dir = tempdir(prefix)?;
|
let tmp_dir = tempdir(prefix)?;
|
||||||
let paths = paths.build_with_root(tmp_dir.path());
|
let paths = paths.build_with_root(tmp_dir.path());
|
||||||
let inner = Project::builder().artifacts().paths(paths).build()?;
|
let inner = Project::builder().artifacts(artifacts).paths(paths).build()?;
|
||||||
Ok(Self::create_new(tmp_dir, inner)?)
|
Ok(Self::create_new(tmp_dir, inner)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new temp project for the given `PathStyle`
|
/// Overwrites the settings to pass to `solc`
|
||||||
pub fn with_style(prefix: &str, style: PathStyle) -> Result<Self> {
|
pub fn with_settings(mut self, settings: impl Into<Settings>) -> Self {
|
||||||
let tmp_dir = tempdir(prefix)?;
|
self.inner.solc_config.settings = settings.into();
|
||||||
let paths = style.paths(tmp_dir.path())?;
|
self
|
||||||
let inner = Project::builder().artifacts().paths(paths).build()?;
|
|
||||||
Ok(Self::create_new(tmp_dir, inner)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new temp project using the provided paths and setting the project root to a temp
|
|
||||||
/// dir
|
|
||||||
pub fn new(paths: ProjectPathsConfigBuilder) -> Result<Self> {
|
|
||||||
Self::prefixed("temp-project", paths)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn project(&self) -> &Project<T> {
|
pub fn project(&self) -> &Project<T> {
|
||||||
|
@ -158,6 +162,27 @@ impl<T: ArtifactOutput> TempProject<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: ArtifactOutput + Default> TempProject<T> {
|
||||||
|
/// Creates a new temp project inside a tempdir with a prefixed directory
|
||||||
|
pub fn prefixed(prefix: &str, paths: ProjectPathsConfigBuilder) -> Result<Self> {
|
||||||
|
Self::prefixed_with_artifacts(prefix, paths, T::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new temp project for the given `PathStyle`
|
||||||
|
pub fn with_style(prefix: &str, style: PathStyle) -> Result<Self> {
|
||||||
|
let tmp_dir = tempdir(prefix)?;
|
||||||
|
let paths = style.paths(tmp_dir.path())?;
|
||||||
|
let inner = Project::builder().artifacts(T::default()).paths(paths).build()?;
|
||||||
|
Ok(Self::create_new(tmp_dir, inner)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new temp project using the provided paths and setting the project root to a temp
|
||||||
|
/// dir
|
||||||
|
pub fn new(paths: ProjectPathsConfigBuilder) -> Result<Self> {
|
||||||
|
Self::prefixed("temp-project", paths)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: ArtifactOutput> fmt::Debug for TempProject<T> {
|
impl<T: ArtifactOutput> fmt::Debug for TempProject<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("TempProject").field("paths", self.paths()).finish()
|
f.debug_struct("TempProject").field("paths", self.paths()).finish()
|
||||||
|
@ -189,18 +214,19 @@ impl TempProject<HardhatArtifacts> {
|
||||||
|
|
||||||
let paths = ProjectPathsConfig::hardhat(tmp_dir.path())?;
|
let paths = ProjectPathsConfig::hardhat(tmp_dir.path())?;
|
||||||
|
|
||||||
let inner = Project::builder().artifacts().paths(paths).build()?;
|
let inner =
|
||||||
|
Project::builder().artifacts(HardhatArtifacts::default()).paths(paths).build()?;
|
||||||
Ok(Self::create_new(tmp_dir, inner)?)
|
Ok(Self::create_new(tmp_dir, inner)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TempProject<MinimalCombinedArtifacts> {
|
impl TempProject<ConfigurableArtifacts> {
|
||||||
/// Creates an empty new dapptools style workspace in a new temporary dir
|
/// Creates an empty new dapptools style workspace in a new temporary dir
|
||||||
pub fn dapptools() -> Result<Self> {
|
pub fn dapptools() -> Result<Self> {
|
||||||
let tmp_dir = tempdir("tmp_dapp")?;
|
let tmp_dir = tempdir("tmp_dapp")?;
|
||||||
let paths = ProjectPathsConfig::dapptools(tmp_dir.path())?;
|
let paths = ProjectPathsConfig::dapptools(tmp_dir.path())?;
|
||||||
|
|
||||||
let inner = Project::builder().artifacts().paths(paths).build()?;
|
let inner = Project::builder().paths(paths).build()?;
|
||||||
Ok(Self::create_new(tmp_dir, inner)?)
|
Ok(Self::create_new(tmp_dir, inner)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ use ethers_solc::{
|
||||||
cache::{SolFilesCache, SOLIDITY_FILES_CACHE_FILENAME},
|
cache::{SolFilesCache, SOLIDITY_FILES_CACHE_FILENAME},
|
||||||
project_util::*,
|
project_util::*,
|
||||||
remappings::Remapping,
|
remappings::Remapping,
|
||||||
Graph, MinimalCombinedArtifacts, Project, ProjectCompileOutput, ProjectPathsConfig,
|
ConfigurableArtifacts, ExtraOutputValues, Graph, Project, ProjectCompileOutput,
|
||||||
|
ProjectPathsConfig,
|
||||||
};
|
};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ fn can_compile_hardhat_sample() {
|
||||||
let paths = ProjectPathsConfig::builder()
|
let paths = ProjectPathsConfig::builder()
|
||||||
.sources(root.join("contracts"))
|
.sources(root.join("contracts"))
|
||||||
.lib(root.join("node_modules"));
|
.lib(root.join("node_modules"));
|
||||||
let project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();
|
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||||
|
|
||||||
let compiled = project.compile().unwrap();
|
let compiled = project.compile().unwrap();
|
||||||
assert!(compiled.find("Greeter").is_some());
|
assert!(compiled.find("Greeter").is_some());
|
||||||
|
@ -53,7 +54,7 @@ fn can_compile_hardhat_sample() {
|
||||||
fn can_compile_dapp_sample() {
|
fn can_compile_dapp_sample() {
|
||||||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/dapp-sample");
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/dapp-sample");
|
||||||
let paths = ProjectPathsConfig::builder().sources(root.join("src")).lib(root.join("lib"));
|
let paths = ProjectPathsConfig::builder().sources(root.join("src")).lib(root.join("lib"));
|
||||||
let project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();
|
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||||
|
|
||||||
let compiled = project.compile().unwrap();
|
let compiled = project.compile().unwrap();
|
||||||
assert!(compiled.find("Dapp").is_some());
|
assert!(compiled.find("Dapp").is_some());
|
||||||
|
@ -76,9 +77,33 @@ fn can_compile_dapp_sample() {
|
||||||
assert_eq!(cache, updated_cache);
|
assert_eq!(cache, updated_cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_compile_configured() {
|
||||||
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/dapp-sample");
|
||||||
|
let paths = ProjectPathsConfig::builder().sources(root.join("src")).lib(root.join("lib"));
|
||||||
|
|
||||||
|
let handler = ConfigurableArtifacts {
|
||||||
|
additional_values: ExtraOutputValues {
|
||||||
|
metadata: true,
|
||||||
|
ir: true,
|
||||||
|
ir_optimized: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let settings = handler.settings();
|
||||||
|
let project = TempProject::with_artifacts(paths, handler).unwrap().with_settings(settings);
|
||||||
|
let compiled = project.compile().unwrap();
|
||||||
|
let artifact = compiled.find("Dapp").unwrap();
|
||||||
|
assert!(artifact.metadata.is_some());
|
||||||
|
assert!(artifact.ir.is_some());
|
||||||
|
assert!(artifact.ir_optimized.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_compile_dapp_detect_changes_in_libs() {
|
fn can_compile_dapp_detect_changes_in_libs() {
|
||||||
let mut project = TempProject::<MinimalCombinedArtifacts>::dapptools().unwrap();
|
let mut project = TempProject::<ConfigurableArtifacts>::dapptools().unwrap();
|
||||||
|
|
||||||
let remapping = project.paths().libraries[0].join("remapping");
|
let remapping = project.paths().libraries[0].join("remapping");
|
||||||
project
|
project
|
||||||
|
@ -151,8 +176,7 @@ fn can_compile_dapp_detect_changes_in_libs() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_compile_dapp_detect_changes_in_sources() {
|
fn can_compile_dapp_detect_changes_in_sources() {
|
||||||
init_tracing();
|
let project = TempProject::<ConfigurableArtifacts>::dapptools().unwrap();
|
||||||
let project = TempProject::<MinimalCombinedArtifacts>::dapptools().unwrap();
|
|
||||||
|
|
||||||
let src = project
|
let src = project
|
||||||
.add_source(
|
.add_source(
|
||||||
|
@ -330,7 +354,7 @@ fn can_flatten_file() {
|
||||||
.sources(root.join("src"))
|
.sources(root.join("src"))
|
||||||
.lib(root.join("lib1"))
|
.lib(root.join("lib1"))
|
||||||
.lib(root.join("lib2"));
|
.lib(root.join("lib2"));
|
||||||
let project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();
|
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||||
|
|
||||||
let result = project.flatten(&target);
|
let result = project.flatten(&target);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
@ -346,7 +370,7 @@ fn can_flatten_file_with_external_lib() {
|
||||||
let paths = ProjectPathsConfig::builder()
|
let paths = ProjectPathsConfig::builder()
|
||||||
.sources(root.join("contracts"))
|
.sources(root.join("contracts"))
|
||||||
.lib(root.join("node_modules"));
|
.lib(root.join("node_modules"));
|
||||||
let project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();
|
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||||
|
|
||||||
let target = root.join("contracts").join("Greeter.sol");
|
let target = root.join("contracts").join("Greeter.sol");
|
||||||
|
|
||||||
|
@ -362,7 +386,7 @@ fn can_flatten_file_with_external_lib() {
|
||||||
fn can_flatten_file_in_dapp_sample() {
|
fn can_flatten_file_in_dapp_sample() {
|
||||||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/dapp-sample");
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/dapp-sample");
|
||||||
let paths = ProjectPathsConfig::builder().sources(root.join("src")).lib(root.join("lib"));
|
let paths = ProjectPathsConfig::builder().sources(root.join("src")).lib(root.join("lib"));
|
||||||
let project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();
|
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||||
|
|
||||||
let target = root.join("src/Dapp.t.sol");
|
let target = root.join("src/Dapp.t.sol");
|
||||||
|
|
||||||
|
@ -379,7 +403,7 @@ fn can_flatten_file_in_dapp_sample() {
|
||||||
fn can_flatten_file_with_duplicates() {
|
fn can_flatten_file_with_duplicates() {
|
||||||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/flatten-sample");
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/flatten-sample");
|
||||||
let paths = ProjectPathsConfig::builder().sources(root.join("contracts"));
|
let paths = ProjectPathsConfig::builder().sources(root.join("contracts"));
|
||||||
let project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();
|
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||||
|
|
||||||
let target = root.join("contracts/FooBar.sol");
|
let target = root.join("contracts/FooBar.sol");
|
||||||
|
|
||||||
|
@ -395,7 +419,7 @@ fn can_flatten_file_with_duplicates() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_detect_type_error() {
|
fn can_detect_type_error() {
|
||||||
let project = TempProject::<MinimalCombinedArtifacts>::dapptools().unwrap();
|
let project = TempProject::<ConfigurableArtifacts>::dapptools().unwrap();
|
||||||
|
|
||||||
project
|
project
|
||||||
.add_source(
|
.add_source(
|
||||||
|
|
Loading…
Reference in New Issue