feat(solc): better metadata support (#894)

This commit is contained in:
Matthias Seitz 2022-02-10 18:53:26 +01:00 committed by GitHub
parent 0a5b0d3495
commit 8aeeaa83b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 97 additions and 2 deletions

View File

@ -103,9 +103,14 @@ impl Default for CompilerInput {
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Settings { 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<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub remappings: Vec<Remapping>, pub remappings: Vec<Remapping>,
pub optimizer: Optimizer, pub optimizer: Optimizer,
/// Metadata settings
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<SettingsMetadata>, pub metadata: Option<SettingsMetadata>,
/// This field can be used to select desired outputs based /// This field can be used to select desired outputs based
@ -199,6 +204,50 @@ impl Settings {
output_selection output_selection
} }
/// Inserts the value for all files and contracts
pub fn push_output_selection(&mut self, value: impl Into<String>) {
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<String>,
value: impl Into<String>,
) {
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<Item = impl Into<String>>) {
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<String>,
values: impl IntoIterator<Item = impl Into<String>>,
) {
self.output_selection
.entry("*".to_string())
.or_default()
.insert(key.into(), values.into_iter().map(Into::into).collect());
}
/// Adds `ast` to output /// Adds `ast` to output
#[must_use] #[must_use]
pub fn with_ast(mut self) -> Self { pub fn with_ast(mut self) -> Self {
@ -211,6 +260,7 @@ impl Settings {
impl Default for Settings { impl Default for Settings {
fn default() -> Self { fn default() -> Self {
Self { Self {
stop_after: None,
optimizer: Default::default(), optimizer: Default::default(),
metadata: None, metadata: None,
output_selection: Self::default_output_selection(), output_selection: Self::default_output_selection(),
@ -392,26 +442,60 @@ impl FromStr for EvmVersion {
} }
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SettingsMetadata { pub struct SettingsMetadata {
/// Use only literal content and not URLs (false by default)
#[serde(default, rename = "useLiteralContent", skip_serializing_if = "Option::is_none")] #[serde(default, rename = "useLiteralContent", skip_serializing_if = "Option::is_none")]
pub use_literal_content: Option<bool>, pub use_literal_content: Option<bool>,
/// 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")] #[serde(default, rename = "bytecodeHash", skip_serializing_if = "Option::is_none")]
pub bytecode_hash: Option<String>, pub bytecode_hash: Option<String>,
} }
/// Bindings for [`solc` contract metadata](https://docs.soliditylang.org/en/latest/metadata.html)
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Metadata { pub struct Metadata {
pub compiler: Compiler, pub compiler: Compiler,
pub language: String, pub language: String,
pub output: Output, pub output: Output,
pub settings: Settings, pub settings: MetadataSettings,
pub sources: MetadataSources, pub sources: MetadataSources,
pub version: i64, 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<String, String>,
#[serde(flatten)]
pub inner: Settings,
}
/// Compilation source files/source units, keys are file names
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct MetadataSources { pub struct MetadataSources {
#[serde(flatten)] #[serde(flatten)]
pub inner: BTreeMap<String, serde_json::Value>, pub inner: BTreeMap<String, MetadataSource>,
}
#[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<String>,
/// Required (unless "url" is used): literal contents of the source file
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
/// Optional: SPDX license identifier as given in the source file
pub license: Option<String>,
} }
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -726,6 +726,17 @@ mod tests {
assert_eq!(out, other); 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")] #[cfg(feature = "async")]
#[tokio::test] #[tokio::test]
async fn async_solc_compile_works() { async fn async_solc_compile_works() {