From e36dea2ebdc76ae3914d2fa65fa0b2214c9ab60e Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Fri, 17 Dec 2021 19:09:54 +0200 Subject: [PATCH 01/13] add --optimize and --optimize-runs to BuildArgs (#245) --- cli/src/cmd/build.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/cli/src/cmd/build.rs b/cli/src/cmd/build.rs index 4148324fc2742..e146d6f15eb06 100644 --- a/cli/src/cmd/build.rs +++ b/cli/src/cmd/build.rs @@ -2,8 +2,10 @@ use ethers::{ solc::{ - artifacts::Settings, remappings::Remapping, EvmVersion, MinimalCombinedArtifacts, Project, - ProjectCompileOutput, ProjectPathsConfig, SolcConfig, + artifacts::{Optimizer, Settings}, + remappings::Remapping, + EvmVersion, MinimalCombinedArtifacts, Project, ProjectCompileOutput, ProjectPathsConfig, + SolcConfig, }, types::Address, }; @@ -50,6 +52,12 @@ pub struct BuildArgs { #[structopt(help = "choose the evm version", long, default_value = "london")] pub evm_version: EvmVersion, + #[structopt(help = "activate the solidity optimizer", long)] + pub optimize: bool, + + #[structopt(help = "optimizer parameter runs", long, default_value = "200")] + pub optimize_runs: u32, + #[structopt( help = "if set to true, skips auto-detecting solc and uses what is in the user's $PATH ", long @@ -194,8 +202,12 @@ impl BuildArgs { let paths = paths_builder.build()?; + let optimizer = + Optimizer { enabled: Some(self.optimize), runs: Some(self.optimize_runs as usize) }; + // build the project w/ allowed paths = root and all the libs - let solc_settings = Settings { evm_version: Some(self.evm_version), ..Default::default() }; + let solc_settings = + Settings { optimizer, evm_version: Some(self.evm_version), ..Default::default() }; let mut builder = Project::builder() .paths(paths) .allowed_path(&root) From b60d97383ef6655418c221d9155283abc1dff6ab Mon Sep 17 00:00:00 2001 From: brockelmore <31553173+brockelmore@users.noreply.github.com> Date: Fri, 17 Dec 2021 10:27:28 -0800 Subject: [PATCH 02/13] better prank (#246) --- .../sputnik/cheatcodes/cheatcode_handler.rs | 42 ++++------------ .../cheatcodes/memory_stackstate_owned.rs | 8 +++- evm-adapters/src/sputnik/cheatcodes/mod.rs | 2 +- evm-adapters/testdata/CheatCodes.sol | 48 ++----------------- 4 files changed, 22 insertions(+), 78 deletions(-) diff --git a/evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs b/evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs index 50b66ab333cbb..cb264cb1aa0d9 100644 --- a/evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs +++ b/evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs @@ -278,12 +278,7 @@ impl<'a, 'b, B: Backend, P: PrecompileSet> CheatcodeStackExecutor<'a, 'b, B, P> /// Given a transaction's calldata, it tries to parse it as an [`HEVM cheatcode`](super::HEVM) /// call and modify the state accordingly. - fn apply_cheatcode( - &mut self, - input: Vec, - transfer: Option, - target_gas: Option, - ) -> Capture<(ExitReason, Vec), Infallible> { + fn apply_cheatcode(&mut self, input: Vec) -> Capture<(ExitReason, Vec), Infallible> { let mut res = vec![]; // Get a mutable ref to the state so we can apply the cheats @@ -382,31 +377,7 @@ impl<'a, 'b, B: Backend, P: PrecompileSet> CheatcodeStackExecutor<'a, 'b, B, P> } HEVMCalls::Prank(inner) => { let caller = inner.0; - let address = inner.1; - let input = inner.2; - - let value = - if let Some(ref transfer) = transfer { transfer.value } else { U256::zero() }; - - // change origin - let context = Context { caller, address, apparent_value: value }; - let ret = self.call( - address, - Some(Transfer { source: caller, target: address, value }), - input.to_vec(), - target_gas, - false, - context, - ); - res = match ret { - Capture::Exit((successful, v)) => match successful { - ExitReason::Succeed(_) => { - ethers::abi::encode(&[Token::Bool(true), Token::Bytes(v.to_vec())]) - } - _ => ethers::abi::encode(&[Token::Bool(false), Token::Bytes(v.to_vec())]), - }, - _ => vec![], - }; + self.state_mut().next_msg_sender = Some(caller); } HEVMCalls::ExpectRevert(inner) => { if self.state().expected_revert.is_some() { @@ -757,9 +728,14 @@ impl<'a, 'b, B: Backend, P: PrecompileSet> Handler for CheatcodeStackExecutor<'a // (e.g. with the StateManager) let expected_revert = self.state_mut().expected_revert.take(); + let caller = self.state_mut().next_msg_sender.take(); + let mut new_context = context; + if let Some(caller) = caller { + new_context.caller = caller; + } if code_address == *CHEATCODE_ADDRESS { - self.apply_cheatcode(input, transfer, target_gas) + self.apply_cheatcode(input) } else if code_address == *CONSOLE_ADDRESS { self.console_log(input) } else { @@ -771,7 +747,7 @@ impl<'a, 'b, B: Backend, P: PrecompileSet> Handler for CheatcodeStackExecutor<'a is_static, true, true, - context, + new_context, ); if let Some(expected_revert) = expected_revert { diff --git a/evm-adapters/src/sputnik/cheatcodes/memory_stackstate_owned.rs b/evm-adapters/src/sputnik/cheatcodes/memory_stackstate_owned.rs index d0f649fbdc388..0147ee9d6e82c 100644 --- a/evm-adapters/src/sputnik/cheatcodes/memory_stackstate_owned.rs +++ b/evm-adapters/src/sputnik/cheatcodes/memory_stackstate_owned.rs @@ -16,6 +16,7 @@ pub struct MemoryStackStateOwned<'config, B> { pub backend: B, pub substate: MemoryStackSubstate<'config>, pub expected_revert: Option>, + pub next_msg_sender: Option, } impl<'config, B: Backend> MemoryStackStateOwned<'config, B> { @@ -26,7 +27,12 @@ impl<'config, B: Backend> MemoryStackStateOwned<'config, B> { impl<'config, B: Backend> MemoryStackStateOwned<'config, B> { pub fn new(metadata: StackSubstateMetadata<'config>, backend: B) -> Self { - Self { backend, substate: MemoryStackSubstate::new(metadata), expected_revert: None } + Self { + backend, + substate: MemoryStackSubstate::new(metadata), + expected_revert: None, + next_msg_sender: None, + } } } diff --git a/evm-adapters/src/sputnik/cheatcodes/mod.rs b/evm-adapters/src/sputnik/cheatcodes/mod.rs index 79961888d277f..090b71c19718c 100644 --- a/evm-adapters/src/sputnik/cheatcodes/mod.rs +++ b/evm-adapters/src/sputnik/cheatcodes/mod.rs @@ -50,7 +50,7 @@ ethers::contract::abigen!( ffi(string[])(bytes) addr(uint256)(address) sign(uint256,bytes32)(uint8,bytes32,bytes32) - prank(address,address,bytes)(bool,bytes) + prank(address) deal(address,uint256) etch(address,bytes) expectRevert(bytes) diff --git a/evm-adapters/testdata/CheatCodes.sol b/evm-adapters/testdata/CheatCodes.sol index a6713b9b96c18..185036f1fc3db 100644 --- a/evm-adapters/testdata/CheatCodes.sol +++ b/evm-adapters/testdata/CheatCodes.sol @@ -20,8 +20,8 @@ interface Hevm { function addr(uint256) external returns (address); // Performs a foreign function call via terminal, (stringInputs) => (result) function ffi(string[] calldata) external returns (bytes memory); - // Calls another contract with a specified `msg.sender`, (newSender, contract, input) => (success, returnData) - function prank(address, address, bytes calldata) external payable returns (bool, bytes memory); + // Sets the *next* call's msg.sender to be the input address + function prank(address) external; // Sets an address' balance, (who, newBalance) function deal(address, uint256) external; // Sets an address' code, (who, newCode) @@ -155,50 +155,12 @@ contract CheatCodes is DSTest { function testPrank() public { Prank prank = new Prank(); address new_sender = address(1337); - bytes4 sig = prank.checksOriginAndSender.selector; - string memory input = "And his name is JOHN CENA!"; - bytes memory calld = abi.encodePacked(sig, abi.encode(input)); - address origin = tx.origin; address sender = msg.sender; - (bool success, bytes memory ret) = hevm.prank(new_sender, address(prank), calld); - assertTrue(success); - string memory expectedRetString = "SUPER SLAM!"; - string memory actualRet = abi.decode(ret, (string)); - assertEq(actualRet, expectedRetString); - - // make sure we returned back to normal - assertEq(origin, tx.origin); - assertEq(sender, msg.sender); - } - - function testPrankValue() public { - Prank prank = new Prank(); - // setup the call - address new_sender = address(1337); - bytes4 sig = prank.checksOriginAndSender.selector; + hevm.prank(new_sender); string memory input = "And his name is JOHN CENA!"; - bytes memory calld = abi.encodePacked(sig, abi.encode(input)); - address origin = tx.origin; - address sender = msg.sender; - - // give the sender some monies - hevm.deal(new_sender, 1337); - - // call the function passing in a value. the eth is pulled from the new sender - sig = hevm.prank.selector; - calld = abi.encodePacked(sig, abi.encode(new_sender, address(prank), calld)); - - // this is nested low level calls effectively - (bool high_level_success, bytes memory outerRet) = address(hevm).call{value: 1}(calld); - assertTrue(high_level_success); - (bool success, bytes memory ret) = abi.decode(outerRet, (bool,bytes)); - assertTrue(success); + string memory retString = prank.checksOriginAndSender(input); string memory expectedRetString = "SUPER SLAM!"; - string memory actualRet = abi.decode(ret, (string)); - assertEq(actualRet, expectedRetString); - - // make sure we returned back to normal - assertEq(origin, tx.origin); + assertEq(retString, expectedRetString); assertEq(sender, msg.sender); } From 062c4e7e09153a0a5b14efceacb0cac7f611d915 Mon Sep 17 00:00:00 2001 From: brockelmore <31553173+brockelmore@users.noreply.github.com> Date: Fri, 17 Dec 2021 11:13:33 -0800 Subject: [PATCH 03/13] fix(forge): empty fuzz cases panic (#248) * fix empty fuzz panic * Update evm-adapters/src/fuzz.rs * nitpicks Co-authored-by: Matthias Seitz --- evm-adapters/src/fuzz.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/evm-adapters/src/fuzz.rs b/evm-adapters/src/fuzz.rs index 6ae236ccc4d1f..409c94e9575fb 100644 --- a/evm-adapters/src/fuzz.rs +++ b/evm-adapters/src/fuzz.rs @@ -169,6 +169,10 @@ impl FuzzedCases { /// Returns the average gas use of all test cases pub fn mean_gas(&self) -> u64 { + if self.cases.is_empty() { + return 0 + } + (self.cases.iter().map(|c| c.gas as u128).sum::() / self.cases.len() as u128) as u64 } From 989abe444e215e53afe5c4fd046ea5ca7d512325 Mon Sep 17 00:00:00 2001 From: Noah Citron Date: Fri, 17 Dec 2021 15:52:27 -0500 Subject: [PATCH 04/13] add dependencies section to readme (#250) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index db4b37b25f0b3..8c9083f3b6c21 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,9 @@ Foundry consists of: ![demo](./assets/demo.svg) +## Dependencies +Currently both Forge and Cast rely on the [libudev-dev](https://packages.debian.org/sid/libudev-dev) package. This package may be preinstalled for your system, but is also available in most packages managers. + ## Forge ``` From 8f18befbbebd93684bb6a4c1fdfbc3fb4d6ea8ce Mon Sep 17 00:00:00 2001 From: brockelmore <31553173+brockelmore@users.noreply.github.com> Date: Fri, 17 Dec 2021 13:14:46 -0800 Subject: [PATCH 05/13] feat(forge): prankStart + prankStop cheatcodes (#251) * prankStart + prankStop cheatcodes * fix addr != addr when depth == depth * complex prank test * Update evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs Co-authored-by: Georgios Konstantopoulos * add comment and fix * fix * update evm error * fmt Co-authored-by: Georgios Konstantopoulos --- .../sputnik/cheatcodes/cheatcode_handler.rs | 74 ++++++++++++++++--- .../cheatcodes/memory_stackstate_owned.rs | 2 + evm-adapters/src/sputnik/cheatcodes/mod.rs | 2 + evm-adapters/testdata/CheatCodes.sol | 66 ++++++++++++++--- 4 files changed, 123 insertions(+), 21 deletions(-) diff --git a/evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs b/evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs index cb264cb1aa0d9..ae41ece7bb74d 100644 --- a/evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs +++ b/evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs @@ -278,7 +278,11 @@ impl<'a, 'b, B: Backend, P: PrecompileSet> CheatcodeStackExecutor<'a, 'b, B, P> /// Given a transaction's calldata, it tries to parse it as an [`HEVM cheatcode`](super::HEVM) /// call and modify the state accordingly. - fn apply_cheatcode(&mut self, input: Vec) -> Capture<(ExitReason, Vec), Infallible> { + fn apply_cheatcode( + &mut self, + input: Vec, + msg_sender: H160, + ) -> Capture<(ExitReason, Vec), Infallible> { let mut res = vec![]; // Get a mutable ref to the state so we can apply the cheats @@ -377,8 +381,48 @@ impl<'a, 'b, B: Backend, P: PrecompileSet> CheatcodeStackExecutor<'a, 'b, B, P> } HEVMCalls::Prank(inner) => { let caller = inner.0; + if let Some((orginal_pranker, caller, depth)) = self.state().msg_sender { + let start_prank_depth = if let Some(depth) = self.state().metadata().depth() { + depth + 1 + } else { + 0 + }; + // we allow someone to do a 1 time prank even when startPrank is set if + // and only if we ensure that the startPrank *cannot* be applied to the + // following call + if start_prank_depth == depth && caller == orginal_pranker { + return evm_error("You have an active `startPrank` at this frame depth already. Use either `prank` or `startPrank`, not both"); + } + } self.state_mut().next_msg_sender = Some(caller); } + HEVMCalls::StartPrank(inner) => { + // startPrank works by using frame depth to determine whether to overwrite + // msg.sender if we set a prank caller at a particular depth, it + // will continue to use the prank caller for any subsequent calls + // until stopPrank is called. + // + // We additionally have to store the original message sender of the cheatcode caller + // so that we dont apply it to any other addresses when depth == + // prank_depth + let caller = inner.0; + if self.state().next_msg_sender.is_some() { + return evm_error("You have an active `prank` call already. Use either `prank` or `startPrank`, not both"); + } else { + self.state_mut().msg_sender = Some(( + msg_sender, + caller, + if let Some(depth) = self.state().metadata().depth() { + depth + 1 + } else { + 0 + }, + )); + } + } + HEVMCalls::StopPrank(_) => { + self.state_mut().msg_sender = None; + } HEVMCalls::ExpectRevert(inner) => { if self.state().expected_revert.is_some() { return evm_error( @@ -727,18 +771,30 @@ impl<'a, 'b, B: Backend, P: PrecompileSet> Handler for CheatcodeStackExecutor<'a // NB: This is very similar to how Optimism's custom intercept logic to "predeploys" work // (e.g. with the StateManager) - let expected_revert = self.state_mut().expected_revert.take(); - let caller = self.state_mut().next_msg_sender.take(); - let mut new_context = context; - if let Some(caller) = caller { - new_context.caller = caller; - } - if code_address == *CHEATCODE_ADDRESS { - self.apply_cheatcode(input) + self.apply_cheatcode(input, context.caller) } else if code_address == *CONSOLE_ADDRESS { self.console_log(input) } else { + // modify execution context depending on the cheatcode + let expected_revert = self.state_mut().expected_revert.take(); + let mut new_context = context; + + // handle `startPrank` - see apply_cheatcodes for more info + if let Some((original_msg_sender, permanent_caller, depth)) = self.state().msg_sender { + let curr_depth = + if let Some(depth) = self.state().metadata().depth() { depth + 1 } else { 0 }; + if curr_depth == depth && new_context.caller == original_msg_sender { + new_context.caller = permanent_caller; + } + } + + // handle normal `prank` + if let Some(caller) = self.state_mut().next_msg_sender.take() { + new_context.caller = caller; + } + + // perform the call let res = self.call_inner( code_address, transfer, diff --git a/evm-adapters/src/sputnik/cheatcodes/memory_stackstate_owned.rs b/evm-adapters/src/sputnik/cheatcodes/memory_stackstate_owned.rs index 0147ee9d6e82c..bb050853f9c36 100644 --- a/evm-adapters/src/sputnik/cheatcodes/memory_stackstate_owned.rs +++ b/evm-adapters/src/sputnik/cheatcodes/memory_stackstate_owned.rs @@ -17,6 +17,7 @@ pub struct MemoryStackStateOwned<'config, B> { pub substate: MemoryStackSubstate<'config>, pub expected_revert: Option>, pub next_msg_sender: Option, + pub msg_sender: Option<(H160, H160, usize)>, } impl<'config, B: Backend> MemoryStackStateOwned<'config, B> { @@ -32,6 +33,7 @@ impl<'config, B: Backend> MemoryStackStateOwned<'config, B> { substate: MemoryStackSubstate::new(metadata), expected_revert: None, next_msg_sender: None, + msg_sender: None, } } } diff --git a/evm-adapters/src/sputnik/cheatcodes/mod.rs b/evm-adapters/src/sputnik/cheatcodes/mod.rs index 090b71c19718c..c4828b9931f53 100644 --- a/evm-adapters/src/sputnik/cheatcodes/mod.rs +++ b/evm-adapters/src/sputnik/cheatcodes/mod.rs @@ -51,6 +51,8 @@ ethers::contract::abigen!( addr(uint256)(address) sign(uint256,bytes32)(uint8,bytes32,bytes32) prank(address) + startPrank(address) + stopPrank() deal(address,uint256) etch(address,bytes) expectRevert(bytes) diff --git a/evm-adapters/testdata/CheatCodes.sol b/evm-adapters/testdata/CheatCodes.sol index 185036f1fc3db..48fa154a9726c 100644 --- a/evm-adapters/testdata/CheatCodes.sol +++ b/evm-adapters/testdata/CheatCodes.sol @@ -22,6 +22,10 @@ interface Hevm { function ffi(string[] calldata) external returns (bytes memory); // Sets the *next* call's msg.sender to be the input address function prank(address) external; + // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called + function startPrank(address) external; + // Resets subsequent calls' msg.sender to be `address(this)` + function stopPrank() external; // Sets an address' balance, (who, newBalance) function deal(address, uint256) external; // Sets an address' code, (who, newCode) @@ -157,11 +161,29 @@ contract CheatCodes is DSTest { address new_sender = address(1337); address sender = msg.sender; hevm.prank(new_sender); - string memory input = "And his name is JOHN CENA!"; - string memory retString = prank.checksOriginAndSender(input); - string memory expectedRetString = "SUPER SLAM!"; - assertEq(retString, expectedRetString); - assertEq(sender, msg.sender); + prank.bar(new_sender); + prank.bar(address(this)); + } + + function testPrankStart() public { + Prank prank = new Prank(); + address new_sender = address(1337); + address sender = msg.sender; + hevm.startPrank(new_sender); + prank.bar(new_sender); + prank.bar(new_sender); + hevm.stopPrank(); + prank.bar(address(this)); + } + + function testPrankStartComplex() public { + // A -> B, B starts pranking, doesnt call stopPrank, A calls C calls D + // C -> D would be pranked + ComplexPrank complexPrank = new ComplexPrank(); + Prank prank = new Prank(); + complexPrank.uncompletedPrank(); + prank.bar(address(this)); + complexPrank.completePrank(prank); } function testEtch() public { @@ -263,12 +285,32 @@ contract ExpectRevertCallee { } } -contract Prank is DSTest { - function checksOriginAndSender(string calldata input) external payable returns (string memory) { - string memory expectedInput = "And his name is JOHN CENA!"; - assertEq(input, expectedInput); - assertEq(address(1337), msg.sender); - string memory expectedRetString = "SUPER SLAM!"; - return expectedRetString; +contract Prank { + function bar(address expectedMsgSender) public { + require(msg.sender == expectedMsgSender, "bad prank"); + InnerPrank inner = new InnerPrank(); + inner.bar(address(this)); } } + +contract InnerPrank { + function bar(address expectedMsgSender) public { + require(msg.sender == expectedMsgSender, "bad prank"); + } +} + +contract ComplexPrank { + Hevm hevm = Hevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + function uncompletedPrank() public { + hevm.startPrank(address(1337)); + } + + function completePrank(Prank prank) public { + prank.bar(address(1337)); + hevm.stopPrank(); + prank.bar(address(this)); + } +} + + From d4de7c8f50af5570dd31a050e1825799c387f743 Mon Sep 17 00:00:00 2001 From: wren <9318247+DrawnWren@users.noreply.github.com> Date: Sat, 18 Dec 2021 03:46:49 -1000 Subject: [PATCH 06/13] fix(forge): no longer git commit on init when in an existing git repo (#252) --- cli/src/forge.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/cli/src/forge.rs b/cli/src/forge.rs index 7a5f358f958a0..19651a81dc3d4 100644 --- a/cli/src/forge.rs +++ b/cli/src/forge.rs @@ -105,14 +105,13 @@ fn main() -> eyre::Result<()> { .wait()?; if !is_git.success() { Command::new("git").arg("init").current_dir(&root).spawn()?.wait()?; + Command::new("git").args(&["add", "."]).current_dir(&root).spawn()?.wait()?; + Command::new("git") + .args(&["commit", "-m", "chore: forge init"]) + .current_dir(&root) + .spawn()? + .wait()?; } - Command::new("git").args(&["add", "."]).current_dir(&root).spawn()?.wait()?; - Command::new("git") - .args(&["commit", "-m", "chore: forge init"]) - .current_dir(&root) - .spawn()? - .wait()?; - Dependency::from_str("https://github.com/dapphub/ds-test") .and_then(|dependency| install(root, vec![dependency]))?; } From eb4e67844826c1c2e2e00a33ccf15af37ae2cd50 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Sat, 18 Dec 2021 20:38:15 +0200 Subject: [PATCH 07/13] fix(forge-test): do not default to using ETH_RPC_URL closes #256 --- cli/src/cmd/test.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/src/cmd/test.rs b/cli/src/cmd/test.rs index da53f36f0f05f..734a9542abe1a 100644 --- a/cli/src/cmd/test.rs +++ b/cli/src/cmd/test.rs @@ -56,7 +56,6 @@ pub struct TestArgs { short )] #[structopt(alias = "rpc-url")] - #[structopt(env = "ETH_RPC_URL")] fork_url: Option, #[structopt(help = "pins the block number for the state fork", long)] From aadd95805fe8250f3a00af70b3c4ac85d3ae1ef1 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Sat, 18 Dec 2021 20:54:50 +0200 Subject: [PATCH 08/13] ci: allow multicall to fail the test_helpers test is failing even when using dapptools --- integration-tests/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/test.sh b/integration-tests/test.sh index fe83bf62388db..78ea7d537397c 100755 --- a/integration-tests/test.sh +++ b/integration-tests/test.sh @@ -7,7 +7,7 @@ set -e REPO=$1 TESTDATA=testdata -ALLOWED_FAILURE_REPOS=("geb" "drai" "guni-lev") +ALLOWED_FAILURE_REPOS=("geb" "drai" "guni-lev", "multicall") if [[ " ${ALLOWED_FAILURE_REPOS[*]} " =~ " ${REPO} " ]]; then export FORGE_ALLOW_FAILURE=1 fi From 5679a42abbab05b597c431acbc49aac3aaf45706 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Sat, 18 Dec 2021 20:24:23 -0800 Subject: [PATCH 09/13] feat(cast): add tx command (#260) This commit ports the `seth tx` command to `cast`. It follows the implementation of `cast block` and includes the feature to print a specific field in the transaction as well as printing the result in JSON. ``` $ cast tx --rpc-url [field] ``` The flag `-j`/`--json` is used to print the result as JSON. --- cast/README.md | 4 ++-- cast/src/lib.rs | 42 +++++++++++++++++++++++++++++++++++++++++- cli/src/cast.rs | 4 ++++ cli/src/opts/cast.rs | 10 ++++++++++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/cast/README.md b/cast/README.md index 72636dba43a78..b57cfeeb69948 100644 --- a/cast/README.md +++ b/cast/README.md @@ -35,7 +35,7 @@ - [x] `calldata` - [x] `chain` - [x] `chain-id` -- [ ] `code` +- [x] `code` - [ ] `debug` - [ ] `estimate` - [ ] `etherscan-source` @@ -56,4 +56,4 @@ - [x] `send` (partial) - [ ] `sign` - [x] `storage` -- [ ] `tx` +- [x] `tx` diff --git a/cast/src/lib.rs b/cast/src/lib.rs index 099e5d46aa874..1488d93b2540e 100644 --- a/cast/src/lib.rs +++ b/cast/src/lib.rs @@ -44,7 +44,7 @@ where /// Makes a read-only call to the specified address /// /// ```no_run - /// + /// /// use cast::Cast; /// use ethers_core::types::Address; /// use ethers_providers::{Provider, Http}; @@ -308,6 +308,46 @@ where ) -> Result { Ok(format!("{}", self.provider.get_code(who, block).await?)) } + + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// use std::convert::TryFrom; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc"; + /// let tx = cast.transaction(tx_hash.to_string(), None, false).await?; + /// println!("{}", tx); + /// # Ok(()) + /// # } + /// ``` + pub async fn transaction( + &self, + tx_hash: String, + field: Option, + to_json: bool, + ) -> Result { + let transaction_result = self + .provider + .get_transaction(H256::from_str(&tx_hash)?) + .await? + .ok_or_else(|| eyre::eyre!("transaction {:?} not found", tx_hash))?; + + let transaction = if let Some(ref field) = field { + serde_json::to_value(&transaction_result)? + .get(field) + .cloned() + .ok_or_else(|| eyre::eyre!("field {} not found", field))? + } else { + serde_json::to_value(&transaction_result)? + }; + + let transaction = + if to_json { serde_json::to_string(&transaction)? } else { to_table(transaction) }; + Ok(transaction) + } } pub struct SimpleCast; diff --git a/cli/src/cast.rs b/cli/src/cast.rs index cc83cbaa21591..393e434b76aa5 100644 --- a/cli/src/cast.rs +++ b/cli/src/cast.rs @@ -124,6 +124,10 @@ async fn main() -> eyre::Result<()> { Subcommands::Namehash { name } => { println!("{}", SimpleCast::namehash(&name)?); } + Subcommands::Tx { rpc_url, hash, field, to_json } => { + let provider = Provider::try_from(rpc_url)?; + println!("{}", Cast::new(&provider).transaction(hash, field, to_json).await?) + } Subcommands::SendTx { eth, to, sig, cast_async, args } => { let provider = Provider::try_from(eth.rpc_url.as_str())?; let chain_id = Cast::new(&provider).chain_id().await?; diff --git a/cli/src/opts/cast.rs b/cli/src/opts/cast.rs index 1a7d0d314f8e0..0e4b3d9b5f733 100644 --- a/cli/src/opts/cast.rs +++ b/cli/src/opts/cast.rs @@ -107,6 +107,16 @@ pub enum Subcommands { #[structopt(name = "namehash")] #[structopt(about = "returns ENS namehash of provided name")] Namehash { name: String }, + #[structopt(name = "tx")] + #[structopt(about = "Show information about the transaction ")] + Tx { + hash: String, + field: Option, + #[structopt(long = "--json", short = "-j")] + to_json: bool, + #[structopt(long, env = "ETH_RPC_URL")] + rpc_url: String, + }, #[structopt(name = "send")] #[structopt(about = "Publish a transaction signed by to call with ")] SendTx { From 6224e0f4db828bef8e2b08bff042b8e3c60c33af Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sun, 19 Dec 2021 13:25:23 +0100 Subject: [PATCH 10/13] feat(forge): add --hh alias for --hardhat (#264) --- cli/src/cmd/build.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/src/cmd/build.rs b/cli/src/cmd/build.rs index e146d6f15eb06..ded5abaa98159 100644 --- a/cli/src/cmd/build.rs +++ b/cli/src/cmd/build.rs @@ -73,7 +73,8 @@ pub struct BuildArgs { #[structopt( help = "uses hardhat style project layout. This a convenience flag and is the same as `--contracts contracts --lib-paths node_modules`", long, - conflicts_with = "contracts" + conflicts_with = "contracts", + alias = "hh" )] pub hardhat: bool, } From 7ac4072a2717566a9737322bd09e04c03c42aa7c Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Sun, 19 Dec 2021 04:40:54 -0800 Subject: [PATCH 11/13] fix(cast): call deserialization (#262) * fix(cast): call deserialization After this commit, the return value of `cast call` will be still be printed even if the ABI type of the return value is not specified. This matches the behavior of `seth`. Example: ``` $ cast call 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D 'WETH()(address)' 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 $ cast call 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D 'WETH()' 0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 ``` Previously, the second command above would print nothing as there would be no decoded values to iterate over when building the string output. This commit also prepends a `0x` to each decoded return type, which matches the behavior of `seth`. * Update cast/src/lib.rs Co-authored-by: Matthias Seitz Co-authored-by: Georgios Konstantopoulos Co-authored-by: Matthias Seitz --- cast/src/lib.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/cast/src/lib.rs b/cast/src/lib.rs index 1488d93b2540e..22f0ec78cdfc3 100644 --- a/cast/src/lib.rs +++ b/cast/src/lib.rs @@ -75,16 +75,20 @@ where let res = self.provider.call(&tx, None).await?; // decode args into tokens - let res = func.decode_output(res.as_ref())?; + let decoded = func.decode_output(res.as_ref())?; + // handle case when return type is not specified + if decoded.is_empty() { + Ok(format!("{}\n", res)) + } else { + // concatenate them + let mut s = String::new(); + for output in decoded { + s.push_str(&format!("0x{}\n", output)); + } - // concatenate them - let mut s = String::new(); - for output in res { - s.push_str(&format!("{}\n", output)); + // return string + Ok(s) } - - // return string - Ok(s) } pub async fn balance + Send + Sync>( From 5275fff0a7852f9a88aa76b276913618a622bac9 Mon Sep 17 00:00:00 2001 From: Noah Citron Date: Sun, 19 Dec 2021 09:56:15 -0500 Subject: [PATCH 12/13] feat: add cast --from-wei (#261) * add cast --from-wei * lint --- cast/README.md | 2 +- cast/src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ cli/src/cast.rs | 10 ++++++++++ cli/src/opts/cast.rs | 3 +++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/cast/README.md b/cast/README.md index b57cfeeb69948..70a08966b4d1b 100644 --- a/cast/README.md +++ b/cast/README.md @@ -7,7 +7,7 @@ - [x] `--from-ascii` (with `--from-utf8` alias) - [ ] `--from-bin` - [ ] `--from-fix` -- [ ] `--from-wei` +- [x] `--from-wei` - [ ] `--max-int` - [x] `--max-uint` - [ ] `--min-int` diff --git a/cast/src/lib.rs b/cast/src/lib.rs index 22f0ec78cdfc3..13a996d11eaaa 100644 --- a/cast/src/lib.rs +++ b/cast/src/lib.rs @@ -515,6 +515,41 @@ impl SimpleCast { }) } + /// Converts wei into an eth amount + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::from_wei(1.into(), "gwei".to_string())?, "0.000000001"); + /// assert_eq!(Cast::from_wei(12340000005u64.into(), "gwei".to_string())?, "12.340000005"); + /// assert_eq!(Cast::from_wei(10.into(), "ether".to_string())?, "0.00000000000000001"); + /// assert_eq!(Cast::from_wei(100.into(), "eth".to_string())?, "0.0000000000000001"); + /// assert_eq!(Cast::from_wei(17.into(), "".to_string())?, "17"); + /// + /// Ok(()) + /// } + /// ``` + pub fn from_wei(value: U256, unit: String) -> Result { + Ok(match &unit[..] { + "gwei" => { + let gwei = U256::pow(10.into(), 9.into()); + let left = value / gwei; + let right = value - left * gwei; + let res = format!("{}.{:0>9}", left, right.to_string()); + res.trim_end_matches('0').to_string() + } + "eth" | "ether" => { + let wei = U256::pow(10.into(), 18.into()); + let left = value / wei; + let right = value - left * wei; + let res = format!("{}.{:0>18}", left, right.to_string()); + res.trim_end_matches('0').to_string() + } + _ => value.to_string(), + }) + } + /// Converts an Ethereum address to its checksum format /// according to [EIP-55](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md) /// diff --git a/cli/src/cast.rs b/cli/src/cast.rs index 393e434b76aa5..e9098130a84b6 100644 --- a/cli/src/cast.rs +++ b/cli/src/cast.rs @@ -94,6 +94,16 @@ async fn main() -> eyre::Result<()> { )? ); } + Subcommands::FromWei { value, unit } => { + let val = unwrap_or_stdin(value)?; + println!( + "{}", + SimpleCast::from_wei( + U256::from_dec_str(&val)?, + unit.unwrap_or_else(|| String::from("wei")) + )? + ); + } Subcommands::Block { rpc_url, block, full, field, to_json } => { let provider = Provider::try_from(rpc_url)?; println!("{}", Cast::new(provider).block(block, full, field, to_json).await?); diff --git a/cli/src/opts/cast.rs b/cli/src/opts/cast.rs index 0e4b3d9b5f733..e4edecbf45f6f 100644 --- a/cli/src/opts/cast.rs +++ b/cli/src/opts/cast.rs @@ -50,6 +50,9 @@ pub enum Subcommands { #[structopt(name = "--to-wei")] #[structopt(about = "convert an ETH amount into wei")] ToWei { value: Option, unit: Option }, + #[structopt(name = "--from-wei")] + #[structopt(about = "convert wei into an ETH amount")] + FromWei { value: Option, unit: Option }, #[structopt(name = "block")] #[structopt( about = "Prints information about . If is given, print only the value of that field" From 8dfa2fa11b4dc234bde9f6b61c837268b68fa059 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sun, 19 Dec 2021 16:04:12 +0100 Subject: [PATCH 13/13] chore: bump ethers with latest remappings (#263) * chore: bump ethers with latest remappings * chore: bump ethers Co-authored-by: Georgios Konstantopoulos --- Cargo.lock | 155 +++---------------------------------------- cast/src/lib.rs | 2 +- cli/src/cmd/build.rs | 5 +- cli/src/forge.rs | 3 +- 4 files changed, 15 insertions(+), 150 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ea57611da118..8711e588f6c0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -755,22 +755,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "core-foundation" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" - [[package]] name = "cpufeatures" version = "0.2.1" @@ -1134,7 +1118,7 @@ dependencies = [ [[package]] name = "ethers" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4d647453e3df09d0934964ef913951cd38366286" +source = "git+https://github.com/gakonst/ethers-rs#d7c29cc615e260bd13f9efd93fa2223d48d9d31b" dependencies = [ "ethers-contract", "ethers-core", @@ -1148,7 +1132,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4d647453e3df09d0934964ef913951cd38366286" +source = "git+https://github.com/gakonst/ethers-rs#d7c29cc615e260bd13f9efd93fa2223d48d9d31b" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -1166,7 +1150,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4d647453e3df09d0934964ef913951cd38366286" +source = "git+https://github.com/gakonst/ethers-rs#d7c29cc615e260bd13f9efd93fa2223d48d9d31b" dependencies = [ "Inflector", "anyhow", @@ -1187,7 +1171,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4d647453e3df09d0934964ef913951cd38366286" +source = "git+https://github.com/gakonst/ethers-rs#d7c29cc615e260bd13f9efd93fa2223d48d9d31b" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -1201,7 +1185,7 @@ dependencies = [ [[package]] name = "ethers-core" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4d647453e3df09d0934964ef913951cd38366286" +source = "git+https://github.com/gakonst/ethers-rs#d7c29cc615e260bd13f9efd93fa2223d48d9d31b" dependencies = [ "arrayvec 0.7.2", "bytes", @@ -1229,7 +1213,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "0.2.0" -source = "git+https://github.com/gakonst/ethers-rs#4d647453e3df09d0934964ef913951cd38366286" +source = "git+https://github.com/gakonst/ethers-rs#d7c29cc615e260bd13f9efd93fa2223d48d9d31b" dependencies = [ "ethers-core", "reqwest", @@ -1242,7 +1226,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4d647453e3df09d0934964ef913951cd38366286" +source = "git+https://github.com/gakonst/ethers-rs#d7c29cc615e260bd13f9efd93fa2223d48d9d31b" dependencies = [ "async-trait", "ethers-contract", @@ -1265,7 +1249,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4d647453e3df09d0934964ef913951cd38366286" +source = "git+https://github.com/gakonst/ethers-rs#d7c29cc615e260bd13f9efd93fa2223d48d9d31b" dependencies = [ "async-trait", "auto_impl", @@ -1294,7 +1278,7 @@ dependencies = [ [[package]] name = "ethers-signers" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4d647453e3df09d0934964ef913951cd38366286" +source = "git+https://github.com/gakonst/ethers-rs#d7c29cc615e260bd13f9efd93fa2223d48d9d31b" dependencies = [ "async-trait", "coins-bip32", @@ -1316,7 +1300,7 @@ dependencies = [ [[package]] name = "ethers-solc" version = "0.1.0" -source = "git+https://github.com/gakonst/ethers-rs#4d647453e3df09d0934964ef913951cd38366286" +source = "git+https://github.com/gakonst/ethers-rs#d7c29cc615e260bd13f9efd93fa2223d48d9d31b" dependencies = [ "colored", "ethers-core", @@ -1490,21 +1474,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "forge" version = "0.1.0" @@ -1993,19 +1962,6 @@ dependencies = [ "tokio-rustls", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "i256" version = "0.1.0" @@ -2314,24 +2270,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "native-tls" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nix" version = "0.13.1" @@ -2484,20 +2422,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "openssl" -version = "0.10.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a" -dependencies = [ - "bitflags", - "cfg-if 1.0.0", - "foreign-types", - "libc", - "once_cell", - "openssl-sys", -] - [[package]] name = "openssl-probe" version = "0.1.4" @@ -3159,13 +3083,11 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", - "hyper-tls", "ipnet", "js-sys", "lazy_static", "log", "mime", - "native-tls", "percent-encoding", "pin-project-lite", "rustls", @@ -3174,7 +3096,6 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", - "tokio-native-tls", "tokio-rustls", "url", "wasm-bindgen", @@ -3388,16 +3309,6 @@ dependencies = [ "syn", ] -[[package]] -name = "schannel" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" -dependencies = [ - "lazy_static", - "winapi", -] - [[package]] name = "scopeguard" version = "1.1.0" @@ -3463,29 +3374,6 @@ dependencies = [ "cc", ] -[[package]] -name = "security-framework" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "0.11.0" @@ -3595,16 +3483,6 @@ dependencies = [ "cpufeatures", "digest 0.9.0", "opaque-debug 0.3.0", - "sha2-asm", -] - -[[package]] -name = "sha2-asm" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf27176fb5d15398e3a479c652c20459d9dac830dedd1fa55b42a77dbcdbfcea" -dependencies = [ - "cc", ] [[package]] @@ -3767,8 +3645,7 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "svm-rs" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8733662d7e2c4bc2bdc5ca4102c7f8b13d1c3c12fb767089de07c2c3df3cd03d" +source = "git+https://github.com/roynalnaruto/svm-rs#4dd8d3f93a6383ff660899899b4dc43c73a14f97" dependencies = [ "anyhow", "cfg-if 1.0.0", @@ -3957,16 +3834,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.23.1" diff --git a/cast/src/lib.rs b/cast/src/lib.rs index 13a996d11eaaa..46fe8576b77ab 100644 --- a/cast/src/lib.rs +++ b/cast/src/lib.rs @@ -44,7 +44,7 @@ where /// Makes a read-only call to the specified address /// /// ```no_run - /// + /// /// use cast::Cast; /// use ethers_core::types::Address; /// use ethers_providers::{Provider, Http}; diff --git a/cli/src/cmd/build.rs b/cli/src/cmd/build.rs index ded5abaa98159..9b98fb6668470 100644 --- a/cli/src/cmd/build.rs +++ b/cli/src/cmd/build.rs @@ -31,7 +31,7 @@ pub struct BuildArgs { pub root: Option, #[structopt( - help = "the directory relative to the root under which the smart contrats are", + help = "the directory relative to the root under which the smart contracts are", long, short )] @@ -165,8 +165,7 @@ impl BuildArgs { let lib_paths = self.libs(&root); // get all the remappings corresponding to the lib paths - let mut remappings: Vec<_> = - lib_paths.iter().flat_map(|path| Remapping::find_many(&path).unwrap()).collect(); + let mut remappings: Vec<_> = lib_paths.iter().flat_map(Remapping::find_many).collect(); // extend them with the once manually provided in the opts remappings.extend_from_slice(&self.remappings); diff --git a/cli/src/forge.rs b/cli/src/forge.rs index 19651a81dc3d4..a8602c508958a 100644 --- a/cli/src/forge.rs +++ b/cli/src/forge.rs @@ -60,8 +60,7 @@ fn main() -> eyre::Result<()> { let root = std::fs::canonicalize(root)?; let lib_paths = if lib_paths.is_empty() { vec![root.join("lib")] } else { lib_paths }; - let remappings: Vec<_> = - lib_paths.iter().flat_map(|path| Remapping::find_many(&path).unwrap()).collect(); + let remappings: Vec<_> = lib_paths.iter().flat_map(Remapping::find_many).collect(); remappings.iter().for_each(|x| println!("{}", x)); } Subcommands::Init { root, template } => {