From 8aeeaa83b0b3ea59981782ae9cc6cd3c20a634c3 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 10 Feb 2022 18:53:26 +0100 Subject: [PATCH] feat(solc): better metadata support (#894) --- ethers-solc/src/artifacts.rs | 88 +++++++++++++++++++++++++++++++++- ethers-solc/src/compile/mod.rs | 11 +++++ 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/ethers-solc/src/artifacts.rs b/ethers-solc/src/artifacts.rs index 1c500a68..60a58d9c 100644 --- a/ethers-solc/src/artifacts.rs +++ b/ethers-solc/src/artifacts.rs @@ -103,9 +103,14 @@ impl Default for CompilerInput { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Settings { + /// Stop compilation after the given stage. + /// since 0.8.11: only "parsing" is valid here + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stop_after: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub remappings: Vec, pub optimizer: Optimizer, + /// Metadata settings #[serde(default, skip_serializing_if = "Option::is_none")] pub metadata: Option, /// This field can be used to select desired outputs based @@ -199,6 +204,50 @@ impl Settings { output_selection } + /// Inserts the value for all files and contracts + pub fn push_output_selection(&mut self, value: impl Into) { + self.push_contract_output_selection("*", value) + } + + /// Inserts the `key` `value` pair to the `output_selection` for all files + /// + /// If the `key` already exists, then the value is added to the existing list + pub fn push_contract_output_selection( + &mut self, + contracts: impl Into, + value: impl Into, + ) { + let value = value.into(); + let values = self + .output_selection + .entry("*".to_string()) + .or_default() + .entry(contracts.into()) + .or_default(); + if !values.contains(&value) { + values.push(value) + } + } + + /// Sets the value for all files and contracts + pub fn set_output_selection(&mut self, values: impl IntoIterator>) { + self.set_contract_output_selection("*", values) + } + + /// Sets the `key` to the `values` pair to the `output_selection` for all files + /// + /// This will replace the existing values for `key` if they're present + pub fn set_contract_output_selection( + &mut self, + key: impl Into, + values: impl IntoIterator>, + ) { + self.output_selection + .entry("*".to_string()) + .or_default() + .insert(key.into(), values.into_iter().map(Into::into).collect()); + } + /// Adds `ast` to output #[must_use] pub fn with_ast(mut self) -> Self { @@ -211,6 +260,7 @@ impl Settings { impl Default for Settings { fn default() -> Self { Self { + stop_after: None, optimizer: Default::default(), metadata: None, output_selection: Self::default_output_selection(), @@ -392,26 +442,60 @@ impl FromStr for EvmVersion { } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SettingsMetadata { + /// Use only literal content and not URLs (false by default) #[serde(default, rename = "useLiteralContent", skip_serializing_if = "Option::is_none")] pub use_literal_content: Option, + /// Use the given hash method for the metadata hash that is appended to the bytecode. + /// The metadata hash can be removed from the bytecode via option "none". + /// The other options are "ipfs" and "bzzr1". + /// If the option is omitted, "ipfs" is used by default. #[serde(default, rename = "bytecodeHash", skip_serializing_if = "Option::is_none")] pub bytecode_hash: Option, } +/// Bindings for [`solc` contract metadata](https://docs.soliditylang.org/en/latest/metadata.html) #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Metadata { pub compiler: Compiler, pub language: String, pub output: Output, - pub settings: Settings, + pub settings: MetadataSettings, pub sources: MetadataSources, pub version: i64, } +/// Compiler settings +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct MetadataSettings { + /// Required for Solidity: File and name of the contract or library this metadata is created + /// for. + #[serde(default, rename = "compilationTarget")] + pub compilation_target: BTreeMap, + #[serde(flatten)] + pub inner: Settings, +} + +/// Compilation source files/source units, keys are file names #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct MetadataSources { #[serde(flatten)] - pub inner: BTreeMap, + pub inner: BTreeMap, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct MetadataSource { + /// Required: keccak256 hash of the source file + pub keccak256: String, + /// Required (unless "content" is used, see below): Sorted URL(s) + /// to the source file, protocol is more or less arbitrary, but a + /// Swarm URL is recommended + #[serde(default)] + pub urls: Vec, + /// Required (unless "url" is used): literal contents of the source file + #[serde(default, skip_serializing_if = "Option::is_none")] + pub content: Option, + /// Optional: SPDX license identifier as given in the source file + pub license: Option, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] diff --git a/ethers-solc/src/compile/mod.rs b/ethers-solc/src/compile/mod.rs index 297c8139..cf04bd3a 100644 --- a/ethers-solc/src/compile/mod.rs +++ b/ethers-solc/src/compile/mod.rs @@ -726,6 +726,17 @@ mod tests { assert_eq!(out, other); } + #[test] + fn solc_metadata_works() { + let input = include_str!("../../test-data/in/compiler-in-1.json"); + let mut input: CompilerInput = serde_json::from_str(input).unwrap(); + input.settings.push_output_selection("metadata"); + let out = solc().compile(&input).unwrap(); + for (_, c) in out.split().1.contracts_iter() { + assert!(c.metadata.is_some()); + } + } + #[cfg(feature = "async")] #[tokio::test] async fn async_solc_compile_works() {