From 1a4ee3a84baa522451c763b2738903a7bf567c0d Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 28 Apr 2022 10:35:45 +0200 Subject: [PATCH 1/6] test: add lib parsing tests --- config/src/lib.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++- config/src/utils.rs | 72 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 1 deletion(-) diff --git a/config/src/lib.rs b/config/src/lib.rs index 6f47d67d4749..907e5afcd7d6 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -3,6 +3,7 @@ use std::{ borrow::Cow, + collections::BTreeMap, path::{Path, PathBuf}, str::FromStr, }; @@ -579,12 +580,20 @@ impl Config { ConfigurableArtifacts::new(self.extra_output.clone(), self.extra_output_files.clone()) } + /// Parses all libraries in the form of + /// `::` + pub fn parsed_libraries( + &self, + ) -> Result>, SolcError> { + parse_libraries(&self.libraries) + } + /// Returns the configured `solc` `Settings` that includes: /// - all libraries /// - the optimizer (including details, if configured) /// - evm version pub fn solc_settings(&self) -> Result { - let libraries = parse_libraries(&self.libraries)?; + let libraries = self.parsed_libraries()?; let optimizer = self.optimizer(); let mut settings = Settings { @@ -2035,6 +2044,69 @@ mod tests { }); } + #[test] + fn test_parse_many_libraries() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [default] + libraries= [ + './src/SizeAuctionDiscount.sol:Chainlink:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5', + './src/SizeAuction.sol:ChainlinkTWAP:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5', + './src/SizeAuction.sol:Math:0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c', + './src/test/ChainlinkTWAP.t.sol:ChainlinkTWAP:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5', + './src/SizeAuctionDiscount.sol:Math:0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c', + ] + "#, + )?; + let config = Config::load(); + + let libs = config.parsed_libraries().unwrap(); + + pretty_assertions::assert_eq!( + libs, + BTreeMap::from([ + ( + "./src/SizeAuctionDiscount.sol".to_string(), + BTreeMap::from([ + ( + "Chainlink".to_string(), + "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() + ), + ( + "Math".to_string(), + "0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string() + ) + ]) + ), + ( + "./src/SizeAuction.sol".to_string(), + BTreeMap::from([ + ( + "ChainlinkTWAP".to_string(), + "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() + ), + ( + "Math".to_string(), + "0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string() + ) + ]) + ), + ( + "./src/test/ChainlinkTWAP.t.sol".to_string(), + BTreeMap::from([( + "ChainlinkTWAP".to_string(), + "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() + )]) + ), + ]) + ); + + Ok(()) + }); + } + #[test] fn config_roundtrip() { figment::Jail::expect_with(|jail| { diff --git a/config/src/utils.rs b/config/src/utils.rs index 812ddcc2b6d4..83f3be32c49c 100644 --- a/config/src/utils.rs +++ b/config/src/utils.rs @@ -152,3 +152,75 @@ pub fn to_array_value(val: &str) -> Result { }; Ok(value) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_libraries() { + let libraries = ["./src/lib/LibraryContract.sol:Library:0xaddress".to_string()]; + + let libs = parse_libraries(&libraries[..]).unwrap(); + + assert_eq!( + libs, + BTreeMap::from([( + "./src/lib/LibraryContract.sol".to_string(), + BTreeMap::from([("Library".to_string(), "0xaddress".to_string())]) + )]) + ); + } + + #[test] + fn can_parse_many_libraries() { + let libraries= [ + "./src/SizeAuctionDiscount.sol:Chainlink:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string(), + "./src/SizeAuction.sol:ChainlinkTWAP:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string(), + "./src/SizeAuction.sol:Math:0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string(), + "./src/test/ChainlinkTWAP.t.sol:ChainlinkTWAP:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string(), + "./src/SizeAuctionDiscount.sol:Math:0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string(), + ]; + + let libs = parse_libraries(&libraries[..]).unwrap(); + + pretty_assertions::assert_eq!( + libs, + BTreeMap::from([ + ( + "./src/SizeAuctionDiscount.sol".to_string(), + BTreeMap::from([ + ( + "Chainlink".to_string(), + "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() + ), + ( + "Math".to_string(), + "0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string() + ) + ]) + ), + ( + "./src/SizeAuction.sol".to_string(), + BTreeMap::from([ + ( + "ChainlinkTWAP".to_string(), + "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() + ), + ( + "Math".to_string(), + "0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string() + ) + ]) + ), + ( + "./src/test/ChainlinkTWAP.t.sol".to_string(), + BTreeMap::from([( + "ChainlinkTWAP".to_string(), + "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() + )]) + ), + ]) + ); + } +} From b7b75ce6e47b71be6afc7b096ee5906189ec897d Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 28 Apr 2022 10:35:57 +0200 Subject: [PATCH 2/6] chore: typo --- cli/src/cmd/forge/create.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/cmd/forge/create.rs b/cli/src/cmd/forge/create.rs index aa4d590d5a69..640f0ef4e478 100644 --- a/cli/src/cmd/forge/create.rs +++ b/cli/src/cmd/forge/create.rs @@ -106,7 +106,7 @@ impl Cmd for CreateArgs { // Find Project & Compile let project = self.opts.project()?; let compiled = if self.json { - // Supress compile stdout messages when printing json output + // Suppress compile stdout messages when printing json output compile::suppress_compile(&project)? } else { compile::compile(&project, false, false)? From ef6bd06111bc68846ee38fd1612400966cd79e53 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 28 Apr 2022 10:46:05 +0200 Subject: [PATCH 3/6] refactor: use libraries new type --- config/src/lib.rs | 13 +++---- config/src/utils.rs | 83 +++++++++++++++++++++++++-------------------- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/config/src/lib.rs b/config/src/lib.rs index 907e5afcd7d6..1e9f7a410841 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -3,7 +3,6 @@ use std::{ borrow::Cow, - collections::BTreeMap, path::{Path, PathBuf}, str::FromStr, }; @@ -582,10 +581,8 @@ impl Config { /// Parses all libraries in the form of /// `::` - pub fn parsed_libraries( - &self, - ) -> Result>, SolcError> { - parse_libraries(&self.libraries) + pub fn parsed_libraries(&self) -> Result { + Libraries::parse(&self.libraries) } /// Returns the configured `solc` `Settings` that includes: @@ -593,7 +590,7 @@ impl Config { /// - the optimizer (including details, if configured) /// - evm version pub fn solc_settings(&self) -> Result { - let libraries = self.parsed_libraries()?; + let libraries = self.parsed_libraries()?.libs; let optimizer = self.optimizer(); let mut settings = Settings { @@ -1512,7 +1509,7 @@ fn canonic(path: impl Into) -> PathBuf { mod tests { use ethers_solc::artifacts::YulDetails; use figment::error::Kind::InvalidType; - use std::str::FromStr; + use std::{collections::BTreeMap, str::FromStr}; use crate::caching::{CachedChains, CachedEndpoints}; use figment::{value::Value, Figment}; @@ -2062,7 +2059,7 @@ mod tests { )?; let config = Config::load(); - let libs = config.parsed_libraries().unwrap(); + let libs = config.parsed_libraries().unwrap().libs; pretty_assertions::assert_eq!( libs, diff --git a/config/src/utils.rs b/config/src/utils.rs index 83f3be32c49c..a3592f08681e 100644 --- a/config/src/utils.rs +++ b/config/src/utils.rs @@ -8,6 +8,7 @@ use ethers_solc::{ remappings::{Remapping, RemappingError}, }; use figment::value::Value; +use serde::{Deserialize, Serialize}; /// Loads the config for the current project workspace pub fn load_config() -> Config { @@ -96,42 +97,52 @@ pub fn remappings_from_env_var(env_var: &str) -> Option, R Some(remappings_from_newline(&val).collect()) } -/// Parses all libraries in the form of -/// `::` -/// -/// # Example -/// -/// ``` -/// use foundry_config::parse_libraries; -/// let libs = parse_libraries(&[ -/// "src/DssSpell.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4".to_string(), -/// ]) -/// .unwrap(); -/// ``` -pub fn parse_libraries( - libs: &[String], -) -> Result>, SolcError> { - let mut libraries = BTreeMap::default(); - for lib in libs { - let mut items = lib.split(':'); - let file = items - .next() - .ok_or_else(|| SolcError::msg(format!("failed to parse invalid library: {lib}")))?; - let lib = items - .next() - .ok_or_else(|| SolcError::msg(format!("failed to parse invalid library: {lib}")))?; - let addr = items - .next() - .ok_or_else(|| SolcError::msg(format!("failed to parse invalid library: {lib}")))?; - if items.next().is_some() { - return Err(SolcError::msg(format!("failed to parse invalid library: {lib}"))) +/// A wrapper type for all libraries in the form of `::` +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(transparent)] +pub struct Libraries { + /// All libraries, `(file path -> (Lib name -> Address)) + pub libs: BTreeMap>, +} + +// === impl Libraries === + +impl Libraries { + /// Parses all libraries in the form of + /// `::` + /// + /// # Example + /// + /// ``` + /// use foundry_config::{Libraries}; + /// let libs = Libraries::parse(&[ + /// "src/DssSpell.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4".to_string(), + /// ]) + /// .unwrap(); + /// ``` + pub fn parse(libs: &[String]) -> Result { + let mut libraries = BTreeMap::default(); + for lib in libs { + let mut items = lib.split(':'); + let file = items + .next() + .ok_or_else(|| SolcError::msg(format!("failed to parse invalid library: {lib}")))?; + let lib = items + .next() + .ok_or_else(|| SolcError::msg(format!("failed to parse invalid library: {lib}")))?; + let addr = items + .next() + .ok_or_else(|| SolcError::msg(format!("failed to parse invalid library: {lib}")))?; + if items.next().is_some() { + return Err(SolcError::msg(format!("failed to parse invalid library: {lib}"))) + } + libraries + .entry(file.to_string()) + .or_insert_with(BTreeMap::default) + .insert(lib.to_string(), addr.to_string()); } - libraries - .entry(file.to_string()) - .or_insert_with(BTreeMap::default) - .insert(lib.to_string(), addr.to_string()); + Ok(Self { libs: libraries }) } - Ok(libraries) } /// Converts the `val` into a `figment::Value::Array` @@ -161,7 +172,7 @@ mod tests { fn can_parse_libraries() { let libraries = ["./src/lib/LibraryContract.sol:Library:0xaddress".to_string()]; - let libs = parse_libraries(&libraries[..]).unwrap(); + let libs = Libraries::parse(&libraries[..]).unwrap().libs; assert_eq!( libs, @@ -182,7 +193,7 @@ mod tests { "./src/SizeAuctionDiscount.sol:Math:0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string(), ]; - let libs = parse_libraries(&libraries[..]).unwrap(); + let libs = Libraries::parse(&libraries[..]).unwrap().libs; pretty_assertions::assert_eq!( libs, From fb59e97c1c0462012dd671f092643b11d67ebad4 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 28 Apr 2022 12:38:52 +0200 Subject: [PATCH 4/6] feat: use new Libraries type --- config/README.md | 2 + config/src/lib.rs | 10 ++-- config/src/utils.rs | 129 +------------------------------------------- 3 files changed, 9 insertions(+), 132 deletions(-) diff --git a/config/README.md b/config/README.md index b2dd243537d6..a76db6503e8c 100644 --- a/config/README.md +++ b/config/README.md @@ -66,6 +66,8 @@ test = 'test' out = 'out' libs = ['lib'] remappings = [] +# list of libraries to link in the form of `::
`: `"src/MyLib.sol:MyLib:0x8De6DDbCd5053d32292AAA0D2105A32d108484a6"` +# the supports remappings libraries = [] cache = true cache_path = 'cache' diff --git a/config/src/lib.rs b/config/src/lib.rs index 1e9f7a410841..18a439acb75f 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -13,7 +13,7 @@ pub use ethers_solc::artifacts::OptimizerDetails; use ethers_solc::{ artifacts::{ output_selection::ContractOutputSelection, serde_helpers, BytecodeHash, DebuggingSettings, - Optimizer, RevertStrings, Settings, + Libraries, Optimizer, RevertStrings, Settings, }, cache::SOLIDITY_FILES_CACHE_FILENAME, error::SolcError, @@ -590,7 +590,7 @@ impl Config { /// - the optimizer (including details, if configured) /// - evm version pub fn solc_settings(&self) -> Result { - let libraries = self.parsed_libraries()?.libs; + let libraries = self.parsed_libraries()?.with_applied_remappings(&self.project_paths()); let optimizer = self.optimizer(); let mut settings = Settings { @@ -2065,7 +2065,7 @@ mod tests { libs, BTreeMap::from([ ( - "./src/SizeAuctionDiscount.sol".to_string(), + PathBuf::from("./src/SizeAuctionDiscount.sol"), BTreeMap::from([ ( "Chainlink".to_string(), @@ -2078,7 +2078,7 @@ mod tests { ]) ), ( - "./src/SizeAuction.sol".to_string(), + PathBuf::from("./src/SizeAuction.sol"), BTreeMap::from([ ( "ChainlinkTWAP".to_string(), @@ -2091,7 +2091,7 @@ mod tests { ]) ), ( - "./src/test/ChainlinkTWAP.t.sol".to_string(), + PathBuf::from("./src/test/ChainlinkTWAP.t.sol"), BTreeMap::from([( "ChainlinkTWAP".to_string(), "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() diff --git a/config/src/utils.rs b/config/src/utils.rs index a3592f08681e..9348f96b9379 100644 --- a/config/src/utils.rs +++ b/config/src/utils.rs @@ -1,14 +1,9 @@ //! Utility functions -use std::{collections::BTreeMap, path::PathBuf, str::FromStr}; - use crate::Config; -use ethers_solc::{ - error::SolcError, - remappings::{Remapping, RemappingError}, -}; +use ethers_solc::remappings::{Remapping, RemappingError}; use figment::value::Value; -use serde::{Deserialize, Serialize}; +use std::{path::PathBuf, str::FromStr}; /// Loads the config for the current project workspace pub fn load_config() -> Config { @@ -97,54 +92,6 @@ pub fn remappings_from_env_var(env_var: &str) -> Option, R Some(remappings_from_newline(&val).collect()) } -/// A wrapper type for all libraries in the form of `::` -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(transparent)] -pub struct Libraries { - /// All libraries, `(file path -> (Lib name -> Address)) - pub libs: BTreeMap>, -} - -// === impl Libraries === - -impl Libraries { - /// Parses all libraries in the form of - /// `::` - /// - /// # Example - /// - /// ``` - /// use foundry_config::{Libraries}; - /// let libs = Libraries::parse(&[ - /// "src/DssSpell.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4".to_string(), - /// ]) - /// .unwrap(); - /// ``` - pub fn parse(libs: &[String]) -> Result { - let mut libraries = BTreeMap::default(); - for lib in libs { - let mut items = lib.split(':'); - let file = items - .next() - .ok_or_else(|| SolcError::msg(format!("failed to parse invalid library: {lib}")))?; - let lib = items - .next() - .ok_or_else(|| SolcError::msg(format!("failed to parse invalid library: {lib}")))?; - let addr = items - .next() - .ok_or_else(|| SolcError::msg(format!("failed to parse invalid library: {lib}")))?; - if items.next().is_some() { - return Err(SolcError::msg(format!("failed to parse invalid library: {lib}"))) - } - libraries - .entry(file.to_string()) - .or_insert_with(BTreeMap::default) - .insert(lib.to_string(), addr.to_string()); - } - Ok(Self { libs: libraries }) - } -} - /// Converts the `val` into a `figment::Value::Array` /// /// The values should be separated by commas, surrounding brackets are also supported `[a,b,c]` @@ -163,75 +110,3 @@ pub fn to_array_value(val: &str) -> Result { }; Ok(value) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_parse_libraries() { - let libraries = ["./src/lib/LibraryContract.sol:Library:0xaddress".to_string()]; - - let libs = Libraries::parse(&libraries[..]).unwrap().libs; - - assert_eq!( - libs, - BTreeMap::from([( - "./src/lib/LibraryContract.sol".to_string(), - BTreeMap::from([("Library".to_string(), "0xaddress".to_string())]) - )]) - ); - } - - #[test] - fn can_parse_many_libraries() { - let libraries= [ - "./src/SizeAuctionDiscount.sol:Chainlink:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string(), - "./src/SizeAuction.sol:ChainlinkTWAP:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string(), - "./src/SizeAuction.sol:Math:0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string(), - "./src/test/ChainlinkTWAP.t.sol:ChainlinkTWAP:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string(), - "./src/SizeAuctionDiscount.sol:Math:0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string(), - ]; - - let libs = Libraries::parse(&libraries[..]).unwrap().libs; - - pretty_assertions::assert_eq!( - libs, - BTreeMap::from([ - ( - "./src/SizeAuctionDiscount.sol".to_string(), - BTreeMap::from([ - ( - "Chainlink".to_string(), - "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() - ), - ( - "Math".to_string(), - "0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string() - ) - ]) - ), - ( - "./src/SizeAuction.sol".to_string(), - BTreeMap::from([ - ( - "ChainlinkTWAP".to_string(), - "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() - ), - ( - "Math".to_string(), - "0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c".to_string() - ) - ]) - ), - ( - "./src/test/ChainlinkTWAP.t.sol".to_string(), - BTreeMap::from([( - "ChainlinkTWAP".to_string(), - "0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5".to_string() - )]) - ), - ]) - ); - } -} From e97696d6b513d1deffcce7980fe18d7b67ba40d5 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 28 Apr 2022 16:29:54 +0200 Subject: [PATCH 5/6] test: add forge create test --- Cargo.lock | 22 ++++----- cli/tests/it/create.rs | 72 ++++++++++++++++++++++++++++ cli/tests/it/main.rs | 4 ++ cli/tests/it/utils.rs | 91 ++++++++++++++++++++++++++++++++++++ cli/tests/it/verify.rs | 103 ++++------------------------------------- 5 files changed, 186 insertions(+), 106 deletions(-) create mode 100644 cli/tests/it/create.rs create mode 100644 cli/tests/it/utils.rs diff --git a/Cargo.lock b/Cargo.lock index b72ee2b4bf4a..f6ec5c87514b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1293,7 +1293,7 @@ dependencies = [ [[package]] name = "ethers" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#19f7a9324394ab2c1e06d6dfc7666cbc55f56aab" +source = "git+https://github.com/gakonst/ethers-rs#c81254a8b61bed1e20f29a72cc2e23454930348f" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -1308,7 +1308,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "0.1.0" -source = "git+https://github.com/gakonst/ethers-rs#19f7a9324394ab2c1e06d6dfc7666cbc55f56aab" +source = "git+https://github.com/gakonst/ethers-rs#c81254a8b61bed1e20f29a72cc2e23454930348f" dependencies = [ "ethers-core", "once_cell", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#19f7a9324394ab2c1e06d6dfc7666cbc55f56aab" +source = "git+https://github.com/gakonst/ethers-rs#c81254a8b61bed1e20f29a72cc2e23454930348f" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -1337,7 +1337,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#19f7a9324394ab2c1e06d6dfc7666cbc55f56aab" +source = "git+https://github.com/gakonst/ethers-rs#c81254a8b61bed1e20f29a72cc2e23454930348f" dependencies = [ "Inflector", "cfg-if 1.0.0", @@ -1359,7 +1359,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#19f7a9324394ab2c1e06d6dfc7666cbc55f56aab" +source = "git+https://github.com/gakonst/ethers-rs#c81254a8b61bed1e20f29a72cc2e23454930348f" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -1373,7 +1373,7 @@ dependencies = [ [[package]] name = "ethers-core" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#19f7a9324394ab2c1e06d6dfc7666cbc55f56aab" +source = "git+https://github.com/gakonst/ethers-rs#c81254a8b61bed1e20f29a72cc2e23454930348f" dependencies = [ "arrayvec 0.7.2", "bytes", @@ -1399,7 +1399,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "0.2.0" -source = "git+https://github.com/gakonst/ethers-rs#19f7a9324394ab2c1e06d6dfc7666cbc55f56aab" +source = "git+https://github.com/gakonst/ethers-rs#c81254a8b61bed1e20f29a72cc2e23454930348f" dependencies = [ "ethers-core", "ethers-solc", @@ -1414,7 +1414,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#19f7a9324394ab2c1e06d6dfc7666cbc55f56aab" +source = "git+https://github.com/gakonst/ethers-rs#c81254a8b61bed1e20f29a72cc2e23454930348f" dependencies = [ "async-trait", "ethers-contract", @@ -1437,7 +1437,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#19f7a9324394ab2c1e06d6dfc7666cbc55f56aab" +source = "git+https://github.com/gakonst/ethers-rs#c81254a8b61bed1e20f29a72cc2e23454930348f" dependencies = [ "async-trait", "auto_impl", @@ -1470,7 +1470,7 @@ dependencies = [ [[package]] name = "ethers-signers" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#19f7a9324394ab2c1e06d6dfc7666cbc55f56aab" +source = "git+https://github.com/gakonst/ethers-rs#c81254a8b61bed1e20f29a72cc2e23454930348f" dependencies = [ "async-trait", "coins-bip32", @@ -1493,7 +1493,7 @@ dependencies = [ [[package]] name = "ethers-solc" version = "0.3.0" -source = "git+https://github.com/gakonst/ethers-rs#19f7a9324394ab2c1e06d6dfc7666cbc55f56aab" +source = "git+https://github.com/gakonst/ethers-rs#c81254a8b61bed1e20f29a72cc2e23454930348f" dependencies = [ "colored", "dunce", diff --git a/cli/tests/it/create.rs b/cli/tests/it/create.rs new file mode 100644 index 000000000000..ff5b0f39db09 --- /dev/null +++ b/cli/tests/it/create.rs @@ -0,0 +1,72 @@ +//! Contains various tests for checking the `forge create` subcommand + +use crate::utils::{self, EnvExternalities}; +use ethers::{solc::remappings::Remapping, types::Address}; +use foundry_cli_test_utils::{ + forgetest, + util::{TestCommand, TestProject}, +}; +use foundry_config::Config; +use std::str::FromStr; + +/// This will insert _dummy_ contract that uses a library +/// +/// **NOTE** This is intended to be linked against a random address and won't actually work. The +/// purpose of this is _only_ to make sure we can deploy contracts linked against addresses. +/// +/// This will create a library `remapping/MyLib.sol:MyLib` +fn setup_with_remapping(prj: &mut TestProject) { + // explicitly set remapping and libraries + let config = Config { + remappings: vec![Remapping::from_str("remapping/=lib/remapping/").unwrap().into()], + libraries: vec![format!("remapping/MyLib.sol:MyLib:{:?}", Address::random())], + ..Default::default() + }; + prj.write_config(config); + + prj.inner() + .add_source( + "LinkTest", + r#" +// SPDX-License-Identifier: MIT +import "remapping/MyLib.sol"; +contract LinkTest { + function foo() public returns (uint256) { + return MyLib.foobar(1); + } +} +"#, + ) + .unwrap(); + + prj.inner() + .add_lib( + "remapping/MyLib", + r#" +// SPDX-License-Identifier: MIT +library MyLib { + function foobar(uint256 a) public view returns (uint256) { + return a * 100; + } +} +"#, + ) + .unwrap(); +} + +fn create_on_chain(info: Option, mut prj: TestProject, mut cmd: TestCommand) { + if let Some(info) = info { + setup_with_remapping(&mut prj); + cmd.arg("create"); + cmd.args(info.create_args()).arg("src/LinkTest.sol:LinkTest"); + + let out = cmd.stdout_lossy(); + let _address = utils::parse_deployed_address(out.as_str()) + .unwrap_or_else(|| panic!("Failed to parse deployer {out}")); + } +} + +// tests `forge` create on goerli if correct env vars are set +forgetest!(can_create_on_goerli, |prj: TestProject, cmd: TestCommand| { + create_on_chain(EnvExternalities::goerli(), prj, cmd); +}); diff --git a/cli/tests/it/main.rs b/cli/tests/it/main.rs index 9928d9fe7f5d..299b1e051a8f 100644 --- a/cli/tests/it/main.rs +++ b/cli/tests/it/main.rs @@ -5,8 +5,12 @@ mod cmd; #[cfg(not(feature = "external-integration-tests"))] mod config; #[cfg(not(feature = "external-integration-tests"))] +mod create; +#[cfg(not(feature = "external-integration-tests"))] mod test_cmd; #[cfg(not(feature = "external-integration-tests"))] +mod utils; +#[cfg(not(feature = "external-integration-tests"))] mod verify; #[cfg(feature = "external-integration-tests")] diff --git a/cli/tests/it/utils.rs b/cli/tests/it/utils.rs new file mode 100644 index 000000000000..fc38718234e0 --- /dev/null +++ b/cli/tests/it/utils.rs @@ -0,0 +1,91 @@ +//! Various helper functions + +use ethers::prelude::Chain; + +/// Returns the current millis since unix epoch. +/// +/// This way we generate unique contracts so, etherscan will always have to verify them +pub fn millis_since_epoch() -> u128 { + let now = std::time::SystemTime::now(); + now.duration_since(std::time::SystemTime::UNIX_EPOCH) + .unwrap_or_else(|err| panic!("Current time {:?} is invalid: {:?}", now, err)) + .as_millis() +} + +pub fn etherscan_key(chain: Chain) -> Option { + match chain { + Chain::Fantom | Chain::FantomTestnet => { + std::env::var("FTMSCAN_API_KEY").or_else(|_| std::env::var("FANTOMSCAN_API_KEY")).ok() + } + _ => std::env::var("ETHERSCAN_API_KEY").ok(), + } +} + +pub fn network_rpc_key(chain: &str) -> Option { + let key = format!("{}_RPC_URL", chain.to_uppercase()); + std::env::var(&key).ok() +} + +pub fn network_private_key(chain: &str) -> Option { + let key = format!("{}_PRIVATE_KEY", chain.to_uppercase()); + std::env::var(&key).ok() +} + +/// Represents external input required for executing verification requests +pub struct EnvExternalities { + pub chain: Chain, + pub rpc: String, + pub pk: String, + pub etherscan: String, +} + +impl EnvExternalities { + pub fn goerli() -> Option { + Some(Self { + chain: Chain::Goerli, + rpc: network_rpc_key("goerli")?, + pk: network_private_key("goerli")?, + etherscan: etherscan_key(Chain::Goerli)?, + }) + } + + pub fn ftm_testnet() -> Option { + Some(Self { + chain: Chain::FantomTestnet, + rpc: network_rpc_key("ftm_testnet")?, + pk: network_private_key("ftm_testnet")?, + etherscan: etherscan_key(Chain::FantomTestnet)?, + }) + } + + /// Returns the arguments required to deploy the contract + pub fn create_args(&self) -> Vec { + vec![ + "--chain".to_string(), + self.chain.to_string(), + "--rpc-url".to_string(), + self.rpc.clone(), + "--private-key".to_string(), + self.pk.clone(), + ] + } +} + +/// Parses the address the contract was deployed to +pub fn parse_deployed_address(out: &str) -> Option { + for line in out.lines() { + if line.starts_with("Deployed to") { + return Some(line.trim_start_matches("Deployed to: ").to_string()) + } + } + None +} + +pub fn parse_verification_guid(out: &str) -> Option { + for line in out.lines() { + if line.contains("GUID") { + return Some(line.replace("GUID:", "").replace('`', "").trim().to_string()) + } + } + None +} diff --git a/cli/tests/it/verify.rs b/cli/tests/it/verify.rs index d61a9ec077fe..0f52f7a419b1 100644 --- a/cli/tests/it/verify.rs +++ b/cli/tests/it/verify.rs @@ -1,6 +1,7 @@ //! Contains various tests for checking forge commands related to verifying contracts on etherscan -use ethers::types::Chain; +use crate::utils::{self, EnvExternalities}; + use foundry_cli_test_utils::{ forgetest, util::{TestCommand, TestProject}, @@ -8,79 +9,10 @@ use foundry_cli_test_utils::{ }; use std::time::Duration; -fn etherscan_key(chain: Chain) -> Option { - match chain { - Chain::Fantom | Chain::FantomTestnet => { - std::env::var("FTMSCAN_API_KEY").or_else(|_| std::env::var("FANTOMSCAN_API_KEY")).ok() - } - _ => std::env::var("ETHERSCAN_API_KEY").ok(), - } -} - -fn network_rpc_key(chain: &str) -> Option { - let key = format!("{}_RPC_URL", chain.to_uppercase()); - std::env::var(&key).ok() -} - -fn network_private_key(chain: &str) -> Option { - let key = format!("{}_PRIVATE_KEY", chain.to_uppercase()); - std::env::var(&key).ok() -} - -/// Represents external input required for executing verification requests -struct VerifyExternalities { - chain: Chain, - rpc: String, - pk: String, - etherscan: String, -} - -impl VerifyExternalities { - fn goerli() -> Option { - Some(Self { - chain: Chain::Goerli, - rpc: network_rpc_key("goerli")?, - pk: network_private_key("goerli")?, - etherscan: etherscan_key(Chain::Goerli)?, - }) - } - - fn ftm_testnet() -> Option { - Some(Self { - chain: Chain::FantomTestnet, - rpc: network_rpc_key("ftm_testnet")?, - pk: network_private_key("ftm_testnet")?, - etherscan: etherscan_key(Chain::FantomTestnet)?, - }) - } - - /// Returns the arguments required to deploy the contract - fn create_args(&self) -> Vec { - vec![ - "--chain".to_string(), - self.chain.to_string(), - "--rpc-url".to_string(), - self.rpc.clone(), - "--private-key".to_string(), - self.pk.clone(), - ] - } -} - -/// Returns the current millis since unix epoch. -/// -/// This way we generate unique contracts so, etherscan will always have to verify them -fn millis_since_epoch() -> u128 { - let now = std::time::SystemTime::now(); - now.duration_since(std::time::SystemTime::UNIX_EPOCH) - .unwrap_or_else(|err| panic!("Current time {:?} is invalid: {:?}", now, err)) - .as_millis() -} - /// Adds a `Unique` contract to the source directory of the project that can be imported as /// `import {Unique} from "./unique.sol";` fn add_unique(prj: &TestProject) { - let timestamp = millis_since_epoch(); + let timestamp = utils::millis_since_epoch(); prj.inner() .add_source( "unique", @@ -99,7 +31,7 @@ contract Unique {{ .unwrap(); } -fn verify_on_chain(info: Option, prj: TestProject, mut cmd: TestCommand) { +fn verify_on_chain(info: Option, prj: TestProject, mut cmd: TestCommand) { // only execute if keys present if let Some(info) = info { add_unique(&prj); @@ -124,7 +56,7 @@ fn verify_on_chain(info: Option, prj: TestProject, mut cmd: cmd.args(info.create_args()).arg(contract_path); let out = cmd.stdout_lossy(); - let address = parse_deployed_address(out.as_str()) + let address = utils::parse_deployed_address(out.as_str()) .unwrap_or_else(|| panic!("Failed to parse deployer {out}")); cmd.forge_fuse().arg("verify-contract").root_arg().args([ @@ -147,7 +79,7 @@ fn verify_on_chain(info: Option, prj: TestProject, mut cmd: .run(|| -> eyre::Result { let output = cmd.unchecked_output(); let out = String::from_utf8_lossy(&output.stdout); - parse_verification_guid(&out).ok_or_else(|| { + utils::parse_verification_guid(&out).ok_or_else(|| { eyre::eyre!( "Failed to get guid, stdout: {}, stderr: {}", out, @@ -189,29 +121,10 @@ fn verify_on_chain(info: Option, prj: TestProject, mut cmd: // tests verify on goerli if correct env vars are set forgetest!(can_verify_random_contract_goerli, |prj: TestProject, cmd: TestCommand| { - verify_on_chain(VerifyExternalities::goerli(), prj, cmd); + verify_on_chain(EnvExternalities::goerli(), prj, cmd); }); // tests verify on Fantom testnet if correct env vars are set forgetest!(can_verify_random_contract_fantom_testnet, |prj: TestProject, cmd: TestCommand| { - verify_on_chain(VerifyExternalities::ftm_testnet(), prj, cmd); + verify_on_chain(EnvExternalities::ftm_testnet(), prj, cmd); }); - -/// Parses the address the contract was deployed to -fn parse_deployed_address(out: &str) -> Option { - for line in out.lines() { - if line.starts_with("Deployed to") { - return Some(line.trim_start_matches("Deployed to: ").to_string()) - } - } - None -} - -fn parse_verification_guid(out: &str) -> Option { - for line in out.lines() { - if line.contains("GUID") { - return Some(line.replace("GUID:", "").replace('`', "").trim().to_string()) - } - } - None -} From 52835d86c558b4929a533b65c3a7089d227042c9 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 28 Apr 2022 16:40:14 +0200 Subject: [PATCH 6/6] fix: update failing test --- forge/src/lib.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/forge/src/lib.rs b/forge/src/lib.rs index 8e4e6b81231e..9b5daa1e0f0d 100644 --- a/forge/src/lib.rs +++ b/forge/src/lib.rs @@ -23,7 +23,7 @@ pub mod test_helpers { use crate::TestFilter; use ethers::{ prelude::{artifacts::Settings, Lazy, ProjectCompileOutput, SolcConfig}, - solc::{Project, ProjectPathsConfig}, + solc::{artifacts::Libraries, Project, ProjectPathsConfig}, types::{Address, U256}, }; use foundry_evm::{ @@ -56,10 +56,8 @@ pub mod test_helpers { let libs = ["fork/Fork.t.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4".to_string()]; - let settings = Settings { - libraries: foundry_config::parse_libraries(&libs).unwrap(), - ..Default::default() - }; + let settings = + Settings { libraries: Libraries::parse(&libs).unwrap(), ..Default::default() }; let solc_config = SolcConfig::builder().settings(settings).build(); Project::builder()