diff --git a/ethers-solc/src/artifacts/mod.rs b/ethers-solc/src/artifacts/mod.rs index 497e008a..1547313f 100644 --- a/ethers-solc/src/artifacts/mod.rs +++ b/ethers-solc/src/artifacts/mod.rs @@ -1299,15 +1299,17 @@ pub struct UserDoc { #[serde(default, skip_serializing_if = "Option::is_none")] pub kind: Option, #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")] - pub methods: BTreeMap, + pub methods: BTreeMap, #[serde(default, skip_serializing_if = "Option::is_none")] pub notice: Option, } -#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)] -pub struct MethodNotice { - #[serde(default, skip_serializing_if = "Option::is_none")] - pub notice: Option, +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[serde(untagged)] +pub enum UserDocNotice { + // NOTE: this a variant used for constructors on older solc versions + Constructor(String), + Method { notice: String }, } #[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)] diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs index f2cee711..55529d51 100644 --- a/ethers-solc/tests/project.rs +++ b/ethers-solc/tests/project.rs @@ -9,7 +9,10 @@ use std::{ use ethers_core::types::Address; use ethers_solc::{ - artifacts::{BytecodeHash, Libraries, ModelCheckerEngine::CHC, ModelCheckerSettings}, + artifacts::{ + BytecodeHash, Libraries, ModelCheckerEngine::CHC, ModelCheckerSettings, UserDoc, + UserDocNotice, + }, cache::{SolFilesCache, SOLIDITY_FILES_CACHE_FILENAME}, project_util::*, remappings::Remapping, @@ -1411,3 +1414,78 @@ fn can_purge_obsolete_artifacts() { assert!(!compiled.is_unchanged()); assert_eq!(compiled.into_artifacts().count(), 1); } + +#[test] +fn can_parse_notice() { + let mut project = TempProject::::dapptools().unwrap(); + project.project_mut().artifacts.additional_values.userdoc = true; + project.project_mut().solc_config.settings = project.project_mut().artifacts.settings(); + + let contract = r#" + pragma solidity $VERSION; + + contract Contract { + string greeting; + + /** + * @notice hello + */ + constructor(string memory _greeting) public { + greeting = _greeting; + } + + /** + * @notice hello + */ + function xyz() public { + } + + /// @notice hello + function abc() public { + } + } + "#; + project.add_source("Contract", contract.replace("$VERSION", "=0.5.17")).unwrap(); + + let mut compiled = project.compile().unwrap(); + assert!(!compiled.has_compiler_errors()); + assert!(!compiled.is_unchanged()); + assert!(compiled.find("Contract").is_some()); + let userdoc = compiled.remove("Contract").unwrap().userdoc; + + assert_eq!( + userdoc, + Some(UserDoc { + version: None, + kind: None, + methods: BTreeMap::from([ + ("abc()".to_string(), UserDocNotice::Method { notice: "hello".to_string() }), + ("xyz()".to_string(), UserDocNotice::Method { notice: "hello".to_string() }), + ("constructor".to_string(), UserDocNotice::Constructor("hello".to_string())), + ]), + notice: None + }) + ); + + project.add_source("Contract", contract.replace("$VERSION", "^0.8.10")).unwrap(); + + let mut compiled = project.compile().unwrap(); + assert!(!compiled.has_compiler_errors()); + assert!(!compiled.is_unchanged()); + assert!(compiled.find("Contract").is_some()); + let userdoc = compiled.remove("Contract").unwrap().userdoc; + + assert_eq!( + userdoc, + Some(UserDoc { + version: Some(1), + kind: Some("user".to_string()), + methods: BTreeMap::from([ + ("abc()".to_string(), UserDocNotice::Method { notice: "hello".to_string() }), + ("xyz()".to_string(), UserDocNotice::Method { notice: "hello".to_string() }), + ("constructor".to_string(), UserDocNotice::Method { notice: "hello".to_string() }), + ]), + notice: None + }) + ); +}