From 36cbce7c78b56dd68359084a5d8b03f84efed8fb Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 14 Nov 2024 20:34:12 +0200 Subject: [PATCH 01/68] feat(forge): allow `--verifier custom` option (#9311) * feat(forge): allow `--verifier custom` option * Changes after review: add description of custom verifier, reorg err message, add custom verifier api key * Fix descriptions * Update crates/verify/src/provider.rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --- crates/common/src/retry.rs | 2 +- crates/verify/src/bytecode.rs | 5 +++++ crates/verify/src/provider.rs | 7 +++++++ crates/verify/src/verify.rs | 17 +++++++++++++++-- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/crates/common/src/retry.rs b/crates/common/src/retry.rs index 7f649c7ed35a..59ba2055f8a3 100644 --- a/crates/common/src/retry.rs +++ b/crates/common/src/retry.rs @@ -83,6 +83,6 @@ impl Retry { fn handle_err(&mut self, err: Error) { self.retries -= 1; - warn!("erroneous attempt ({} tries remaining): {}", self.retries, err.root_cause()); + let _ = sh_warn!("{} ({} tries remaining)", err.root_cause(), self.retries); } } diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index a8fc1ace7066..0c3f9e5b0a91 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -96,6 +96,11 @@ impl figment::Provider for VerifyBytecodeArgs { &self, ) -> Result, figment::Error> { let mut dict = self.etherscan.dict(); + + if let Some(api_key) = &self.verifier.verifier_api_key { + dict.insert("etherscan_api_key".into(), api_key.as_str().into()); + } + if let Some(block) = &self.block { dict.insert("block".into(), figment::value::Value::serialize(block)?); } diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index 6e5b29d5e34c..bc01bd9e304d 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -124,6 +124,7 @@ impl FromStr for VerificationProviderType { "s" | "sourcify" => Ok(Self::Sourcify), "b" | "blockscout" => Ok(Self::Blockscout), "o" | "oklink" => Ok(Self::Oklink), + "c" | "custom" => Ok(Self::Custom), _ => Err(format!("Unknown provider: {s}")), } } @@ -144,6 +145,9 @@ impl fmt::Display for VerificationProviderType { Self::Oklink => { write!(f, "oklink")?; } + Self::Custom => { + write!(f, "custom")?; + } }; Ok(()) } @@ -156,6 +160,8 @@ pub enum VerificationProviderType { Sourcify, Blockscout, Oklink, + /// Custom verification provider, requires compatibility with the Etherscan API. + Custom, } impl VerificationProviderType { @@ -171,6 +177,7 @@ impl VerificationProviderType { Self::Sourcify => Ok(Box::::default()), Self::Blockscout => Ok(Box::::default()), Self::Oklink => Ok(Box::::default()), + Self::Custom => Ok(Box::::default()), } } } diff --git a/crates/verify/src/verify.rs b/crates/verify/src/verify.rs index 5f3e55329d88..89cfd99aa676 100644 --- a/crates/verify/src/verify.rs +++ b/crates/verify/src/verify.rs @@ -31,14 +31,22 @@ pub struct VerifierArgs { #[arg(long, help_heading = "Verifier options", default_value = "etherscan", value_enum)] pub verifier: VerificationProviderType, - /// The verifier URL, if using a custom provider + /// The verifier API KEY, if using a custom provider. + #[arg(long, help_heading = "Verifier options", env = "VERIFIER_API_KEY")] + pub verifier_api_key: Option, + + /// The verifier URL, if using a custom provider. #[arg(long, help_heading = "Verifier options", env = "VERIFIER_URL")] pub verifier_url: Option, } impl Default for VerifierArgs { fn default() -> Self { - Self { verifier: VerificationProviderType::Etherscan, verifier_url: None } + Self { + verifier: VerificationProviderType::Etherscan, + verifier_api_key: None, + verifier_url: None, + } } } @@ -162,6 +170,11 @@ impl figment::Provider for VerifyArgs { if self.via_ir { dict.insert("via_ir".to_string(), figment::value::Value::serialize(self.via_ir)?); } + + if let Some(api_key) = &self.verifier.verifier_api_key { + dict.insert("etherscan_api_key".into(), api_key.as_str().into()); + } + Ok(figment::value::Map::from([(Config::selected_profile(), dict)])) } } From c526cab8364fdf410fb8b04d256ca83d4dc632bf Mon Sep 17 00:00:00 2001 From: James <107906898+EdwardJES@users.noreply.github.com> Date: Fri, 15 Nov 2024 21:02:44 +0700 Subject: [PATCH 02/68] feat(`cheatcodes`): add `delegatecall` to `prank`ing (#8863) * begin api and rough comments * impl cheatcode * add check for eoa * fix eoa check on each prank call * add to assets * prank compiling * delegate call working, storage not upating * delegate call working, some tidy up * add prank2 calls * impl remaining tests * formatting * forge fmt * add pranks to cheatcodes.json * run cargo cheats * If verbosity level is 1 or higher, it shows dirty files. * Fix, add EOA prank test * Revert "If verbosity level is 1 or higher, it shows dirty files." This reverts commit d03ac1d59acb8096b12f46dfb5a397bcc4d28ce9. * Fix test * apply on extdelegatecall --------- Co-authored-by: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Co-authored-by: grandizzy --- crates/cheatcodes/assets/cheatcodes.json | 80 ++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 16 ++++ crates/cheatcodes/src/evm/prank.rs | 58 +++++++++++- crates/cheatcodes/src/inspector.rs | 15 ++- testdata/cheats/Vm.sol | 4 + testdata/default/cheats/Prank.t.sol | 114 +++++++++++++++++++++++ 6 files changed, 281 insertions(+), 6 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 41797106d6bb..347d738e01ec 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -7182,6 +7182,46 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "prank_2", + "description": "Sets the *next* delegate call's `msg.sender` to be the input address.", + "declaration": "function prank(address msgSender, bool delegateCall) external;", + "visibility": "external", + "mutability": "", + "signature": "prank(address,bool)", + "selector": "0xa7f8bf5c", + "selectorBytes": [ + 167, + 248, + 191, + 92 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "prank_3", + "description": "Sets the *next* delegate call's `msg.sender` to be the input address, and the `tx.origin` to be the second input.", + "declaration": "function prank(address msgSender, address txOrigin, bool delegateCall) external;", + "visibility": "external", + "mutability": "", + "signature": "prank(address,address,bool)", + "selector": "0x7d73d042", + "selectorBytes": [ + 125, + 115, + 208, + 66 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "prevrandao_0", @@ -9288,6 +9328,46 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "startPrank_2", + "description": "Sets all subsequent delegate calls' `msg.sender` to be the input address until `stopPrank` is called.", + "declaration": "function startPrank(address msgSender, bool delegateCall) external;", + "visibility": "external", + "mutability": "", + "signature": "startPrank(address,bool)", + "selector": "0x1cc0b435", + "selectorBytes": [ + 28, + 192, + 180, + 53 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "startPrank_3", + "description": "Sets all subsequent delegate calls' `msg.sender` to be the input address until `stopPrank` is called, and the `tx.origin` to be the second input.", + "declaration": "function startPrank(address msgSender, address txOrigin, bool delegateCall) external;", + "visibility": "external", + "mutability": "", + "signature": "startPrank(address,address,bool)", + "selector": "0x4eb859b5", + "selectorBytes": [ + 78, + 184, + 89, + 181 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "startSnapshotGas_0", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 3c11cd53aca3..d703f48e7ae5 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -600,6 +600,22 @@ interface Vm { #[cheatcode(group = Evm, safety = Unsafe)] function startPrank(address msgSender, address txOrigin) external; + /// Sets the *next* delegate call's `msg.sender` to be the input address. + #[cheatcode(group = Evm, safety = Unsafe)] + function prank(address msgSender, bool delegateCall) external; + + /// Sets all subsequent delegate calls' `msg.sender` to be the input address until `stopPrank` is called. + #[cheatcode(group = Evm, safety = Unsafe)] + function startPrank(address msgSender, bool delegateCall) external; + + /// Sets the *next* delegate call's `msg.sender` to be the input address, and the `tx.origin` to be the second input. + #[cheatcode(group = Evm, safety = Unsafe)] + function prank(address msgSender, address txOrigin, bool delegateCall) external; + + /// Sets all subsequent delegate calls' `msg.sender` to be the input address until `stopPrank` is called, and the `tx.origin` to be the second input. + #[cheatcode(group = Evm, safety = Unsafe)] + function startPrank(address msgSender, address txOrigin, bool delegateCall) external; + /// Resets subsequent calls' `msg.sender` to be `address(this)`. #[cheatcode(group = Evm, safety = Unsafe)] function stopPrank() external; diff --git a/crates/cheatcodes/src/evm/prank.rs b/crates/cheatcodes/src/evm/prank.rs index a310e28e515b..1d7ca5a07947 100644 --- a/crates/cheatcodes/src/evm/prank.rs +++ b/crates/cheatcodes/src/evm/prank.rs @@ -16,6 +16,8 @@ pub struct Prank { pub depth: u64, /// Whether the prank stops by itself after the next call pub single_call: bool, + /// Whether the prank should be be applied to delegate call + pub delegate_call: bool, /// Whether the prank has been used yet (false if unused) pub used: bool, } @@ -29,8 +31,18 @@ impl Prank { new_origin: Option
, depth: u64, single_call: bool, + delegate_call: bool, ) -> Self { - Self { prank_caller, prank_origin, new_caller, new_origin, depth, single_call, used: false } + Self { + prank_caller, + prank_origin, + new_caller, + new_origin, + depth, + single_call, + delegate_call, + used: false, + } } /// Apply the prank by setting `used` to true iff it is false @@ -47,28 +59,56 @@ impl Prank { impl Cheatcode for prank_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { msgSender } = self; - prank(ccx, msgSender, None, true) + prank(ccx, msgSender, None, true, false) } } impl Cheatcode for startPrank_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { msgSender } = self; - prank(ccx, msgSender, None, false) + prank(ccx, msgSender, None, false, false) } } impl Cheatcode for prank_1Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { msgSender, txOrigin } = self; - prank(ccx, msgSender, Some(txOrigin), true) + prank(ccx, msgSender, Some(txOrigin), true, false) } } impl Cheatcode for startPrank_1Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { msgSender, txOrigin } = self; - prank(ccx, msgSender, Some(txOrigin), false) + prank(ccx, msgSender, Some(txOrigin), false, false) + } +} + +impl Cheatcode for prank_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { msgSender, delegateCall } = self; + prank(ccx, msgSender, None, true, *delegateCall) + } +} + +impl Cheatcode for startPrank_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { msgSender, delegateCall } = self; + prank(ccx, msgSender, None, false, *delegateCall) + } +} + +impl Cheatcode for prank_3Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { msgSender, txOrigin, delegateCall } = self; + prank(ccx, msgSender, Some(txOrigin), true, *delegateCall) + } +} + +impl Cheatcode for startPrank_3Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { msgSender, txOrigin, delegateCall } = self; + prank(ccx, msgSender, Some(txOrigin), false, *delegateCall) } } @@ -85,6 +125,7 @@ fn prank( new_caller: &Address, new_origin: Option<&Address>, single_call: bool, + delegate_call: bool, ) -> Result { let prank = Prank::new( ccx.caller, @@ -93,8 +134,15 @@ fn prank( new_origin.copied(), ccx.ecx.journaled_state.depth(), single_call, + delegate_call, ); + // Ensure that code exists at `msg.sender` if delegate calling. + if delegate_call { + let code = ccx.code(*new_caller)?; + ensure!(!code.is_empty(), "cannot `prank` delegate call from an EOA"); + } + if let Some(Prank { used, single_call: current_single_call, .. }) = ccx.state.prank { ensure!(used, "cannot overwrite a prank until it is applied at least once"); // This case can only fail if the user calls `vm.startPrank` and then `vm.prank` later on. diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 684f5eb110d2..046a495a10e8 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -42,7 +42,7 @@ use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner}; use rand::Rng; use revm::{ interpreter::{ - opcode as op, CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, + opcode as op, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, EOFCreateInputs, EOFCreateKind, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }, @@ -941,6 +941,19 @@ where { // Apply our prank if let Some(prank) = &self.prank { + // Apply delegate call, `call.caller`` will not equal `prank.prank_caller` + if let CallScheme::DelegateCall | CallScheme::ExtDelegateCall = call.scheme { + if prank.delegate_call { + call.target_address = prank.new_caller; + call.caller = prank.new_caller; + let acc = ecx.journaled_state.account(prank.new_caller); + call.value = CallValue::Apparent(acc.info.balance); + if let Some(new_origin) = prank.new_origin { + ecx.env.tx.caller = new_origin; + } + } + } + if ecx.journaled_state.depth() >= prank.depth && call.caller == prank.prank_caller { let mut prank_applied = false; diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index d3a377f403ef..2004c44563d0 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -353,6 +353,8 @@ interface Vm { function pauseTracing() external view; function prank(address msgSender) external; function prank(address msgSender, address txOrigin) external; + function prank(address msgSender, bool delegateCall) external; + function prank(address msgSender, address txOrigin, bool delegateCall) external; function prevrandao(bytes32 newPrevrandao) external; function prevrandao(uint256 newPrevrandao) external; function projectRoot() external view returns (string memory path); @@ -458,6 +460,8 @@ interface Vm { function startMappingRecording() external; function startPrank(address msgSender) external; function startPrank(address msgSender, address txOrigin) external; + function startPrank(address msgSender, bool delegateCall) external; + function startPrank(address msgSender, address txOrigin, bool delegateCall) external; function startSnapshotGas(string calldata name) external; function startSnapshotGas(string calldata group, string calldata name) external; function startStateDiffRecording() external; diff --git a/testdata/default/cheats/Prank.t.sol b/testdata/default/cheats/Prank.t.sol index d833c0513d83..130e819606a2 100644 --- a/testdata/default/cheats/Prank.t.sol +++ b/testdata/default/cheats/Prank.t.sol @@ -85,9 +85,123 @@ contract NestedPranker { } } +contract ImplementationTest { + uint256 public num; + address public sender; + + function assertCorrectCaller(address expectedSender) public { + require(msg.sender == expectedSender); + } + + function assertCorrectOrigin(address expectedOrigin) public { + require(tx.origin == expectedOrigin); + } + + function setNum(uint256 _num) public { + num = _num; + } +} + +contract ProxyTest { + uint256 public num; + address public sender; +} + contract PrankTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); + function testPrankDelegateCallPrank2() public { + ProxyTest proxy = new ProxyTest(); + ImplementationTest impl = new ImplementationTest(); + vm.prank(address(proxy), true); + + // Assert correct `msg.sender` + (bool success,) = + address(impl).delegatecall(abi.encodeWithSignature("assertCorrectCaller(address)", address(proxy))); + require(success, "prank2: delegate call failed assertCorrectCaller"); + + // Assert storage updates + uint256 num = 42; + vm.prank(address(proxy), true); + (bool successTwo,) = address(impl).delegatecall(abi.encodeWithSignature("setNum(uint256)", num)); + require(successTwo, "prank2: delegate call failed setNum"); + require(proxy.num() == num, "prank2: proxy's storage was not set correctly"); + vm.stopPrank(); + } + + function testPrankDelegateCallStartPrank2() public { + ProxyTest proxy = new ProxyTest(); + ImplementationTest impl = new ImplementationTest(); + vm.startPrank(address(proxy), true); + + // Assert correct `msg.sender` + (bool success,) = + address(impl).delegatecall(abi.encodeWithSignature("assertCorrectCaller(address)", address(proxy))); + require(success, "startPrank2: delegate call failed assertCorrectCaller"); + + // Assert storage updates + uint256 num = 42; + (bool successTwo,) = address(impl).delegatecall(abi.encodeWithSignature("setNum(uint256)", num)); + require(successTwo, "startPrank2: delegate call failed setNum"); + require(proxy.num() == num, "startPrank2: proxy's storage was not set correctly"); + vm.stopPrank(); + } + + function testPrankDelegateCallPrank3(address origin) public { + ProxyTest proxy = new ProxyTest(); + ImplementationTest impl = new ImplementationTest(); + vm.prank(address(proxy), origin, true); + + // Assert correct `msg.sender` + (bool success,) = + address(impl).delegatecall(abi.encodeWithSignature("assertCorrectCaller(address)", address(proxy))); + require(success, "prank3: delegate call failed assertCorrectCaller"); + + // Assert correct `tx.origin` + vm.prank(address(proxy), origin, true); + (bool successTwo,) = address(impl).delegatecall(abi.encodeWithSignature("assertCorrectOrigin(address)", origin)); + require(successTwo, "prank3: delegate call failed assertCorrectOrigin"); + + // Assert storage updates + uint256 num = 42; + vm.prank(address(proxy), address(origin), true); + (bool successThree,) = address(impl).delegatecall(abi.encodeWithSignature("setNum(uint256)", num)); + require(successThree, "prank3: delegate call failed setNum"); + require(proxy.num() == num, "prank3: proxy's storage was not set correctly"); + vm.stopPrank(); + } + + function testPrankDelegateCallStartPrank3(address origin) public { + ProxyTest proxy = new ProxyTest(); + ImplementationTest impl = new ImplementationTest(); + vm.startPrank(address(proxy), origin, true); + + // Assert correct `msg.sender` + (bool success,) = + address(impl).delegatecall(abi.encodeWithSignature("assertCorrectCaller(address)", address(proxy))); + require(success, "startPrank3: delegate call failed assertCorrectCaller"); + + // Assert correct `tx.origin` + (bool successTwo,) = address(impl).delegatecall(abi.encodeWithSignature("assertCorrectOrigin(address)", origin)); + require(successTwo, "startPrank3: delegate call failed assertCorrectOrigin"); + + // Assert storage updates + uint256 num = 42; + (bool successThree,) = address(impl).delegatecall(abi.encodeWithSignature("setNum(uint256)", num)); + require(successThree, "startPrank3: delegate call failed setNum"); + require(proxy.num() == num, "startPrank3: proxy's storage was not set correctly"); + vm.stopPrank(); + } + + function testFailPrankDelegateCallToEOA() public { + uint256 privateKey = uint256(keccak256(abi.encodePacked("alice"))); + address alice = vm.addr(privateKey); + ImplementationTest impl = new ImplementationTest(); + vm.prank(alice, true); + // Should fail when EOA pranked with delegatecall. + address(impl).delegatecall(abi.encodeWithSignature("assertCorrectCaller(address)", alice)); + } + function testPrankSender(address sender) public { // Perform the prank Victim victim = new Victim(); From 9d7557fcf0f758ea0e8ef5d2db853bd1e1d660dc Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:29:06 +0200 Subject: [PATCH 03/68] feat(forge build): err if no source file in specified paths (#9329) --- crates/forge/bin/cmd/build.rs | 3 +++ crates/forge/tests/cli/cmd.rs | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index f2a1891e2634..f803f45f6cf0 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -94,6 +94,9 @@ impl BuildArgs { let path = if joined.exists() { &joined } else { path }; files.extend(source_files_iter(path, MultiCompilerLanguage::FILE_EXTENSIONS)); } + if files.is_empty() { + eyre::bail!("No source files found in specified build paths.") + } } let format_json = shell::is_json(); diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index d4f3973a337a..e2e38cc3c1f1 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -2829,7 +2829,7 @@ Compiler run successful! "#]]); // Expect compilation to be skipped as no files have changed - cmd.arg("build").assert_success().stdout_eq(str![[r#" + cmd.forge_fuse().arg("build").assert_success().stdout_eq(str![[r#" No files changed, compilation skipped "#]]); @@ -2952,6 +2952,14 @@ Compiler run successful! [SOLC_VERSION] [ELAPSED] Compiler run successful! +"#]]); + + // Fail if no source file found. + prj.clear(); + cmd.forge_fuse(); + cmd.args(["build", "test/Dummy.sol", "--force"]).assert_failure().stderr_eq(str![[r#" +Error: No source files found in specified build paths. + "#]]); }); From a79dfaed6fc6f88cda5f314a25d1b484d9d8c051 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:01:01 +0100 Subject: [PATCH 04/68] feat(`forge build`): add `--sizes` and `--names` JSON compatibility (#9321) * add --sizes and --names JSON compatibility + generalize report kind * add additional json output tests * fix feedback nit --- crates/common/Cargo.toml | 2 +- crates/common/src/compile.rs | 91 +++++++++++++++++++++++++------- crates/common/src/lib.rs | 1 + crates/common/src/reports.rs | 19 +++++++ crates/forge/bin/cmd/build.rs | 3 +- crates/forge/bin/cmd/test/mod.rs | 3 +- crates/forge/src/gas_report.rs | 37 ++++++------- crates/forge/tests/cli/build.rs | 45 ++++++++++++++++ crates/forge/tests/cli/cmd.rs | 19 +++++++ 9 files changed, 175 insertions(+), 45 deletions(-) create mode 100644 crates/common/src/reports.rs diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index af95e94bcc35..8fa745a13982 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -54,7 +54,7 @@ num-format.workspace = true reqwest.workspace = true semver.workspace = true serde_json.workspace = true -serde.workspace = true +serde = { workspace = true, features = ["derive"] } thiserror.workspace = true tokio.workspace = true tracing.workspace = true diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 4c7e7ba0fcef..507f328307c5 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -1,6 +1,11 @@ //! Support for compiling [foundry_compilers::Project] -use crate::{term::SpinnerReporter, TestFunctionExt}; +use crate::{ + reports::{report_kind, ReportKind}, + shell, + term::SpinnerReporter, + TestFunctionExt, +}; use comfy_table::{presets::ASCII_MARKDOWN, Attribute, Cell, CellAlignment, Color, Table}; use eyre::Result; use foundry_block_explorers::contract::Metadata; @@ -181,11 +186,13 @@ impl ProjectCompiler { } if !quiet { - if output.is_unchanged() { - sh_println!("No files changed, compilation skipped")?; - } else { - // print the compiler output / warnings - sh_println!("{output}")?; + if !shell::is_json() { + if output.is_unchanged() { + sh_println!("No files changed, compilation skipped")?; + } else { + // print the compiler output / warnings + sh_println!("{output}")?; + } } self.handle_output(&output); @@ -205,26 +212,32 @@ impl ProjectCompiler { for (name, (_, version)) in output.versioned_artifacts() { artifacts.entry(version).or_default().push(name); } - for (version, names) in artifacts { - let _ = sh_println!( - " compiler version: {}.{}.{}", - version.major, - version.minor, - version.patch - ); - for name in names { - let _ = sh_println!(" - {name}"); + + if shell::is_json() { + let _ = sh_println!("{}", serde_json::to_string(&artifacts).unwrap()); + } else { + for (version, names) in artifacts { + let _ = sh_println!( + " compiler version: {}.{}.{}", + version.major, + version.minor, + version.patch + ); + for name in names { + let _ = sh_println!(" - {name}"); + } } } } if print_sizes { // add extra newline if names were already printed - if print_names { + if print_names && !shell::is_json() { let _ = sh_println!(); } - let mut size_report = SizeReport { contracts: BTreeMap::new() }; + let mut size_report = + SizeReport { report_kind: report_kind(), contracts: BTreeMap::new() }; let artifacts: BTreeMap<_, _> = output .artifact_ids() @@ -278,6 +291,8 @@ const CONTRACT_INITCODE_SIZE_LIMIT: usize = 49152; /// Contracts with info about their size pub struct SizeReport { + /// What kind of report to generate. + report_kind: ReportKind, /// `contract name -> info` pub contracts: BTreeMap, } @@ -316,6 +331,43 @@ impl SizeReport { impl Display for SizeReport { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self.report_kind { + ReportKind::Markdown => { + let table = self.format_table_output(); + writeln!(f, "{table}")?; + } + ReportKind::JSON => { + writeln!(f, "{}", self.format_json_output())?; + } + } + + Ok(()) + } +} + +impl SizeReport { + fn format_json_output(&self) -> String { + let contracts = self + .contracts + .iter() + .filter(|(_, c)| !c.is_dev_contract && (c.runtime_size > 0 || c.init_size > 0)) + .map(|(name, contract)| { + ( + name.clone(), + serde_json::json!({ + "runtime_size": contract.runtime_size, + "init_size": contract.init_size, + "runtime_margin": CONTRACT_RUNTIME_SIZE_LIMIT as isize - contract.runtime_size as isize, + "init_margin": CONTRACT_INITCODE_SIZE_LIMIT as isize - contract.init_size as isize, + }), + ) + }) + .collect::>(); + + serde_json::to_string(&contracts).unwrap() + } + + fn format_table_output(&self) -> Table { let mut table = Table::new(); table.load_preset(ASCII_MARKDOWN); table.set_header([ @@ -366,8 +418,7 @@ impl Display for SizeReport { ]); } - writeln!(f, "{table}")?; - Ok(()) + table } } @@ -476,7 +527,7 @@ pub fn etherscan_project( /// Configures the reporter and runs the given closure. pub fn with_compilation_reporter(quiet: bool, f: impl FnOnce() -> O) -> O { #[allow(clippy::collapsible_else_if)] - let reporter = if quiet { + let reporter = if quiet || shell::is_json() { Report::new(NoReporter::default()) } else { if std::io::stdout().is_terminal() { diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 68559d081b0a..259e8fee2dac 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -26,6 +26,7 @@ pub mod errors; pub mod evm; pub mod fs; pub mod provider; +pub mod reports; pub mod retry; pub mod selectors; pub mod serde_helpers; diff --git a/crates/common/src/reports.rs b/crates/common/src/reports.rs new file mode 100644 index 000000000000..adbdc11bf66a --- /dev/null +++ b/crates/common/src/reports.rs @@ -0,0 +1,19 @@ +use serde::{Deserialize, Serialize}; + +use crate::shell; + +#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub enum ReportKind { + #[default] + Markdown, + JSON, +} + +/// Determine the kind of report to generate based on the current shell. +pub fn report_kind() -> ReportKind { + if shell::is_json() { + ReportKind::JSON + } else { + ReportKind::Markdown + } +} diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index f803f45f6cf0..3efe68db7722 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -105,12 +105,11 @@ impl BuildArgs { .print_names(self.names) .print_sizes(self.sizes) .ignore_eip_3860(self.ignore_eip_3860) - .quiet(format_json) .bail(!format_json); let output = compiler.compile(&project)?; - if format_json { + if format_json && !self.names && !self.sizes { sh_println!("{}", serde_json::to_string_pretty(&output.output())?)?; } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 77ae5d2631f6..613d570785b9 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -5,7 +5,7 @@ use clap::{Parser, ValueHint}; use eyre::{Context, OptionExt, Result}; use forge::{ decode::decode_console_logs, - gas_report::{GasReport, GasReportKind}, + gas_report::GasReport, multi_runner::matches_contract, result::{SuiteResult, TestOutcome, TestStatus}, traces::{ @@ -583,7 +583,6 @@ impl TestArgs { config.gas_reports.clone(), config.gas_reports_ignore.clone(), config.gas_reports_include_tests, - if shell::is_json() { GasReportKind::JSON } else { GasReportKind::Markdown }, ) }); diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index e3e44994b91c..7e87af077029 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -6,31 +6,23 @@ use crate::{ }; use alloy_primitives::map::HashSet; use comfy_table::{presets::ASCII_MARKDOWN, *}; -use foundry_common::{calc, TestFunctionExt}; +use foundry_common::{ + calc, + reports::{report_kind, ReportKind}, + TestFunctionExt, +}; use foundry_evm::traces::CallKind; use serde::{Deserialize, Serialize}; use serde_json::json; use std::{collections::BTreeMap, fmt::Display}; -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub enum GasReportKind { - Markdown, - JSON, -} - -impl Default for GasReportKind { - fn default() -> Self { - Self::Markdown - } -} - /// Represents the gas report for a set of contracts. #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct GasReport { /// Whether to report any contracts. report_any: bool, /// What kind of report to generate. - report_type: GasReportKind, + report_kind: ReportKind, /// Contracts to generate the report for. report_for: HashSet, /// Contracts to ignore when generating the report. @@ -47,13 +39,18 @@ impl GasReport { report_for: impl IntoIterator, ignore: impl IntoIterator, include_tests: bool, - report_kind: GasReportKind, ) -> Self { let report_for = report_for.into_iter().collect::>(); let ignore = ignore.into_iter().collect::>(); let report_any = report_for.is_empty() || report_for.contains("*"); - let report_type = report_kind; - Self { report_any, report_type, report_for, ignore, include_tests, ..Default::default() } + Self { + report_any, + report_kind: report_kind(), + report_for, + ignore, + include_tests, + ..Default::default() + } } /// Whether the given contract should be reported. @@ -158,8 +155,8 @@ impl GasReport { impl Display for GasReport { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - match self.report_type { - GasReportKind::Markdown => { + match self.report_kind { + ReportKind::Markdown => { for (name, contract) in &self.contracts { if contract.functions.is_empty() { trace!(name, "gas report contract without functions"); @@ -171,7 +168,7 @@ impl Display for GasReport { writeln!(f, "\n")?; } } - GasReportKind::JSON => { + ReportKind::JSON => { writeln!(f, "{}", &self.format_json_output())?; } } diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 4b257fe8d20a..dd2545995753 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -82,6 +82,20 @@ forgetest!(initcode_size_exceeds_limit, |prj, cmd| { ... "# ]); + + cmd.forge_fuse().args(["build", "--sizes", "--json"]).assert_failure().stdout_eq( + str![[r#" +{ + "HugeContract":{ + "runtime_size":202, + "init_size":49359, + "runtime_margin":24374, + "init_margin":-207 + } +} +"#]] + .is_json(), + ); }); forgetest!(initcode_size_limit_can_be_ignored, |prj, cmd| { @@ -95,6 +109,23 @@ forgetest!(initcode_size_limit_can_be_ignored, |prj, cmd| { ... "# ]); + + cmd.forge_fuse() + .args(["build", "--sizes", "--ignore-eip-3860", "--json"]) + .assert_success() + .stdout_eq( + str![[r#" +{ + "HugeContract": { + "runtime_size": 202, + "init_size": 49359, + "runtime_margin": 24374, + "init_margin": -207 + } +} +"#]] + .is_json(), + ); }); // tests build output is as expected @@ -118,6 +149,20 @@ forgetest_init!(build_sizes_no_forge_std, |prj, cmd| { ... "# ]); + + cmd.forge_fuse().args(["build", "--sizes", "--json"]).assert_success().stdout_eq( + str![[r#" +{ + "Counter": { + "runtime_size": 247, + "init_size": 277, + "runtime_margin": 24329, + "init_margin": 48875 + } +} +"#]] + .is_json(), + ); }); // tests that skip key in config can be used to skip non-compilable contract diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index e2e38cc3c1f1..1e887791e569 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -2977,6 +2977,20 @@ Compiler run successful! "#]]); + + cmd.forge_fuse().args(["build", "--sizes", "--json"]).assert_success().stdout_eq( + str![[r#" +{ + "Counter": { + "runtime_size": 247, + "init_size": 277, + "runtime_margin": 24329, + "init_margin": 48875 + } +} +"#]] + .is_json(), + ); }); // checks that build --names includes all contracts even if unchanged @@ -2992,6 +3006,11 @@ Compiler run successful! ... "#]]); + + cmd.forge_fuse() + .args(["build", "--names", "--json"]) + .assert_success() + .stdout_eq(str![[r#""{...}""#]].is_json()); }); // From 3eb47ea41ba0b26f0c97ce20cad177a43f55d3b5 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Sat, 16 Nov 2024 12:12:14 +0200 Subject: [PATCH 05/68] chore: fix clippy (#9333) --- crates/cast/bin/tx.rs | 2 +- crates/cheatcodes/src/json.rs | 2 +- crates/cheatcodes/src/test/expect.rs | 2 +- crates/common/src/provider/mod.rs | 2 +- crates/evm/core/src/backend/mod.rs | 2 +- crates/evm/core/src/utils.rs | 2 +- crates/evm/coverage/src/anchors.rs | 2 +- crates/fmt/src/comments.rs | 2 +- crates/fmt/src/formatter.rs | 12 ++++++------ crates/forge/bin/cmd/init.rs | 2 +- crates/forge/src/runner.rs | 2 +- crates/script/src/transaction.rs | 2 +- crates/script/src/verify.rs | 2 +- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index a6f64b3db2d0..29f8e2435186 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -252,7 +252,7 @@ where }; if self.state.to.is_none() && code.is_none() { - let has_value = self.tx.value.map_or(false, |v| !v.is_zero()); + let has_value = self.tx.value.is_some_and(|v| !v.is_zero()); let has_auth = self.auth.is_some(); // We only allow user to omit the recipient address if transaction is an EIP-7702 tx // without a value. diff --git a/crates/cheatcodes/src/json.rs b/crates/cheatcodes/src/json.rs index 0908f247ca94..6ad36e474290 100644 --- a/crates/cheatcodes/src/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -682,7 +682,7 @@ mod tests { match value { DynSolValue::Tuple(_) | DynSolValue::CustomStruct { .. } => true, DynSolValue::Array(v) | DynSolValue::FixedArray(v) => { - v.first().map_or(false, contains_tuple) + v.first().is_some_and(contains_tuple) } _ => false, } diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 602ac512514e..3ee58407a7e6 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -633,7 +633,7 @@ pub(crate) fn handle_expect_emit( return false } // Maybe match source address. - if event_to_fill_or_check.address.map_or(false, |addr| addr != log.address) { + if event_to_fill_or_check.address.is_some_and(|addr| addr != log.address) { return false; } // Maybe match data. diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index 4fb9e29aec0c..75efdb869f9f 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -137,7 +137,7 @@ impl ProviderBuilder { .wrap_err_with(|| format!("invalid provider URL: {url_str:?}")); // Use the final URL string to guess if it's a local URL. - let is_local = url.as_ref().map_or(false, |url| guess_local_url(url.as_str())); + let is_local = url.as_ref().is_ok_and(|url| guess_local_url(url.as_str())); Self { url, diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index c9bb89047181..e70468ef944c 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1972,7 +1972,7 @@ pub fn update_state( persistent_accounts: Option<&HashSet
>, ) -> Result<(), DB::Error> { for (addr, acc) in state.iter_mut() { - if !persistent_accounts.map_or(false, |accounts| accounts.contains(addr)) { + if !persistent_accounts.is_some_and(|accounts| accounts.contains(addr)) { acc.info = db.basic(*addr)?.unwrap_or_default(); for (key, val) in acc.storage.iter_mut() { val.present_value = db.storage(*addr, *key)?; diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index bd17aae44292..d76cd087ce16 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -236,7 +236,7 @@ pub fn create2_handler_register( if create2_overrides_inner .borrow() .last() - .map_or(false, |(depth, _)| *depth == ctx.evm.journaled_state.depth()) + .is_some_and(|(depth, _)| *depth == ctx.evm.journaled_state.depth()) { let (_, call_inputs) = create2_overrides_inner.borrow_mut().pop().unwrap(); outcome = ctx.external.call_end(&mut ctx.evm, &call_inputs, outcome); diff --git a/crates/evm/coverage/src/anchors.rs b/crates/evm/coverage/src/anchors.rs index f42fd1a62726..6643524d61de 100644 --- a/crates/evm/coverage/src/anchors.rs +++ b/crates/evm/coverage/src/anchors.rs @@ -171,7 +171,7 @@ pub fn find_anchor_branch( /// Calculates whether `element` is within the range of the target `location`. fn is_in_source_range(element: &SourceElement, location: &SourceLocation) -> bool { // Source IDs must match. - let source_ids_match = element.index().map_or(false, |a| a as usize == location.source_id); + let source_ids_match = element.index().is_some_and(|a| a as usize == location.source_id); if !source_ids_match { return false; } diff --git a/crates/fmt/src/comments.rs b/crates/fmt/src/comments.rs index 90054f1325ee..e3fb79043ec6 100644 --- a/crates/fmt/src/comments.rs +++ b/crates/fmt/src/comments.rs @@ -112,7 +112,7 @@ impl CommentWithMetadata { // line has something // check if the last comment after code was a postfix comment if last_comment - .map_or(false, |last| last.loc.end() > code_end && !last.is_prefix()) + .is_some_and(|last| last.loc.end() > code_end && !last.is_prefix()) { // get the indent size of the next item of code let next_indent_len = src[comment.loc().end()..] diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 6bb814e23269..8f52d9e259b8 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -88,7 +88,7 @@ struct Context { impl Context { /// Returns true if the current function context is the constructor pub(crate) fn is_constructor_function(&self) -> bool { - self.function.as_ref().map_or(false, |f| matches!(f.ty, FunctionTy::Constructor)) + self.function.as_ref().is_some_and(|f| matches!(f.ty, FunctionTy::Constructor)) } } @@ -341,7 +341,7 @@ impl<'a, W: Write> Formatter<'a, W> { _ => stmt.loc().start(), }; - self.find_next_line(start_from).map_or(false, |loc| loc >= end_at) + self.find_next_line(start_from).is_some_and(|loc| loc >= end_at) } } } @@ -560,7 +560,7 @@ impl<'a, W: Write> Formatter<'a, W> { fn write_doc_block_line(&mut self, comment: &CommentWithMetadata, line: &str) -> Result<()> { if line.trim().starts_with('*') { let line = line.trim().trim_start_matches('*'); - let needs_space = line.chars().next().map_or(false, |ch| !ch.is_whitespace()); + let needs_space = line.chars().next().is_some_and(|ch| !ch.is_whitespace()); write!(self.buf(), " *{}", if needs_space { " " } else { "" })?; self.write_comment_line(comment, line)?; self.write_whitespace_separator(true)?; @@ -1945,7 +1945,7 @@ impl Visitor for Formatter<'_, W> { )?; // EOF newline - if self.last_char().map_or(true, |char| char != '\n') { + if self.last_char() != Some('\n') { writeln!(self.buf())?; } @@ -3259,7 +3259,7 @@ impl Visitor for Formatter<'_, W> { // we can however check if the contract `is` the `base`, this however also does // not cover all cases - let is_contract_base = self.context.contract.as_ref().map_or(false, |contract| { + let is_contract_base = self.context.contract.as_ref().is_some_and(|contract| { contract.base.iter().any(|contract_base| { contract_base .name @@ -3280,7 +3280,7 @@ impl Visitor for Formatter<'_, W> { let mut base_or_modifier = self.visit_to_chunk(loc.start(), Some(loc.end()), base)?; let is_lowercase = - base_or_modifier.content.chars().next().map_or(false, |c| c.is_lowercase()); + base_or_modifier.content.chars().next().is_some_and(|c| c.is_lowercase()); if is_lowercase && base_or_modifier.content.ends_with("()") { base_or_modifier.content.truncate(base_or_modifier.content.len() - 2); } diff --git a/crates/forge/bin/cmd/init.rs b/crates/forge/bin/cmd/init.rs index 82f296e417e9..472d575bd9a8 100644 --- a/crates/forge/bin/cmd/init.rs +++ b/crates/forge/bin/cmd/init.rs @@ -88,7 +88,7 @@ impl InitArgs { } } else { // if target is not empty - if root.read_dir().map_or(false, |mut i| i.next().is_some()) { + if root.read_dir().is_ok_and(|mut i| i.next().is_some()) { if !force { eyre::bail!( "Cannot run `init` on a non-empty directory.\n\ diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index d0f4f579e537..8ed3706defa8 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -296,7 +296,7 @@ impl ContractRunner<'_> { warnings, ) } - let call_after_invariant = after_invariant_fns.first().map_or(false, |after_invariant_fn| { + let call_after_invariant = after_invariant_fns.first().is_some_and(|after_invariant_fn| { let match_sig = after_invariant_fn.name == "afterInvariant"; if !match_sig { warnings.push(format!( diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index fcd4eefc36d4..ca6a6226949c 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -152,7 +152,7 @@ impl ScriptTransactionBuilder { // Add the additional contracts created in this transaction, so we can verify them later. created_contracts.retain(|contract| { // Filter out the contract that was created by the transaction itself. - self.transaction.contract_address.map_or(true, |addr| addr != contract.address) + self.transaction.contract_address != Some(contract.address) }); self.transaction.additional_contracts = created_contracts; diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index c013ee8628b9..55435c559242 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -116,7 +116,7 @@ impl VerifyBundle { if data.split_at(create2_offset).1.starts_with(bytecode) { let constructor_args = data.split_at(create2_offset + bytecode.len()).1.to_vec(); - if artifact.source.extension().map_or(false, |e| e.to_str() == Some("vy")) { + if artifact.source.extension().is_some_and(|e| e.to_str() == Some("vy")) { warn!("Skipping verification of Vyper contract: {}", artifact.name); } From d14c09f15a9849fe177d097451919810e5877617 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 16 Nov 2024 13:27:41 +0100 Subject: [PATCH 06/68] test: enhance tests (#9334) * test: enhance tests * update ws url * Assert json unordered * Update crates/test-utils/src/util.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Changes after review * Fix rpc url test --------- Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cast/tests/cli/main.rs | 9 +++------ crates/test-utils/src/rpc.rs | 5 ++++- crates/test-utils/src/util.rs | 11 ++++++++++- testdata/default/cheats/RpcUrls.t.sol | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index bec345b1710c..a88369e97ffe 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -650,8 +650,7 @@ casttest!(rpc_with_args, |_prj, cmd| { // Call `cast rpc eth_getBlockByNumber 0x123 false` cmd.args(["rpc", "--rpc-url", eth_rpc_url.as_str(), "eth_getBlockByNumber", "0x123", "false"]) - .assert_success() - .stdout_eq(str![[r#" + .assert_json_stdout(str![[r#" {"number":"0x123","hash":"0xc5dab4e189004a1312e9db43a40abb2de91ad7dd25e75880bf36016d8e9df524","transactions":[],"totalDifficulty":"0x4dea420908b","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","extraData":"0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32","nonce":"0x29d6547c196e00e0","miner":"0xbb7b8287f3f0a933474a79eae42cbca977791171","difficulty":"0x494433b31","gasLimit":"0x1388","gasUsed":"0x0","uncles":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x220","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","stateRoot":"0x3fe6bd17aa85376c7d566df97d9f2e536f37f7a87abb3a6f9e2891cf9442f2e4","mixHash":"0x943056aa305aa6d22a3c06110942980342d1f4d4b11c17711961436a0f963ea0","parentHash":"0x7abfd11e862ccde76d6ea8ee20978aac26f4bcb55de1188cc0335be13e817017","timestamp":"0x55ba4564"} "#]]); @@ -670,8 +669,7 @@ casttest!(rpc_raw_params, |_prj, cmd| { "--raw", r#"["0x123", false]"#, ]) - .assert_success() - .stdout_eq(str![[r#" + .assert_json_stdout(str![[r#" {"number":"0x123","hash":"0xc5dab4e189004a1312e9db43a40abb2de91ad7dd25e75880bf36016d8e9df524","transactions":[],"totalDifficulty":"0x4dea420908b","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","extraData":"0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32","nonce":"0x29d6547c196e00e0","miner":"0xbb7b8287f3f0a933474a79eae42cbca977791171","difficulty":"0x494433b31","gasLimit":"0x1388","gasUsed":"0x0","uncles":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x220","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","stateRoot":"0x3fe6bd17aa85376c7d566df97d9f2e536f37f7a87abb3a6f9e2891cf9442f2e4","mixHash":"0x943056aa305aa6d22a3c06110942980342d1f4d4b11c17711961436a0f963ea0","parentHash":"0x7abfd11e862ccde76d6ea8ee20978aac26f4bcb55de1188cc0335be13e817017","timestamp":"0x55ba4564"} "#]]); @@ -687,8 +685,7 @@ casttest!(rpc_raw_params_stdin, |_prj, cmd| { stdin.write_all(b"\n[\n\"0x123\",\nfalse\n]\n").unwrap(); }, ) - .assert_success() - .stdout_eq(str![[r#" + .assert_json_stdout(str![[r#" {"number":"0x123","hash":"0xc5dab4e189004a1312e9db43a40abb2de91ad7dd25e75880bf36016d8e9df524","transactions":[],"totalDifficulty":"0x4dea420908b","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","extraData":"0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32","nonce":"0x29d6547c196e00e0","miner":"0xbb7b8287f3f0a933474a79eae42cbca977791171","difficulty":"0x494433b31","gasLimit":"0x1388","gasUsed":"0x0","uncles":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x220","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","stateRoot":"0x3fe6bd17aa85376c7d566df97d9f2e536f37f7a87abb3a6f9e2891cf9442f2e4","mixHash":"0x943056aa305aa6d22a3c06110942980342d1f4d4b11c17711961436a0f963ea0","parentHash":"0x7abfd11e862ccde76d6ea8ee20978aac26f4bcb55de1188cc0335be13e817017","timestamp":"0x55ba4564"} "#]]); diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index a974e395406d..44deda60cddf 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -10,6 +10,9 @@ use std::sync::{ // List of general purpose infura keys to rotate through static INFURA_KEYS: LazyLock> = LazyLock::new(|| { let mut keys = vec![ + "6cb19d07ca2d44f59befd61563b1037b", + "6d46c0cca653407b861f3f93f7b0236a", + "69a36846dec146e3a2898429be60be85", // "16a8be88795540b9b3903d8de0f7baa5", // "f4a0bdad42674adab5fc0ac077ffab2b", // "5c812e02193c4ba793f8c214317582bd", @@ -184,7 +187,7 @@ fn next_url(is_ws: bool, chain: NamedChain) -> String { match (is_ws, is_infura) { (false, true) => format!("https://{full}.infura.io/v3/{key}"), - (true, true) => format!("wss://{full}.infura.io/v3/{key}"), + (true, true) => format!("wss://{full}.infura.io/ws/v3/{key}"), (false, false) => format!("https://{full}.g.alchemy.com/v2/{key}"), (true, false) => format!("wss://{full}.g.alchemy.com/v2/{key}"), } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 5d242800f6f9..8d7f6cbb5fcc 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -11,7 +11,7 @@ use foundry_compilers::{ use foundry_config::Config; use parking_lot::Mutex; use regex::Regex; -use snapbox::{cmd::OutputAssert, str}; +use snapbox::{assert_data_eq, cmd::OutputAssert, str, IntoData}; use std::{ env, ffi::OsStr, @@ -893,6 +893,15 @@ impl TestCommand { self.assert().success() } + /// Runs the command and asserts that it resulted in success, with expected JSON data. + #[track_caller] + pub fn assert_json_stdout(&mut self, expected: impl IntoData) { + let expected = expected.is(snapbox::data::DataFormat::Json).unordered(); + let stdout = self.assert_success().get_output().stdout.clone(); + let actual = stdout.into_data().is(snapbox::data::DataFormat::Json).unordered(); + assert_data_eq!(actual, expected); + } + /// Runs the command and asserts that it **failed** nothing was printed to stdout. #[track_caller] pub fn assert_empty_stdout(&mut self) { diff --git a/testdata/default/cheats/RpcUrls.t.sol b/testdata/default/cheats/RpcUrls.t.sol index 7976fa572243..aaa5a00bdd97 100644 --- a/testdata/default/cheats/RpcUrls.t.sol +++ b/testdata/default/cheats/RpcUrls.t.sol @@ -10,7 +10,7 @@ contract RpcUrlTest is DSTest { // returns the correct url function testCanGetRpcUrl() public { string memory url = vm.rpcUrl("mainnet"); - assertEq(bytes(url).length, 69); + assertTrue(bytes(url).length == 61 || bytes(url).length == 69); } // returns an error if env alias does not exist From e649e62f125244a3ef116be25dfdc81a2afbaf2a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 17 Nov 2024 09:06:44 +0000 Subject: [PATCH 07/68] chore(deps): weekly `cargo update` (#9336) Locking 40 packages to latest compatible versions Updating allocator-api2 v0.2.19 -> v0.2.20 Updating alloy-dyn-abi v0.8.11 -> v0.8.12 Updating alloy-json-abi v0.8.11 -> v0.8.12 Updating alloy-primitives v0.8.11 -> v0.8.12 Updating alloy-sol-macro v0.8.11 -> v0.8.12 Updating alloy-sol-macro-expander v0.8.11 -> v0.8.12 Updating alloy-sol-macro-input v0.8.11 -> v0.8.12 Updating alloy-sol-type-parser v0.8.11 -> v0.8.12 Updating alloy-sol-types v0.8.11 -> v0.8.12 Updating aws-sdk-sts v1.49.0 -> v1.50.0 Updating axum v0.7.7 -> v0.7.9 Updating bstr v1.10.0 -> v1.11.0 Updating cc v1.1.37 -> v1.2.1 Updating clap v4.5.20 -> v4.5.21 Updating clap_builder v4.5.20 -> v4.5.21 Updating clap_complete v4.5.37 -> v4.5.38 Updating clap_lex v0.7.2 -> v0.7.3 Updating comfy-table v7.1.1 -> v7.1.3 Updating cpufeatures v0.2.14 -> v0.2.15 Removing crossterm v0.27.0 Adding diff v0.1.13 Updating flate2 v1.0.34 -> v1.0.35 Updating indicatif v0.17.8 -> v0.17.9 Adding indoc v2.0.5 Updating instability v0.3.2 -> v0.3.3 Removing instant v0.1.13 Updating libc v0.2.162 -> v0.2.164 Adding pretty_assertions v1.4.1 Updating quinn v0.11.5 -> v0.11.6 Updating quinn-proto v0.11.8 -> v0.11.9 Updating regex-automata v0.4.8 -> v0.4.9 Updating rustix v0.38.39 -> v0.38.40 Updating rustls v0.23.16 -> v0.23.17 Updating scc v2.2.4 -> v2.2.5 Updating serde v1.0.214 -> v1.0.215 Updating serde_derive v1.0.214 -> v1.0.215 Updating syn-solidity v0.8.11 -> v0.8.12 Removing thiserror v1.0.68 Adding thiserror v1.0.69 (available: v2.0.3) Adding thiserror v2.0.3 Removing thiserror-impl v1.0.68 Adding thiserror-impl v1.0.69 Adding thiserror-impl v2.0.3 Adding web-time v1.1.0 note: pass `--verbose` to see 44 unchanged dependencies behind latest Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> --- Cargo.lock | 416 +++++++++++++++++++++++++++++------------------------ 1 file changed, 228 insertions(+), 188 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af730814c936..1922c69b89af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611cc2ae7d2e242c457e4be7f97036b8ad9ca152b499f53faf99b1ed8fc2553f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "alloy-chains" @@ -118,14 +118,14 @@ dependencies = [ "alloy-transport", "futures", "futures-util", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "alloy-dyn-abi" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85132f2698b520fab3f54beed55a44389f7006a7b557a0261e1e69439dcc1572" +checksum = "ef2364c782a245cf8725ea6dbfca5f530162702b5d685992ea03ce64529136cc" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded610181f3dad5810f6ff12d1a99994cf9b42d2fcb7709029352398a5da5ae6" +checksum = "b84c506bf264110fa7e90d9924f742f40ef53c6572ea56a0b0bd714a567ed389" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -221,7 +221,7 @@ dependencies = [ "alloy-sol-types", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -243,7 +243,7 @@ dependencies = [ "async-trait", "auto_impl", "futures-utils-wasm", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -261,9 +261,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd58d377699e6cfeab52c4a9d28bdc4ef37e2bd235ff2db525071fe37a2e9af5" +checksum = "9fce5dbd6a4f118eecc4719eaa9c7ffc31c315e6c5ccde3642db927802312425" dependencies = [ "alloy-rlp", "arbitrary", @@ -326,7 +326,7 @@ dependencies = [ "schnellru", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "url", @@ -475,7 +475,7 @@ dependencies = [ "alloy-serde", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -514,7 +514,7 @@ dependencies = [ "auto_impl", "elliptic-curve", "k256", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -531,7 +531,7 @@ dependencies = [ "aws-sdk-kms", "k256", "spki", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -549,7 +549,7 @@ dependencies = [ "gcloud-sdk", "k256", "spki", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -569,7 +569,7 @@ dependencies = [ "coins-ledger", "futures-util", "semver 1.0.23", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -589,7 +589,7 @@ dependencies = [ "eth-keystore", "k256", "rand", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -604,16 +604,16 @@ dependencies = [ "alloy-signer", "async-trait", "semver 1.0.23", - "thiserror", + "thiserror 1.0.69", "tracing", "trezor-client", ] [[package]] name = "alloy-sol-macro" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a1b42ac8f45e2f49f4bcdd72cbfde0bb148f5481d403774ffa546e48b83efc1" +checksum = "9343289b4a7461ed8bab8618504c995c049c082b70c7332efd7b32125633dc05" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -625,9 +625,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06318f1778e57f36333e850aa71bd1bb5e560c10279e236622faae0470c50412" +checksum = "4222d70bec485ceccc5d8fd4f2909edd65b5d5e43d4aca0b5dcee65d519ae98f" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -644,9 +644,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaebb9b0ad61a41345a22c9279975c0cdd231b97947b10d7aad1cf0a7181e4a5" +checksum = "2e17f2677369571b976e51ea1430eb41c3690d344fef567b840bfc0b01b6f83a" dependencies = [ "alloy-json-abi", "const-hex", @@ -661,9 +661,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12c71028bfbfec210e24106a542aad3def7caf1a70e2c05710e92a98481980d3" +checksum = "aa64d80ae58ffaafdff9d5d84f58d03775f66c84433916dc9a64ed16af5755da" dependencies = [ "serde", "winnow", @@ -671,9 +671,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d7fb042d68ddfe79ccb23359de3007f6d4d53c13f703b64fb0db422132111" +checksum = "6520d427d4a8eb7aa803d852d7a52ceb0c519e784c292f64bb339e636918cf27" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -694,7 +694,7 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tower 0.5.1", "tracing", @@ -748,7 +748,7 @@ dependencies = [ "alloy-transport", "futures", "http 1.1.0", - "rustls 0.23.16", + "rustls 0.23.17", "serde_json", "tokio", "tokio-tungstenite", @@ -908,7 +908,7 @@ dependencies = [ "serde_repr", "similar-asserts", "tempfile", - "thiserror", + "thiserror 1.0.69", "tikv-jemallocator", "tokio", "tower 0.4.13", @@ -938,7 +938,7 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -964,7 +964,7 @@ dependencies = [ "pin-project 1.1.7", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio-util", "tower-http", "tracing", @@ -1394,9 +1394,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.49.0" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53dcf5e7d9bd1517b8b998e170e650047cea8a2b85fe1835abe3210713e541b7" +checksum = "6ada54e5f26ac246dc79727def52f7f8ed38915cb47781e2a72213957dc3a7d5" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1580,9 +1580,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", "axum-core", @@ -1797,12 +1797,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" dependencies = [ "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "serde", ] @@ -1920,7 +1920,7 @@ dependencies = [ "semver 1.0.23", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1998,9 +1998,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.37" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "shlex", ] @@ -2112,9 +2112,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -2122,9 +2122,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -2137,9 +2137,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11611dca53440593f38e6b25ec629de50b14cdfa63adc0fb856115a2c6d97595" +checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01" dependencies = [ "clap", ] @@ -2168,9 +2168,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "clearscreen" @@ -2180,7 +2180,7 @@ checksum = "2f8c93eb5f77c9050c7750e14f13ef1033a40a0aac70c6371535b6763a01438c" dependencies = [ "nix 0.28.0", "terminfo", - "thiserror", + "thiserror 1.0.69", "which", "winapi", ] @@ -2221,7 +2221,7 @@ dependencies = [ "k256", "serde", "sha2", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2237,7 +2237,7 @@ dependencies = [ "pbkdf2 0.12.2", "rand", "sha2", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2256,7 +2256,7 @@ dependencies = [ "serde", "sha2", "sha3", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2275,7 +2275,7 @@ dependencies = [ "log", "nix 0.26.4", "once_cell", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "wasm-bindgen", @@ -2317,14 +2317,14 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "comfy-table" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" +checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9" dependencies = [ - "crossterm 0.27.0", + "crossterm", "strum", "strum_macros", - "unicode-width 0.1.14", + "unicode-width 0.2.0", ] [[package]] @@ -2425,9 +2425,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -2475,19 +2475,6 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" -[[package]] -name = "crossterm" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" -dependencies = [ - "bitflags 2.6.0", - "crossterm_winapi", - "libc", - "parking_lot", - "winapi", -] - [[package]] name = "crossterm" version = "0.28.1" @@ -2730,10 +2717,16 @@ dependencies = [ "console", "shell-words", "tempfile", - "thiserror", + "thiserror 1.0.69", "zeroize", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.9.0" @@ -3043,7 +3036,7 @@ dependencies = [ "serde_json", "sha2", "sha3", - "thiserror", + "thiserror 1.0.69", "uuid 0.8.2", ] @@ -3060,7 +3053,7 @@ dependencies = [ "serde", "serde_json", "sha3", - "thiserror", + "thiserror 1.0.69", "uint", ] @@ -3142,7 +3135,7 @@ dependencies = [ "strum", "syn 2.0.87", "tempfile", - "thiserror", + "thiserror 1.0.69", "tiny-keccak", "unicode-xid", ] @@ -3229,7 +3222,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182f7dbc2ef73d9ef67351c5fbbea084729c48362d3ce9dd44c28e32e277fe5" dependencies = [ "libc", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -3290,9 +3283,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", @@ -3401,7 +3394,7 @@ dependencies = [ "strum", "svm-rs", "tempfile", - "thiserror", + "thiserror 1.0.69", "tikv-jemallocator", "tokio", "toml 0.8.19", @@ -3434,7 +3427,7 @@ dependencies = [ "serde", "serde_json", "solang-parser", - "thiserror", + "thiserror 1.0.69", "toml 0.8.19", "tracing", ] @@ -3449,7 +3442,7 @@ dependencies = [ "itertools 0.13.0", "similar-asserts", "solang-parser", - "thiserror", + "thiserror 1.0.69", "toml 0.8.19", "tracing", "tracing-subscriber", @@ -3591,7 +3584,7 @@ dependencies = [ "semver 1.0.23", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -3635,7 +3628,7 @@ dependencies = [ "revm-inspectors", "semver 1.0.23", "serde_json", - "thiserror", + "thiserror 1.0.69", "toml 0.8.19", "tracing", "vergen", @@ -3730,7 +3723,7 @@ dependencies = [ "serde_json", "similar-asserts", "terminal_size", - "thiserror", + "thiserror 1.0.69", "tokio", "tower 0.4.13", "tracing", @@ -3790,7 +3783,7 @@ dependencies = [ "svm-rs", "svm-rs-builds", "tempfile", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "winnow", @@ -3824,7 +3817,7 @@ dependencies = [ "serde", "serde_json", "serde_repr", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "walkdir", @@ -3864,7 +3857,7 @@ dependencies = [ "serde_json", "svm-rs", "tempfile", - "thiserror", + "thiserror 1.0.69", "tokio", "walkdir", ] @@ -3897,7 +3890,7 @@ dependencies = [ "similar-asserts", "solang-parser", "tempfile", - "thiserror", + "thiserror 1.0.69", "toml 0.8.19", "toml_edit", "tracing", @@ -3910,7 +3903,7 @@ name = "foundry-debugger" version = "0.2.0" dependencies = [ "alloy-primitives", - "crossterm 0.28.1", + "crossterm", "eyre", "foundry-common", "foundry-compilers", @@ -3946,7 +3939,7 @@ dependencies = [ "revm", "revm-inspectors", "serde", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -3991,7 +3984,7 @@ dependencies = [ "revm-inspectors", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -4033,7 +4026,7 @@ dependencies = [ "rand", "revm", "serde", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -4081,7 +4074,7 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "url", @@ -4094,7 +4087,7 @@ dependencies = [ "alloy-primitives", "foundry-compilers", "semver 1.0.23", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4155,7 +4148,7 @@ dependencies = [ "gcloud-sdk", "rpassword", "serde", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -4383,7 +4376,7 @@ dependencies = [ "gix-date", "gix-utils", "itoa", - "thiserror", + "thiserror 1.0.69", "winnow", ] @@ -4403,7 +4396,7 @@ dependencies = [ "memchr", "once_cell", "smallvec", - "thiserror", + "thiserror 1.0.69", "unicode-bom", "winnow", ] @@ -4418,7 +4411,7 @@ dependencies = [ "bstr", "gix-path", "libc", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4430,7 +4423,7 @@ dependencies = [ "bstr", "itoa", "jiff", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4478,7 +4471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93d7df7366121b5018f947a04d37f034717e113dcf9ccd85c34b58e57a74d5e" dependencies = [ "faster-hex", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4489,7 +4482,7 @@ checksum = "e3bc7fe297f1f4614774989c00ec8b1add59571dc9b024b4c00acb7dedd4e19d" dependencies = [ "gix-tempfile", "gix-utils", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4507,7 +4500,7 @@ dependencies = [ "gix-validate", "itoa", "smallvec", - "thiserror", + "thiserror 1.0.69", "winnow", ] @@ -4521,7 +4514,7 @@ dependencies = [ "gix-trace", "home", "once_cell", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4541,7 +4534,7 @@ dependencies = [ "gix-utils", "gix-validate", "memmap2", - "thiserror", + "thiserror 1.0.69", "winnow", ] @@ -4593,7 +4586,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e187b263461bc36cea17650141567753bc6207d036cedd1de6e81a52f277ff68" dependencies = [ "bstr", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4611,7 +4604,7 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -4686,7 +4679,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4957,7 +4950,7 @@ dependencies = [ "http 1.1.0", "hyper 1.5.0", "hyper-util", - "rustls 0.23.16", + "rustls 0.23.17", "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", @@ -5192,7 +5185,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "same-file", "walkdir", "winapi-util", @@ -5212,7 +5205,7 @@ dependencies = [ "normalize-path", "project-origins", "radix_trie", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -5285,17 +5278,23 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" dependencies = [ "console", - "instant", "number_prefix", "portable-atomic", - "unicode-width 0.1.14", + "unicode-width 0.2.0", + "web-time", ] +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + [[package]] name = "inferno" version = "0.11.21" @@ -5350,23 +5349,18 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" +checksum = "b829f37dead9dc39df40c2d3376c179fdfd2ac771f53f55d3c30dc096a3c0c6e" dependencies = [ + "darling", + "indoc", + "pretty_assertions", + "proc-macro2", "quote", "syn 2.0.87", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "interprocess" version = "2.2.1" @@ -5578,7 +5572,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.8", + "regex-automata 0.4.9", ] [[package]] @@ -5592,9 +5586,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.162" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "libdbus-sys" @@ -5802,7 +5796,7 @@ checksum = "d04b0347d2799ef17df4623dbcb03531031142105168e0c549e0bf1f980e9e7e" dependencies = [ "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -5813,7 +5807,7 @@ checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" dependencies = [ "cfg-if", "miette-derive", - "thiserror", + "thiserror 1.0.69", "unicode-width 0.1.14", ] @@ -6527,7 +6521,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.69", "ucd-trie", ] @@ -6777,6 +6771,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "prettyplease" version = "0.2.25" @@ -6989,7 +6993,7 @@ checksum = "b65f4a8ec18723a734e5dc09c173e0abf9690432da5340285d536edcb4dac190" dependencies = [ "once_cell", "protobuf-support", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -6998,7 +7002,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6872f4d4f4b98303239a2b5838f5bbbb77b01ffc892d627957f37a22d7cfe69c" dependencies = [ - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7036,7 +7040,7 @@ dependencies = [ "newtype-uuid", "quick-xml 0.36.2", "strip-ansi-escapes", - "thiserror", + "thiserror 1.0.69", "uuid 1.11.0", ] @@ -7069,37 +7073,40 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.16", + "rustls 0.23.17", "socket2", - "thiserror", + "thiserror 2.0.3", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom", "rand", "ring", "rustc-hash", - "rustls 0.23.16", + "rustls 0.23.17", + "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.3", "tinyvec", "tracing", + "web-time", ] [[package]] @@ -7190,7 +7197,7 @@ dependencies = [ "bitflags 2.6.0", "cassowary", "compact_str", - "crossterm 0.28.1", + "crossterm", "instability", "itertools 0.13.0", "lru", @@ -7245,7 +7252,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7256,7 +7263,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -7271,9 +7278,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -7327,7 +7334,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.16", + "rustls 0.23.17", "rustls-native-certs 0.8.0", "rustls-pemfile 2.2.0", "rustls-pki-types", @@ -7380,7 +7387,7 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7601,9 +7608,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.39" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags 2.6.0", "errno", @@ -7626,9 +7633,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.16" +version = "0.23.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e" dependencies = [ "log", "once_cell", @@ -7687,6 +7694,9 @@ name = "rustls-pki-types" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -7809,9 +7819,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.2.4" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d25269dd3a12467afe2e510f69fb0b46b698e5afb296b59f2145259deaf8e8" +checksum = "66b202022bb57c049555430e11fc22fea12909276a80a4c3d368da36ac1d88ed" dependencies = [ "sdd", ] @@ -8004,18 +8014,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -8266,7 +8276,7 @@ checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -8342,7 +8352,7 @@ dependencies = [ "lalrpop", "lalrpop-util", "phf", - "thiserror", + "thiserror 1.0.69", "unicode-xid", ] @@ -8384,7 +8394,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "thiserror", + "thiserror 1.0.69", "tokio", "toml_edit", "uuid 1.11.0", @@ -8526,7 +8536,7 @@ dependencies = [ "serde_json", "sha2", "tempfile", - "thiserror", + "thiserror 1.0.69", "url", "zip", ] @@ -8568,9 +8578,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edf42e81491fb8871b74df3d222c64ae8cbc1269ea509fa768a3ed3e1b0ac8cb" +checksum = "f76fe0a3e1476bdaa0775b9aec5b869ed9520c2b2fedfe9c6df3618f8ea6290b" dependencies = [ "paste", "proc-macro2", @@ -8687,18 +8697,38 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.68" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.3", ] [[package]] name = "thiserror-impl" -version = "1.0.68" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" dependencies = [ "proc-macro2", "quote", @@ -8866,7 +8896,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.16", + "rustls 0.23.17", "rustls-pki-types", "tokio", ] @@ -8879,7 +8909,7 @@ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -8903,7 +8933,7 @@ checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", - "rustls 0.23.16", + "rustls 0.23.17", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -9193,7 +9223,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3637e734239e12ab152cd269302500bd063f37624ee210cd04b4936ed671f3b1" dependencies = [ "cc", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -9206,7 +9236,7 @@ dependencies = [ "hex", "protobuf", "rusb", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -9229,10 +9259,10 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.23.16", + "rustls 0.23.17", "rustls-pki-types", "sha1", - "thiserror", + "thiserror 1.0.69", "utf-8", ] @@ -9612,7 +9642,7 @@ dependencies = [ "once_cell", "process-wrap", "project-origins", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "watchexec-events", @@ -9639,7 +9669,7 @@ checksum = "8f7ccc54db7df8cbbe3251508321e46986ce179af4c4a03b4c70bda539d72755" dependencies = [ "miette", "nix 0.28.0", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9667,6 +9697,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.26.6" @@ -9716,7 +9756,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -10050,7 +10090,7 @@ dependencies = [ "pharos", "rustc_version 0.4.1", "send_wrapper", - "thiserror", + "thiserror 1.0.69", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -10202,7 +10242,7 @@ dependencies = [ "flate2", "indexmap 2.6.0", "memchr", - "thiserror", + "thiserror 1.0.69", "zopfli", ] @@ -10213,7 +10253,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a8c9e90f27d1435088a7b540b6cc8ae6ee525d992a695f16012d2f365b3d3c" dependencies = [ "log", - "thiserror", + "thiserror 1.0.69", "zip", ] From 44c86e76d5e37505cd7349a867e64a845b5b9b2d Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 18 Nov 2024 08:55:59 +0200 Subject: [PATCH 08/68] chore: fix getArtifactPath flaky test (#9339) --- testdata/default/cheats/GetArtifactPath.t.sol | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/testdata/default/cheats/GetArtifactPath.t.sol b/testdata/default/cheats/GetArtifactPath.t.sol index 538c2e6c96ec..4b0df4ba6e6e 100644 --- a/testdata/default/cheats/GetArtifactPath.t.sol +++ b/testdata/default/cheats/GetArtifactPath.t.sol @@ -13,25 +13,15 @@ contract GetArtifactPathTest is DSTest { DummyForGetArtifactPath dummy = new DummyForGetArtifactPath(); bytes memory dummyCreationCode = type(DummyForGetArtifactPath).creationCode; - string memory root = vm.projectRoot(); string memory path = vm.getArtifactPathByCode(dummyCreationCode); - - string memory expectedPath = - string.concat(root, "/out/default/GetArtifactPath.t.sol/DummyForGetArtifactPath.json"); - - assertEq(path, expectedPath); + assertTrue(vm.contains(path, "/out/default/GetArtifactPath.t.sol/DummyForGetArtifactPath.json")); } function testGetArtifactPathByDeployedCode() public { DummyForGetArtifactPath dummy = new DummyForGetArtifactPath(); bytes memory dummyRuntimeCode = address(dummy).code; - string memory root = vm.projectRoot(); string memory path = vm.getArtifactPathByDeployedCode(dummyRuntimeCode); - - string memory expectedPath = - string.concat(root, "/out/default/GetArtifactPath.t.sol/DummyForGetArtifactPath.json"); - - assertEq(path, expectedPath); + assertTrue(vm.contains(path, "/out/default/GetArtifactPath.t.sol/DummyForGetArtifactPath.json")); } } From d275a4901f60a50c5a82fcf10fd5774ddb4598d8 Mon Sep 17 00:00:00 2001 From: Max <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:18:11 +0530 Subject: [PATCH 09/68] fix(cast storage): respect `--json` for layout (#9332) * feat(cast storage): allow ugly printing of layout Prior to this change, `cast storage $ADDRESS --rpc-url $RPC_URL --etherscan-api-key $ETHERSCAN_API_KEY` always provided a prettified output. This change adds a `--pretty` flag to `cast storage` which defaults to `true` thus retaining backwards compatibility. Passing `--pretty=false` to `cast storage` results in the json output of the storage layout being produced instead. * fix: remove default value from help text The default value is accessible via `cast storage --help` * fix(cast storage): provide output json path * test(cast): add storage_layout_simple_json test * fix(cast storage): use `--json` flag to ugly print * fix(cast storage): include values in json mode * fix(cast-storage): quiet compilation in all cases * chore: cargo clippy * use fixtures, assert JSON * only quiet if JSON mode, avoid unnecessary warning (if you pass an API key you already expect to fetch remote, very likely default) --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: zerosnacks --- crates/cast/bin/cmd/storage.rs | 46 +- crates/cast/tests/cli/main.rs | 33 ++ .../fixtures/storage_layout_complex.json | 397 ++++++++++++++++++ .../tests/fixtures/storage_layout_simple.json | 36 ++ 4 files changed, 503 insertions(+), 9 deletions(-) create mode 100644 crates/cast/tests/fixtures/storage_layout_complex.json create mode 100644 crates/cast/tests/fixtures/storage_layout_simple.json diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index 9fca4172e345..5e2459127001 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -17,6 +17,7 @@ use foundry_common::{ abi::find_source, compile::{etherscan_project, ProjectCompiler}, ens::NameOrAddress, + shell, }; use foundry_compilers::{ artifacts::{ConfigurableContractArtifact, StorageLayout}, @@ -31,6 +32,7 @@ use foundry_config::{ impl_figment_convert_cast, Config, }; use semver::Version; +use serde::{Deserialize, Serialize}; use std::str::FromStr; /// The minimum Solc version for outputting storage layouts. @@ -45,7 +47,7 @@ pub struct StorageArgs { #[arg(value_parser = NameOrAddress::from_str)] address: NameOrAddress, - /// The storage slot number. + /// The storage slot number. If not provided, it gets the full storage layout. #[arg(value_parser = parse_slot)] slot: Option, @@ -109,19 +111,22 @@ impl StorageArgs { if project.paths.has_input_files() { // Find in artifacts and pretty print add_storage_layout_output(&mut project); - let out = ProjectCompiler::new().compile(&project)?; + let out = ProjectCompiler::new().quiet(shell::is_json()).compile(&project)?; let artifact = out.artifacts().find(|(_, artifact)| { artifact.get_deployed_bytecode_bytes().is_some_and(|b| *b == address_code) }); if let Some((_, artifact)) = artifact { - return fetch_and_print_storage(provider, address, block, artifact, true).await; + return fetch_and_print_storage( + provider, + address, + block, + artifact, + !shell::is_json(), + ) + .await; } } - // Not a forge project or artifact not found - // Get code from Etherscan - sh_warn!("No matching artifacts found, fetching source code from Etherscan...")?; - if !self.etherscan.has_key() { eyre::bail!("You must provide an Etherscan API key if you're fetching a remote contract's storage."); } @@ -180,7 +185,7 @@ impl StorageArgs { // Clear temp directory root.close()?; - fetch_and_print_storage(provider, address, block, artifact, true).await + fetch_and_print_storage(provider, address, block, artifact, !shell::is_json()).await } } @@ -215,6 +220,14 @@ impl StorageValue { } } +/// Represents the storage layout of a contract and its values. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct StorageReport { + #[serde(flatten)] + layout: StorageLayout, + values: Vec, +} + async fn fetch_and_print_storage, T: Transport + Clone>( provider: P, address: Address, @@ -255,7 +268,22 @@ async fn fetch_storage_slots, T: Transport + Clone>( fn print_storage(layout: StorageLayout, values: Vec, pretty: bool) -> Result<()> { if !pretty { - sh_println!("{}", serde_json::to_string_pretty(&serde_json::to_value(layout)?)?)?; + let values: Vec<_> = layout + .storage + .iter() + .zip(&values) + .map(|(slot, storage_value)| { + let storage_type = layout.types.get(&slot.storage_type); + storage_value.value( + slot.offset, + storage_type.and_then(|t| t.number_of_bytes.parse::().ok()), + ) + }) + .collect(); + sh_println!( + "{}", + serde_json::to_string_pretty(&serde_json::to_value(StorageReport { layout, values })?)? + )?; return Ok(()) } diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index a88369e97ffe..2483fa479820 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1131,6 +1131,23 @@ casttest!(storage_layout_simple, |_prj, cmd| { "#]]); }); +// +casttest!(storage_layout_simple_json, |_prj, cmd| { + cmd.args([ + "storage", + "--rpc-url", + next_rpc_endpoint(NamedChain::Mainnet).as_str(), + "--block", + "21034138", + "--etherscan-api-key", + next_mainnet_etherscan_api_key().as_str(), + "0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2", + "--json", + ]) + .assert_success() + .stdout_eq(file!["../fixtures/storage_layout_simple.json": Json]); +}); + // casttest!(storage_layout_complex, |_prj, cmd| { cmd.args([ @@ -1164,6 +1181,22 @@ casttest!(storage_layout_complex, |_prj, cmd| { "#]]); }); +casttest!(storage_layout_complex_json, |_prj, cmd| { + cmd.args([ + "storage", + "--rpc-url", + next_rpc_endpoint(NamedChain::Mainnet).as_str(), + "--block", + "21034138", + "--etherscan-api-key", + next_mainnet_etherscan_api_key().as_str(), + "0xBA12222222228d8Ba445958a75a0704d566BF2C8", + "--json", + ]) + .assert_success() + .stdout_eq(file!["../fixtures/storage_layout_complex.json": Json]); +}); + casttest!(balance, |_prj, cmd| { let rpc = next_http_rpc_endpoint(); let usdt = "0xdac17f958d2ee523a2206206994597c13d831ec7"; diff --git a/crates/cast/tests/fixtures/storage_layout_complex.json b/crates/cast/tests/fixtures/storage_layout_complex.json new file mode 100644 index 000000000000..2cad9dc8c221 --- /dev/null +++ b/crates/cast/tests/fixtures/storage_layout_complex.json @@ -0,0 +1,397 @@ +{ + "storage": [ + { + "astId": 3805, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_status", + "offset": 0, + "slot": "0", + "type": "t_uint256" + }, + { + "astId": 9499, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_generalPoolsBalances", + "offset": 0, + "slot": "1", + "type": "t_mapping(t_bytes32,t_struct(IERC20ToBytes32Map)3177_storage)" + }, + { + "astId": 716, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_nextNonce", + "offset": 0, + "slot": "2", + "type": "t_mapping(t_address,t_uint256)" + }, + { + "astId": 967, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_paused", + "offset": 0, + "slot": "3", + "type": "t_bool" + }, + { + "astId": 8639, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_authorizer", + "offset": 1, + "slot": "3", + "type": "t_contract(IAuthorizer)11086" + }, + { + "astId": 8645, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_approvedRelayers", + "offset": 0, + "slot": "4", + "type": "t_mapping(t_address,t_mapping(t_address,t_bool))" + }, + { + "astId": 5769, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_isPoolRegistered", + "offset": 0, + "slot": "5", + "type": "t_mapping(t_bytes32,t_bool)" + }, + { + "astId": 5771, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_nextPoolNonce", + "offset": 0, + "slot": "6", + "type": "t_uint256" + }, + { + "astId": 9915, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_minimalSwapInfoPoolsBalances", + "offset": 0, + "slot": "7", + "type": "t_mapping(t_bytes32,t_mapping(t_contract(IERC20)3793,t_bytes32))" + }, + { + "astId": 9919, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_minimalSwapInfoPoolsTokens", + "offset": 0, + "slot": "8", + "type": "t_mapping(t_bytes32,t_struct(AddressSet)3520_storage)" + }, + { + "astId": 10373, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_twoTokenPoolTokens", + "offset": 0, + "slot": "9", + "type": "t_mapping(t_bytes32,t_struct(TwoTokenPoolTokens)10369_storage)" + }, + { + "astId": 4007, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_poolAssetManagers", + "offset": 0, + "slot": "10", + "type": "t_mapping(t_bytes32,t_mapping(t_contract(IERC20)3793,t_address))" + }, + { + "astId": 8019, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_internalTokenBalance", + "offset": 0, + "slot": "11", + "type": "t_mapping(t_address,t_mapping(t_contract(IERC20)3793,t_uint256))" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_address)dyn_storage": { + "encoding": "dynamic_array", + "label": "address[]", + "numberOfBytes": "32", + "base": "t_address" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "encoding": "inplace", + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(IAuthorizer)11086": { + "encoding": "inplace", + "label": "contract IAuthorizer", + "numberOfBytes": "20" + }, + "t_contract(IERC20)3793": { + "encoding": "inplace", + "label": "contract IERC20", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_bool)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_mapping(t_address,t_mapping(t_address,t_bool))": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => mapping(address => bool))", + "numberOfBytes": "32", + "value": "t_mapping(t_address,t_bool)" + }, + "t_mapping(t_address,t_mapping(t_contract(IERC20)3793,t_uint256))": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => mapping(contract IERC20 => uint256))", + "numberOfBytes": "32", + "value": "t_mapping(t_contract(IERC20)3793,t_uint256)" + }, + "t_mapping(t_address,t_uint256)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": "t_uint256" + }, + "t_mapping(t_bytes32,t_bool)": { + "encoding": "mapping", + "key": "t_bytes32", + "label": "mapping(bytes32 => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_mapping(t_bytes32,t_mapping(t_contract(IERC20)3793,t_address))": { + "encoding": "mapping", + "key": "t_bytes32", + "label": "mapping(bytes32 => mapping(contract IERC20 => address))", + "numberOfBytes": "32", + "value": "t_mapping(t_contract(IERC20)3793,t_address)" + }, + "t_mapping(t_bytes32,t_mapping(t_contract(IERC20)3793,t_bytes32))": { + "encoding": "mapping", + "key": "t_bytes32", + "label": "mapping(bytes32 => mapping(contract IERC20 => bytes32))", + "numberOfBytes": "32", + "value": "t_mapping(t_contract(IERC20)3793,t_bytes32)" + }, + "t_mapping(t_bytes32,t_struct(AddressSet)3520_storage)": { + "encoding": "mapping", + "key": "t_bytes32", + "label": "mapping(bytes32 => struct EnumerableSet.AddressSet)", + "numberOfBytes": "32", + "value": "t_struct(AddressSet)3520_storage" + }, + "t_mapping(t_bytes32,t_struct(IERC20ToBytes32Map)3177_storage)": { + "encoding": "mapping", + "key": "t_bytes32", + "label": "mapping(bytes32 => struct EnumerableMap.IERC20ToBytes32Map)", + "numberOfBytes": "32", + "value": "t_struct(IERC20ToBytes32Map)3177_storage" + }, + "t_mapping(t_bytes32,t_struct(TwoTokenPoolBalances)10360_storage)": { + "encoding": "mapping", + "key": "t_bytes32", + "label": "mapping(bytes32 => struct TwoTokenPoolsBalance.TwoTokenPoolBalances)", + "numberOfBytes": "32", + "value": "t_struct(TwoTokenPoolBalances)10360_storage" + }, + "t_mapping(t_bytes32,t_struct(TwoTokenPoolTokens)10369_storage)": { + "encoding": "mapping", + "key": "t_bytes32", + "label": "mapping(bytes32 => struct TwoTokenPoolsBalance.TwoTokenPoolTokens)", + "numberOfBytes": "32", + "value": "t_struct(TwoTokenPoolTokens)10369_storage" + }, + "t_mapping(t_contract(IERC20)3793,t_address)": { + "encoding": "mapping", + "key": "t_contract(IERC20)3793", + "label": "mapping(contract IERC20 => address)", + "numberOfBytes": "32", + "value": "t_address" + }, + "t_mapping(t_contract(IERC20)3793,t_bytes32)": { + "encoding": "mapping", + "key": "t_contract(IERC20)3793", + "label": "mapping(contract IERC20 => bytes32)", + "numberOfBytes": "32", + "value": "t_bytes32" + }, + "t_mapping(t_contract(IERC20)3793,t_uint256)": { + "encoding": "mapping", + "key": "t_contract(IERC20)3793", + "label": "mapping(contract IERC20 => uint256)", + "numberOfBytes": "32", + "value": "t_uint256" + }, + "t_mapping(t_uint256,t_struct(IERC20ToBytes32MapEntry)3166_storage)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => struct EnumerableMap.IERC20ToBytes32MapEntry)", + "numberOfBytes": "32", + "value": "t_struct(IERC20ToBytes32MapEntry)3166_storage" + }, + "t_struct(AddressSet)3520_storage": { + "encoding": "inplace", + "label": "struct EnumerableSet.AddressSet", + "numberOfBytes": "64", + "members": [ + { + "astId": 3515, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_values", + "offset": 0, + "slot": "0", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 3519, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_indexes", + "offset": 0, + "slot": "1", + "type": "t_mapping(t_address,t_uint256)" + } + ] + }, + "t_struct(IERC20ToBytes32Map)3177_storage": { + "encoding": "inplace", + "label": "struct EnumerableMap.IERC20ToBytes32Map", + "numberOfBytes": "96", + "members": [ + { + "astId": 3168, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_length", + "offset": 0, + "slot": "0", + "type": "t_uint256" + }, + { + "astId": 3172, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_entries", + "offset": 0, + "slot": "1", + "type": "t_mapping(t_uint256,t_struct(IERC20ToBytes32MapEntry)3166_storage)" + }, + { + "astId": 3176, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_indexes", + "offset": 0, + "slot": "2", + "type": "t_mapping(t_contract(IERC20)3793,t_uint256)" + } + ] + }, + "t_struct(IERC20ToBytes32MapEntry)3166_storage": { + "encoding": "inplace", + "label": "struct EnumerableMap.IERC20ToBytes32MapEntry", + "numberOfBytes": "64", + "members": [ + { + "astId": 3163, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_key", + "offset": 0, + "slot": "0", + "type": "t_contract(IERC20)3793" + }, + { + "astId": 3165, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "_value", + "offset": 0, + "slot": "1", + "type": "t_bytes32" + } + ] + }, + "t_struct(TwoTokenPoolBalances)10360_storage": { + "encoding": "inplace", + "label": "struct TwoTokenPoolsBalance.TwoTokenPoolBalances", + "numberOfBytes": "64", + "members": [ + { + "astId": 10357, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "sharedCash", + "offset": 0, + "slot": "0", + "type": "t_bytes32" + }, + { + "astId": 10359, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "sharedManaged", + "offset": 0, + "slot": "1", + "type": "t_bytes32" + } + ] + }, + "t_struct(TwoTokenPoolTokens)10369_storage": { + "encoding": "inplace", + "label": "struct TwoTokenPoolsBalance.TwoTokenPoolTokens", + "numberOfBytes": "96", + "members": [ + { + "astId": 10362, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "tokenA", + "offset": 0, + "slot": "0", + "type": "t_contract(IERC20)3793" + }, + { + "astId": 10364, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "tokenB", + "offset": 0, + "slot": "1", + "type": "t_contract(IERC20)3793" + }, + { + "astId": 10368, + "contract": "contracts/vault/Vault.sol:Vault", + "label": "balances", + "offset": 0, + "slot": "2", + "type": "t_mapping(t_bytes32,t_struct(TwoTokenPoolBalances)10360_storage)" + } + ] + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + }, + "values": [ + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000006048a8c631fb7e77eca533cf9c29784e482391e7", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000000000000000000000000000000000000000006e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ] +} \ No newline at end of file diff --git a/crates/cast/tests/fixtures/storage_layout_simple.json b/crates/cast/tests/fixtures/storage_layout_simple.json new file mode 100644 index 000000000000..35f4777d02b3 --- /dev/null +++ b/crates/cast/tests/fixtures/storage_layout_simple.json @@ -0,0 +1,36 @@ +{ + "storage": [ + { + "astId": 7, + "contract": "contracts/Create2Deployer.sol:Create2Deployer", + "label": "_owner", + "offset": 0, + "slot": "0", + "type": "t_address" + }, + { + "astId": 122, + "contract": "contracts/Create2Deployer.sol:Create2Deployer", + "label": "_paused", + "offset": 20, + "slot": "0", + "type": "t_bool" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + } + }, + "values": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ] +} \ No newline at end of file From 60dd1d7fe9879008a52da40eb74d5b6706d00b78 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 18 Nov 2024 18:24:53 +0200 Subject: [PATCH 10/68] feat(`config`): set default evm version to cancun (#9131) * chore: Update to Cancun * cancun to TEST_DATA_DEFAULT, add TEST_DATA_PARIS * Shanghai compat test * Redact gaswaster address --- crates/config/src/lib.rs | 2 +- crates/forge/src/multi_runner.rs | 2 +- crates/forge/tests/cli/build.rs | 6 +- crates/forge/tests/cli/cmd.rs | 462 +++++++++--------- crates/forge/tests/cli/compiler.rs | 2 +- crates/forge/tests/cli/config.rs | 37 ++ crates/forge/tests/cli/ext_integration.rs | 1 + crates/forge/tests/cli/script.rs | 40 +- crates/forge/tests/cli/test_cmd.rs | 86 ++-- crates/forge/tests/it/cheats.rs | 8 +- crates/forge/tests/it/core.rs | 27 +- crates/forge/tests/it/fork.rs | 8 +- crates/forge/tests/it/fuzz.rs | 4 +- crates/forge/tests/it/repros.rs | 5 +- crates/forge/tests/it/spec.rs | 4 +- crates/forge/tests/it/test_helpers.rs | 28 +- .../cheats/BlobBaseFee.t.sol | 0 .../cheats/Blobhashes.t.sol | 0 testdata/default/cheats/Etch.t.sol | 2 +- testdata/default/repros/Issue5929.t.sol | 4 +- testdata/default/repros/Issue6538.t.sol | 4 +- testdata/default/repros/Issue8006.t.sol | 6 +- testdata/paris/cheats/Fork.t.sol | 125 +++++ .../cheats/GasSnapshots.t.sol | 0 .../cheats/LastCallGas.t.sol | 0 .../core/BeforeTest.t.sol} | 2 +- .../{default => paris}/fork/Transact.t.sol | 2 +- testdata/paris/spec/ShanghaiCompat.t.sol | 29 ++ 28 files changed, 555 insertions(+), 341 deletions(-) rename testdata/{cancun => default}/cheats/BlobBaseFee.t.sol (100%) rename testdata/{cancun => default}/cheats/Blobhashes.t.sol (100%) create mode 100644 testdata/paris/cheats/Fork.t.sol rename testdata/{default => paris}/cheats/GasSnapshots.t.sol (100%) rename testdata/{default => paris}/cheats/LastCallGas.t.sol (100%) rename testdata/{default/repros/Issue1543.t.sol => paris/core/BeforeTest.t.sol} (98%) rename testdata/{default => paris}/fork/Transact.t.sol (99%) create mode 100644 testdata/paris/spec/ShanghaiCompat.t.sol diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 986a0181dd73..b87eadcefa0b 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -2149,7 +2149,7 @@ impl Default for Config { allow_paths: vec![], include_paths: vec![], force: false, - evm_version: EvmVersion::Paris, + evm_version: EvmVersion::Cancun, gas_reports: vec!["*".to_string()], gas_reports_ignore: vec![], gas_reports_include_tests: false, diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index e7361db0004f..a9f2a93eb37a 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -449,7 +449,7 @@ impl MultiContractRunnerBuilder { contracts: deployable_contracts, evm_opts, env, - evm_spec: self.evm_spec.unwrap_or(SpecId::MERGE), + evm_spec: self.evm_spec.unwrap_or(SpecId::CANCUN), sender: self.sender, revert_decoder, fork: self.fork, diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index dd2545995753..51c358612e86 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -78,7 +78,7 @@ forgetest!(initcode_size_exceeds_limit, |prj, cmd| { ... | Contract | Runtime Size (B) | Initcode Size (B) | Runtime Margin (B) | Initcode Margin (B) | |--------------|------------------|-------------------|--------------------|---------------------| -| HugeContract | 202 | 49,359 | 24,374 | -207 | +| HugeContract | 194 | 49,344 | 24,382 | -192 | ... "# ]); @@ -105,7 +105,7 @@ forgetest!(initcode_size_limit_can_be_ignored, |prj, cmd| { ... | Contract | Runtime Size (B) | Initcode Size (B) | Runtime Margin (B) | Initcode Margin (B) | |--------------|------------------|-------------------|--------------------|---------------------| -| HugeContract | 202 | 49,359 | 24,374 | -207 | +| HugeContract | 194 | 49,344 | 24,382 | -192 | ... "# ]); @@ -145,7 +145,7 @@ forgetest_init!(build_sizes_no_forge_std, |prj, cmd| { ... | Contract | Runtime Size (B) | Initcode Size (B) | Runtime Margin (B) | Initcode Margin (B) | |----------|------------------|-------------------|--------------------|---------------------| -| Counter | 247 | 277 | 24,329 | 48,875 | +| Counter | 236 | 263 | 24,340 | 48,889 | ... "# ]); diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 1e887791e569..18f0327f6e90 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1582,25 +1582,25 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { | src/Contracts.sol:ContractOne contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101532 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| foo | 45387 | 45387 | 45387 | 45387 | 1 | +| foo | 45370 | 45370 | 45370 | 45370 | 1 | | src/Contracts.sol:ContractThree contract | | | | | | |------------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 103591 | 256 | | | | | +| 101748 | 242 | | | | | | Function Name | min | avg | median | max | # calls | -| baz | 260712 | 260712 | 260712 | 260712 | 1 | +| baz | 259210 | 259210 | 259210 | 259210 | 1 | | src/Contracts.sol:ContractTwo contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101520 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| bar | 64984 | 64984 | 64984 | 64984 | 1 | +| bar | 64832 | 64832 | 64832 | 64832 | 1 | ... "#]]); @@ -1610,48 +1610,48 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101532, + "size": 241 }, "functions": { "foo()": { "calls": 1, - "min": 45387, - "mean": 45387, - "median": 45387, - "max": 45387 + "min": 45370, + "mean": 45370, + "median": 45370, + "max": 45370 } } }, { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 103591, - "size": 256 + "gas": 101748, + "size": 242 }, "functions": { "baz()": { "calls": 1, - "min": 260712, - "mean": 260712, - "median": 260712, - "max": 260712 + "min": 259210, + "mean": 259210, + "median": 259210, + "max": 259210 } } }, { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101520, + "size": 241 }, "functions": { "bar()": { "calls": 1, - "min": 64984, - "mean": 64984, - "median": 64984, - "max": 64984 + "min": 64832, + "mean": 64832, + "median": 64832, + "max": 64832 } } } @@ -1666,25 +1666,25 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { | src/Contracts.sol:ContractOne contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101532 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| foo | 45387 | 45387 | 45387 | 45387 | 1 | +| foo | 45370 | 45370 | 45370 | 45370 | 1 | | src/Contracts.sol:ContractThree contract | | | | | | |------------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 103591 | 256 | | | | | +| 101748 | 242 | | | | | | Function Name | min | avg | median | max | # calls | -| baz | 260712 | 260712 | 260712 | 260712 | 1 | +| baz | 259210 | 259210 | 259210 | 259210 | 1 | | src/Contracts.sol:ContractTwo contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101520 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| bar | 64984 | 64984 | 64984 | 64984 | 1 | +| bar | 64832 | 64832 | 64832 | 64832 | 1 | ... "#]]); @@ -1694,48 +1694,48 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101532, + "size": 241 }, "functions": { "foo()": { "calls": 1, - "min": 45387, - "mean": 45387, - "median": 45387, - "max": 45387 + "min": 45370, + "mean": 45370, + "median": 45370, + "max": 45370 } } }, { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 103591, - "size": 256 + "gas": 101748, + "size": 242 }, "functions": { "baz()": { "calls": 1, - "min": 260712, - "mean": 260712, - "median": 260712, - "max": 260712 + "min": 259210, + "mean": 259210, + "median": 259210, + "max": 259210 } } }, { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101520, + "size": 241 }, "functions": { "bar()": { "calls": 1, - "min": 64984, - "mean": 64984, - "median": 64984, - "max": 64984 + "min": 64832, + "mean": 64832, + "median": 64832, + "max": 64832 } } } @@ -1750,25 +1750,25 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { | src/Contracts.sol:ContractOne contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101532 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| foo | 45387 | 45387 | 45387 | 45387 | 1 | +| foo | 45370 | 45370 | 45370 | 45370 | 1 | | src/Contracts.sol:ContractThree contract | | | | | | |------------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 103591 | 256 | | | | | +| 101748 | 242 | | | | | | Function Name | min | avg | median | max | # calls | -| baz | 260712 | 260712 | 260712 | 260712 | 1 | +| baz | 259210 | 259210 | 259210 | 259210 | 1 | | src/Contracts.sol:ContractTwo contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101520 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| bar | 64984 | 64984 | 64984 | 64984 | 1 | +| bar | 64832 | 64832 | 64832 | 64832 | 1 | ... "#]]); @@ -1778,48 +1778,48 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101532, + "size": 241 }, "functions": { "foo()": { "calls": 1, - "min": 45387, - "mean": 45387, - "median": 45387, - "max": 45387 + "min": 45370, + "mean": 45370, + "median": 45370, + "max": 45370 } } }, { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 103591, - "size": 256 + "gas": 101748, + "size": 242 }, "functions": { "baz()": { "calls": 1, - "min": 260712, - "mean": 260712, - "median": 260712, - "max": 260712 + "min": 259210, + "mean": 259210, + "median": 259210, + "max": 259210 } } }, { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101520, + "size": 241 }, "functions": { "bar()": { "calls": 1, - "min": 64984, - "mean": 64984, - "median": 64984, - "max": 64984 + "min": 64832, + "mean": 64832, + "median": 64832, + "max": 64832 } } } @@ -1841,25 +1841,25 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { | src/Contracts.sol:ContractOne contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101532 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| foo | 45387 | 45387 | 45387 | 45387 | 1 | +| foo | 45370 | 45370 | 45370 | 45370 | 1 | | src/Contracts.sol:ContractThree contract | | | | | | |------------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 103591 | 256 | | | | | +| 101748 | 242 | | | | | | Function Name | min | avg | median | max | # calls | -| baz | 260712 | 260712 | 260712 | 260712 | 1 | +| baz | 259210 | 259210 | 259210 | 259210 | 1 | | src/Contracts.sol:ContractTwo contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101520 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| bar | 64984 | 64984 | 64984 | 64984 | 1 | +| bar | 64832 | 64832 | 64832 | 64832 | 1 | ... "#]]); @@ -1869,48 +1869,48 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101532, + "size": 241 }, "functions": { "foo()": { "calls": 1, - "min": 45387, - "mean": 45387, - "median": 45387, - "max": 45387 + "min": 45370, + "mean": 45370, + "median": 45370, + "max": 45370 } } }, { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 103591, - "size": 256 + "gas": 101748, + "size": 242 }, "functions": { "baz()": { "calls": 1, - "min": 260712, - "mean": 260712, - "median": 260712, - "max": 260712 + "min": 259210, + "mean": 259210, + "median": 259210, + "max": 259210 } } }, { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101520, + "size": 241 }, "functions": { "bar()": { "calls": 1, - "min": 64984, - "mean": 64984, - "median": 64984, - "max": 64984 + "min": 64832, + "mean": 64832, + "median": 64832, + "max": 64832 } } } @@ -1932,9 +1932,9 @@ forgetest!(gas_report_some_contracts, |prj, cmd| { | src/Contracts.sol:ContractOne contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101532 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| foo | 45387 | 45387 | 45387 | 45387 | 1 | +| foo | 45370 | 45370 | 45370 | 45370 | 1 | ... "#]]); @@ -1944,16 +1944,16 @@ forgetest!(gas_report_some_contracts, |prj, cmd| { { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101532, + "size": 241 }, "functions": { "foo()": { "calls": 1, - "min": 45387, - "mean": 45387, - "median": 45387, - "max": 45387 + "min": 45370, + "mean": 45370, + "median": 45370, + "max": 45370 } } } @@ -1970,9 +1970,9 @@ forgetest!(gas_report_some_contracts, |prj, cmd| { | src/Contracts.sol:ContractTwo contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101520 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| bar | 64984 | 64984 | 64984 | 64984 | 1 | +| bar | 64832 | 64832 | 64832 | 64832 | 1 | ... "#]]); @@ -1982,16 +1982,16 @@ forgetest!(gas_report_some_contracts, |prj, cmd| { { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101520, + "size": 241 }, "functions": { "bar()": { "calls": 1, - "min": 64984, - "mean": 64984, - "median": 64984, - "max": 64984 + "min": 64832, + "mean": 64832, + "median": 64832, + "max": 64832 } } } @@ -2011,9 +2011,9 @@ forgetest!(gas_report_some_contracts, |prj, cmd| { | src/Contracts.sol:ContractThree contract | | | | | | |------------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 103591 | 256 | | | | | +| 101748 | 242 | | | | | | Function Name | min | avg | median | max | # calls | -| baz | 260712 | 260712 | 260712 | 260712 | 1 | +| baz | 259210 | 259210 | 259210 | 259210 | 1 | ... "#]]); @@ -2023,16 +2023,16 @@ forgetest!(gas_report_some_contracts, |prj, cmd| { { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 103591, - "size": 256 + "gas": 101748, + "size": 242 }, "functions": { "baz()": { "calls": 1, - "min": 260712, - "mean": 260712, - "median": 260712, - "max": 260712 + "min": 259210, + "mean": 259210, + "median": 259210, + "max": 259210 } } } @@ -2058,17 +2058,17 @@ forgetest!(gas_report_ignore_some_contracts, |prj, cmd| { | src/Contracts.sol:ContractThree contract | | | | | | |------------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 103591 | 256 | | | | | +| 101748 | 242 | | | | | | Function Name | min | avg | median | max | # calls | -| baz | 260712 | 260712 | 260712 | 260712 | 1 | +| baz | 259210 | 259210 | 259210 | 259210 | 1 | | src/Contracts.sol:ContractTwo contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101520 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| bar | 64984 | 64984 | 64984 | 64984 | 1 | +| bar | 64832 | 64832 | 64832 | 64832 | 1 | ... "#]]); @@ -2078,32 +2078,32 @@ forgetest!(gas_report_ignore_some_contracts, |prj, cmd| { { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 103591, - "size": 256 + "gas": 101748, + "size": 242 }, "functions": { "baz()": { "calls": 1, - "min": 260712, - "mean": 260712, - "median": 260712, - "max": 260712 + "min": 259210, + "mean": 259210, + "median": 259210, + "max": 259210 } } }, { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101520, + "size": 241 }, "functions": { "bar()": { "calls": 1, - "min": 64984, - "mean": 64984, - "median": 64984, - "max": 64984 + "min": 64832, + "mean": 64832, + "median": 64832, + "max": 64832 } } } @@ -2125,17 +2125,17 @@ forgetest!(gas_report_ignore_some_contracts, |prj, cmd| { | src/Contracts.sol:ContractOne contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101532 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| foo | 45387 | 45387 | 45387 | 45387 | 1 | +| foo | 45370 | 45370 | 45370 | 45370 | 1 | | src/Contracts.sol:ContractThree contract | | | | | | |------------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 103591 | 256 | | | | | +| 101748 | 242 | | | | | | Function Name | min | avg | median | max | # calls | -| baz | 260712 | 260712 | 260712 | 260712 | 1 | +| baz | 259210 | 259210 | 259210 | 259210 | 1 | ... "#]]); @@ -2145,32 +2145,32 @@ forgetest!(gas_report_ignore_some_contracts, |prj, cmd| { { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101532, + "size": 241 }, "functions": { "foo()": { "calls": 1, - "min": 45387, - "mean": 45387, - "median": 45387, - "max": 45387 + "min": 45370, + "mean": 45370, + "median": 45370, + "max": 45370 } } }, { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 103591, - "size": 256 + "gas": 101748, + "size": 242 }, "functions": { "baz()": { "calls": 1, - "min": 260712, - "mean": 260712, - "median": 260712, - "max": 260712 + "min": 259210, + "mean": 259210, + "median": 259210, + "max": 259210 } } } @@ -2203,25 +2203,25 @@ forgetest!(gas_report_ignore_some_contracts, |prj, cmd| { | src/Contracts.sol:ContractOne contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101532 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| foo | 45387 | 45387 | 45387 | 45387 | 1 | +| foo | 45370 | 45370 | 45370 | 45370 | 1 | | src/Contracts.sol:ContractThree contract | | | | | | |------------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 103591 | 256 | | | | | +| 101748 | 242 | | | | | | Function Name | min | avg | median | max | # calls | -| baz | 260712 | 260712 | 260712 | 260712 | 1 | +| baz | 259210 | 259210 | 259210 | 259210 | 1 | | src/Contracts.sol:ContractTwo contract | | | | | | |----------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 103375 | 255 | | | | | +| 101520 | 241 | | | | | | Function Name | min | avg | median | max | # calls | -| bar | 64984 | 64984 | 64984 | 64984 | 1 | +| bar | 64832 | 64832 | 64832 | 64832 | 1 | ... "#]]) .stderr_eq(str![[r#" @@ -2240,48 +2240,48 @@ Warning: ContractThree is listed in both 'gas_reports' and 'gas_reports_ignore'. { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101532, + "size": 241 }, "functions": { "foo()": { "calls": 1, - "min": 45387, - "mean": 45387, - "median": 45387, - "max": 45387 + "min": 45370, + "mean": 45370, + "median": 45370, + "max": 45370 } } }, { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 103591, - "size": 256 + "gas": 101748, + "size": 242 }, "functions": { "baz()": { "calls": 1, - "min": 260712, - "mean": 260712, - "median": 260712, - "max": 260712 + "min": 259210, + "mean": 259210, + "median": 259210, + "max": 259210 } } }, { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 103375, - "size": 255 + "gas": 101520, + "size": 241 }, "functions": { "bar()": { "calls": 1, - "min": 64984, - "mean": 64984, - "median": 64984, - "max": 64984 + "min": 64832, + "mean": 64832, + "median": 64832, + "max": 64832 } } } @@ -2346,12 +2346,12 @@ contract CounterTest is DSTest { | src/Counter.sol:Counter contract | | | | | | |----------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 101137 | 250 | | | | | +| 99711 | 240 | | | | | | Function Name | min | avg | median | max | # calls | -| a | 2261 | 2261 | 2261 | 2261 | 1 | -| b | 2305 | 2305 | 2305 | 2305 | 1 | -| setNumber(int256) | 23648 | 33604 | 33604 | 43560 | 2 | -| setNumber(uint256) | 23604 | 33560 | 33560 | 43516 | 2 | +| a | 2259 | 2259 | 2259 | 2259 | 1 | +| b | 2304 | 2304 | 2304 | 2304 | 1 | +| setNumber(int256) | 23646 | 33602 | 33602 | 43558 | 2 | +| setNumber(uint256) | 23601 | 33557 | 33557 | 43513 | 2 | ... "#]]); cmd.forge_fuse().arg("test").arg("--gas-report").arg("--json").assert_success().stdout_eq( @@ -2360,37 +2360,37 @@ contract CounterTest is DSTest { { "contract": "src/Counter.sol:Counter", "deployment": { - "gas": 101137, - "size": 250 + "gas": 99711, + "size": 240 }, "functions": { "a()": { "calls": 1, - "min": 2261, - "mean": 2261, - "median": 2261, - "max": 2261 + "min": 2259, + "mean": 2259, + "median": 2259, + "max": 2259 }, "b()": { "calls": 1, - "min": 2305, - "mean": 2305, - "median": 2305, - "max": 2305 + "min": 2304, + "mean": 2304, + "median": 2304, + "max": 2304 }, "setNumber(int256)": { "calls": 2, - "min": 23648, - "mean": 33604, - "median": 33604, - "max": 43560 + "min": 23646, + "mean": 33602, + "median": 33602, + "max": 43558 }, "setNumber(uint256)": { "calls": 2, - "min": 23604, - "mean": 33560, - "median": 33560, - "max": 43516 + "min": 23601, + "mean": 33557, + "median": 33557, + "max": 43513 } } } @@ -2973,7 +2973,7 @@ forgetest_init!(can_build_sizes_repeatedly, |prj, cmd| { Compiler run successful! | Contract | Runtime Size (B) | Initcode Size (B) | Runtime Margin (B) | Initcode Margin (B) | |----------|------------------|-------------------|--------------------|---------------------| -| Counter | 247 | 277 | 24,329 | 48,875 | +| Counter | 236 | 263 | 24,340 | 48,889 | "#]]); @@ -3042,20 +3042,20 @@ forgetest_init!(gas_report_include_tests, |prj, cmd| { | src/Counter.sol:Counter contract | | | | | | |----------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 106715 | 277 | | | | | +| 104475 | 263 | | | | | | Function Name | min | avg | median | max | # calls | -| increment | 43404 | 43404 | 43404 | 43404 | 1 | -| number | 283 | 283 | 283 | 283 | 1 | -| setNumber | 23582 | 23582 | 23582 | 23582 | 1 | +| increment | 43401 | 43401 | 43401 | 43401 | 1 | +| number | 281 | 281 | 281 | 281 | 1 | +| setNumber | 23579 | 23579 | 23579 | 23579 | 1 | | test/Counter.t.sol:CounterTest contract | | | | | | |-----------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 965418 | 4661 | | | | | +| 938190 | 4522 | | | | | | Function Name | min | avg | median | max | # calls | -| setUp | 168064 | 168064 | 168064 | 168064 | 1 | -| test_Increment | 52367 | 52367 | 52367 | 52367 | 1 | +| setUp | 165834 | 165834 | 165834 | 165834 | 1 | +| test_Increment | 52357 | 52357 | 52357 | 52357 | 1 | ... "#] @@ -3070,53 +3070,53 @@ forgetest_init!(gas_report_include_tests, |prj, cmd| { { "contract": "src/Counter.sol:Counter", "deployment": { - "gas": 106715, - "size": 277 + "gas": 104475, + "size": 263 }, "functions": { "increment()": { "calls": 1, - "min": 43404, - "mean": 43404, - "median": 43404, - "max": 43404 + "min": 43401, + "mean": 43401, + "median": 43401, + "max": 43401 }, "number()": { "calls": 1, - "min": 283, - "mean": 283, - "median": 283, - "max": 283 + "min": 281, + "mean": 281, + "median": 281, + "max": 281 }, "setNumber(uint256)": { "calls": 1, - "min": 23582, - "mean": 23582, - "median": 23582, - "max": 23582 + "min": 23579, + "mean": 23579, + "median": 23579, + "max": 23579 } } }, { "contract": "test/Counter.t.sol:CounterTest", "deployment": { - "gas": 965418, - "size": 4661 + "gas": 938190, + "size": 4522 }, "functions": { "setUp()": { "calls": 1, - "min": 168064, - "mean": 168064, - "median": 168064, - "max": 168064 + "min": 165834, + "mean": 165834, + "median": 165834, + "max": 165834 }, "test_Increment()": { "calls": 1, - "min": 52367, - "mean": 52367, - "median": 52367, - "max": 52367 + "min": 52357, + "mean": 52357, + "median": 52357, + "max": 52357 } } } diff --git a/crates/forge/tests/cli/compiler.rs b/crates/forge/tests/cli/compiler.rs index b8453b67b944..0b58221f2a1d 100644 --- a/crates/forge/tests/cli/compiler.rs +++ b/crates/forge/tests/cli/compiler.rs @@ -242,7 +242,7 @@ Solidity: Vyper: -0.4.0 (<= [..]): +0.4.0 (<= cancun): ├── src/Counter.vy └── src/ICounter.vyi diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 91e0781afb1d..11bcd49e2c40 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -776,6 +776,43 @@ forgetest!(normalize_config_evm_version, |_prj, cmd| { .stdout_lossy(); let config: Config = serde_json::from_str(&output).unwrap(); assert_eq!(config.evm_version, EvmVersion::Istanbul); + + // See + let output = cmd + .forge_fuse() + .args(["config", "--use", "0.8.17", "--json"]) + .assert_success() + .get_output() + .stdout_lossy(); + let config: Config = serde_json::from_str(&output).unwrap(); + assert_eq!(config.evm_version, EvmVersion::London); + + let output = cmd + .forge_fuse() + .args(["config", "--use", "0.8.18", "--json"]) + .assert_success() + .get_output() + .stdout_lossy(); + let config: Config = serde_json::from_str(&output).unwrap(); + assert_eq!(config.evm_version, EvmVersion::Paris); + + let output = cmd + .forge_fuse() + .args(["config", "--use", "0.8.23", "--json"]) + .assert_success() + .get_output() + .stdout_lossy(); + let config: Config = serde_json::from_str(&output).unwrap(); + assert_eq!(config.evm_version, EvmVersion::Shanghai); + + let output = cmd + .forge_fuse() + .args(["config", "--use", "0.8.26", "--json"]) + .assert_success() + .get_output() + .stdout_lossy(); + let config: Config = serde_json::from_str(&output).unwrap(); + assert_eq!(config.evm_version, EvmVersion::Cancun); }); // Tests that root paths are properly resolved even if submodule specifies remappings for them. diff --git a/crates/forge/tests/cli/ext_integration.rs b/crates/forge/tests/cli/ext_integration.rs index f0b1c5144ad3..b2747e3ccb80 100644 --- a/crates/forge/tests/cli/ext_integration.rs +++ b/crates/forge/tests/cli/ext_integration.rs @@ -89,6 +89,7 @@ fn stringutils() { fn lootloose() { ExtTester::new("gakonst", "lootloose", "7b639efe97836155a6a6fc626bf1018d4f8b2495") .install_command(&["make", "install"]) + .args(["--evm-version", "paris"]) .run(); } diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 901ef592211d..82c61ccbc388 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -230,7 +230,7 @@ Traces: ├─ [0] VM::startBroadcast() │ └─ ← [Return] ├─ [..] → new GasWaster@[..] - │ └─ ← [Return] 226 bytes of code + │ └─ ← [Return] 221 bytes of code ├─ [..] GasWaster::wasteGas(200000 [2e5]) │ └─ ← [Stop] └─ ← [Stop] @@ -242,10 +242,10 @@ Script ran successfully. ========================== Simulated On-chain Traces: - [45299] → new GasWaster@[..] - └─ ← [Return] 226 bytes of code + [44291] → new GasWaster@[..] + └─ ← [Return] 221 bytes of code - [226] GasWaster::wasteGas(200000 [2e5]) + [224] GasWaster::wasteGas(200000 [2e5]) └─ ← [Stop] @@ -337,7 +337,7 @@ Traces: ├─ [0] VM::startBroadcast() │ └─ ← [Return] ├─ [..] → new GasWaster@[..] - │ └─ ← [Return] 226 bytes of code + │ └─ ← [Return] 221 bytes of code ├─ [..] GasWaster::wasteGas(200000 [2e5]) │ └─ ← [Stop] └─ ← [Stop] @@ -349,10 +349,10 @@ Script ran successfully. ========================== Simulated On-chain Traces: - [45299] → new GasWaster@[..] - └─ ← [Return] 226 bytes of code + [44291] → new GasWaster@[..] + └─ ← [Return] 221 bytes of code - [226] GasWaster::wasteGas(200000 [2e5]) + [224] GasWaster::wasteGas(200000 [2e5]) └─ ← [Stop] @@ -522,7 +522,7 @@ Traces: ├─ [0] VM::startBroadcast() │ └─ ← [Return] ├─ [..] → new HashChecker@[..] - │ └─ ← [Return] 378 bytes of code + │ └─ ← [Return] 368 bytes of code └─ ← [Stop] @@ -2219,15 +2219,15 @@ contract SimpleScript is Script { [SOLC_VERSION] [ELAPSED] Compiler run successful! Traces: - [104553] SimpleScript::run() + [103771] SimpleScript::run() ├─ [0] VM::startBroadcast() │ └─ ← [Return] - ├─ [23875] → new A@0x5b73C5498c1E3b4dbA84de0F1833c4a029d90519 - │ └─ ← [Return] 119 bytes of code - ├─ [13367] → new B@0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 - │ ├─ [146] A::getValue() [staticcall] + ├─ [23273] → new A@0x5b73C5498c1E3b4dbA84de0F1833c4a029d90519 + │ └─ ← [Return] 116 bytes of code + ├─ [13162] → new B@0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 + │ ├─ [145] A::getValue() [staticcall] │ │ └─ ← [Return] 100 - │ └─ ← [Return] 63 bytes of code + │ └─ ← [Return] 62 bytes of code └─ ← [Stop] @@ -2237,13 +2237,13 @@ Script ran successfully. ========================== Simulated On-chain Traces: - [23875] → new A@0x5b73C5498c1E3b4dbA84de0F1833c4a029d90519 - └─ ← [Return] 119 bytes of code + [23273] → new A@0x5b73C5498c1E3b4dbA84de0F1833c4a029d90519 + └─ ← [Return] 116 bytes of code - [15867] → new B@0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 - ├─ [146] A::getValue() [staticcall] + [15662] → new B@0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 + ├─ [145] A::getValue() [staticcall] │ └─ ← [Return] 100 - └─ ← [Return] 63 bytes of code + └─ ← [Return] 62 bytes of code ... "#]]); }); diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 8389c381ef9f..f51cf6703a17 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -575,7 +575,7 @@ Compiler run successful! Ran 1 test for test/Contract.t.sol:USDTCallingTest [PASS] test() ([GAS]) Traces: - [9516] USDTCallingTest::test() + [9406] USDTCallingTest::test() ├─ [0] VM::createSelectFork("[..]") │ └─ ← [Return] 0 ├─ [3110] 0xdAC17F958D2ee523a2206206994597C13D831ec7::name() [staticcall] @@ -621,12 +621,12 @@ Compiler run successful! Ran 2 tests for test/Contract.t.sol:CustomTypesTest [FAIL: PoolNotInitialized()] testErr() ([GAS]) Traces: - [254] CustomTypesTest::testErr() + [253] CustomTypesTest::testErr() └─ ← [Revert] PoolNotInitialized() [PASS] testEvent() ([GAS]) Traces: - [1268] CustomTypesTest::testEvent() + [1267] CustomTypesTest::testEvent() ├─ emit MyEvent(a: 100) └─ ← [Stop] @@ -996,7 +996,7 @@ Compiler run successful! Ran 1 test for test/Contract.t.sol:PrecompileLabelsTest [PASS] testPrecompileLabels() ([GAS]) Traces: - [9474] PrecompileLabelsTest::testPrecompileLabels() + [9383] PrecompileLabelsTest::testPrecompileLabels() ├─ [0] VM::deal(VM: [0x7109709ECfa91a80626fF3989D68f67F5b1DD12D], 1000000000000000000 [1e18]) │ └─ ← [Return] ├─ [0] VM::deal(console: [0x000000000000000000636F6e736F6c652e6c6f67], 1000000000000000000 [1e18]) @@ -1264,17 +1264,17 @@ Compiler run successful! Ran 1 test for test/Simple.sol:SimpleContractTest [PASS] test() ([GAS]) Traces: - [250463] SimpleContractTest::test() - ├─ [171014] → new SimpleContract@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f - │ └─ ← [Return] 854 bytes of code - ├─ [22638] SimpleContract::increment() - │ ├─ [20150] SimpleContract::_setNum(1) + [244864] SimpleContractTest::test() + ├─ [165406] → new SimpleContract@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ └─ ← [Return] 826 bytes of code + ├─ [22630] SimpleContract::increment() + │ ├─ [20147] SimpleContract::_setNum(1) │ │ └─ ← 0 │ └─ ← [Stop] - ├─ [23219] SimpleContract::setValues(100, 0x0000000000000000000000000000000000000123) - │ ├─ [250] SimpleContract::_setNum(100) + ├─ [23204] SimpleContract::setValues(100, 0x0000000000000000000000000000000000000123) + │ ├─ [247] SimpleContract::_setNum(100) │ │ └─ ← 1 - │ ├─ [22339] SimpleContract::_setAddr(0x0000000000000000000000000000000000000123) + │ ├─ [22336] SimpleContract::_setAddr(0x0000000000000000000000000000000000000123) │ │ └─ ← 0x0000000000000000000000000000000000000000 │ └─ ← [Stop] └─ ← [Stop] @@ -1326,11 +1326,11 @@ contract SimpleContractTest is Test { r#" ... Traces: - [421947] SimpleContractTest::test() - ├─ [385978] → new SimpleContract@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f - │ └─ ← [Return] 1814 bytes of code - ├─ [2534] SimpleContract::setStr("new value") - │ ├─ [1600] SimpleContract::_setStr("new value") + [406629] SimpleContractTest::test() + ├─ [370554] → new SimpleContract@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ └─ ← [Return] 1737 bytes of code + ├─ [2511] SimpleContract::setStr("new value") + │ ├─ [1588] SimpleContract::_setStr("new value") │ │ └─ ← "initial value" │ └─ ← [Stop] └─ ← [Stop] @@ -1460,10 +1460,10 @@ contract ATest is Test { cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... -[PASS] testNormalGas() (gas: 3202) -[PASS] testWeirdGas1() (gas: 3040) -[PASS] testWeirdGas2() (gas: 3148) -[PASS] testWithAssembly() (gas: 3083) +[PASS] testNormalGas() (gas: 3194) +[PASS] testWeirdGas1() (gas: 3032) +[PASS] testWeirdGas2() (gas: 3139) +[PASS] testWithAssembly() (gas: 3075) ... "#]]); }); @@ -1548,9 +1548,9 @@ contract ATest is Test { cmd.args(["test", "-vvvv"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... Logs: - Gas cost: 34468 + Gas cost: 34367 ... -[PASS] test_GasMeter() (gas: 37512) +[PASS] test_GasMeter() (gas: 37407) ... "#]]); }); @@ -1635,33 +1635,33 @@ contract PauseTracingTest is DSTest { cmd.args(["test", "-vvvvv"]).assert_success().stdout_eq(str![[r#" ... Traces: - [7285] PauseTracingTest::setUp() + [7282] PauseTracingTest::setUp() ├─ emit DummyEvent(i: 1) ├─ [0] VM::pauseTracing() [staticcall] │ └─ ← [Return] └─ ← [Stop] - [294725] PauseTracingTest::test() + [282512] PauseTracingTest::test() ├─ [0] VM::resumeTracing() [staticcall] │ └─ ← [Return] - ├─ [18373] TraceGenerator::generate() - │ ├─ [1280] TraceGenerator::call(0) + ├─ [18327] TraceGenerator::generate() + │ ├─ [1278] TraceGenerator::call(0) │ │ ├─ emit DummyEvent(i: 0) │ │ └─ ← [Stop] - │ ├─ [1280] TraceGenerator::call(1) + │ ├─ [1278] TraceGenerator::call(1) │ │ ├─ emit DummyEvent(i: 1) │ │ └─ ← [Stop] - │ ├─ [1280] TraceGenerator::call(2) + │ ├─ [1278] TraceGenerator::call(2) │ │ ├─ emit DummyEvent(i: 2) │ │ └─ ← [Stop] │ ├─ [0] VM::pauseTracing() [staticcall] │ │ └─ ← [Return] │ ├─ [0] VM::resumeTracing() [staticcall] │ │ └─ ← [Return] - │ ├─ [1280] TraceGenerator::call(8) + │ ├─ [1278] TraceGenerator::call(8) │ │ ├─ emit DummyEvent(i: 8) │ │ └─ ← [Stop] - │ ├─ [1280] TraceGenerator::call(9) + │ ├─ [1278] TraceGenerator::call(9) │ │ ├─ emit DummyEvent(i: 9) │ │ └─ ← [Stop] │ └─ ← [Stop] @@ -2357,11 +2357,11 @@ Compiler run successful! Ran 1 test for test/MetadataTraceTest.t.sol:MetadataTraceTest [PASS] test_proxy_trace() ([GAS]) Traces: - [152142] MetadataTraceTest::test_proxy_trace() - ├─ [49499] → new Counter@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f - │ └─ ← [Return] 247 bytes of code - ├─ [37978] → new Proxy@0x2e234DAe75C793f67A35089C9d99245E1C58470b - │ └─ ← [Return] 63 bytes of code + [149783] MetadataTraceTest::test_proxy_trace() + ├─ [47297] → new Counter@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ └─ ← [Return] 236 bytes of code + ├─ [37762] → new Proxy@0x2e234DAe75C793f67A35089C9d99245E1C58470b + │ └─ ← [Return] 62 bytes of code └─ ← [Stop] Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] @@ -2382,11 +2382,11 @@ Compiler run successful! Ran 1 test for test/MetadataTraceTest.t.sol:MetadataTraceTest [PASS] test_proxy_trace() ([GAS]) Traces: - [130521] MetadataTraceTest::test_proxy_trace() - ├─ [38693] → new Counter@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f - │ └─ ← [Return] 193 bytes of code - ├─ [27175] → new Proxy@0x2e234DAe75C793f67A35089C9d99245E1C58470b - │ └─ ← [Return] 9 bytes of code + [128142] MetadataTraceTest::test_proxy_trace() + ├─ [36485] → new Counter@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ └─ ← [Return] 182 bytes of code + ├─ [26959] → new Proxy@0x2e234DAe75C793f67A35089C9d99245E1C58470b + │ └─ ← [Return] 8 bytes of code └─ ← [Stop] Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] @@ -2561,7 +2561,7 @@ forgetest_async!(can_get_broadcast_txs, |prj, cmd| { 31337 ); - assertEq(deployedAddress, address(0x030D07c16e2c0a77f74ab16f3C8F10ACeF89FF81)); + assertEq(deployedAddress, address(0x78280279172ED4C0E65BCE5Ee9DFdcd828f837DB)); } function test_getDeployments() public { @@ -2571,7 +2571,7 @@ forgetest_async!(can_get_broadcast_txs, |prj, cmd| { ); assertEq(deployments.length, 2); - assertEq(deployments[0], address(0x030D07c16e2c0a77f74ab16f3C8F10ACeF89FF81)); // Create2 address - latest deployment + assertEq(deployments[0], address(0x78280279172ED4C0E65BCE5Ee9DFdcd828f837DB)); // Create2 address - latest deployment assertEq(deployments[1], address(0x5FbDB2315678afecb367f032d93F642f64180aa3)); // Create address - oldest deployment } diff --git a/crates/forge/tests/it/cheats.rs b/crates/forge/tests/it/cheats.rs index a60602cbc1cc..871cda045fa7 100644 --- a/crates/forge/tests/it/cheats.rs +++ b/crates/forge/tests/it/cheats.rs @@ -3,8 +3,8 @@ use crate::{ config::*, test_helpers::{ - ForgeTestData, RE_PATH_SEPARATOR, TEST_DATA_CANCUN, TEST_DATA_DEFAULT, - TEST_DATA_MULTI_VERSION, + ForgeTestData, RE_PATH_SEPARATOR, TEST_DATA_DEFAULT, TEST_DATA_MULTI_VERSION, + TEST_DATA_PARIS, }, }; use alloy_primitives::U256; @@ -77,6 +77,6 @@ async fn test_cheats_local_multi_version() { } #[tokio::test(flavor = "multi_thread")] -async fn test_cheats_local_cancun() { - test_cheats_local(&TEST_DATA_CANCUN).await +async fn test_cheats_local_paris() { + test_cheats_local(&TEST_DATA_PARIS).await } diff --git a/crates/forge/tests/it/core.rs b/crates/forge/tests/it/core.rs index 17d9f59d88fa..abab87d2866e 100644 --- a/crates/forge/tests/it/core.rs +++ b/crates/forge/tests/it/core.rs @@ -1,6 +1,9 @@ //! Forge tests for core functionality. -use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; +use crate::{ + config::*, + test_helpers::{TEST_DATA_DEFAULT, TEST_DATA_PARIS}, +}; use forge::result::SuiteResult; use foundry_evm::traces::TraceKind; use foundry_test_utils::Filter; @@ -803,3 +806,25 @@ async fn test_legacy_assertions() { )]), ); } + +/// Test `beforeTest` functionality and `selfdestruct`. +/// See +#[tokio::test(flavor = "multi_thread")] +async fn test_before_setup_with_selfdestruct() { + let filter = Filter::new(".*", ".*BeforeTestSelfDestructTest", ".*"); + let results = TEST_DATA_PARIS.runner().test_collect(&filter); + + assert_multiple( + &results, + BTreeMap::from([( + "paris/core/BeforeTest.t.sol:BeforeTestSelfDestructTest", + vec![ + ("testKill()", true, None, None, None), + ("testA()", true, None, None, None), + ("testSimpleA()", true, None, None, None), + ("testB()", true, None, None, None), + ("testC(uint256)", true, None, None, None), + ], + )]), + ); +} diff --git a/crates/forge/tests/it/fork.rs b/crates/forge/tests/it/fork.rs index 26c45aa184e5..8dc637528ddd 100644 --- a/crates/forge/tests/it/fork.rs +++ b/crates/forge/tests/it/fork.rs @@ -2,7 +2,7 @@ use crate::{ config::*, - test_helpers::{RE_PATH_SEPARATOR, TEST_DATA_DEFAULT}, + test_helpers::{RE_PATH_SEPARATOR, TEST_DATA_DEFAULT, TEST_DATA_PARIS}, }; use alloy_chains::Chain; use forge::result::SuiteResult; @@ -35,9 +35,9 @@ async fn test_cheats_fork_revert() { /// Executes all non-reverting fork cheatcodes #[tokio::test(flavor = "multi_thread")] async fn test_cheats_fork() { - let mut config = TEST_DATA_DEFAULT.config.clone(); + let mut config = TEST_DATA_PARIS.config.clone(); config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - let runner = TEST_DATA_DEFAULT.runner_with_config(config); + let runner = TEST_DATA_PARIS.runner_with_config(config); let filter = Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) .exclude_tests(".*Revert"); TestConfig::with_filter(runner, filter).run().await; @@ -86,7 +86,7 @@ async fn test_launch_fork_ws() { /// Tests that we can transact transactions in forking mode #[tokio::test(flavor = "multi_thread")] async fn test_transact_fork() { - let runner = TEST_DATA_DEFAULT.runner(); + let runner = TEST_DATA_PARIS.runner(); let filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}Transact")); TestConfig::with_filter(runner, filter).run().await; } diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index d6b047a17a93..8972c9bd98f1 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -203,10 +203,10 @@ contract FuzzerDictTest is Test { .unwrap(); // Test that immutable address is used as fuzzed input, causing test to fail. - cmd.args(["test", "--fuzz-seed", "100", "--mt", "testImmutableOwner"]).assert_failure(); + cmd.args(["test", "--fuzz-seed", "119", "--mt", "testImmutableOwner"]).assert_failure(); // Test that storage address is used as fuzzed input, causing test to fail. cmd.forge_fuse() - .args(["test", "--fuzz-seed", "100", "--mt", "testStorageOwner"]) + .args(["test", "--fuzz-seed", "119", "--mt", "testStorageOwner"]) .assert_failure(); }); diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 9f46e3c2ec1f..53185cf97856 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -377,14 +377,11 @@ test_repro!(8383, false, None, |res| { let test = res.test_results.remove("testP256VerifyOutOfBounds()").unwrap(); assert_eq!(test.status, TestStatus::Success); match test.kind { - TestKind::Unit { gas } => assert_eq!(gas, 3103), + TestKind::Unit { gas } => assert_eq!(gas, 3101), _ => panic!("not a unit test kind"), } }); -// https://github.com/foundry-rs/foundry/issues/1543 -test_repro!(1543); - // https://github.com/foundry-rs/foundry/issues/6643 test_repro!(6643); diff --git a/crates/forge/tests/it/spec.rs b/crates/forge/tests/it/spec.rs index db98a15d1af2..aed2063a0fba 100644 --- a/crates/forge/tests/it/spec.rs +++ b/crates/forge/tests/it/spec.rs @@ -1,13 +1,13 @@ //! Integration tests for EVM specifications. -use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; +use crate::{config::*, test_helpers::TEST_DATA_PARIS}; use foundry_evm::revm::primitives::SpecId; use foundry_test_utils::Filter; #[tokio::test(flavor = "multi_thread")] async fn test_shanghai_compat() { let filter = Filter::new("", "ShanghaiCompat", ".*spec"); - TestConfig::with_filter(TEST_DATA_DEFAULT.runner(), filter) + TestConfig::with_filter(TEST_DATA_PARIS.runner(), filter) .evm_spec(SpecId::SHANGHAI) .run() .await; diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index eb5a0bf0a3c0..5e540d8c67aa 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -34,7 +34,7 @@ static VYPER: LazyLock = LazyLock::new(|| std::env::temp_dir().join("vy /// Profile for the tests group. Used to configure separate configurations for test runs. pub enum ForgeTestProfile { Default, - Cancun, + Paris, MultiVersion, } @@ -42,16 +42,16 @@ impl fmt::Display for ForgeTestProfile { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Default => write!(f, "default"), - Self::Cancun => write!(f, "cancun"), + Self::Paris => write!(f, "paris"), Self::MultiVersion => write!(f, "multi-version"), } } } impl ForgeTestProfile { - /// Returns true if the profile is Cancun. - pub fn is_cancun(&self) -> bool { - matches!(self, Self::Cancun) + /// Returns true if the profile is Paris. + pub fn is_paris(&self) -> bool { + matches!(self, Self::Paris) } pub fn root(&self) -> PathBuf { @@ -66,8 +66,8 @@ impl ForgeTestProfile { let mut settings = Settings { libraries: Libraries::parse(&libs).unwrap(), ..Default::default() }; - if matches!(self, Self::Cancun) { - settings.evm_version = Some(EvmVersion::Cancun); + if matches!(self, Self::Paris) { + settings.evm_version = Some(EvmVersion::Paris); } let settings = SolcConfig::builder().settings(settings).build(); @@ -155,8 +155,8 @@ impl ForgeTestProfile { "fork/Fork.t.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4".to_string(), ]; - if self.is_cancun() { - config.evm_version = EvmVersion::Cancun; + if self.is_paris() { + config.evm_version = EvmVersion::Paris; } config @@ -195,8 +195,8 @@ impl ForgeTestData { let mut runner = MultiContractRunnerBuilder::new(Arc::new(self.config.clone())) .sender(self.evm_opts.sender) .with_test_options(self.test_opts.clone()); - if self.profile.is_cancun() { - runner = runner.evm_spec(SpecId::CANCUN); + if self.profile.is_paris() { + runner = runner.evm_spec(SpecId::MERGE); } runner @@ -338,9 +338,9 @@ pub fn get_compiled(project: &mut Project) -> ProjectCompileOutput { pub static TEST_DATA_DEFAULT: LazyLock = LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Default)); -/// Data for tests requiring Cancun support on Solc and EVM level. -pub static TEST_DATA_CANCUN: LazyLock = - LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Cancun)); +/// Data for tests requiring Paris support on Solc and EVM level. +pub static TEST_DATA_PARIS: LazyLock = + LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Paris)); /// Data for tests requiring Cancun support on Solc and EVM level. pub static TEST_DATA_MULTI_VERSION: LazyLock = diff --git a/testdata/cancun/cheats/BlobBaseFee.t.sol b/testdata/default/cheats/BlobBaseFee.t.sol similarity index 100% rename from testdata/cancun/cheats/BlobBaseFee.t.sol rename to testdata/default/cheats/BlobBaseFee.t.sol diff --git a/testdata/cancun/cheats/Blobhashes.t.sol b/testdata/default/cheats/Blobhashes.t.sol similarity index 100% rename from testdata/cancun/cheats/Blobhashes.t.sol rename to testdata/default/cheats/Blobhashes.t.sol diff --git a/testdata/default/cheats/Etch.t.sol b/testdata/default/cheats/Etch.t.sol index 33eaaf44ef87..f60ea4cad650 100644 --- a/testdata/default/cheats/Etch.t.sol +++ b/testdata/default/cheats/Etch.t.sol @@ -8,7 +8,7 @@ contract EtchTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function testEtch() public { - address target = address(10); + address target = address(11); bytes memory code = hex"1010"; vm.etch(target, code); assertEq(string(code), string(target.code)); diff --git a/testdata/default/repros/Issue5929.t.sol b/testdata/default/repros/Issue5929.t.sol index 70c5a4f4ffbd..ced9d6d9b4a3 100644 --- a/testdata/default/repros/Issue5929.t.sol +++ b/testdata/default/repros/Issue5929.t.sol @@ -9,8 +9,8 @@ contract Issue5929Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function test_transact_not_working() public { - vm.createSelectFork("mainnet", 15625301); + vm.createSelectFork("mainnet", 21134547); // https://etherscan.io/tx/0x96a129768ec66fd7d65114bf182f4e173bf0b73a44219adaf71f01381a3d0143 - vm.transact(hex"96a129768ec66fd7d65114bf182f4e173bf0b73a44219adaf71f01381a3d0143"); + vm.transact(hex"7dcff74771babf9c23363c4228e55a27f50224d4596b1ba6608b0b45712f94ba"); } } diff --git a/testdata/default/repros/Issue6538.t.sol b/testdata/default/repros/Issue6538.t.sol index 5b318a04c868..34c4e2253a68 100644 --- a/testdata/default/repros/Issue6538.t.sol +++ b/testdata/default/repros/Issue6538.t.sol @@ -9,9 +9,9 @@ contract Issue6538Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function test_transact() public { - bytes32 lastHash = 0xdbdce1d5c14a6ca17f0e527ab762589d6a73f68697606ae0bb90df7ac9ec5087; + bytes32 lastHash = 0x4b70ca8c5a0990b43df3064372d424d46efa41dfaab961754b86c5afb2df4f61; vm.createSelectFork("mainnet", lastHash); - bytes32 txhash = 0xadbe5cf9269a001d50990d0c29075b402bcc3a0b0f3258821881621b787b35c6; + bytes32 txhash = 0x7dcff74771babf9c23363c4228e55a27f50224d4596b1ba6608b0b45712f94ba; vm.transact(txhash); } } diff --git a/testdata/default/repros/Issue8006.t.sol b/testdata/default/repros/Issue8006.t.sol index 95b16e6f64fa..efe339d9fef2 100644 --- a/testdata/default/repros/Issue8006.t.sol +++ b/testdata/default/repros/Issue8006.t.sol @@ -18,10 +18,10 @@ contract Mock { contract Issue8006Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); IERC20 dai; - bytes32 transaction = 0x67cbad73764049e228495a3f90144aab4a37cb4b5fd697dffc234aa5ed811ace; + bytes32 transaction = 0xb23f389b26eb6f95c08e275ec2c360ab3990169492ff0d3e7b7233a3f81d299f; function setUp() public { - vm.createSelectFork("mainnet", 16261704); + vm.createSelectFork("mainnet", 21134541); dai = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); } @@ -30,7 +30,7 @@ contract Issue8006Test is DSTest { vm.etch(address(dai), address(new Mock()).code); assertEq(dai.totalSupply(), 1); vm.rollFork(transaction); - assertEq(dai.totalSupply(), 5155217627191887307044676292); + assertEq(dai.totalSupply(), 3324657947511778619416491233); } function testRollForkEtchCalled() public { diff --git a/testdata/paris/cheats/Fork.t.sol b/testdata/paris/cheats/Fork.t.sol new file mode 100644 index 000000000000..2f2e627de131 --- /dev/null +++ b/testdata/paris/cheats/Fork.t.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +interface IWETH { + function deposit() external payable; + function balanceOf(address) external view returns (uint256); +} + +contract ForkTest is DSTest { + address constant WETH_TOKEN_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + uint256 constant mainblock = 14_608_400; + + Vm constant vm = Vm(HEVM_ADDRESS); + IWETH WETH = IWETH(WETH_TOKEN_ADDR); + + uint256 forkA; + uint256 forkB; + + uint256 testValue; + + // this will create two _different_ forks during setup + function setUp() public { + forkA = vm.createFork("mainnet", mainblock); + forkB = vm.createFork("mainnet2", mainblock - 1); + testValue = 999; + } + + // ensures forks use different ids + function testForkIdDiffer() public { + assert(forkA != forkB); + } + + // ensures we can create and select in one step + function testCreateSelect() public { + uint256 fork = vm.createSelectFork("mainnet"); + assertEq(fork, vm.activeFork()); + } + + // ensures forks use different ids + function testCanSwitchForks() public { + vm.selectFork(forkA); + vm.selectFork(forkB); + vm.selectFork(forkB); + vm.selectFork(forkA); + } + + function testForksHaveSeparatedStorage() public { + vm.selectFork(forkA); + // read state from forkA + assert(WETH.balanceOf(0x0000000000000000000000000000000000000000) != 1); + + vm.selectFork(forkB); + // read state from forkB + uint256 forkBbalance = WETH.balanceOf(0x0000000000000000000000000000000000000000); + assert(forkBbalance != 1); + + vm.selectFork(forkA); + + // modify state + bytes32 value = bytes32(uint256(1)); + // "0x3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff" is the slot storing the balance of zero address for the weth contract + // `cast index address uint 0x0000000000000000000000000000000000000000 3` + bytes32 zero_address_balance_slot = 0x3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff; + vm.store(WETH_TOKEN_ADDR, zero_address_balance_slot, value); + assertEq( + WETH.balanceOf(0x0000000000000000000000000000000000000000), + 1, + "Cheatcode did not change value at the storage slot." + ); + + // switch forks and ensure the balance on forkB remains untouched + vm.selectFork(forkB); + assert(forkBbalance != 1); + // balance of forkB is untouched + assertEq( + WETH.balanceOf(0x0000000000000000000000000000000000000000), + forkBbalance, + "Cheatcode did not change value at the storage slot." + ); + } + + function testCanShareDataAcrossSwaps() public { + assertEq(testValue, 999); + + uint256 val = 300; + vm.selectFork(forkA); + assertEq(val, 300); + + testValue = 100; + + vm.selectFork(forkB); + assertEq(val, 300); + assertEq(testValue, 100); + + val = 99; + testValue = 300; + + vm.selectFork(forkA); + assertEq(val, 99); + assertEq(testValue, 300); + } + + // ensures forks use different ids + function testCanChangeChainId() public { + vm.selectFork(forkA); + uint256 newChainId = 1337; + vm.chainId(newChainId); + uint256 expected = block.chainid; + assertEq(newChainId, expected); + } + + // ensures forks change chain ids automatically + function testCanAutoUpdateChainId() public { + vm.createSelectFork("sepolia"); + assertEq(block.chainid, 11155111); + } + + // ensures forks storage is cached at block + function testStorageCaching() public { + vm.createSelectFork("mainnet", 19800000); + } +} diff --git a/testdata/default/cheats/GasSnapshots.t.sol b/testdata/paris/cheats/GasSnapshots.t.sol similarity index 100% rename from testdata/default/cheats/GasSnapshots.t.sol rename to testdata/paris/cheats/GasSnapshots.t.sol diff --git a/testdata/default/cheats/LastCallGas.t.sol b/testdata/paris/cheats/LastCallGas.t.sol similarity index 100% rename from testdata/default/cheats/LastCallGas.t.sol rename to testdata/paris/cheats/LastCallGas.t.sol diff --git a/testdata/default/repros/Issue1543.t.sol b/testdata/paris/core/BeforeTest.t.sol similarity index 98% rename from testdata/default/repros/Issue1543.t.sol rename to testdata/paris/core/BeforeTest.t.sol index e58f331c4af9..2b14bcad1d2e 100644 --- a/testdata/default/repros/Issue1543.t.sol +++ b/testdata/paris/core/BeforeTest.t.sol @@ -10,7 +10,7 @@ contract SelfDestructor { } // https://github.com/foundry-rs/foundry/issues/1543 -contract Issue1543Test is DSTest { +contract BeforeTestSelfDestructTest is DSTest { SelfDestructor killer; uint256 a; uint256 b; diff --git a/testdata/default/fork/Transact.t.sol b/testdata/paris/fork/Transact.t.sol similarity index 99% rename from testdata/default/fork/Transact.t.sol rename to testdata/paris/fork/Transact.t.sol index 3756587725eb..92d595f98c51 100644 --- a/testdata/default/fork/Transact.t.sol +++ b/testdata/paris/fork/Transact.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; -import "../logs/console.sol"; +import "../../default/logs/console.sol"; interface IERC20 { function transfer(address to, uint256 amount) external returns (bool); diff --git a/testdata/paris/spec/ShanghaiCompat.t.sol b/testdata/paris/spec/ShanghaiCompat.t.sol new file mode 100644 index 000000000000..fd7213b3d070 --- /dev/null +++ b/testdata/paris/spec/ShanghaiCompat.t.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract ShanghaiCompat is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testPush0() public { + address target = address(uint160(uint256(0xc4f3))); + + bytes memory bytecode = hex"365f5f37365ff3"; + // 36 CALLDATASIZE + // 5F PUSH0 + // 5F PUSH0 + // 37 CALLDATACOPY -> copies calldata at mem[0..calldatasize] + + // 36 CALLDATASIZE + // 5F PUSH0 + // F3 RETURN -> returns mem[0..calldatasize] + + vm.etch(target, bytecode); + + (bool success, bytes memory result) = target.call(bytes("hello PUSH0")); + assertTrue(success); + assertEq(string(result), "hello PUSH0"); + } +} From 550ebd8f473c0f02434ddef9ad9cdca36be4bd54 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 18 Nov 2024 19:15:25 +0200 Subject: [PATCH 11/68] chore: update test values to cancun (#9344) --- crates/forge/tests/cli/build.rs | 16 ++--- crates/forge/tests/cli/cmd.rs | 110 ++++++++++++++++---------------- 2 files changed, 63 insertions(+), 63 deletions(-) diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 51c358612e86..9585b216b159 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -87,10 +87,10 @@ forgetest!(initcode_size_exceeds_limit, |prj, cmd| { str![[r#" { "HugeContract":{ - "runtime_size":202, - "init_size":49359, - "runtime_margin":24374, - "init_margin":-207 + "runtime_size":194, + "init_size":49344, + "runtime_margin":24382, + "init_margin":-192 } } "#]] @@ -117,10 +117,10 @@ forgetest!(initcode_size_limit_can_be_ignored, |prj, cmd| { str![[r#" { "HugeContract": { - "runtime_size": 202, - "init_size": 49359, - "runtime_margin": 24374, - "init_margin": -207 + "runtime_size": 194, + "init_size": 49344, + "runtime_margin": 24382, + "init_margin": -192 } } "#]] diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 18f0327f6e90..3cd4ae5edb04 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -2460,16 +2460,16 @@ contract GasReportFallbackTest is Test { Ran 1 test for test/DelegateProxyTest.sol:GasReportFallbackTest [PASS] test_fallback_gas_report() ([GAS]) Traces: - [331067] GasReportFallbackTest::test_fallback_gas_report() - ├─ [106511] → new ProxiedContract@[..] - │ └─ ← [Return] 246 bytes of code - ├─ [108698] → new DelegateProxy@[..] - │ └─ ← [Return] 143 bytes of code - ├─ [29396] DelegateProxy::fallback(100) - │ ├─ [3320] ProxiedContract::deposit(100) [delegatecall] + [327404] GasReportFallbackTest::test_fallback_gas_report() + ├─ [104475] → new ProxiedContract@[..] + │ └─ ← [Return] 236 bytes of code + ├─ [107054] → new DelegateProxy@[..] + │ └─ ← [Return] 135 bytes of code + ├─ [29384] DelegateProxy::fallback(100) + │ ├─ [3316] ProxiedContract::deposit(100) [delegatecall] │ │ └─ ← [Stop] │ └─ ← [Return] - ├─ [21160] DelegateProxy::deposit() + ├─ [21159] DelegateProxy::deposit() │ └─ ← [Stop] └─ ← [Stop] @@ -2477,18 +2477,18 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] | test/DelegateProxyTest.sol:DelegateProxy contract | | | | | | |---------------------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 108698 | 315 | | | | | +| 107054 | 300 | | | | | | Function Name | min | avg | median | max | # calls | -| deposit | 21160 | 21160 | 21160 | 21160 | 1 | -| fallback | 29396 | 29396 | 29396 | 29396 | 1 | +| deposit | 21159 | 21159 | 21159 | 21159 | 1 | +| fallback | 29384 | 29384 | 29384 | 29384 | 1 | | test/DelegateProxyTest.sol:ProxiedContract contract | | | | | | |-----------------------------------------------------|-----------------|------|--------|------|---------| | Deployment Cost | Deployment Size | | | | | -| 106511 | 276 | | | | | +| 104475 | 263 | | | | | | Function Name | min | avg | median | max | # calls | -| deposit | 3320 | 3320 | 3320 | 3320 | 1 | +| deposit | 3316 | 3316 | 3316 | 3316 | 1 | ... "#]]); @@ -2502,39 +2502,39 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] { "contract": "test/DelegateProxyTest.sol:DelegateProxy", "deployment": { - "gas": 108698, - "size": 315 + "gas": 107054, + "size": 300 }, "functions": { "deposit()": { "calls": 1, - "min": 21160, - "mean": 21160, - "median": 21160, - "max": 21160 + "min": 21159, + "mean": 21159, + "median": 21159, + "max": 21159 }, "fallback()": { "calls": 1, - "min": 29396, - "mean": 29396, - "median": 29396, - "max": 29396 + "min": 29384, + "mean": 29384, + "median": 29384, + "max": 29384 } } }, { "contract": "test/DelegateProxyTest.sol:ProxiedContract", "deployment": { - "gas": 106511, - "size": 276 + "gas": 104475, + "size": 263 }, "functions": { "deposit(uint256)": { "calls": 1, - "min": 3320, - "mean": 3320, - "median": 3320, - "max": 3320 + "min": 3316, + "mean": 3316, + "median": 3316, + "max": 3316 } } } @@ -2588,25 +2588,25 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] | test/NestedDeployTest.sol:AnotherChild contract | | | | | | |-------------------------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 0 | 130 | | | | | +| 0 | 124 | | | | | | Function Name | min | avg | median | max | # calls | -| w | 21162 | 21162 | 21162 | 21162 | 1 | +| w | 21161 | 21161 | 21161 | 21161 | 1 | | test/NestedDeployTest.sol:Child contract | | | | | | |------------------------------------------|-----------------|-----|--------|-----|---------| | Deployment Cost | Deployment Size | | | | | -| 0 | 498 | | | | | +| 0 | 477 | | | | | | Function Name | min | avg | median | max | # calls | -| child | 325 | 325 | 325 | 325 | 1 | +| child | 323 | 323 | 323 | 323 | 1 | | test/NestedDeployTest.sol:Parent contract | | | | | | |-------------------------------------------|-----------------|-----|--------|-----|---------| | Deployment Cost | Deployment Size | | | | | -| 254857 | 770 | | | | | +| 251997 | 739 | | | | | | Function Name | min | avg | median | max | # calls | -| child | 182 | 182 | 182 | 182 | 1 | +| child | 181 | 181 | 181 | 181 | 1 | ... "#]]); @@ -2620,15 +2620,15 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] "contract": "test/NestedDeployTest.sol:AnotherChild", "deployment": { "gas": 0, - "size": 130 + "size": 124 }, "functions": { "w()": { "calls": 1, - "min": 21162, - "mean": 21162, - "median": 21162, - "max": 21162 + "min": 21161, + "mean": 21161, + "median": 21161, + "max": 21161 } } }, @@ -2636,31 +2636,31 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] "contract": "test/NestedDeployTest.sol:Child", "deployment": { "gas": 0, - "size": 498 + "size": 477 }, "functions": { "child()": { "calls": 1, - "min": 325, - "mean": 325, - "median": 325, - "max": 325 + "min": 323, + "mean": 323, + "median": 323, + "max": 323 } } }, { "contract": "test/NestedDeployTest.sol:Parent", "deployment": { - "gas": 254857, - "size": 770 + "gas": 251997, + "size": 739 }, "functions": { "child()": { "calls": 1, - "min": 182, - "mean": 182, - "median": 182, - "max": 182 + "min": 181, + "mean": 181, + "median": 181, + "max": 181 } } } @@ -2982,10 +2982,10 @@ Compiler run successful! str![[r#" { "Counter": { - "runtime_size": 247, - "init_size": 277, - "runtime_margin": 24329, - "init_margin": 48875 + "runtime_size": 236, + "init_size": 263, + "runtime_margin": 24340, + "init_margin": 48889 } } "#]] From 7e323c23463193f70c025f0df57b559a79db9676 Mon Sep 17 00:00:00 2001 From: mgiagante <5287175+mgiagante@users.noreply.github.com> Date: Mon, 18 Nov 2024 19:42:38 +0000 Subject: [PATCH 12/68] feat(`forge build -vvvvv`): If verbosity level is 5 or higher show files to compile (#9325) * If verbosity level is 1 or higher, it shows dirty files. * Adds verbose message variant for compilation. * Removing `if..else` statement to always display `self.send_msg`. * Changes order of messages. * Removes semicolons and adds comment on message order. * Removes verbose variant in favor of the already existing variant. * nits, sort the dirty files list and prefix with - * Raises verbosity level to 5+ * Update crates/common/src/term.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Co-authored-by: zerosnacks Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- Cargo.lock | 1 + crates/common/Cargo.toml | 1 + crates/common/src/term.rs | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 1922c69b89af..7dc745cbf0de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3716,6 +3716,7 @@ dependencies = [ "foundry-compilers", "foundry-config", "foundry-macros", + "itertools 0.13.0", "num-format", "reqwest", "semver 1.0.23", diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 8fa745a13982..033c53e49dba 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -50,6 +50,7 @@ clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } comfy-table.workspace = true dunce.workspace = true eyre.workspace = true +itertools.workspace = true num-format.workspace = true reqwest.workspace = true semver.workspace = true diff --git a/crates/common/src/term.rs b/crates/common/src/term.rs index ead39b5face3..e673987454cb 100644 --- a/crates/common/src/term.rs +++ b/crates/common/src/term.rs @@ -3,6 +3,8 @@ use foundry_compilers::{ artifacts::remappings::Remapping, report::{self, BasicStdoutReporter, Reporter}, }; +use foundry_config::find_project_root; +use itertools::Itertools; use semver::Version; use std::{ io, @@ -17,6 +19,8 @@ use std::{ }; use yansi::Paint; +use crate::shell; + /// Some spinners // https://github.com/gernest/wow/blob/master/spin/spinners.go pub static SPINNERS: &[&[&str]] = &[ @@ -151,6 +155,24 @@ impl Drop for SpinnerReporter { impl Reporter for SpinnerReporter { fn on_compiler_spawn(&self, compiler_name: &str, version: &Version, dirty_files: &[PathBuf]) { + // Verbose message with dirty files displays first to avoid being overlapped + // by the spinner in .tick() which prints repeatedly over the same line. + if shell::verbosity() >= 5 { + let project_root = find_project_root(None); + + self.send_msg(format!( + "Files to compile:\n{}", + dirty_files + .iter() + .map(|path| { + let trimmed_path = path.strip_prefix(&project_root).unwrap_or(path); + format!("- {}", trimmed_path.display()) + }) + .sorted() + .format("\n") + )); + } + self.send_msg(format!( "Compiling {} files with {} {}.{}.{}", dirty_files.len(), From 6625e16e57bd66e4f7d43b1d2d6dfb74c4a88469 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 18 Nov 2024 21:46:06 +0100 Subject: [PATCH 13/68] chore: add some more debugging to forge bind (#9345) --- crates/sol-macro-gen/src/sol_macro_gen.rs | 70 +++++++++++++---------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/crates/sol-macro-gen/src/sol_macro_gen.rs b/crates/sol-macro-gen/src/sol_macro_gen.rs index 7309104fec61..8c146aa05dda 100644 --- a/crates/sol-macro-gen/src/sol_macro_gen.rs +++ b/crates/sol-macro-gen/src/sol_macro_gen.rs @@ -11,7 +11,7 @@ use alloy_sol_macro_expander::expand::expand; use alloy_sol_macro_input::{SolInput, SolInputKind}; -use eyre::{Context, Ok, OptionExt, Result}; +use eyre::{Context, OptionExt, Result}; use foundry_common::fs; use proc_macro2::{Span, TokenStream}; use std::{ @@ -39,7 +39,7 @@ impl SolMacroGen { #path }; - let sol_input: SolInput = syn::parse2(tokens).wrap_err("Failed to parse SolInput {e}")?; + let sol_input: SolInput = syn::parse2(tokens).wrap_err("failed to parse input")?; Ok(sol_input) } @@ -69,24 +69,35 @@ impl MultiSolMacroGen { pub fn generate_bindings(&mut self) -> Result<()> { for instance in &mut self.instances { - let input = instance.get_sol_input()?.normalize_json()?; + Self::generate_binding(instance).wrap_err_with(|| { + format!( + "failed to generate bindings for {}:{}", + instance.path.display(), + instance.name + ) + })?; + } - let SolInput { attrs: _attrs, path: _path, kind } = input; + Ok(()) + } - let tokens = match kind { - SolInputKind::Sol(mut file) => { - let sol_attr: syn::Attribute = syn::parse_quote! { - #[sol(rpc, alloy_sol_types = alloy::sol_types, alloy_contract = alloy::contract)] - }; - file.attrs.push(sol_attr); - expand(file).wrap_err("Failed to expand SolInput")? - } - _ => unreachable!(), - }; + fn generate_binding(instance: &mut SolMacroGen) -> Result<()> { + let input = instance.get_sol_input()?.normalize_json()?; - instance.expansion = Some(tokens); - } + let SolInput { attrs: _, path: _, kind } = input; + let tokens = match kind { + SolInputKind::Sol(mut file) => { + let sol_attr: syn::Attribute = syn::parse_quote! { + #[sol(rpc, alloy_sol_types = alloy::sol_types, alloy_contract = alloy::contract)] + }; + file.attrs.push(sol_attr); + expand(file).wrap_err("failed to expand")? + } + _ => unreachable!(), + }; + + instance.expansion = Some(tokens); Ok(()) } @@ -139,27 +150,28 @@ edition = "2021" )?; // Write src + let parse_error = |name: &str| { + format!("failed to parse generated tokens as an AST for {name};\nthis is likely a bug") + }; for instance in &self.instances { - let name = instance.name.to_lowercase(); - let contents = instance.expansion.as_ref().unwrap().to_string(); + let contents = instance.expansion.as_ref().unwrap(); - if !single_file { - let path = src.join(format!("{name}.rs")); - let file = syn::parse_file(&contents)?; - let contents = prettyplease::unparse(&file); - - fs::write(path.clone(), contents).wrap_err("Failed to write file")?; - writeln!(&mut lib_contents, "pub mod {name};")?; - } else { + let name = instance.name.to_lowercase(); + let path = src.join(format!("{name}.rs")); + let file = syn::parse2(contents.clone()) + .wrap_err_with(|| parse_error(&format!("{}:{}", path.display(), name)))?; + let contents = prettyplease::unparse(&file); + if single_file { write!(&mut lib_contents, "{contents}")?; + } else { + fs::write(path, contents).wrap_err("failed to write to file")?; + writeln!(&mut lib_contents, "pub mod {name};")?; } } let lib_path = src.join("lib.rs"); - let lib_file = syn::parse_file(&lib_contents)?; - + let lib_file = syn::parse_file(&lib_contents).wrap_err_with(|| parse_error("lib.rs"))?; let lib_contents = prettyplease::unparse(&lib_file); - fs::write(lib_path, lib_contents).wrap_err("Failed to write lib.rs")?; Ok(()) From 547d8a52ec7d286214511eb9c8ef5d5be601e81b Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 19 Nov 2024 01:11:59 +0400 Subject: [PATCH 14/68] feat: compilation restrictions (#8668) * [wip] feat: compilation restrictions * Cargo.lock * update patch * fixes * update patch * update patch * wip * deps * bytecode hash * fixes * rm patches * pub --- Cargo.lock | 194 ++++++++++++++++--- Cargo.toml | 4 +- crates/cast/bin/cmd/storage.rs | 4 +- crates/cli/src/utils/cmd.rs | 31 ++- crates/config/src/compilation.rs | 115 +++++++++++ crates/config/src/lib.rs | 76 +++++++- crates/forge/bin/cmd/bind_json.rs | 8 +- crates/forge/bin/cmd/compiler.rs | 8 +- crates/forge/bin/cmd/create.rs | 44 ++++- crates/forge/bin/cmd/eip712.rs | 14 +- crates/forge/bin/cmd/test/mod.rs | 4 +- crates/forge/tests/cli/config.rs | 2 + crates/script/src/verify.rs | 1 + crates/verify/src/etherscan/flatten.rs | 2 +- crates/verify/src/etherscan/standard_json.rs | 9 +- crates/verify/src/provider.rs | 11 +- crates/verify/src/verify.rs | 63 +++++- 17 files changed, 500 insertions(+), 90 deletions(-) create mode 100644 crates/config/src/compilation.rs diff --git a/Cargo.lock b/Cargo.lock index 7dc745cbf0de..81adf0f4b1e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -799,6 +799,16 @@ dependencies = [ "libc", ] +[[package]] +name = "annotate-snippets" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e35ed54e5ea7997c14ed4c70ba043478db1112e98263b3b035907aa197d991" +dependencies = [ + "anstyle", + "unicode-width 0.1.14", +] + [[package]] name = "anstream" version = "0.6.18" @@ -3572,9 +3582,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.7.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff37530e7c5deead0f9d7dc2a27b070e683bef79735ab453849ebdee74fa848f" +checksum = "0faa449506113b4969029da2ac1df3a1b3201bf10c99a4a8e6d684977b80c938" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3755,9 +3765,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.11.6" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4754b3f3bb924202b29bd7f0584ea1446018926342884c86029a7d56ef1a22c1" +checksum = "9edf09554357ebfcd2ea28503badbaca311aac3c947d269a6bae5256543aa172" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3780,7 +3790,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "solang-parser", + "solar-parse", "svm-rs", "svm-rs-builds", "tempfile", @@ -3793,9 +3803,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.11.6" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6289da0f17fdb5a0454020dce595502b0abd2a56c15a36d4f6c05bd6c4ff864" +checksum = "134b2499a20136716422f1ae5afd3a5d6c2ef833bf97b7c956285ca96c1d9743" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3803,9 +3813,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.11.6" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf322ab7b726f2bafe9a7e6fb67db02801b35584a2b1d122b4feb52d8e9e7f" +checksum = "78a9be34b3a43e77871e5bbd75f4a81d23eebf8f098519ae1ae9e163a0f3d0da" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3827,9 +3837,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.11.6" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec47f94c7833adfe8049c819d9e31a60c3f440a68cf5baf34c318413d3eb0700" +checksum = "1738950051ebcee2135adac07b3ef1708c6377ffed0c5e9a2bb485f31498befb" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3842,9 +3852,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.11.6" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61971b34545e8ea01502df9d076e811ad3926f27d31adf2641e0c931ca646933" +checksum = "2ac2d9982eedb0eb3819f82c7efa1e28c1f78e031cdfb6adfa34690364fe5e0d" dependencies = [ "alloy-primitives", "cfg-if", @@ -4700,6 +4710,10 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "hashbrown" @@ -5255,6 +5269,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "index_vec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44faf5bb8861a9c72e20d3fb0fdbd59233e43056e2b80475ab0aacdc2e781355" + [[package]] name = "indexmap" version = "1.9.3" @@ -5576,6 +5596,16 @@ dependencies = [ "regex-automata 0.4.9", ] +[[package]] +name = "lasso" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e14eda50a3494b3bf7b9ce51c52434a761e383d7238ce1dd5dcec2fbc13e9fb" +dependencies = [ + "dashmap", + "hashbrown 0.14.5", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -5712,6 +5742,12 @@ dependencies = [ "tendril", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matchers" version = "0.1.0" @@ -5890,9 +5926,9 @@ dependencies = [ [[package]] name = "mockall" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" dependencies = [ "cfg-if", "downcast", @@ -5904,9 +5940,9 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" dependencies = [ "cfg-if", "proc-macro2", @@ -6383,28 +6419,29 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.12" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +checksum = "8be4817d39f3272f69c59fe05d0535ae6456c2dc2fa1ba02910296c7e0a5c590" dependencies = [ "arrayvec", "bitvec", "byte-slice-cast", "impl-trait-for-tuples", "parity-scale-codec-derive", + "rustversion", "serde", ] [[package]] name = "parity-scale-codec-derive" -version = "3.6.12" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +checksum = "8781a75c6205af67215f382092b6e0a4ff3734798523e69073d4bcd294ec767b" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.87", ] [[package]] @@ -8046,9 +8083,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "indexmap 2.6.0", "itoa", @@ -8357,6 +8394,109 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "solar-ast" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5aeaf7a4bd326242c909bd287291226a540b62b36fa5824880248f4b1d4d6af" +dependencies = [ + "alloy-primitives", + "bumpalo", + "either", + "num-bigint", + "num-rational", + "semver 1.0.23", + "solar-data-structures", + "solar-interface", + "solar-macros", + "strum", + "typed-arena", +] + +[[package]] +name = "solar-config" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d00d672a40a1a3620d7696f01a2d3301abf883d8168e1a9da3bf83f0c8e343" +dependencies = [ + "strum", +] + +[[package]] +name = "solar-data-structures" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6e4eb0b72ed7adbb808897c85de08ea99609774a58c72e3dce55c758043ca2" +dependencies = [ + "bumpalo", + "index_vec", + "indexmap 2.6.0", + "parking_lot", + "rayon", + "rustc-hash", + "smallvec", +] + +[[package]] +name = "solar-interface" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21fb8925638f3da1bba7a9a6ebeac3511e5c6354f921f2bb2e1ddce4ac70c107" +dependencies = [ + "annotate-snippets", + "anstream", + "anstyle", + "const-hex", + "derive_builder", + "dunce", + "itertools 0.13.0", + "itoa", + "lasso", + "match_cfg", + "normalize-path", + "rayon", + "scc", + "scoped-tls", + "solar-config", + "solar-data-structures", + "solar-macros", + "thiserror 1.0.69", + "tracing", + "unicode-width 0.2.0", +] + +[[package]] +name = "solar-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0cc54b74e214647c1bbfc098d080cc5deac77f8dcb99aca91747276b01a15ad" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "solar-parse" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82c3659c15975cd80e5e1c44591278c230c59ad89082d797837499a4784e1b" +dependencies = [ + "alloy-primitives", + "bitflags 2.6.0", + "bumpalo", + "itertools 0.13.0", + "memchr", + "num-bigint", + "num-rational", + "num-traits", + "smallvec", + "solar-ast", + "solar-data-structures", + "solar-interface", + "tracing", +] + [[package]] name = "soldeer-commands" version = "0.5.1" @@ -9267,6 +9407,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typenum" version = "1.17.0" diff --git a/Cargo.toml b/Cargo.toml index 0c0962a2d95e..624264cf218e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -168,8 +168,8 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.7.3", default-features = false } -foundry-compilers = { version = "0.11.6", default-features = false } +foundry-block-explorers = { version = "0.9.0", default-features = false } +foundry-compilers = { version = "0.12.1", default-features = false } foundry-fork-db = "0.6.0" solang-parser = "=0.3.3" diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index 5e2459127001..13fa908bc94a 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -23,7 +23,7 @@ use foundry_compilers::{ artifacts::{ConfigurableContractArtifact, StorageLayout}, compilers::{ solc::{Solc, SolcCompiler}, - Compiler, CompilerSettings, + Compiler, }, Artifact, Project, }; @@ -316,7 +316,7 @@ fn print_storage(layout: StorageLayout, values: Vec, pretty: bool) fn add_storage_layout_output(project: &mut Project) { project.artifacts.additional_values.storage_layout = true; - project.settings.update_output_selection(|selection| { + project.update_output_selection(|selection| { selection.0.values_mut().for_each(|contract_selection| { contract_selection .values_mut() diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 2ebdc2539dff..19e4425b54c7 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -3,10 +3,10 @@ use alloy_primitives::Address; use eyre::{Result, WrapErr}; use foundry_common::{fs, TestFunctionExt}; use foundry_compilers::{ - artifacts::{CompactBytecode, CompactDeployedBytecode, Settings}, + artifacts::{CompactBytecode, Settings}, cache::{CacheEntry, CompilerCache}, utils::read_json_file, - Artifact, ProjectCompileOutput, + Artifact, ArtifactId, ProjectCompileOutput, }; use foundry_config::{error::ExtractConfigError, figment::Figment, Chain, Config, NamedChain}; use foundry_debugger::Debugger; @@ -32,17 +32,21 @@ use yansi::Paint; /// Runtime Bytecode of the given contract. #[track_caller] pub fn remove_contract( - output: &mut ProjectCompileOutput, + output: ProjectCompileOutput, path: &Path, name: &str, -) -> Result<(JsonAbi, CompactBytecode, CompactDeployedBytecode)> { - let contract = if let Some(contract) = output.remove(path, name) { - contract - } else { +) -> Result<(JsonAbi, CompactBytecode, ArtifactId)> { + let mut other = Vec::new(); + let Some((id, contract)) = output.into_artifacts().find_map(|(id, artifact)| { + if id.name == name && id.source == path { + Some((id, artifact)) + } else { + other.push(id.name); + None + } + }) else { let mut err = format!("could not find artifact: `{name}`"); - if let Some(suggestion) = - super::did_you_mean(name, output.artifacts().map(|(name, _)| name)).pop() - { + if let Some(suggestion) = super::did_you_mean(name, other).pop() { if suggestion != name { err = format!( r#"{err} @@ -64,12 +68,7 @@ pub fn remove_contract( .ok_or_else(|| eyre::eyre!("contract {} does not contain bytecode", name))? .into_owned(); - let runtime = contract - .get_deployed_bytecode() - .ok_or_else(|| eyre::eyre!("contract {} does not contain deployed bytecode", name))? - .into_owned(); - - Ok((abi, bin, runtime)) + Ok((abi, bin, id)) } /// Helper function for finding a contract by ContractName diff --git a/crates/config/src/compilation.rs b/crates/config/src/compilation.rs new file mode 100644 index 000000000000..b4f00b91b0d9 --- /dev/null +++ b/crates/config/src/compilation.rs @@ -0,0 +1,115 @@ +use crate::{filter::GlobMatcher, serde_helpers}; +use foundry_compilers::{ + artifacts::{BytecodeHash, EvmVersion}, + multi::{MultiCompilerRestrictions, MultiCompilerSettings}, + settings::VyperRestrictions, + solc::{Restriction, SolcRestrictions}, + RestrictionsWithVersion, +}; +use semver::VersionReq; +use serde::{Deserialize, Serialize}; + +/// Keeps possible overrides for default settings which users may configure to construct additional +/// settings profile. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct SettingsOverrides { + pub name: String, + pub via_ir: Option, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + pub evm_version: Option, + pub optimizer: Option, + pub optimizer_runs: Option, + pub bytecode_hash: Option, +} + +impl SettingsOverrides { + /// Applies the overrides to the given settings. + pub fn apply(&self, settings: &mut MultiCompilerSettings) { + if let Some(via_ir) = self.via_ir { + settings.solc.via_ir = Some(via_ir); + } + + if let Some(evm_version) = self.evm_version { + settings.solc.evm_version = Some(evm_version); + settings.vyper.evm_version = Some(evm_version); + } + + if let Some(enabled) = self.optimizer { + settings.solc.optimizer.enabled = Some(enabled); + } + + if let Some(optimizer_runs) = self.optimizer_runs { + settings.solc.optimizer.runs = Some(optimizer_runs); + } + + if let Some(bytecode_hash) = self.bytecode_hash { + if let Some(metadata) = settings.solc.metadata.as_mut() { + metadata.bytecode_hash = Some(bytecode_hash); + } else { + settings.solc.metadata = Some(bytecode_hash.into()); + } + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum RestrictionsError { + #[error("specified both exact and relative restrictions for {0}")] + BothExactAndRelative(&'static str), +} + +/// Restrictions for compilation of given paths. +/// +/// Only purpose of this type is to accept user input to later construct +/// `RestrictionsWithVersion`. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct CompilationRestrictions { + pub paths: GlobMatcher, + pub version: Option, + pub via_ir: Option, + pub bytecode_hash: Option, + + pub min_optimizer_runs: Option, + pub optimizer_runs: Option, + pub max_optimizer_runs: Option, + + #[serde(default, with = "serde_helpers::display_from_str_opt")] + pub min_evm_version: Option, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + pub evm_version: Option, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + pub max_evm_version: Option, +} + +impl TryFrom for RestrictionsWithVersion { + type Error = RestrictionsError; + + fn try_from(value: CompilationRestrictions) -> Result { + let (min_evm, max_evm) = + match (value.min_evm_version, value.max_evm_version, value.evm_version) { + (None, None, Some(exact)) => (Some(exact), Some(exact)), + (min, max, None) => (min, max), + _ => return Err(RestrictionsError::BothExactAndRelative("evm_version")), + }; + let (min_opt, max_opt) = + match (value.min_optimizer_runs, value.max_optimizer_runs, value.optimizer_runs) { + (None, None, Some(exact)) => (Some(exact), Some(exact)), + (min, max, None) => (min, max), + _ => return Err(RestrictionsError::BothExactAndRelative("optimizer_runs")), + }; + Ok(Self { + restrictions: MultiCompilerRestrictions { + solc: SolcRestrictions { + evm_version: Restriction { min: min_evm, max: max_evm }, + via_ir: value.via_ir, + optimizer_runs: Restriction { min: min_opt, max: max_opt }, + bytecode_hash: value.bytecode_hash, + }, + vyper: VyperRestrictions { + evm_version: Restriction { min: min_evm, max: max_evm }, + }, + }, + version: value.version, + }) + } +} diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index b87eadcefa0b..6444802e3bc9 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -33,8 +33,10 @@ use foundry_compilers::{ Compiler, }, error::SolcError, + multi::{MultiCompilerParsedSource, MultiCompilerRestrictions}, solc::{CliSettings, SolcSettings}, - ConfigurableArtifacts, Project, ProjectPathsConfig, VyperLanguage, + ConfigurableArtifacts, Graph, Project, ProjectPathsConfig, RestrictionsWithVersion, + VyperLanguage, }; use inflector::Inflector; use regex::Regex; @@ -43,6 +45,7 @@ use semver::Version; use serde::{Deserialize, Serialize, Serializer}; use std::{ borrow::Cow, + collections::BTreeMap, fs, path::{Path, PathBuf}, str::FromStr, @@ -116,6 +119,9 @@ use vyper::VyperConfig; mod bind_json; use bind_json::BindJsonConfig; +mod compilation; +use compilation::{CompilationRestrictions, SettingsOverrides}; + /// Foundry configuration /// /// # Defaults @@ -479,6 +485,14 @@ pub struct Config { #[serde(rename = "__warnings", default, skip_serializing)] pub warnings: Vec, + /// Additional settings profiles to use when compiling. + #[serde(default)] + pub additional_compiler_profiles: Vec, + + /// Restrictions on compilation of certain files. + #[serde(default)] + pub compilation_restrictions: Vec, + /// PRIVATE: This structure may grow, As such, constructing this structure should /// _always_ be done using a public constructor or update syntax: /// @@ -867,12 +881,66 @@ impl Config { self.create_project(false, true) } + /// Builds mapping with additional settings profiles. + fn additional_settings( + &self, + base: &MultiCompilerSettings, + ) -> BTreeMap { + let mut map = BTreeMap::new(); + + for profile in &self.additional_compiler_profiles { + let mut settings = base.clone(); + profile.apply(&mut settings); + map.insert(profile.name.clone(), settings); + } + + map + } + + /// Resolves globs and builds a mapping from individual source files to their restrictions + fn restrictions( + &self, + paths: &ProjectPathsConfig, + ) -> Result>, SolcError> + { + let mut map = BTreeMap::new(); + + let graph = Graph::::resolve(paths)?; + let (sources, _) = graph.into_sources(); + + for res in &self.compilation_restrictions { + for source in sources.keys().filter(|path| { + if res.paths.is_match(path) { + true + } else if let Ok(path) = path.strip_prefix(&paths.root) { + res.paths.is_match(path) + } else { + false + } + }) { + let res: RestrictionsWithVersion<_> = + res.clone().try_into().map_err(SolcError::msg)?; + if !map.contains_key(source) { + map.insert(source.clone(), res); + } else { + map.get_mut(source.as_path()).unwrap().merge(res); + } + } + } + + Ok(map) + } + /// Creates a [Project] with the given `cached` and `no_artifacts` flags pub fn create_project(&self, cached: bool, no_artifacts: bool) -> Result { + let settings = self.compiler_settings()?; + let paths = self.project_paths(); let mut builder = Project::builder() .artifacts(self.configured_artifacts_handler()) - .paths(self.project_paths()) - .settings(self.compiler_settings()?) + .additional_settings(self.additional_settings(&settings)) + .restrictions(self.restrictions(&paths)?) + .settings(settings) + .paths(paths) .ignore_error_codes(self.ignored_error_codes.iter().copied().map(Into::into)) .ignore_paths(self.ignored_file_paths.clone()) .set_compiler_severity_filter(if self.deny_warnings { @@ -2243,6 +2311,8 @@ impl Default for Config { eof_version: None, alphanet: false, transaction_timeout: 120, + additional_compiler_profiles: Default::default(), + compilation_restrictions: Default::default(), eof: false, _non_exhaustive: (), } diff --git a/crates/forge/bin/cmd/bind_json.rs b/crates/forge/bin/cmd/bind_json.rs index c6a052836baf..de7a5a7a71aa 100644 --- a/crates/forge/bin/cmd/bind_json.rs +++ b/crates/forge/bin/cmd/bind_json.rs @@ -11,7 +11,7 @@ use foundry_compilers::{ multi::{MultiCompilerLanguage, MultiCompilerParsedSource}, project::ProjectCompiler, solc::SolcLanguage, - CompilerSettings, Graph, Project, + Graph, Project, }; use foundry_config::Config; use itertools::Itertools; @@ -72,7 +72,7 @@ impl BindJsonArgs { // We only generate bindings for a single Solidity version to avoid conflicts. let mut sources = graph // resolve graph into mapping language -> version -> sources - .into_sources_by_version(project.offline, &project.locked_versions, &project.compiler)? + .into_sources_by_version(&project)? .0 .into_iter() // we are only interested in Solidity sources @@ -81,7 +81,7 @@ impl BindJsonArgs { .1 .into_iter() // For now, we are always picking the latest version. - .max_by(|(v1, _), (v2, _)| v1.cmp(v2)) + .max_by(|(v1, _, _), (v2, _, _)| v1.cmp(v2)) .unwrap() .1; @@ -229,7 +229,7 @@ impl PreprocessedState { fn compile(self) -> Result { let Self { sources, target_path, mut project, config } = self; - project.settings.update_output_selection(|selection| { + project.update_output_selection(|selection| { *selection = OutputSelection::ast_output_selection(); }); diff --git a/crates/forge/bin/cmd/compiler.rs b/crates/forge/bin/cmd/compiler.rs index a7115c4876b8..f5d62b671419 100644 --- a/crates/forge/bin/cmd/compiler.rs +++ b/crates/forge/bin/cmd/compiler.rs @@ -63,18 +63,14 @@ impl ResolveArgs { let project = config.project()?; let graph = Graph::resolve(&project.paths)?; - let (sources, _) = graph.into_sources_by_version( - project.offline, - &project.locked_versions, - &project.compiler, - )?; + let (sources, _) = graph.into_sources_by_version(&project)?; let mut output: BTreeMap> = BTreeMap::new(); for (language, sources) in sources { let mut versions_with_paths: Vec = sources .iter() - .map(|(version, sources)| { + .map(|(version, sources, _)| { let paths: Vec = sources .iter() .filter_map(|(path_file, _)| { diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 83494acb99de..e19feafcc628 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -20,7 +20,9 @@ use foundry_common::{ fmt::parse_tokens, shell, }; -use foundry_compilers::{artifacts::BytecodeObject, info::ContractInfo, utils::canonicalize}; +use foundry_compilers::{ + artifacts::BytecodeObject, info::ContractInfo, utils::canonicalize, ArtifactId, +}; use foundry_config::{ figment::{ self, @@ -106,9 +108,9 @@ impl CreateArgs { project.find_contract_path(&self.contract.name)? }; - let mut output = compile::compile_target(&target_path, &project, shell::is_json())?; + let output = compile::compile_target(&target_path, &project, shell::is_json())?; - let (abi, bin, _) = remove_contract(&mut output, &target_path, &self.contract.name)?; + let (abi, bin, id) = remove_contract(output, &target_path, &self.contract.name)?; let bin = match bin.object { BytecodeObject::Bytecode(_) => bin.object, @@ -148,8 +150,17 @@ impl CreateArgs { if self.unlocked { // Deploy with unlocked account let sender = self.eth.wallet.from.expect("required"); - self.deploy(abi, bin, params, provider, chain_id, sender, config.transaction_timeout) - .await + self.deploy( + abi, + bin, + params, + provider, + chain_id, + sender, + config.transaction_timeout, + id, + ) + .await } else { // Deploy with signer let signer = self.eth.wallet.signer().await?; @@ -157,8 +168,17 @@ impl CreateArgs { let provider = ProviderBuilder::<_, _, AnyNetwork>::default() .wallet(EthereumWallet::new(signer)) .on_provider(provider); - self.deploy(abi, bin, params, provider, chain_id, deployer, config.transaction_timeout) - .await + self.deploy( + abi, + bin, + params, + provider, + chain_id, + deployer, + config.transaction_timeout, + id, + ) + .await } } @@ -177,13 +197,14 @@ impl CreateArgs { &self, constructor_args: Option, chain: u64, + id: &ArtifactId, ) -> Result<()> { // NOTE: this does not represent the same `VerifyArgs` that would be sent after deployment, // since we don't know the address yet. let mut verify = forge_verify::VerifyArgs { address: Default::default(), contract: Some(self.contract.clone()), - compiler_version: None, + compiler_version: Some(id.version.to_string()), constructor_args, constructor_args_path: None, num_of_optimizations: None, @@ -204,6 +225,7 @@ impl CreateArgs { evm_version: self.opts.compiler.evm_version, show_standard_json_input: self.show_standard_json_input, guess_constructor_args: false, + compilation_profile: Some(id.profile.to_string()), }; // Check config for Etherscan API Keys to avoid preflight check failing if no @@ -229,6 +251,7 @@ impl CreateArgs { chain: u64, deployer_address: Address, timeout: u64, + id: ArtifactId, ) -> Result<()> { let bin = bin.into_bytes().unwrap_or_else(|| { panic!("no bytecode found in bin object for {}", self.contract.name) @@ -305,7 +328,7 @@ impl CreateArgs { constructor_args = Some(hex::encode(encoded_args)); } - self.verify_preflight_check(constructor_args.clone(), chain).await?; + self.verify_preflight_check(constructor_args.clone(), chain, &id).await?; } // Deploy the actual contract @@ -339,7 +362,7 @@ impl CreateArgs { let verify = forge_verify::VerifyArgs { address, contract: Some(self.contract), - compiler_version: None, + compiler_version: Some(id.version.to_string()), constructor_args, constructor_args_path: None, num_of_optimizations, @@ -357,6 +380,7 @@ impl CreateArgs { evm_version: self.opts.compiler.evm_version, show_standard_json_input: self.show_standard_json_input, guess_constructor_args: false, + compilation_profile: Some(id.profile.to_string()), }; sh_println!("Waiting for {} to detect contract deployment...", verify.verifier.verifier)?; verify.run().await diff --git a/crates/forge/bin/cmd/eip712.rs b/crates/forge/bin/cmd/eip712.rs index 5014d38d703b..eb1d8dc1d315 100644 --- a/crates/forge/bin/cmd/eip712.rs +++ b/crates/forge/bin/cmd/eip712.rs @@ -2,14 +2,10 @@ use clap::{Parser, ValueHint}; use eyre::{Ok, OptionExt, Result}; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::compile::ProjectCompiler; -use foundry_compilers::{ - artifacts::{ - output_selection::OutputSelection, - visitor::{Visitor, Walk}, - ContractDefinition, EnumDefinition, SourceUnit, StructDefinition, TypeDescriptions, - TypeName, - }, - CompilerSettings, +use foundry_compilers::artifacts::{ + output_selection::OutputSelection, + visitor::{Visitor, Walk}, + ContractDefinition, EnumDefinition, SourceUnit, StructDefinition, TypeDescriptions, TypeName, }; use std::{collections::BTreeMap, fmt::Write, path::PathBuf}; @@ -31,7 +27,7 @@ impl Eip712Args { let config = self.try_load_config_emit_warnings()?; let mut project = config.create_project(false, true)?; let target_path = dunce::canonicalize(self.target_path)?; - project.settings.update_output_selection(|selection| { + project.update_output_selection(|selection| { *selection = OutputSelection::ast_output_selection(); }); diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 613d570785b9..4e811bcbfbf9 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -23,7 +23,7 @@ use foundry_cli::{ use foundry_common::{compile::ProjectCompiler, evm::EvmArgs, fs, shell, TestFunctionExt}; use foundry_compilers::{ artifacts::output_selection::OutputSelection, - compilers::{multi::MultiCompilerLanguage, CompilerSettings, Language}, + compilers::{multi::MultiCompilerLanguage, Language}, utils::source_files_iter, ProjectCompileOutput, }; @@ -203,7 +203,7 @@ impl TestArgs { filter: &ProjectPathsAwareFilter, ) -> Result> { let mut project = config.create_project(true, true)?; - project.settings.update_output_selection(|selection| { + project.update_output_selection(|selection| { *selection = OutputSelection::common_output_selection(["abi".to_string()]); }); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 11bcd49e2c40..db87a85ba0d1 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -154,6 +154,8 @@ forgetest!(can_extract_config_values, |prj, cmd| { eof_version: None, alphanet: false, transaction_timeout: 120, + additional_compiler_profiles: Default::default(), + compilation_restrictions: Default::default(), eof: false, _non_exhaustive: (), }; diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index 55435c559242..220991703df7 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -155,6 +155,7 @@ impl VerifyBundle { evm_version: None, show_standard_json_input: false, guess_constructor_args: false, + compilation_profile: Some(artifact.profile.to_string()), }; return Some(verify) diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index ffe1496cad5d..a0b3defd7f90 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -90,7 +90,7 @@ impl EtherscanFlattenedSource { let out = SolcCompiler::Specific(solc).compile(&input)?; if out.errors.iter().any(|e| e.is_error()) { let mut o = AggregatedCompilerOutput::::default(); - o.extend(version, RawBuildInfo::new(&input, &out, false)?, out); + o.extend(version, RawBuildInfo::new(&input, &out, false)?, "default", out); let diags = o.diagnostics(&[], &[], Default::default()); eyre::bail!( diff --git a/crates/verify/src/etherscan/standard_json.rs b/crates/verify/src/etherscan/standard_json.rs index cab3010fa638..e2fb5d2a47c0 100644 --- a/crates/verify/src/etherscan/standard_json.rs +++ b/crates/verify/src/etherscan/standard_json.rs @@ -18,7 +18,8 @@ impl EtherscanSourceProvider for EtherscanStandardJsonSource { .wrap_err("Failed to get standard json input")? .normalize_evm_version(&context.compiler_version); - input.settings.libraries.libs = input + let mut settings = context.compiler_settings.solc.settings.clone(); + settings.libraries.libs = input .settings .libraries .libs @@ -28,8 +29,12 @@ impl EtherscanSourceProvider for EtherscanStandardJsonSource { }) .collect(); + settings.remappings = input.settings.remappings; + // remove all incompatible settings - input.settings.sanitize(&context.compiler_version, SolcLanguage::Solidity); + settings.sanitize(&context.compiler_version, SolcLanguage::Solidity); + + input.settings = settings; let source = serde_json::to_string(&input).wrap_err("Failed to parse standard json input")?; diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index bc01bd9e304d..ab6c5e9f642f 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -9,7 +9,8 @@ use eyre::{OptionExt, Result}; use foundry_common::compile::ProjectCompiler; use foundry_compilers::{ artifacts::{output_selection::OutputSelection, Metadata, Source}, - compilers::{multi::MultiCompilerParsedSource, solc::SolcCompiler, CompilerSettings}, + compilers::{multi::MultiCompilerParsedSource, solc::SolcCompiler}, + multi::MultiCompilerSettings, solc::Solc, Graph, Project, }; @@ -25,6 +26,7 @@ pub struct VerificationContext { pub target_path: PathBuf, pub target_name: String, pub compiler_version: Version, + pub compiler_settings: MultiCompilerSettings, } impl VerificationContext { @@ -33,6 +35,7 @@ impl VerificationContext { target_name: String, compiler_version: Version, config: Config, + compiler_settings: MultiCompilerSettings, ) -> Result { let mut project = config.project()?; project.no_artifacts = true; @@ -40,13 +43,13 @@ impl VerificationContext { let solc = Solc::find_or_install(&compiler_version)?; project.compiler.solc = Some(SolcCompiler::Specific(solc)); - Ok(Self { config, project, target_name, target_path, compiler_version }) + Ok(Self { config, project, target_name, target_path, compiler_version, compiler_settings }) } /// Compiles target contract requesting only ABI and returns it. pub fn get_target_abi(&self) -> Result { let mut project = self.project.clone(); - project.settings.update_output_selection(|selection| { + project.update_output_selection(|selection| { *selection = OutputSelection::common_output_selection(["abi".to_string()]) }); @@ -65,7 +68,7 @@ impl VerificationContext { /// Compiles target file requesting only metadata and returns it. pub fn get_target_metadata(&self) -> Result { let mut project = self.project.clone(); - project.settings.update_output_selection(|selection| { + project.update_output_selection(|selection| { *selection = OutputSelection::common_output_selection(["metadata".to_string()]); }); diff --git a/crates/verify/src/verify.rs b/crates/verify/src/verify.rs index 89cfd99aa676..9c32ee95f5bf 100644 --- a/crates/verify/src/verify.rs +++ b/crates/verify/src/verify.rs @@ -80,6 +80,10 @@ pub struct VerifyArgs { #[arg(long, value_name = "VERSION")] pub compiler_version: Option, + /// The compilation profile to use to build the smart contract. + #[arg(long, value_name = "PROFILE_NAME")] + pub compilation_profile: Option, + /// The number of optimization runs used to build the smart contract. #[arg(long, visible_alias = "optimizer-runs", value_name = "NUM")] pub num_of_optimizations: Option, @@ -258,6 +262,8 @@ impl VerifyArgs { project.find_contract_path(&contract.name)? }; + let cache = project.read_cache_file().ok(); + let version = if let Some(ref version) = self.compiler_version { version.trim_start_matches('v').parse()? } else if let Some(ref solc) = config.solc { @@ -265,10 +271,8 @@ impl VerifyArgs { SolcReq::Version(version) => version.to_owned(), SolcReq::Local(solc) => Solc::new(solc)?.version, } - } else if let Some(entry) = project - .read_cache_file() - .ok() - .and_then(|mut cache| cache.files.remove(&contract_path)) + } else if let Some(entry) = + cache.as_ref().and_then(|cache| cache.files.get(&contract_path).cloned()) { let unique_versions = entry .artifacts @@ -291,7 +295,48 @@ impl VerifyArgs { eyre::bail!("If cache is disabled, compiler version must be either provided with `--compiler-version` option or set in foundry.toml") }; - VerificationContext::new(contract_path, contract.name.clone(), version, config) + let settings = if let Some(profile) = &self.compilation_profile { + if profile == "default" { + &project.settings + } else if let Some(settings) = project.additional_settings.get(profile.as_str()) { + settings + } else { + eyre::bail!("Unknown compilation profile: {}", profile) + } + } else if let Some((cache, entry)) = cache + .as_ref() + .and_then(|cache| Some((cache, cache.files.get(&contract_path)?.clone()))) + { + let profiles = entry + .artifacts + .get(&contract.name) + .and_then(|artifacts| artifacts.get(&version)) + .map(|artifacts| artifacts.keys().collect::>()) + .unwrap_or_default(); + + if profiles.is_empty() { + eyre::bail!("No matching artifact found for {}", contract.name); + } else if profiles.len() > 1 { + eyre::bail!("Ambiguous compilation profiles found in cache: {}, please specify the profile through `--compilation-profile` flag", profiles.iter().join(", ")) + } + + let profile = profiles.into_iter().next().unwrap().to_owned(); + let settings = cache.profiles.get(&profile).expect("must be present"); + + settings + } else if project.additional_settings.is_empty() { + &project.settings + } else { + eyre::bail!("If cache is disabled, compilation profile must be provided with `--compiler-version` option or set in foundry.toml") + }; + + VerificationContext::new( + contract_path, + contract.name.clone(), + version, + config, + settings.clone(), + ) } else { if config.get_rpc_url().is_none() { eyre::bail!("You have to provide a contract name or a valid RPC URL") @@ -311,11 +356,19 @@ impl VerifyArgs { )) }; + let settings = project + .settings_profiles() + .find_map(|(name, settings)| { + (name == artifact_id.profile.as_str()).then_some(settings) + }) + .expect("must be present"); + VerificationContext::new( artifact_id.source.clone(), artifact_id.name.split('.').next().unwrap().to_owned(), artifact_id.version.clone(), config, + settings.clone(), ) } } From c13d42e850da353c0856a8b0d4123e13cc40045d Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 19 Nov 2024 01:27:51 +0400 Subject: [PATCH 15/68] fix: identification of contracts in scripts (#9346) * fix: identification of contracts in scripts * clippy --- crates/script/src/simulate.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index f8f9c9a3662f..a9798c43e471 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -16,7 +16,7 @@ use eyre::{Context, Result}; use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_different_gas_calc, now}; -use foundry_common::{get_contract_name, ContractData}; +use foundry_common::ContractData; use foundry_evm::traces::{decode_trace_arena, render_trace_arena}; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; @@ -205,9 +205,8 @@ impl PreSimulationState { .contracts .iter() .filter_map(move |(addr, contract_id)| { - let contract_name = get_contract_name(contract_id); if let Ok(Some((_, data))) = - self.build_data.known_contracts.find_by_name_or_identifier(contract_name) + self.build_data.known_contracts.find_by_name_or_identifier(contract_id) { return Some((*addr, data)); } From dacf3410e84bab1d8bab34a3c53364ab4fca4063 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:44:53 +0200 Subject: [PATCH 16/68] fix(`coverage`): allow `ir-minimum` for versions < 0.8.5 (#9341) * fix(coverage): allow ir-minimum for versions < 0.8.5 * Fix * Remove 0.8.13 restriction, update message and sanitize for 0.8.4 if version cannot be detected * Update crates/forge/bin/cmd/coverage.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/forge/bin/cmd/coverage.rs | 27 +++++++++++------------- crates/forge/tests/cli/coverage.rs | 34 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 56b2024e391b..7e60a5451efc 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -16,7 +16,7 @@ use forge::{ use foundry_cli::utils::{LoadConfig, STATIC_FUZZ_SEED}; use foundry_common::{compile::ProjectCompiler, fs}; use foundry_compilers::{ - artifacts::{sourcemap::SourceMap, CompactBytecode, CompactDeployedBytecode}, + artifacts::{sourcemap::SourceMap, CompactBytecode, CompactDeployedBytecode, SolcLanguage}, Artifact, ArtifactId, Project, ProjectCompileOutput, }; use foundry_config::{Config, SolcReq}; @@ -94,23 +94,12 @@ impl CoverageArgs { // Set up the project let mut project = config.create_project(false, false)?; if self.ir_minimum { - // TODO: How to detect solc version if the user does not specify a solc version in - // config case1: specify local installed solc ? - // case2: multiple solc versions used and auto_detect_solc == true - if let Some(SolcReq::Version(version)) = &config.solc { - if *version < Version::new(0, 8, 13) { - return Err(eyre::eyre!( - "viaIR with minimum optimization is only available in Solidity 0.8.13 and above." - )); - } - } - // print warning message sh_warn!("{}", concat!( - "Warning! \"--ir-minimum\" flag enables viaIR with minimum optimization, \ + "`--ir-minimum` enables viaIR with minimum optimization, \ which can result in inaccurate source mappings.\n", "Only use this flag as a workaround if you are experiencing \"stack too deep\" errors.\n", - "Note that \"viaIR\" is only available in Solidity 0.8.13 and above.\n", + "Note that \"viaIR\" is production ready since Solidity 0.8.13 and above.\n", "See more: https://github.com/foundry-rs/foundry/issues/3357", ))?; @@ -119,7 +108,15 @@ impl CoverageArgs { // And also in new releases of solidity: // https://github.com/ethereum/solidity/issues/13972#issuecomment-1628632202 project.settings.solc.settings = - project.settings.solc.settings.with_via_ir_minimum_optimization() + project.settings.solc.settings.with_via_ir_minimum_optimization(); + let version = if let Some(SolcReq::Version(version)) = &config.solc { + version + } else { + // Sanitize settings for solc 0.8.4 if version cannot be detected. + // See . + &Version::new(0, 8, 4) + }; + project.settings.solc.settings.sanitize(version, SolcLanguage::Solidity); } else { project.settings.solc.optimizer.disable(); project.settings.solc.optimizer.runs = None; diff --git a/crates/forge/tests/cli/coverage.rs b/crates/forge/tests/cli/coverage.rs index 0ed272ae2531..65900c592ba5 100644 --- a/crates/forge/tests/cli/coverage.rs +++ b/crates/forge/tests/cli/coverage.rs @@ -1413,3 +1413,37 @@ contract AContractTest is DSTest { "#]]); }); + +// +// Test coverage with `--ir-minimum` for solidity < 0.8.5. +forgetest!(test_ir_minimum_coverage, |prj, cmd| { + prj.insert_ds_test(); + prj.add_source( + "AContract.sol", + r#" +pragma solidity 0.8.4; + +contract AContract { + function isContract(address account) internal view returns (bool) { + bytes32 codehash; + bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; + assembly { + codehash := extcodehash(account) + } + return (codehash != accountHash && codehash != 0x0); + } +} + "#, + ) + .unwrap(); + + // Assert coverage doesn't fail with `Error: Unknown key "inliner"`. + cmd.arg("coverage").arg("--ir-minimum").assert_success().stdout_eq(str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|-------------|--------------|---------------|-------------| +| src/AContract.sol | 0.00% (0/4) | 0.00% (0/4) | 100.00% (0/0) | 0.00% (0/1) | +| Total | 0.00% (0/4) | 0.00% (0/4) | 100.00% (0/0) | 0.00% (0/1) | + +"#]]); +}); From 25b317afd1387da82deca96e2b98d9a9c7b34784 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 19 Nov 2024 15:23:05 +0100 Subject: [PATCH 17/68] chore: don't color error message in red (#9352) * chore: don't color error message in red * Update crates/cli/src/handler.rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --- crates/cli/src/handler.rs | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/crates/cli/src/handler.rs b/crates/cli/src/handler.rs index b2fbb49d4ea8..ed32fa16b808 100644 --- a/crates/cli/src/handler.rs +++ b/crates/cli/src/handler.rs @@ -1,21 +1,16 @@ use eyre::EyreHandler; -use std::error::Error; -use yansi::Paint; +use std::{error::Error, fmt}; /// A custom context type for Foundry specific error reporting via `eyre` #[derive(Debug)] pub struct Handler; impl EyreHandler for Handler { - fn debug( - &self, - error: &(dyn Error + 'static), - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { + fn debug(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { - return core::fmt::Debug::fmt(error, f) + return fmt::Debug::fmt(error, f) } - write!(f, "{}", error.red())?; + write!(f, "{error}")?; if let Some(cause) = error.source() { write!(f, "\n\nContext:")?; @@ -37,7 +32,7 @@ impl EyreHandler for Handler { } } -/// Installs the Foundry eyre hook as the global error report hook. +/// Installs the Foundry [eyre] and [panic](mod@std::panic) hooks as the global ones. /// /// # Details /// @@ -51,19 +46,17 @@ pub fn install() { std::env::set_var("RUST_BACKTRACE", "1"); } - if std::env::var_os("FOUNDRY_DEBUG").is_some() { - if let Err(e) = color_eyre::install() { - debug!("failed to install color eyre error hook: {e}"); - } + let panic_section = + "This is a bug. Consider reporting it at https://github.com/foundry-rs/foundry"; + let (panic_hook, debug_eyre_hook) = + color_eyre::config::HookBuilder::default().panic_section(panic_section).into_hooks(); + panic_hook.install(); + let eyre_install_result = if std::env::var_os("FOUNDRY_DEBUG").is_some() { + debug_eyre_hook.install() } else { - let (panic_hook, _) = color_eyre::config::HookBuilder::default() - .panic_section( - "This is a bug. Consider reporting it at https://github.com/foundry-rs/foundry", - ) - .into_hooks(); - panic_hook.install(); - if let Err(e) = eyre::set_hook(Box::new(move |_| Box::new(Handler))) { - debug!("failed to install eyre error hook: {e}"); - } + eyre::set_hook(Box::new(|_| Box::new(Handler))) + }; + if let Err(e) = eyre_install_result { + debug!("failed to install eyre error hook: {e}"); } } From 19249c3874146ec1a60c35fbe7800f9f21319207 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 19 Nov 2024 15:23:21 +0100 Subject: [PATCH 18/68] chore: remove redundant 'setup failed' in setup errors (#9354) --- crates/forge/src/runner.rs | 10 ++-------- crates/forge/tests/it/core.rs | 10 ++-------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 8ed3706defa8..1302c39ad1f5 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -157,15 +157,9 @@ impl ContractRunner<'_> { raw: RawCallResult { traces, labels, logs, coverage, .. }, reason, } = *err; - (logs, traces, labels, Some(format!("setup failed: {reason}")), coverage) + (logs, traces, labels, Some(reason), coverage) } - Err(err) => ( - Vec::new(), - None, - HashMap::default(), - Some(format!("setup failed: {err}")), - None, - ), + Err(err) => (Vec::new(), None, HashMap::default(), Some(err.to_string()), None), }; traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces))); logs.extend(setup_logs); diff --git a/crates/forge/tests/it/core.rs b/crates/forge/tests/it/core.rs index abab87d2866e..c8a599195441 100644 --- a/crates/forge/tests/it/core.rs +++ b/crates/forge/tests/it/core.rs @@ -23,7 +23,7 @@ async fn test_core() { vec![( "setUp()", false, - Some("setup failed: revert: setup failed predictably".to_string()), + Some("revert: setup failed predictably".to_string()), None, None, )], @@ -70,13 +70,7 @@ async fn test_core() { ), ( "default/core/FailingTestAfterFailedSetup.t.sol:FailingTestAfterFailedSetupTest", - vec![( - "setUp()", - false, - Some("setup failed: execution error".to_string()), - None, - None, - )], + vec![("setUp()", false, Some("execution error".to_string()), None, None)], ), ( "default/core/MultipleAfterInvariant.t.sol:MultipleAfterInvariant", From 9b490823166927b218e62d86ac183c87aaf923ce Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 19 Nov 2024 15:50:13 +0100 Subject: [PATCH 19/68] chore: make environment variable error nicer (#9353) --- crates/config/src/resolve.rs | 18 +++++++++++++----- testdata/default/cheats/RpcUrls.t.sol | 4 +--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/crates/config/src/resolve.rs b/crates/config/src/resolve.rs index 179ecffa2901..20606cb11aa5 100644 --- a/crates/config/src/resolve.rs +++ b/crates/config/src/resolve.rs @@ -23,15 +23,23 @@ impl UnresolvedEnvVarError { pub fn try_resolve(&self) -> Result { interpolate(&self.unresolved) } + + fn is_simple(&self) -> bool { + RE_PLACEHOLDER.captures_iter(&self.unresolved).count() <= 1 + } } impl fmt::Display for UnresolvedEnvVarError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "Failed to resolve env var `{}` in `{}`: {}", - self.var, self.unresolved, self.source - ) + write!(f, "environment variable `{}` ", self.var)?; + f.write_str(match self.source { + VarError::NotPresent => "not found", + VarError::NotUnicode(_) => "is not valid unicode", + })?; + if !self.is_simple() { + write!(f, " in `{}`", self.unresolved)?; + } + Ok(()) } } diff --git a/testdata/default/cheats/RpcUrls.t.sol b/testdata/default/cheats/RpcUrls.t.sol index aaa5a00bdd97..9425d59631b5 100644 --- a/testdata/default/cheats/RpcUrls.t.sol +++ b/testdata/default/cheats/RpcUrls.t.sol @@ -22,9 +22,7 @@ contract RpcUrlTest is DSTest { // can set env and return correct url function testCanSetAndGetURLAndAllUrls() public { // this will fail because alias is not set - vm._expectCheatcodeRevert( - "Failed to resolve env var `RPC_ENV_ALIAS` in `${RPC_ENV_ALIAS}`: environment variable not found" - ); + vm._expectCheatcodeRevert("environment variable `RPC_ENV_ALIAS` not found"); string[2][] memory _urls = vm.rpcUrls(); string memory url = vm.rpcUrl("mainnet"); From 9f0c26d9b61e6b1c0536945391a7158d68eda32c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 19 Nov 2024 17:33:23 +0100 Subject: [PATCH 20/68] test: update external forge-std (#9356) --- crates/forge/tests/cli/ext_integration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/tests/cli/ext_integration.rs b/crates/forge/tests/cli/ext_integration.rs index b2747e3ccb80..e9437f04c9e9 100644 --- a/crates/forge/tests/cli/ext_integration.rs +++ b/crates/forge/tests/cli/ext_integration.rs @@ -2,7 +2,7 @@ use foundry_test_utils::util::ExtTester; #[test] fn forge_std() { - ExtTester::new("foundry-rs", "forge-std", "1d0766bc5d814f117c7b1e643828f7d85024fb51") + ExtTester::new("foundry-rs", "forge-std", "2b59872eee0b8088ddcade39fe8c041e17bb79c0") // Skip fork tests. .args(["--nmc", "Fork"]) .run(); From 7538c4ed5a8575f403321d06c52882d3804eab3d Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 19 Nov 2024 18:48:29 +0200 Subject: [PATCH 21/68] chore(ci): archive endpoints config (#9348) * chore: move archive endpoints to different provider * Make archive endpoints configurable in env vars * Truncate fork url in err * Include only provider in failed fork message * Add env vars from secrets * Fix tests --------- Co-authored-by: Matthias Seitz --- .github/workflows/nextest.yml | 2 ++ Cargo.lock | 1 + crates/anvil/tests/it/fork.rs | 1 + crates/evm/core/Cargo.toml | 1 + crates/evm/core/src/opts.rs | 9 ++++++- crates/forge/tests/cli/test_cmd.rs | 28 +++++++++++++++++++++- crates/test-utils/src/rpc.rs | 38 ++++++++++++++++++++++++------ 7 files changed, 71 insertions(+), 9 deletions(-) diff --git a/.github/workflows/nextest.yml b/.github/workflows/nextest.yml index da862c657abf..6fc1a6703901 100644 --- a/.github/workflows/nextest.yml +++ b/.github/workflows/nextest.yml @@ -91,4 +91,6 @@ jobs: - name: Test env: SVM_TARGET_PLATFORM: ${{ matrix.svm_target_platform }} + HTTP_ARCHIVE_URLS: ${{ secrets.HTTP_ARCHIVE_URLS }} + WS_ARCHIVE_URLS: ${{ secrets.WS_ARCHIVE_URLS }} run: cargo nextest run ${{ matrix.flags }} diff --git a/Cargo.lock b/Cargo.lock index 81adf0f4b1e1..144c6e07800c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3998,6 +3998,7 @@ dependencies = [ "thiserror 1.0.69", "tokio", "tracing", + "url", ] [[package]] diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index a35d7c267d62..821395417fa3 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -1040,6 +1040,7 @@ async fn can_impersonate_in_fork() { // #[tokio::test(flavor = "multi_thread")] +#[ignore] async fn test_total_difficulty_fork() { let (api, handle) = spawn(fork_config()).await; diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 46441201664e..c9f5131d7f34 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -59,6 +59,7 @@ serde_json.workspace = true thiserror.workspace = true tokio = { workspace = true, features = ["time", "macros"] } tracing.workspace = true +url.workspace = true [dev-dependencies] foundry-test-utils.workspace = true diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 17ecbcca170e..99cc8dabaf07 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -8,6 +8,7 @@ use foundry_common::{provider::ProviderBuilder, ALCHEMY_FREE_TIER_CUPS}; use foundry_config::{Chain, Config}; use revm::primitives::{BlockEnv, CfgEnv, TxEnv}; use serde::{Deserialize, Deserializer, Serialize}; +use url::Url; #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct EvmOpts { @@ -102,7 +103,13 @@ impl EvmOpts { ) .await .wrap_err_with(|| { - format!("Could not instantiate forked environment with fork url: {fork_url}") + let mut err_msg = "Could not instantiate forked environment".to_string(); + if let Ok(url) = Url::parse(fork_url) { + if let Some(provider) = url.host() { + err_msg.push_str(&format!(" with provider {provider}")); + } + } + err_msg }) } diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index f51cf6703a17..8e064c63c9fd 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -575,7 +575,7 @@ Compiler run successful! Ran 1 test for test/Contract.t.sol:USDTCallingTest [PASS] test() ([GAS]) Traces: - [9406] USDTCallingTest::test() + [..] USDTCallingTest::test() ├─ [0] VM::createSelectFork("[..]") │ └─ ← [Return] 0 ├─ [3110] 0xdAC17F958D2ee523a2206206994597C13D831ec7::name() [staticcall] @@ -2639,3 +2639,29 @@ contract ScrollForkTest is Test { cmd.args(["test", "--mt", "test_roll_scroll_fork_to_tx", "--evm-version", "cancun"]) .assert_success(); }); + +// Test that only provider is included in failed fork error. +forgetest_init!(test_display_provider_on_error, |prj, cmd| { + prj.add_test( + "ForkTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract ForkTest is Test { + function test_fork_err_message() public { + vm.createSelectFork("https://eth-mainnet.g.alchemy.com/v2/DUMMY_KEY"); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test", "--mt", "test_fork_err_message"]).assert_failure().stdout_eq(str![[r#" +... +Ran 1 test for test/ForkTest.t.sol:ForkTest +[FAIL: vm.createSelectFork: Could not instantiate forked environment with provider eth-mainnet.g.alchemy.com;] test_fork_err_message() ([GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] +... + +"#]]); +}); diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index 44deda60cddf..ba27f381cb10 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -2,11 +2,19 @@ use foundry_config::{NamedChain, NamedChain::Optimism}; use rand::seq::SliceRandom; -use std::sync::{ - atomic::{AtomicUsize, Ordering}, - LazyLock, +use std::{ + env, + sync::{ + atomic::{AtomicUsize, Ordering}, + LazyLock, + }, }; +/// Env var key for ws archive endpoints. +const ENV_WS_ARCHIVE_ENDPOINTS: &str = "WS_ARCHIVE_URLS"; +/// Env var key for http archive endpoints. +const ENV_HTTP_ARCHIVE_ENDPOINTS: &str = "HTTP_ARCHIVE_URLS"; + // List of general purpose infura keys to rotate through static INFURA_KEYS: LazyLock> = LazyLock::new(|| { let mut keys = vec![ @@ -118,14 +126,30 @@ pub fn next_ws_endpoint(chain: NamedChain) -> String { /// Returns endpoint that has access to archive state pub fn next_http_archive_rpc_endpoint() -> String { - let idx = next() % ALCHEMY_KEYS.len(); - format!("https://eth-mainnet.g.alchemy.com/v2/{}", ALCHEMY_KEYS[idx]) + next_archive_endpoint(false) } /// Returns endpoint that has access to archive state pub fn next_ws_archive_rpc_endpoint() -> String { - let idx = next() % ALCHEMY_KEYS.len(); - format!("wss://eth-mainnet.g.alchemy.com/v2/{}", ALCHEMY_KEYS[idx]) + next_archive_endpoint(true) +} + +/// Returns endpoint that has access to archive state, http or ws. +/// Use env vars (comma separated urls) or default inline keys (Alchemy for ws, Infura for http). +fn next_archive_endpoint(is_ws: bool) -> String { + let env_urls = if is_ws { ENV_WS_ARCHIVE_ENDPOINTS } else { ENV_HTTP_ARCHIVE_ENDPOINTS }; + + let rpc_env_vars = env::var(env_urls).unwrap_or_default(); + if !rpc_env_vars.is_empty() { + let urls = rpc_env_vars.split(',').collect::>(); + urls.choose(&mut rand::thread_rng()).unwrap().to_string() + } else if is_ws { + let idx = next() % ALCHEMY_KEYS.len(); + format!("wss://eth-mainnet.g.alchemy.com/v2/{}", ALCHEMY_KEYS[idx]) + } else { + let idx = next() % INFURA_KEYS.len(); + format!("https://mainnet.infura.io/v3/{}", INFURA_KEYS[idx]) + } } /// Returns the next etherscan api key From 4648d5c77f2ca8ee646464836f08be48b5072df6 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:46:45 +0200 Subject: [PATCH 22/68] chore: pick provider urls by next index (#9359) --- crates/test-utils/src/rpc.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index ba27f381cb10..0934ea179701 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -142,7 +142,8 @@ fn next_archive_endpoint(is_ws: bool) -> String { let rpc_env_vars = env::var(env_urls).unwrap_or_default(); if !rpc_env_vars.is_empty() { let urls = rpc_env_vars.split(',').collect::>(); - urls.choose(&mut rand::thread_rng()).unwrap().to_string() + let idx = next() % urls.len(); + urls[idx].to_string() } else if is_ws { let idx = next() % ALCHEMY_KEYS.len(); format!("wss://eth-mainnet.g.alchemy.com/v2/{}", ALCHEMY_KEYS[idx]) From d20c142d0655490122e79fb66aa119df3638bad6 Mon Sep 17 00:00:00 2001 From: Dmitry <98899785+mdqst@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:57:17 +0300 Subject: [PATCH 23/68] fix: correct shell substitution in installer (#9351) Fix typographical error in default value assignment for FOUNDRY_DIR --- foundryup/install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foundryup/install b/foundryup/install index da8156a09fe3..1a8bc8c6ed5a 100755 --- a/foundryup/install +++ b/foundryup/install @@ -4,7 +4,7 @@ set -eo pipefail echo "Installing foundryup..." BASE_DIR="${XDG_CONFIG_HOME:-$HOME}" -FOUNDRY_DIR="${FOUNDRY_DIR-"$BASE_DIR/.foundry"}" +FOUNDRY_DIR="${FOUNDRY_DIR:-"$BASE_DIR/.foundry"}" FOUNDRY_BIN_DIR="$FOUNDRY_DIR/bin" FOUNDRY_MAN_DIR="$FOUNDRY_DIR/share/man/man1" From e5776932e865c28acf002371e6fce8d95017b0a1 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:39:20 +0100 Subject: [PATCH 24/68] chore: TestSetup cleanup (#9355) Co-authored-by: Matthias Seitz --- crates/evm/coverage/src/lib.rs | 8 ++ crates/evm/evm/src/executors/fuzz/mod.rs | 6 +- crates/evm/evm/src/executors/invariant/mod.rs | 5 +- .../evm/evm/src/executors/invariant/replay.rs | 10 +- crates/evm/evm/src/executors/mod.rs | 23 ++++ crates/forge/src/result.rs | 110 ++++++------------ crates/forge/src/runner.rs | 98 ++++++---------- 7 files changed, 103 insertions(+), 157 deletions(-) diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 7ac609123277..ad4ab53e3cf1 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -174,6 +174,14 @@ impl CoverageReport { pub struct HitMaps(pub HashMap); impl HitMaps { + pub fn merge_opt(a: &mut Option, b: Option) { + match (a, b) { + (_, None) => {} + (a @ None, Some(b)) => *a = Some(b), + (Some(a), Some(b)) => a.merge(b), + } + } + pub fn merge(&mut self, other: Self) { for (code_hash, hit_map) in other.0 { if let Some(HitMap { hits: extra_hits, .. }) = self.insert(code_hash, hit_map) { diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 982e44ea9d6a..2bbe80a63ead 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -127,11 +127,7 @@ impl FuzzedExecutor { data.logs.extend(case.logs); } - // Collect and merge coverage if `forge snapshot` context. - match &mut data.coverage { - Some(prev) => prev.merge(case.coverage.unwrap()), - opt => *opt = case.coverage, - } + HitMaps::merge_opt(&mut data.coverage, case.coverage); data.deprecated_cheatcodes = case.deprecated_cheatcodes; diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 860acdbb26d0..f98dd21114cb 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -204,10 +204,7 @@ impl InvariantTest { /// Merge current collected coverage with the new coverage from last fuzzed call. pub fn merge_coverage(&self, new_coverage: Option) { - match &mut self.execution_data.borrow_mut().coverage { - Some(prev) => prev.merge(new_coverage.unwrap()), - opt => *opt = new_coverage, - } + HitMaps::merge_opt(&mut self.execution_data.borrow_mut().coverage, new_coverage); } /// Update metrics for a fuzzed selector, extracted from tx details. diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index 969f1587f27e..36192a6d6914 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -48,16 +48,10 @@ pub fn replay_run( tx.call_details.calldata.clone(), U256::ZERO, )?; + logs.extend(call_result.logs); traces.push((TraceKind::Execution, call_result.traces.clone().unwrap())); - - if let Some(new_coverage) = call_result.coverage { - if let Some(old_coverage) = coverage { - *coverage = Some(std::mem::take(old_coverage).merged(new_coverage)); - } else { - *coverage = Some(new_coverage); - } - } + HitMaps::merge_opt(coverage, call_result.coverage); // Identify newly generated contracts, if they exist. ided_contracts diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 723b520aa1b7..6f458258a5ea 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -713,6 +713,12 @@ impl std::ops::DerefMut for DeployResult { } } +impl From for RawCallResult { + fn from(d: DeployResult) -> Self { + d.raw + } +} + /// The result of a raw call. #[derive(Debug)] pub struct RawCallResult { @@ -780,6 +786,23 @@ impl Default for RawCallResult { } impl RawCallResult { + /// Unpacks an EVM result. + pub fn from_evm_result(r: Result) -> eyre::Result<(Self, Option)> { + match r { + Ok(r) => Ok((r, None)), + Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))), + Err(e) => Err(e.into()), + } + } + + /// Unpacks an execution result. + pub fn from_execution_result(r: Result) -> (Self, Option) { + match r { + Ok(r) => (r, None), + Err(e) => (e.raw, Some(e.reason)), + } + } + /// Converts the result of the call into an `EvmError`. pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError { if let Some(reason) = SkipReason::decode(&self.result) { diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 60c07472eac0..7f02db577c0d 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -13,7 +13,7 @@ use foundry_common::{evm::Breakpoints, get_contract_name, get_file_name, shell}; use foundry_evm::{ coverage::HitMaps, decode::SkipReason, - executors::{invariant::InvariantMetrics, EvmError, RawCallResult}, + executors::{invariant::InvariantMetrics, RawCallResult}, fuzz::{CounterExample, FuzzCase, FuzzFixtures, FuzzTestResult}, traces::{CallTraceArena, CallTraceDecoder, TraceKind, Traces}, }; @@ -469,7 +469,7 @@ impl TestResult { /// Creates a new test result starting from test setup results. pub fn new(setup: TestSetup) -> Self { Self { - labeled_addresses: setup.labeled_addresses, + labeled_addresses: setup.labels, logs: setup.logs, traces: setup.traces, coverage: setup.coverage, @@ -490,7 +490,7 @@ impl TestResult { logs: setup.logs, traces: setup.traces, coverage: setup.coverage, - labeled_addresses: setup.labeled_addresses, + labeled_addresses: setup.labels, ..Default::default() } } @@ -651,21 +651,17 @@ impl TestResult { format!("{self} {name} {}", self.kind.report()) } - /// Function to merge logs, addresses, traces and coverage from a call result into test result. - pub fn merge_call_result(&mut self, call_result: &RawCallResult) { - self.logs.extend(call_result.logs.clone()); - self.labeled_addresses.extend(call_result.labels.clone()); - self.traces.extend(call_result.traces.clone().map(|traces| (TraceKind::Execution, traces))); - self.merge_coverages(call_result.coverage.clone()); + /// Merges the given raw call result into `self`. + pub fn extend(&mut self, call_result: RawCallResult) { + self.logs.extend(call_result.logs); + self.labeled_addresses.extend(call_result.labels); + self.traces.extend(call_result.traces.map(|traces| (TraceKind::Execution, traces))); + self.merge_coverages(call_result.coverage); } - /// Function to merge given coverage in current test result coverage. + /// Merges the given coverage result into `self`. pub fn merge_coverages(&mut self, other_coverage: Option) { - let old_coverage = std::mem::take(&mut self.coverage); - self.coverage = match (old_coverage, other_coverage) { - (Some(old_coverage), Some(other)) => Some(old_coverage.merged(other)), - (a, b) => a.or(b), - }; + HitMaps::merge_opt(&mut self.coverage, other_coverage); } } @@ -747,77 +743,39 @@ impl TestKind { } } +/// The result of a test setup. +/// +/// Includes the deployment of the required libraries and the test contract itself, and the call to +/// the `setUp()` function. #[derive(Clone, Debug, Default)] pub struct TestSetup { - /// The address at which the test contract was deployed + /// The address at which the test contract was deployed. pub address: Address, - /// The logs emitted during setup + /// Defined fuzz test fixtures. + pub fuzz_fixtures: FuzzFixtures, + + /// The logs emitted during setup. pub logs: Vec, - /// Call traces of the setup + /// Addresses labeled during setup. + pub labels: AddressHashMap, + /// Call traces of the setup. pub traces: Traces, - /// Addresses labeled during setup - pub labeled_addresses: AddressHashMap, - /// The reason the setup failed, if it did - pub reason: Option, - /// Coverage info during setup + /// Coverage info during setup. pub coverage: Option, - /// Defined fuzz test fixtures - pub fuzz_fixtures: FuzzFixtures, + + /// The reason the setup failed, if it did. + pub reason: Option, } impl TestSetup { - pub fn from_evm_error_with( - error: EvmError, - mut logs: Vec, - mut traces: Traces, - mut labeled_addresses: AddressHashMap, - ) -> Self { - match error { - EvmError::Execution(err) => { - // force the tracekind to be setup so a trace is shown. - traces.extend(err.raw.traces.map(|traces| (TraceKind::Setup, traces))); - logs.extend(err.raw.logs); - labeled_addresses.extend(err.raw.labels); - Self::failed_with(logs, traces, labeled_addresses, err.reason) - } - e => Self::failed_with( - logs, - traces, - labeled_addresses, - format!("failed to deploy contract: {e}"), - ), - } - } - - pub fn success( - address: Address, - logs: Vec, - traces: Traces, - labeled_addresses: AddressHashMap, - coverage: Option, - fuzz_fixtures: FuzzFixtures, - ) -> Self { - Self { address, logs, traces, labeled_addresses, reason: None, coverage, fuzz_fixtures } - } - - pub fn failed_with( - logs: Vec, - traces: Traces, - labeled_addresses: AddressHashMap, - reason: String, - ) -> Self { - Self { - address: Address::ZERO, - logs, - traces, - labeled_addresses, - reason: Some(reason), - coverage: None, - fuzz_fixtures: FuzzFixtures::default(), - } - } - pub fn failed(reason: String) -> Self { Self { reason: Some(reason), ..Default::default() } } + + pub fn extend(&mut self, raw: RawCallResult, trace_kind: TraceKind) { + self.logs.extend(raw.logs); + self.labels.extend(raw.labels); + self.traces.extend(raw.traces.map(|traces| (trace_kind, traces))); + HitMaps::merge_opt(&mut self.coverage, raw.coverage); + } } diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 1302c39ad1f5..4b66a482c27c 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -24,7 +24,7 @@ use foundry_evm::{ invariant::{ check_sequence, replay_error, replay_run, InvariantExecutor, InvariantFuzzError, }, - CallResult, EvmError, ExecutionErr, Executor, ITest, RawCallResult, + CallResult, EvmError, Executor, ITest, RawCallResult, }, fuzz::{ fixture_name, @@ -88,53 +88,51 @@ impl ContractRunner<'_> { self.executor.set_balance(self.sender, U256::MAX)?; self.executor.set_balance(CALLER, U256::MAX)?; - // We set the nonce of the deployer accounts to 1 to get the same addresses as DappTools + // We set the nonce of the deployer accounts to 1 to get the same addresses as DappTools. self.executor.set_nonce(self.sender, 1)?; - // Deploy libraries + // Deploy libraries. self.executor.set_balance(LIBRARY_DEPLOYER, U256::MAX)?; - let mut logs = Vec::new(); - let mut traces = Vec::with_capacity(self.libs_to_deploy.len()); + let mut result = TestSetup::default(); for code in self.libs_to_deploy.iter() { - match self.executor.deploy( + let deploy_result = self.executor.deploy( LIBRARY_DEPLOYER, code.clone(), U256::ZERO, Some(self.revert_decoder), - ) { - Ok(d) => { - logs.extend(d.raw.logs); - traces.extend(d.raw.traces.map(|traces| (TraceKind::Deployment, traces))); - } - Err(e) => { - return Ok(TestSetup::from_evm_error_with(e, logs, traces, Default::default())) - } + ); + let (raw, reason) = RawCallResult::from_evm_result(deploy_result.map(Into::into))?; + result.extend(raw, TraceKind::Deployment); + if reason.is_some() { + result.reason = reason; + return Ok(result); } } let address = self.sender.create(self.executor.get_nonce(self.sender)?); + result.address = address; // Set the contracts initial balance before deployment, so it is available during // construction self.executor.set_balance(address, self.initial_balance)?; // Deploy the test contract - match self.executor.deploy( + let deploy_result = self.executor.deploy( self.sender, self.contract.bytecode.clone(), U256::ZERO, Some(self.revert_decoder), - ) { - Ok(d) => { - logs.extend(d.raw.logs); - traces.extend(d.raw.traces.map(|traces| (TraceKind::Deployment, traces))); - d.address - } - Err(e) => { - return Ok(TestSetup::from_evm_error_with(e, logs, traces, Default::default())) - } - }; + ); + if let Ok(dr) = &deploy_result { + debug_assert_eq!(dr.address, address); + } + let (raw, reason) = RawCallResult::from_evm_result(deploy_result.map(Into::into))?; + result.extend(raw, TraceKind::Deployment); + if reason.is_some() { + result.reason = reason; + return Ok(result); + } // Reset `self.sender`s, `CALLER`s and `LIBRARY_DEPLOYER`'s balance to the initial balance. self.executor.set_balance(self.sender, self.initial_balance)?; @@ -144,45 +142,15 @@ impl ContractRunner<'_> { self.executor.deploy_create2_deployer()?; // Optionally call the `setUp` function - let result = if call_setup { + if call_setup { trace!("calling setUp"); let res = self.executor.setup(None, address, Some(self.revert_decoder)); - let (setup_logs, setup_traces, labeled_addresses, reason, coverage) = match res { - Ok(RawCallResult { traces, labels, logs, coverage, .. }) => { - trace!(%address, "successfully called setUp"); - (logs, traces, labels, None, coverage) - } - Err(EvmError::Execution(err)) => { - let ExecutionErr { - raw: RawCallResult { traces, labels, logs, coverage, .. }, - reason, - } = *err; - (logs, traces, labels, Some(reason), coverage) - } - Err(err) => (Vec::new(), None, HashMap::default(), Some(err.to_string()), None), - }; - traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces))); - logs.extend(setup_logs); - - TestSetup { - address, - logs, - traces, - labeled_addresses, - reason, - coverage, - fuzz_fixtures: self.fuzz_fixtures(address), - } - } else { - TestSetup::success( - address, - logs, - traces, - Default::default(), - None, - self.fuzz_fixtures(address), - ) - }; + let (raw, reason) = RawCallResult::from_evm_result(res)?; + result.extend(raw, TraceKind::Setup); + result.reason = reason; + } + + result.fuzz_fixtures = self.fuzz_fixtures(address); Ok(result) } @@ -698,11 +666,13 @@ impl ContractRunner<'_> { // Apply before test configured calldata. match executor.to_mut().transact_raw(self.sender, address, calldata, U256::ZERO) { Ok(call_result) => { + let reverted = call_result.reverted; + // Merge tx result traces in unit test result. - test_result.merge_call_result(&call_result); + test_result.extend(call_result); // To continue unit test execution the call should not revert. - if call_result.reverted { + if reverted { return Err(test_result.single_fail(None)) } } From 3a954409e350164d6267ed72b8c3fb5a2f0a01c4 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:07:24 +0200 Subject: [PATCH 25/68] chore(ci): proper read of archive nodes (#9362) inherit secrets, use alchemy as default for external PRs, comment out infura --- .github/workflows/test.yml | 1 + crates/test-utils/src/rpc.rs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index acb63a994fec..d5a2eabb07c0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,6 +18,7 @@ jobs: uses: ./.github/workflows/nextest.yml with: profile: default + secrets: inherit docs: runs-on: ubuntu-latest diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index 0934ea179701..7bf820a208aa 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -18,9 +18,9 @@ const ENV_HTTP_ARCHIVE_ENDPOINTS: &str = "HTTP_ARCHIVE_URLS"; // List of general purpose infura keys to rotate through static INFURA_KEYS: LazyLock> = LazyLock::new(|| { let mut keys = vec![ - "6cb19d07ca2d44f59befd61563b1037b", - "6d46c0cca653407b861f3f93f7b0236a", - "69a36846dec146e3a2898429be60be85", + // "6cb19d07ca2d44f59befd61563b1037b", + // "6d46c0cca653407b861f3f93f7b0236a", + // "69a36846dec146e3a2898429be60be85", // "16a8be88795540b9b3903d8de0f7baa5", // "f4a0bdad42674adab5fc0ac077ffab2b", // "5c812e02193c4ba793f8c214317582bd", @@ -39,11 +39,11 @@ static ALCHEMY_KEYS: LazyLock> = LazyLock::new(|| { "GL4M0hfzSYGU5e1_t804HoUDOObWP-FA", "WV407BEiBmjNJfKo9Uo_55u0z0ITyCOX", "Ge56dH9siMF4T0whP99sQXOcr2mFs8wZ", - "QC55XC151AgkS3FNtWvz9VZGeu9Xd9lb", - "pwc5rmJhrdoaSEfimoKEmsvOjKSmPDrP", - "A5sZ85MIr4SzCMkT0zXh2eeamGIq3vGL", - "9VWGraLx0tMiSWx05WH-ywgSVmMxs66W", - "U4hsGWgl9lBM1j3jhSgJ4gbjHg2jRwKy", + // "QC55XC151AgkS3FNtWvz9VZGeu9Xd9lb", + // "pwc5rmJhrdoaSEfimoKEmsvOjKSmPDrP", + // "A5sZ85MIr4SzCMkT0zXh2eeamGIq3vGL", + // "9VWGraLx0tMiSWx05WH-ywgSVmMxs66W", + // "U4hsGWgl9lBM1j3jhSgJ4gbjHg2jRwKy", "K-uNlqYoYCO9cdBHcifwCDAcEjDy1UHL", "GWdgwabOE2XfBdLp_gIq-q6QHa7DSoag", "Uz0cF5HCXFtpZlvd9NR7kHxfB_Wdpsx7", @@ -148,8 +148,8 @@ fn next_archive_endpoint(is_ws: bool) -> String { let idx = next() % ALCHEMY_KEYS.len(); format!("wss://eth-mainnet.g.alchemy.com/v2/{}", ALCHEMY_KEYS[idx]) } else { - let idx = next() % INFURA_KEYS.len(); - format!("https://mainnet.infura.io/v3/{}", INFURA_KEYS[idx]) + let idx = next() % ALCHEMY_KEYS.len(); + format!("https://eth-mainnet.g.alchemy.com/v2/{}", ALCHEMY_KEYS[idx]) } } From 999cc1bf270bc9fd63af05b6dccbbf0743bf5e55 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 20 Nov 2024 12:30:59 +0100 Subject: [PATCH 26/68] chore(test-utils): simplify next calls (#9361) Co-authored-by: grandizzy --- crates/test-utils/src/rpc.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index 7bf820a208aa..361bf56c2706 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -91,13 +91,14 @@ static ETHERSCAN_OPTIMISM_KEYS: LazyLock> = LazyLock::new(|| vec!["JQNGFHINKS1W7Y5FRXU4SPBYF43J3NYK46"]); /// Returns the next index to use. -fn next() -> usize { +fn next_idx() -> usize { static NEXT_INDEX: AtomicUsize = AtomicUsize::new(0); NEXT_INDEX.fetch_add(1, Ordering::SeqCst) } -fn num_keys() -> usize { - INFURA_KEYS.len() + ALCHEMY_KEYS.len() +/// Returns the next item in the list to use. +fn next(list: &[T]) -> &T { + &list[next_idx() % list.len()] } /// Returns the next _mainnet_ rpc endpoint in inline @@ -142,21 +143,17 @@ fn next_archive_endpoint(is_ws: bool) -> String { let rpc_env_vars = env::var(env_urls).unwrap_or_default(); if !rpc_env_vars.is_empty() { let urls = rpc_env_vars.split(',').collect::>(); - let idx = next() % urls.len(); - urls[idx].to_string() + next(&urls).to_string() } else if is_ws { - let idx = next() % ALCHEMY_KEYS.len(); - format!("wss://eth-mainnet.g.alchemy.com/v2/{}", ALCHEMY_KEYS[idx]) + format!("wss://eth-mainnet.g.alchemy.com/v2/{}", next(&ALCHEMY_KEYS)) } else { - let idx = next() % ALCHEMY_KEYS.len(); - format!("https://eth-mainnet.g.alchemy.com/v2/{}", ALCHEMY_KEYS[idx]) + format!("https://eth-mainnet.g.alchemy.com/v2/{}", next(&ALCHEMY_KEYS)) } } /// Returns the next etherscan api key pub fn next_mainnet_etherscan_api_key() -> String { - let idx = next() % ETHERSCAN_MAINNET_KEYS.len(); - ETHERSCAN_MAINNET_KEYS[idx].to_string() + next_etherscan_api_key(NamedChain::Mainnet) } /// Returns the next etherscan api key for given chain. @@ -165,8 +162,7 @@ pub fn next_etherscan_api_key(chain: NamedChain) -> String { Optimism => ÐERSCAN_OPTIMISM_KEYS, _ => ÐERSCAN_MAINNET_KEYS, }; - let idx = next() % keys.len(); - keys[idx].to_string() + next(keys).to_string() } fn next_url(is_ws: bool, chain: NamedChain) -> String { @@ -176,7 +172,7 @@ fn next_url(is_ws: bool, chain: NamedChain) -> String { return "https://mainnet.base.org".to_string(); } - let idx = next() % num_keys(); + let idx = next_idx() % (INFURA_KEYS.len() + ALCHEMY_KEYS.len()); let is_infura = idx < INFURA_KEYS.len(); let key = if is_infura { INFURA_KEYS[idx] } else { ALCHEMY_KEYS[idx - INFURA_KEYS.len()] }; From 2a194bd75a5d2477c3e8236dcdea64f4508e32ed Mon Sep 17 00:00:00 2001 From: Evan Chipman <42247026+evchip@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:49:10 +0700 Subject: [PATCH 27/68] Support EIP-7702 Delegations in Forge (#9236) * add EIP-7702 cheatcodes: createDelegation, signDelegation, attachDelegation * add cheatcode implementations for EIP-7702: createDelegationCall, signDelegationCall, attachDelegationCall; modify broadcast to check if sender has a delegation * add delegations hashmap to Cheatcodes struct * add revm crate * create AttachDelegationTest for EIP-7702 transactions * regen cheatcodes.json * cargo fmt * move broadcast under attachDelegation * combine createDelegationCall logic with signDelegationCall in order to create and sign delegation in a single call; remove delegation logic from broadcast() - no need to track delegations here * remove revm import from workspace * combine createDelegation logic inton signDelegation for simplicity * remove revm from forge script deps * combine createDelegation with signDelegation * WIP - refactor test to use SimpleDelegateContract and ERC20 - test currently failing bc 7702 implementation.execute not executed as Alice EOA * add logic to include authorization_list for EIP 7702 in TransactionRequest by searching delegations hash map by active_delegation * add address authority param to attachDelegation; remove nonce param from signDelegation, as it can be constructed in cheatcode. * remove 7702 tx request construction logic - now handled in attachDelegation cheatcode implementation * refactor attachDelegation cheatcode implementation to handle verifying signature and setting bytecode on EOA; refactor signDelegation cheatcode implementation to get nonce from signer * remove nonce param from attachDelegation cheatcode in favor of loading from authority account * refactor test to check for code on alice account and call execute on alice account through SimpleDelegateContract * revert refactor on TransactionRequest * format * cargo fmt * fix clippy errors * remove faulty logic comparing nonce to itself - nonce still checked by recovered signature * add more tests to cover revert cases on attachDelegation and multiple calls via delegation contract * cargo fmt * restore logic to check if there's an active delegation when building TransactionRequest; add fixed values for gas and max_priority_fee_per_gas to ensure tx success, with TODO comment to explain what's left * remove obsolete comment * add comments explaining delegations and active_delegation * cargo fmt * add logic to increase gas limit by PER_EMPTY_ACCOUNT_COST(25k) if tx includes authorization list for EIP 7702 tx, which is seemingly not accounted for in gas estimation; remove hardcoded gas values from call_with_executor * revert logic to add PER_EMPTY_ACCOUNT_COST for EIP 7702 txs - handled inside of revm now * remove manually setting transaction type to 4 if auth list is present - handled in revm * add method set_delegation to Executor for setting EIP-7702 authorization list in the transaction environment; call set_delegation from simulate_and_fill if auth list is not empty * remove redundancy with TransactionMaybeSigned var tx * cargo fmt * refactor: use authorization_list() helper to return authorization_list and set delegation * refactor: change Cheatcodes::active_delegation to Option and remove delegations hashmap - tx will only use one active delegation at a time, so no need for mapping * replace verbose logic to set bytecode on EOA with journaled_state.set_code helper * cargo fmt * increment nonce of authority account * add logic to set authorization_list to None if active_delegation is None * add test testSwitchDelegation to assert that attaching an additional delegation switches the implementation on the EOA * remove set_delegation logic in favor of adding call_raw_with_authorization - previous approach kept the delegation in the TxEnv, resulting in higher gas cost for all subsequent calls after the delegation was applied * refactor signDelegation to return struct SignedDelegation and for attachDelegation to accept SignedDelegation * update delegation tests to reflect change in cheatcode interface for signDelegation and attachDelegation * add cheatcode signAndAttachDelegation * add signAndAttachDelegationCall cheatcode logic; refactor helper methods for shared logic used in 7702 delegation cheatcodes * add test testCallSingleSignAndAttachDelegation for new cheatcode signAndAttachDelegation * add comments to SignedDelegation struct and cargo fmt * cargo fmt * fix ci * fix spec --------- Co-authored-by: Arsenii Kulikov Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> --- Cargo.lock | 1 + crates/cheatcodes/assets/cheatcodes.json | 91 +++++++++ crates/cheatcodes/spec/src/lib.rs | 1 + crates/cheatcodes/spec/src/vm.rs | 28 +++ crates/cheatcodes/src/inspector.rs | 39 ++-- crates/cheatcodes/src/script.rs | 89 +++++++++ crates/common/Cargo.toml | 1 + crates/common/src/transactions.rs | 9 + crates/evm/evm/src/executors/mod.rs | 19 +- crates/script/src/runner.rs | 26 ++- crates/script/src/simulate.rs | 5 +- testdata/cheats/Vm.sol | 4 + .../default/cheats/AttachDelegation.t.sol | 182 ++++++++++++++++++ 13 files changed, 477 insertions(+), 18 deletions(-) create mode 100644 testdata/default/cheats/AttachDelegation.t.sol diff --git a/Cargo.lock b/Cargo.lock index 144c6e07800c..6759a6d2fcfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3701,6 +3701,7 @@ dependencies = [ "alloy-consensus", "alloy-contract", "alloy-dyn-abi", + "alloy-eips", "alloy-json-abi", "alloy-json-rpc", "alloy-primitives", diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 347d738e01ec..d19c776b9916 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -573,6 +573,37 @@ "description": "Status of the transaction, retrieved from the transaction receipt." } ] + }, + { + "name": "SignedDelegation", + "description": "Holds a signed EIP-7702 authorization for an authority account to delegate to an implementation.", + "fields": [ + { + "name": "v", + "ty": "uint8", + "description": "The y-parity of the recovered secp256k1 signature (0 or 1)." + }, + { + "name": "r", + "ty": "bytes32", + "description": "First 32 bytes of the signature." + }, + { + "name": "s", + "ty": "bytes32", + "description": "Second 32 bytes of the signature." + }, + { + "name": "nonce", + "ty": "uint64", + "description": "The current nonce of the authority account at signing time.\n Used to ensure signature can't be replayed after account nonce changes." + }, + { + "name": "implementation", + "ty": "address", + "description": "Address of the contract implementation that will be delegated to.\n Gets encoded into delegation code: 0xef0100 || implementation." + } + ] } ], "cheatcodes": [ @@ -3076,6 +3107,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "attachDelegation", + "description": "Designate the next call as an EIP-7702 transaction", + "declaration": "function attachDelegation(SignedDelegation memory signedDelegation) external;", + "visibility": "external", + "mutability": "", + "signature": "attachDelegation((uint8,bytes32,bytes32,uint64,address))", + "selector": "0x14ae3519", + "selectorBytes": [ + 20, + 174, + 53, + 25 + ] + }, + "group": "scripting", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "blobBaseFee", @@ -8806,6 +8857,26 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "signAndAttachDelegation", + "description": "Sign an EIP-7702 authorization and designate the next call as an EIP-7702 transaction", + "declaration": "function signAndAttachDelegation(address implementation, uint256 privateKey) external returns (SignedDelegation memory signedDelegation);", + "visibility": "external", + "mutability": "", + "signature": "signAndAttachDelegation(address,uint256)", + "selector": "0xc7fa7288", + "selectorBytes": [ + 199, + 250, + 114, + 136 + ] + }, + "group": "scripting", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "signCompact_0", @@ -8886,6 +8957,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "signDelegation", + "description": "Sign an EIP-7702 authorization for delegation", + "declaration": "function signDelegation(address implementation, uint256 privateKey) external returns (SignedDelegation memory signedDelegation);", + "visibility": "external", + "mutability": "", + "signature": "signDelegation(address,uint256)", + "selector": "0x5b593c7b", + "selectorBytes": [ + 91, + 89, + 60, + 123 + ] + }, + "group": "scripting", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "signP256", diff --git a/crates/cheatcodes/spec/src/lib.rs b/crates/cheatcodes/spec/src/lib.rs index 5692dfc48b9c..c4d7e9868fe1 100644 --- a/crates/cheatcodes/spec/src/lib.rs +++ b/crates/cheatcodes/spec/src/lib.rs @@ -87,6 +87,7 @@ impl Cheatcodes<'static> { Vm::Gas::STRUCT.clone(), Vm::DebugStep::STRUCT.clone(), Vm::BroadcastTxSummary::STRUCT.clone(), + Vm::SignedDelegation::STRUCT.clone(), ]), enums: Cow::Owned(vec![ Vm::CallerMode::ENUM.clone(), diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index d703f48e7ae5..bf0fe9781f8a 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -308,6 +308,22 @@ interface Vm { bool success; } + /// Holds a signed EIP-7702 authorization for an authority account to delegate to an implementation. + struct SignedDelegation { + /// The y-parity of the recovered secp256k1 signature (0 or 1). + uint8 v; + /// First 32 bytes of the signature. + bytes32 r; + /// Second 32 bytes of the signature. + bytes32 s; + /// The current nonce of the authority account at signing time. + /// Used to ensure signature can't be replayed after account nonce changes. + uint64 nonce; + /// Address of the contract implementation that will be delegated to. + /// Gets encoded into delegation code: 0xef0100 || implementation. + address implementation; + } + // ======== EVM ======== /// Gets the address for a given private key. @@ -2018,6 +2034,18 @@ interface Vm { #[cheatcode(group = Scripting)] function broadcastRawTransaction(bytes calldata data) external; + /// Sign an EIP-7702 authorization for delegation + #[cheatcode(group = Scripting)] + function signDelegation(address implementation, uint256 privateKey) external returns (SignedDelegation memory signedDelegation); + + /// Designate the next call as an EIP-7702 transaction + #[cheatcode(group = Scripting)] + function attachDelegation(SignedDelegation memory signedDelegation) external; + + /// Sign an EIP-7702 authorization and designate the next call as an EIP-7702 transaction + #[cheatcode(group = Scripting)] + function signAndAttachDelegation(address implementation, uint256 privateKey) external returns (SignedDelegation memory signedDelegation); + /// Returns addresses of available unlocked wallets in the script environment. #[cheatcode(group = Scripting)] function getWallets() external returns (address[] memory wallets); diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 046a495a10e8..6f3acb58c273 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -46,7 +46,10 @@ use revm::{ EOFCreateInputs, EOFCreateKind, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }, - primitives::{BlockEnv, CreateScheme, EVMError, EvmStorageSlot, SpecId, EOF_MAGIC_BYTES}, + primitives::{ + BlockEnv, CreateScheme, EVMError, EvmStorageSlot, SignedAuthorization, SpecId, + EOF_MAGIC_BYTES, + }, EvmContext, InnerEvmContext, Inspector, }; use serde_json::Value; @@ -373,6 +376,11 @@ pub struct Cheatcodes { /// execution block environment. pub block: Option, + /// Currently active EIP-7702 delegation that will be consumed when building the next + /// transaction. Set by `vm.attachDelegation()` and consumed via `.take()` during + /// transaction construction. + pub active_delegation: Option, + /// The gas price. /// /// Used in the cheatcode handler to overwrite the gas price separately from the gas price @@ -497,6 +505,7 @@ impl Cheatcodes { labels: config.labels.clone(), config, block: Default::default(), + active_delegation: Default::default(), gas_price: Default::default(), prank: Default::default(), expected_revert: Default::default(), @@ -1014,18 +1023,26 @@ where { let account = ecx.journaled_state.state().get_mut(&broadcast.new_origin).unwrap(); + let mut tx_req = TransactionRequest { + from: Some(broadcast.new_origin), + to: Some(TxKind::from(Some(call.target_address))), + value: call.transfer_value(), + input: TransactionInput::new(call.input.clone()), + nonce: Some(account.info.nonce), + chain_id: Some(ecx.env.cfg.chain_id), + gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None }, + ..Default::default() + }; + + if let Some(auth_list) = self.active_delegation.take() { + tx_req.authorization_list = Some(vec![auth_list]); + } else { + tx_req.authorization_list = None; + } + self.broadcastable_transactions.push_back(BroadcastableTransaction { rpc: ecx.db.active_fork_url(), - transaction: TransactionRequest { - from: Some(broadcast.new_origin), - to: Some(TxKind::from(Some(call.target_address))), - value: call.transfer_value(), - input: TransactionInput::new(call.input.clone()), - nonce: Some(account.info.nonce), - gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None }, - ..Default::default() - } - .into(), + transaction: tx_req.into(), }); debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call"); diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 29a804efd7ec..3e1239237e83 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -2,10 +2,13 @@ use crate::{Cheatcode, CheatsCtxt, Result, Vm::*}; use alloy_primitives::{Address, B256, U256}; +use alloy_rpc_types::Authorization; +use alloy_signer::{Signature, SignerSync}; use alloy_signer_local::PrivateKeySigner; use alloy_sol_types::SolValue; use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner}; use parking_lot::Mutex; +use revm::primitives::{Bytecode, SignedAuthorization}; use std::sync::Arc; impl Cheatcode for broadcast_0Call { @@ -29,6 +32,92 @@ impl Cheatcode for broadcast_2Call { } } +impl Cheatcode for attachDelegationCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { signedDelegation } = self; + let SignedDelegation { v, r, s, nonce, implementation } = signedDelegation; + + let auth = Authorization { + address: *implementation, + nonce: *nonce, + chain_id: ccx.ecx.env.cfg.chain_id, + }; + let signed_auth = SignedAuthorization::new_unchecked( + auth, + *v, + U256::from_be_bytes(r.0), + U256::from_be_bytes(s.0), + ); + write_delegation(ccx, signed_auth.clone())?; + ccx.state.active_delegation = Some(signed_auth); + Ok(Default::default()) + } +} + +impl Cheatcode for signDelegationCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { implementation, privateKey } = self; + let signer = PrivateKeySigner::from_bytes(&B256::from(*privateKey))?; + let authority = signer.address(); + let (auth, nonce) = create_auth(ccx, *implementation, authority)?; + let sig = signer.sign_hash_sync(&auth.signature_hash())?; + Ok(sig_to_delegation(sig, nonce, *implementation).abi_encode()) + } +} + +impl Cheatcode for signAndAttachDelegationCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { implementation, privateKey } = self; + let signer = PrivateKeySigner::from_bytes(&B256::from(*privateKey))?; + let authority = signer.address(); + let (auth, nonce) = create_auth(ccx, *implementation, authority)?; + let sig = signer.sign_hash_sync(&auth.signature_hash())?; + let signed_auth = sig_to_auth(sig, auth); + write_delegation(ccx, signed_auth.clone())?; + ccx.state.active_delegation = Some(signed_auth); + Ok(sig_to_delegation(sig, nonce, *implementation).abi_encode()) + } +} + +fn create_auth( + ccx: &mut CheatsCtxt, + implementation: Address, + authority: Address, +) -> Result<(Authorization, u64)> { + let authority_acc = ccx.ecx.journaled_state.load_account(authority, &mut ccx.ecx.db)?; + let nonce = authority_acc.data.info.nonce; + Ok(( + Authorization { address: implementation, nonce, chain_id: ccx.ecx.env.cfg.chain_id }, + nonce, + )) +} + +fn write_delegation(ccx: &mut CheatsCtxt, auth: SignedAuthorization) -> Result<()> { + let authority = auth.recover_authority().map_err(|e| format!("{e}"))?; + let authority_acc = ccx.ecx.journaled_state.load_account(authority, &mut ccx.ecx.db)?; + if authority_acc.data.info.nonce != auth.nonce { + return Err("invalid nonce".into()); + } + authority_acc.data.info.nonce += 1; + let bytecode = Bytecode::new_eip7702(*auth.address()); + ccx.ecx.journaled_state.set_code(authority, bytecode); + Ok(()) +} + +fn sig_to_delegation(sig: Signature, nonce: u64, implementation: Address) -> SignedDelegation { + SignedDelegation { + v: sig.v().y_parity() as u8, + r: sig.r().into(), + s: sig.s().into(), + nonce, + implementation, + } +} + +fn sig_to_auth(sig: Signature, auth: Authorization) -> SignedAuthorization { + SignedAuthorization::new_unchecked(auth, sig.v().y_parity() as u8, sig.r(), sig.s()) +} + impl Cheatcode for startBroadcast_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 033c53e49dba..585e0080cc87 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -20,6 +20,7 @@ foundry-config.workspace = true alloy-contract.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } +alloy-eips.workspace = true alloy-json-abi.workspace = true alloy-json-rpc.workspace = true alloy-primitives = { workspace = true, features = [ diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index 4ddef26dd03f..f0ed0b54f8bd 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -1,6 +1,7 @@ //! Wrappers for transactions. use alloy_consensus::{Transaction, TxEnvelope}; +use alloy_eips::eip7702::SignedAuthorization; use alloy_primitives::{Address, TxKind, U256}; use alloy_provider::{ network::{AnyNetwork, ReceiptResponse, TransactionBuilder}, @@ -226,6 +227,14 @@ impl TransactionMaybeSigned { Self::Unsigned(tx) => tx.nonce, } } + + pub fn authorization_list(&self) -> Option> { + match self { + Self::Signed { tx, .. } => tx.authorization_list().map(|auths| auths.to_vec()), + Self::Unsigned(tx) => tx.authorization_list.as_deref().map(|auths| auths.to_vec()), + } + .filter(|auths| !auths.is_empty()) + } } impl From for TransactionMaybeSigned { diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 6f458258a5ea..b5b31b812298 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -31,8 +31,8 @@ use revm::{ db::{DatabaseCommit, DatabaseRef}, interpreter::{return_ok, InstructionResult}, primitives::{ - BlockEnv, Bytecode, Env, EnvWithHandlerCfg, ExecutionResult, Output, ResultAndState, - SpecId, TxEnv, TxKind, + AuthorizationList, BlockEnv, Bytecode, Env, EnvWithHandlerCfg, ExecutionResult, Output, + ResultAndState, SignedAuthorization, SpecId, TxEnv, TxKind, }, }; use std::borrow::Cow; @@ -378,6 +378,21 @@ impl Executor { self.call_with_env(env) } + /// Performs a raw call to an account on the current state of the VM with an EIP-7702 + /// authorization list. + pub fn call_raw_with_authorization( + &mut self, + from: Address, + to: Address, + calldata: Bytes, + value: U256, + authorization_list: Vec, + ) -> eyre::Result { + let mut env = self.build_test_env(from, to.into(), calldata, value); + env.tx.authorization_list = Some(AuthorizationList::Signed(authorization_list)); + self.call_with_env(env) + } + /// Performs a raw call to an account on the current state of the VM. pub fn transact_raw( &mut self, diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index a7d950690b92..fa8ff19d9a9d 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -1,5 +1,6 @@ use super::ScriptResult; use crate::build::ScriptPredeployLibraries; +use alloy_eips::eip7702::SignedAuthorization; use alloy_primitives::{Address, Bytes, TxKind, U256}; use alloy_rpc_types::TransactionRequest; use eyre::Result; @@ -223,7 +224,7 @@ impl ScriptRunner { /// Executes the method that will collect all broadcastable transactions. pub fn script(&mut self, address: Address, calldata: Bytes) -> Result { - self.call(self.evm_opts.sender, address, calldata, U256::ZERO, false) + self.call(self.evm_opts.sender, address, calldata, U256::ZERO, None, false) } /// Runs a broadcastable transaction locally and persists its state. @@ -233,9 +234,17 @@ impl ScriptRunner { to: Option
, calldata: Option, value: Option, + authorization_list: Option>, ) -> Result { if let Some(to) = to { - self.call(from, to, calldata.unwrap_or_default(), value.unwrap_or(U256::ZERO), true) + self.call( + from, + to, + calldata.unwrap_or_default(), + value.unwrap_or(U256::ZERO), + authorization_list, + true, + ) } else if to.is_none() { let res = self.executor.deploy( from, @@ -282,9 +291,20 @@ impl ScriptRunner { to: Address, calldata: Bytes, value: U256, + authorization_list: Option>, commit: bool, ) -> Result { - let mut res = self.executor.call_raw(from, to, calldata.clone(), value)?; + let mut res = if let Some(authorization_list) = authorization_list { + self.executor.call_raw_with_authorization( + from, + to, + calldata.clone(), + value, + authorization_list, + )? + } else { + self.executor.call_raw(from, to, calldata.clone(), value)? + }; let mut gas_used = res.gas_used; // We should only need to calculate realistic gas costs when preparing to broadcast diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index a9798c43e471..95427225f202 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -112,10 +112,10 @@ impl PreSimulationState { // Executes all transactions from the different forks concurrently. let futs = transactions .into_iter() - .map(|transaction| async { + .map(|mut transaction| async { let mut runner = runners.get(&transaction.rpc).expect("invalid rpc url").write(); + let tx = transaction.tx_mut(); - let tx = transaction.tx(); let to = if let Some(TxKind::Call(to)) = tx.to() { Some(to) } else { None }; let result = runner .simulate( @@ -124,6 +124,7 @@ impl PreSimulationState { to, tx.input().map(Bytes::copy_from_slice), tx.value(), + tx.authorization_list(), ) .wrap_err("Internal EVM error during simulation")?; diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 2004c44563d0..9d1d3efd1420 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -23,6 +23,7 @@ interface Vm { struct Gas { uint64 gasLimit; uint64 gasTotalUsed; uint64 gasMemoryUsed; int64 gasRefunded; uint64 gasRemaining; } struct DebugStep { uint256[] stack; bytes memoryInput; uint8 opcode; uint64 depth; bool isOutOfGas; address contractAddr; } struct BroadcastTxSummary { bytes32 txHash; BroadcastTxType txType; address contractAddress; uint64 blockNumber; bool success; } + struct SignedDelegation { uint8 v; bytes32 r; bytes32 s; uint64 nonce; address implementation; } function _expectCheatcodeRevert() external; function _expectCheatcodeRevert(bytes4 revertData) external; function _expectCheatcodeRevert(bytes calldata revertData) external; @@ -148,6 +149,7 @@ interface Vm { function assertTrue(bool condition, string calldata error) external pure; function assume(bool condition) external pure; function assumeNoRevert() external pure; + function attachDelegation(SignedDelegation memory signedDelegation) external; function blobBaseFee(uint256 newBlobBaseFee) external; function blobhashes(bytes32[] calldata hashes) external; function breakpoint(string calldata char) external pure; @@ -434,10 +436,12 @@ interface Vm { function setEnv(string calldata name, string calldata value) external; function setNonce(address account, uint64 newNonce) external; function setNonceUnsafe(address account, uint64 newNonce) external; + function signAndAttachDelegation(address implementation, uint256 privateKey) external returns (SignedDelegation memory signedDelegation); function signCompact(Wallet calldata wallet, bytes32 digest) external returns (bytes32 r, bytes32 vs); function signCompact(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); function signCompact(bytes32 digest) external pure returns (bytes32 r, bytes32 vs); function signCompact(address signer, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); + function signDelegation(address implementation, uint256 privateKey) external returns (SignedDelegation memory signedDelegation); function signP256(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 s); function sign(Wallet calldata wallet, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s); function sign(uint256 privateKey, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); diff --git a/testdata/default/cheats/AttachDelegation.t.sol b/testdata/default/cheats/AttachDelegation.t.sol new file mode 100644 index 000000000000..7befc9a32047 --- /dev/null +++ b/testdata/default/cheats/AttachDelegation.t.sol @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract AttachDelegationTest is DSTest { + event ExecutedBy(uint256 id); + + Vm constant vm = Vm(HEVM_ADDRESS); + uint256 alice_pk = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d; + address payable alice = payable(0x70997970C51812dc3A010C7d01b50e0d17dc79C8); + uint256 bob_pk = 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a; + address bob = 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC; + + SimpleDelegateContract implementation; + SimpleDelegateContract implementation2; + ERC20 token; + + function setUp() public { + implementation = new SimpleDelegateContract(1); + implementation2 = new SimpleDelegateContract(2); + token = new ERC20(alice); + } + + function testCallSingleAttachDelegation() public { + Vm.SignedDelegation memory signedDelegation = vm.signDelegation(address(implementation), alice_pk); + SimpleDelegateContract.Call[] memory calls = new SimpleDelegateContract.Call[](1); + bytes memory data = abi.encodeCall(ERC20.mint, (100, bob)); + calls[0] = SimpleDelegateContract.Call({to: address(token), data: data, value: 0}); + // executing as bob to make clear that we don't need to execute the tx as alice + vm.broadcast(bob_pk); + vm.attachDelegation(signedDelegation); + + bytes memory code = address(alice).code; + require(code.length > 0, "no code written to alice"); + SimpleDelegateContract(alice).execute(calls); + + assertEq(token.balanceOf(bob), 100); + } + + function testMultiCallAttachDelegation() public { + Vm.SignedDelegation memory signedDelegation = vm.signDelegation(address(implementation), alice_pk); + vm.broadcast(bob_pk); + vm.attachDelegation(signedDelegation); + + SimpleDelegateContract.Call[] memory calls = new SimpleDelegateContract.Call[](2); + calls[0] = + SimpleDelegateContract.Call({to: address(token), data: abi.encodeCall(ERC20.mint, (50, bob)), value: 0}); + calls[1] = SimpleDelegateContract.Call({ + to: address(token), + data: abi.encodeCall(ERC20.mint, (50, address(this))), + value: 0 + }); + + SimpleDelegateContract(alice).execute(calls); + + assertEq(token.balanceOf(bob), 50); + assertEq(token.balanceOf(address(this)), 50); + } + + function testSwitchAttachDelegation() public { + Vm.SignedDelegation memory signedDelegation = vm.signDelegation(address(implementation), alice_pk); + + SimpleDelegateContract.Call[] memory calls = new SimpleDelegateContract.Call[](1); + bytes memory data = abi.encodeCall(ERC20.mint, (100, bob)); + calls[0] = SimpleDelegateContract.Call({to: address(token), data: data, value: 0}); + + vm.broadcast(bob_pk); + vm.attachDelegation(signedDelegation); + + vm.expectEmit(true, true, true, true); + emit ExecutedBy(1); + SimpleDelegateContract(alice).execute(calls); + + // switch to implementation2 + Vm.SignedDelegation memory signedDelegation2 = vm.signDelegation(address(implementation2), alice_pk); + vm.broadcast(bob_pk); + vm.attachDelegation(signedDelegation2); + + vm.expectEmit(true, true, true, true); + emit ExecutedBy(2); + SimpleDelegateContract(alice).execute(calls); + + // verify final state + assertEq(token.balanceOf(bob), 200); + } + + function testAttachDelegationRevertInvalidSignature() public { + Vm.SignedDelegation memory signedDelegation = vm.signDelegation(address(implementation), alice_pk); + // change v from 1 to 0 + signedDelegation.v = (signedDelegation.v + 1) % 2; + vm.attachDelegation(signedDelegation); + + SimpleDelegateContract.Call[] memory calls = new SimpleDelegateContract.Call[](1); + bytes memory data = abi.encodeCall(ERC20.mint, (100, bob)); + calls[0] = SimpleDelegateContract.Call({to: address(token), data: data, value: 0}); + + vm.broadcast(alice_pk); + // empty revert because no bytecode was set to Alice's account + vm.expectRevert(); + SimpleDelegateContract(alice).execute(calls); + } + + function testAttachDelegationRevertsAfterNonceChange() public { + Vm.SignedDelegation memory signedDelegation = vm.signDelegation(address(implementation), alice_pk); + + vm.broadcast(alice_pk); + // send tx to increment alice's nonce + token.mint(1, bob); + + vm.expectRevert("vm.attachDelegation: invalid nonce"); + vm.attachDelegation(signedDelegation); + } + + function testCallSingleSignAndAttachDelegation() public { + SimpleDelegateContract.Call[] memory calls = new SimpleDelegateContract.Call[](1); + bytes memory data = abi.encodeCall(ERC20.mint, (100, bob)); + calls[0] = SimpleDelegateContract.Call({to: address(token), data: data, value: 0}); + vm.signAndAttachDelegation(address(implementation), alice_pk); + bytes memory code = address(alice).code; + require(code.length > 0, "no code written to alice"); + vm.broadcast(bob_pk); + SimpleDelegateContract(alice).execute(calls); + + assertEq(token.balanceOf(bob), 100); + } +} + +contract SimpleDelegateContract { + event Executed(address indexed to, uint256 value, bytes data); + event ExecutedBy(uint256 id); + + struct Call { + bytes data; + address to; + uint256 value; + } + + uint256 public immutable id; + + constructor(uint256 _id) { + id = _id; + } + + function execute(Call[] memory calls) external payable { + for (uint256 i = 0; i < calls.length; i++) { + Call memory call = calls[i]; + (bool success, bytes memory result) = call.to.call{value: call.value}(call.data); + require(success, string(result)); + emit Executed(call.to, call.value, call.data); + emit ExecutedBy(id); + } + } + + receive() external payable {} +} + +contract ERC20 { + address public minter; + mapping(address => uint256) private _balances; + + constructor(address _minter) { + minter = _minter; + } + + function mint(uint256 amount, address to) public { + _mint(to, amount); + } + + function balanceOf(address account) public view returns (uint256) { + return _balances[account]; + } + + function _mint(address account, uint256 amount) internal { + require(msg.sender == minter, "ERC20: msg.sender is not minter"); + require(account != address(0), "ERC20: mint to the zero address"); + unchecked { + _balances[account] += amount; + } + } +} From b9ee7de28ff7468a58eec811cb283e98c24fbecb Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:21:38 +0100 Subject: [PATCH 28/68] chore(deps): bump foundry-compilers 0.12.2 (#9364) --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6759a6d2fcfd..189f2b222b52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3766,9 +3766,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edf09554357ebfcd2ea28503badbaca311aac3c947d269a6bae5256543aa172" +checksum = "bb4df7894aa6a5371df998d3945a801e21daafaf5aa20541be8c25671768c984" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3804,9 +3804,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134b2499a20136716422f1ae5afd3a5d6c2ef833bf97b7c956285ca96c1d9743" +checksum = "732c9950ef23e96584e8b9f022562dbceb5b8d33dbb7a6ec033ee3b3e63d26e2" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3814,9 +3814,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78a9be34b3a43e77871e5bbd75f4a81d23eebf8f098519ae1ae9e163a0f3d0da" +checksum = "b43d648a62b85344d1d7cc445541a2db6aa85b5acd971799f0cc3c18bbdf801b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3838,9 +3838,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1738950051ebcee2135adac07b3ef1708c6377ffed0c5e9a2bb485f31498befb" +checksum = "167b634d89209acd6c28d9f4abd19e0d77e93a40595fb1c42dab7b4ca7a2bfe5" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3853,9 +3853,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac2d9982eedb0eb3819f82c7efa1e28c1f78e031cdfb6adfa34690364fe5e0d" +checksum = "dfe866840fe05f80cd105cd7f535f5c567a500a3dda80e70f6ebe8f149001544" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index 624264cf218e..2e52d1e59327 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -169,7 +169,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.9.0", default-features = false } -foundry-compilers = { version = "0.12.1", default-features = false } +foundry-compilers = { version = "0.12.2", default-features = false } foundry-fork-db = "0.6.0" solang-parser = "=0.3.3" From 622f922739923ed243b1b5d701bb9e0898b3ffee Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:16:51 +0100 Subject: [PATCH 29/68] feat: rename `ShellOtps` to `GlobalOpts` (#9313) * rename ShellOpts to GlobalOpts * prefer arg / command over clap * add global opts * remove redundant GlobalOpts injection, only use where access to the global variables is required * add global thread pool * add try_jobs method for global rayon pool * limit unnecessary globalopts injection where shell::* is preferred * fix tests * port custom threads iterator to use global rayon thread pool * remove redundant ignores * remove leftover from merge conflict, fix clashing args with inlined global in nodeargs / anvil top level args * leftovers * add back global args in script args * fix unused global opts * ignore attempted multiple initializations of the global thread pool * add init, default spawn with default rayon settings on forge test * make test thread number configurable * add back max threads back test to reduce pressure * remove global --jobs rayon pool, revert to current implementation * fix import --- crates/anvil/src/anvil.rs | 11 +++++---- crates/cast/bin/args.rs | 9 +++---- crates/cast/bin/main.rs | 3 ++- crates/chisel/bin/main.rs | 12 ++++++---- crates/cli/src/opts/{shell.rs => global.rs} | 26 ++++++++++++++------- crates/cli/src/opts/mod.rs | 4 ++-- crates/common/src/io/shell.rs | 15 ++++++++++-- crates/forge/bin/cmd/create.rs | 8 +++---- crates/forge/bin/cmd/soldeer.rs | 1 - crates/forge/bin/cmd/test/mod.rs | 9 +++---- crates/forge/bin/main.rs | 2 +- crates/forge/bin/opts.rs | 9 +++---- crates/script/src/lib.rs | 9 +++---- crates/verify/src/bytecode.rs | 13 ++++++----- 14 files changed, 79 insertions(+), 52 deletions(-) rename crates/cli/src/opts/{shell.rs => global.rs} (80%) diff --git a/crates/anvil/src/anvil.rs b/crates/anvil/src/anvil.rs index 5e2a42f59ef0..48e17ed44d29 100644 --- a/crates/anvil/src/anvil.rs +++ b/crates/anvil/src/anvil.rs @@ -3,7 +3,7 @@ use anvil::cmd::NodeArgs; use clap::{CommandFactory, Parser, Subcommand}; use eyre::Result; -use foundry_cli::{opts::ShellOpts, utils}; +use foundry_cli::{opts::GlobalOpts, utils}; #[cfg(all(feature = "jemalloc", unix))] #[global_allocator] @@ -13,14 +13,15 @@ static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; #[derive(Parser)] #[command(name = "anvil", version = anvil::VERSION_MESSAGE, next_display_order = None)] pub struct Anvil { + /// Include the global options. + #[command(flatten)] + pub global: GlobalOpts, + #[command(flatten)] pub node: NodeArgs, #[command(subcommand)] pub cmd: Option, - - #[clap(flatten)] - pub shell: ShellOpts, } #[derive(Subcommand)] @@ -48,7 +49,7 @@ fn run() -> Result<()> { utils::load_dotenv(); let mut args = Anvil::parse(); - args.shell.shell().set(); + args.global.init()?; args.node.evm_opts.resolve_rpc_alias(); if let Some(cmd) = &args.cmd { diff --git a/crates/cast/bin/args.rs b/crates/cast/bin/args.rs index 8bb7f2d54433..f010c279e0d0 100644 --- a/crates/cast/bin/args.rs +++ b/crates/cast/bin/args.rs @@ -9,7 +9,7 @@ use alloy_primitives::{Address, B256, U256}; use alloy_rpc_types::BlockId; use clap::{Parser, Subcommand, ValueHint}; use eyre::Result; -use foundry_cli::opts::{EtherscanOpts, RpcOpts, ShellOpts}; +use foundry_cli::opts::{EtherscanOpts, GlobalOpts, RpcOpts}; use foundry_common::ens::NameOrAddress; use std::{path::PathBuf, str::FromStr}; @@ -31,11 +31,12 @@ const VERSION_MESSAGE: &str = concat!( next_display_order = None, )] pub struct Cast { + /// Include the global options. + #[command(flatten)] + pub global: GlobalOpts, + #[command(subcommand)] pub cmd: CastSubcommand, - - #[clap(flatten)] - pub shell: ShellOpts, } #[derive(Subcommand)] diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 2eac8682c21c..ffce79099c9c 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -50,8 +50,9 @@ fn run() -> Result<()> { utils::load_dotenv(); utils::subscriber(); utils::enable_paint(); + let args = CastArgs::parse(); - args.shell.shell().set(); + args.global.init()?; main_args(args) } diff --git a/crates/chisel/bin/main.rs b/crates/chisel/bin/main.rs index dca99d4a594d..acc2c6f17263 100644 --- a/crates/chisel/bin/main.rs +++ b/crates/chisel/bin/main.rs @@ -11,7 +11,7 @@ use clap::{Parser, Subcommand}; use eyre::Context; use foundry_cli::{ handler, - opts::{CoreBuildArgs, ShellOpts}, + opts::{CoreBuildArgs, GlobalOpts}, utils::{self, LoadConfig}, }; use foundry_common::{evm::EvmArgs, fs}; @@ -50,12 +50,13 @@ const VERSION_MESSAGE: &str = concat!( #[derive(Debug, Parser)] #[command(name = "chisel", version = VERSION_MESSAGE)] pub struct Chisel { + /// Include the global options. + #[command(flatten)] + pub global: GlobalOpts, + #[command(subcommand)] pub cmd: Option, - #[clap(flatten)] - pub shell: ShellOpts, - /// Path to a directory containing Solidity files to import, or path to a single Solidity file. /// /// These files will be evaluated before the top-level of the @@ -117,8 +118,9 @@ fn run() -> eyre::Result<()> { handler::install(); utils::subscriber(); utils::load_dotenv(); + let args = Chisel::parse(); - args.shell.shell().set(); + args.global.init()?; main_args(args) } diff --git a/crates/cli/src/opts/shell.rs b/crates/cli/src/opts/global.rs similarity index 80% rename from crates/cli/src/opts/shell.rs rename to crates/cli/src/opts/global.rs index e559cc0f275f..99690a530d3f 100644 --- a/crates/cli/src/opts/shell.rs +++ b/crates/cli/src/opts/global.rs @@ -1,11 +1,10 @@ use clap::{ArgAction, Parser}; use foundry_common::shell::{ColorChoice, OutputFormat, OutputMode, Shell, Verbosity}; +use serde::{Deserialize, Serialize}; -// note: `verbose` and `quiet` cannot have `short` because of conflicts with multiple commands. - -/// Global shell options. -#[derive(Clone, Copy, Debug, Default, Parser)] -pub struct ShellOpts { +/// Global options. +#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, Parser)] +pub struct GlobalOpts { /// Verbosity level of the log messages. /// /// Pass multiple times to increase the verbosity (e.g. -v, -vv, -vvv). @@ -22,7 +21,7 @@ pub struct ShellOpts { /// Do not print log messages. #[clap(short, long, global = true, alias = "silent", help_heading = "Display options")] - pub quiet: bool, + quiet: bool, /// Format log messages as JSON. #[clap( @@ -32,14 +31,23 @@ pub struct ShellOpts { conflicts_with_all = &["quiet", "color"], help_heading = "Display options" )] - pub json: bool, + json: bool, /// The color of the log messages. #[clap(long, global = true, value_enum, help_heading = "Display options")] - pub color: Option, + color: Option, } -impl ShellOpts { +impl GlobalOpts { + /// Initialize the global options. + pub fn init(self) -> eyre::Result<()> { + // Set the global shell. + self.shell().set(); + + Ok(()) + } + + /// Create a new shell instance. pub fn shell(self) -> Shell { let mode = match self.quiet { true => OutputMode::Quiet, diff --git a/crates/cli/src/opts/mod.rs b/crates/cli/src/opts/mod.rs index 95bf9e126cc6..4e5d35572438 100644 --- a/crates/cli/src/opts/mod.rs +++ b/crates/cli/src/opts/mod.rs @@ -2,12 +2,12 @@ mod build; mod chain; mod dependency; mod ethereum; -mod shell; +mod global; mod transaction; pub use build::*; pub use chain::*; pub use dependency::*; pub use ethereum::*; -pub use shell::*; +pub use global::*; pub use transaction::*; diff --git a/crates/common/src/io/shell.rs b/crates/common/src/io/shell.rs index 09a167df9440..fd667ea682eb 100644 --- a/crates/common/src/io/shell.rs +++ b/crates/common/src/io/shell.rs @@ -7,6 +7,7 @@ use anstream::AutoStream; use anstyle::Style; use clap::ValueEnum; use eyre::Result; +use serde::{Deserialize, Serialize}; use std::{ fmt, io::{prelude::*, IsTerminal}, @@ -34,7 +35,7 @@ pub fn is_quiet() -> bool { /// Returns whether the output format is [`OutputFormat::Json`]. pub fn is_json() -> bool { - Shell::get().output_format().is_json() + Shell::get().is_json() } /// The global shell instance. @@ -172,7 +173,7 @@ enum ShellOut { } /// Whether messages should use color output. -#[derive(Debug, Default, PartialEq, Clone, Copy, ValueEnum)] +#[derive(Debug, Default, PartialEq, Clone, Copy, Serialize, Deserialize, ValueEnum)] pub enum ColorChoice { /// Intelligently guess whether to use color output (default). #[default] @@ -262,6 +263,16 @@ impl Shell { self.needs_clear.swap(needs_clear, Ordering::Relaxed) } + /// Returns `true` if the output format is JSON. + pub fn is_json(&self) -> bool { + self.output_format.is_json() + } + + /// Returns `true` if the verbosity level is `Quiet`. + pub fn is_quiet(&self) -> bool { + self.output_mode.is_quiet() + } + /// Returns `true` if the `needs_clear` flag is set. #[inline] pub fn needs_clear(&self) -> bool { diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index e19feafcc628..9fc2629e5988 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -10,7 +10,7 @@ use alloy_signer::Signer; use alloy_transport::{Transport, TransportError}; use clap::{Parser, ValueHint}; use eyre::{Context, Result}; -use forge_verify::RetryArgs; +use forge_verify::{RetryArgs, VerifierArgs, VerifyArgs}; use foundry_cli::{ opts::{CoreBuildArgs, EthereumOpts, EtherscanOpts, TransactionOpts}, utils::{self, read_constructor_args_file, remove_contract, LoadConfig}, @@ -89,7 +89,7 @@ pub struct CreateArgs { eth: EthereumOpts, #[command(flatten)] - pub verifier: forge_verify::VerifierArgs, + pub verifier: VerifierArgs, #[command(flatten)] retry: RetryArgs, @@ -201,7 +201,7 @@ impl CreateArgs { ) -> Result<()> { // NOTE: this does not represent the same `VerifyArgs` that would be sent after deployment, // since we don't know the address yet. - let mut verify = forge_verify::VerifyArgs { + let mut verify = VerifyArgs { address: Default::default(), contract: Some(self.contract.clone()), compiler_version: Some(id.version.to_string()), @@ -359,7 +359,7 @@ impl CreateArgs { } else { None }; - let verify = forge_verify::VerifyArgs { + let verify = VerifyArgs { address, contract: Some(self.contract), compiler_version: Some(id.version.to_string()), diff --git a/crates/forge/bin/cmd/soldeer.rs b/crates/forge/bin/cmd/soldeer.rs index 56b8c31f3a51..b2c9a77f1074 100644 --- a/crates/forge/bin/cmd/soldeer.rs +++ b/crates/forge/bin/cmd/soldeer.rs @@ -1,6 +1,5 @@ use clap::Parser; use eyre::Result; - use soldeer_commands::Command; // CLI arguments for `forge soldeer`. diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 4e811bcbfbf9..435c34275850 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -17,7 +17,7 @@ use forge::{ MultiContractRunner, MultiContractRunnerBuilder, TestFilter, TestOptions, TestOptionsBuilder, }; use foundry_cli::{ - opts::{CoreBuildArgs, ShellOpts}, + opts::{CoreBuildArgs, GlobalOpts}, utils::{self, LoadConfig}, }; use foundry_common::{compile::ProjectCompiler, evm::EvmArgs, fs, shell, TestFunctionExt}; @@ -65,6 +65,10 @@ foundry_config::merge_impl_figment_convert!(TestArgs, opts, evm_opts); #[derive(Clone, Debug, Parser)] #[command(next_help_heading = "Test options")] pub struct TestArgs { + /// Include the global options. + #[command(flatten)] + pub global: GlobalOpts, + /// The contract file you want to test, it's a shortcut for --match-path. #[arg(value_hint = ValueHint::FilePath)] pub path: Option, @@ -178,9 +182,6 @@ pub struct TestArgs { /// Print detailed test summary table. #[arg(long, help_heading = "Display options", requires = "summary")] pub detailed: bool, - - #[command(flatten)] - shell: ShellOpts, } impl TestArgs { diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index ee49f7f692b2..ac0992cf9f65 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -35,7 +35,7 @@ fn run() -> Result<()> { utils::enable_paint(); let args = Forge::parse(); - args.shell.shell().set(); + args.global.init()?; init_execution_context(&args.cmd); match args.cmd { diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index 39bc89e63b8f..380cb61d403a 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -8,7 +8,7 @@ use crate::cmd::{ use clap::{Parser, Subcommand, ValueHint}; use forge_script::ScriptArgs; use forge_verify::{VerifyArgs, VerifyBytecodeArgs, VerifyCheckArgs}; -use foundry_cli::opts::ShellOpts; +use foundry_cli::opts::GlobalOpts; use std::path::PathBuf; const VERSION_MESSAGE: &str = concat!( @@ -29,11 +29,12 @@ const VERSION_MESSAGE: &str = concat!( next_display_order = None, )] pub struct Forge { + /// Include the global options. + #[command(flatten)] + pub global: GlobalOpts, + #[command(subcommand)] pub cmd: ForgeSubcommand, - - #[clap(flatten)] - pub shell: ShellOpts, } #[derive(Subcommand)] diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 180a09647d4f..0f0283b9f01a 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -27,7 +27,7 @@ use eyre::{ContextCompat, Result}; use forge_script_sequence::{AdditionalContract, NestedValue}; use forge_verify::RetryArgs; use foundry_cli::{ - opts::{CoreBuildArgs, ShellOpts}, + opts::{CoreBuildArgs, GlobalOpts}, utils::LoadConfig, }; use foundry_common::{ @@ -78,6 +78,10 @@ foundry_config::merge_impl_figment_convert!(ScriptArgs, opts, evm_opts); /// CLI arguments for `forge script`. #[derive(Clone, Debug, Default, Parser)] pub struct ScriptArgs { + /// Include the global options. + #[command(flatten)] + pub global: GlobalOpts, + /// The contract you want to run. Either the file path or contract name. /// /// If multiple contracts exist in the same file you must specify the target contract with @@ -213,9 +217,6 @@ pub struct ScriptArgs { #[command(flatten)] pub retry: RetryArgs, - - #[clap(flatten)] - pub shell: ShellOpts, } impl ScriptArgs { diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 0c3f9e5b0a91..cd86c4200625 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -35,11 +35,11 @@ pub struct VerifyBytecodeArgs { pub contract: ContractInfo, /// The block at which the bytecode should be verified. - #[clap(long, value_name = "BLOCK")] + #[arg(long, value_name = "BLOCK")] pub block: Option, /// The constructor args to generate the creation code. - #[clap( + #[arg( long, num_args(1..), conflicts_with_all = &["constructor_args_path", "encoded_constructor_args"], @@ -65,14 +65,15 @@ pub struct VerifyBytecodeArgs { pub constructor_args_path: Option, /// The rpc url to use for verification. - #[clap(short = 'r', long, value_name = "RPC_URL", env = "ETH_RPC_URL")] + #[arg(short = 'r', long, value_name = "RPC_URL", env = "ETH_RPC_URL")] pub rpc_url: Option, - #[clap(flatten)] + /// Etherscan options. + #[command(flatten)] pub etherscan: EtherscanOpts, /// Verifier options. - #[clap(flatten)] + #[command(flatten)] pub verifier: VerifierArgs, /// The project's root path. @@ -83,7 +84,7 @@ pub struct VerifyBytecodeArgs { pub root: Option, /// Ignore verification for creation or runtime bytecode. - #[clap(long, value_name = "BYTECODE_TYPE")] + #[arg(long, value_name = "BYTECODE_TYPE")] pub ignore: Option, } From fb5f0e1c4d9b9b0861be3e3bd07963524c5ac08e Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:06:42 +0200 Subject: [PATCH 30/68] chore(deps): bump foundry-compilers 0.12.3 (#9368) --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 189f2b222b52..7451f42b7ba9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3766,9 +3766,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb4df7894aa6a5371df998d3945a801e21daafaf5aa20541be8c25671768c984" +checksum = "3a6131af72175c55aa531c4851290d9cf67af7a82b03f20a8a9d28dba81b9fd3" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3804,9 +3804,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732c9950ef23e96584e8b9f022562dbceb5b8d33dbb7a6ec033ee3b3e63d26e2" +checksum = "c522c473e7a111b81f0d47f8a65ca1ef0642c8c8d1ca2d1e230338210d16bb02" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3814,9 +3814,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b43d648a62b85344d1d7cc445541a2db6aa85b5acd971799f0cc3c18bbdf801b" +checksum = "c7cd569a0c38d232244932e71933b54e418cddcc8f0c16fd8af96dd844b27788" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3838,9 +3838,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167b634d89209acd6c28d9f4abd19e0d77e93a40595fb1c42dab7b4ca7a2bfe5" +checksum = "3b80563ba10981ec4a9667fda9318d02721a81f248ae73303603388580150a35" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3853,9 +3853,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe866840fe05f80cd105cd7f535f5c567a500a3dda80e70f6ebe8f149001544" +checksum = "4a840a87cb4845d208df3390a12d3724e6d1cc1268a51ac334a01f80f9b6419b" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index 2e52d1e59327..0cefa4cdb571 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -169,7 +169,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.9.0", default-features = false } -foundry-compilers = { version = "0.12.2", default-features = false } +foundry-compilers = { version = "0.12.3", default-features = false } foundry-fork-db = "0.6.0" solang-parser = "=0.3.3" From 25cc1ac68b5f6977f23d713c01ec455ad7f03d21 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 21 Nov 2024 00:17:59 +0530 Subject: [PATCH 31/68] chore(deps): bump alloy 0.6.4 (#9280) * bump alloy to 0.6.1 * fix: ui - use AnyRpcBlock * fix: wallets - use PrimitveSig * bump 0.6.2 * replace: AnyNetworkBlock with AnyRpcBlock + HeaderResponse with BlockHeader * fix: configure_tx_env * fix: crypto cheatcodes * fix: anvil_core tx * fix * fix: verify-bytecode * fix cast + get_pretty_tx_attr * fix(`anvil`): core TypedTx + BlockListener task * fix * fix: anvil tests * fix: test_deser_block * fix: transaction_build * fix: test load state files * fix: deny.toml * fix: try_from AnyRpcTx to DepositTx + bump op-alloy types * bump * fix: configure_tx_env * fix: UiFmt * fix: vb * fix: common-fmt tests * nit * fix: sig encoding * fix: process deposit tx in transaction_build * fix: common-fmt tests * fix * Update deny.toml Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fixes * fix: use alloy impls for conversions * nit * fix: transaction_build * nit * fix: eip155 check and rm anvil core utils * clippy * nits * fix * nit * fix: impl UIfmt for TxEnvelope and AnyTxEnvelope * make header in pretty_block_basics exhaustive * clippy * fix * fix: txpool_content * fix * fix * fix overriding hashes * fix --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> --- Cargo.lock | 158 +++--- Cargo.toml | 54 +- crates/anvil/Cargo.toml | 2 +- crates/anvil/core/Cargo.toml | 6 +- crates/anvil/core/src/eth/mod.rs | 1 - crates/anvil/core/src/eth/transaction/mod.rs | 473 +++++----------- crates/anvil/core/src/eth/utils.rs | 13 - crates/anvil/src/config.rs | 35 +- crates/anvil/src/eth/api.rs | 87 ++- crates/anvil/src/eth/backend/db.rs | 4 +- crates/anvil/src/eth/backend/fork.rs | 43 +- crates/anvil/src/eth/backend/info.rs | 8 +- crates/anvil/src/eth/backend/mem/mod.rs | 273 ++++++---- crates/anvil/src/eth/error.rs | 5 + crates/anvil/src/eth/otterscan/api.rs | 57 +- crates/anvil/src/eth/pool/transactions.rs | 9 +- crates/anvil/src/eth/sign.rs | 2 +- crates/anvil/src/tasks/mod.rs | 10 +- .../test-data/state-dump-legacy-stress.json | 2 +- crates/anvil/test-data/state-dump-legacy.json | 2 +- crates/anvil/test-data/state-dump.json | 2 +- crates/anvil/tests/it/anvil_api.rs | 12 +- crates/anvil/tests/it/api.rs | 2 +- crates/anvil/tests/it/eip4844.rs | 18 +- crates/anvil/tests/it/eip7702.rs | 2 +- crates/anvil/tests/it/fork.rs | 4 +- crates/anvil/tests/it/ipc.rs | 2 +- crates/anvil/tests/it/logs.rs | 9 +- crates/anvil/tests/it/otterscan.rs | 5 +- crates/anvil/tests/it/pubsub.rs | 4 +- crates/anvil/tests/it/transaction.rs | 4 +- crates/cast/bin/cmd/creation_code.rs | 5 +- crates/cast/bin/cmd/run.rs | 31 +- crates/cast/src/lib.rs | 6 +- crates/cheatcodes/src/crypto.rs | 20 +- crates/cheatcodes/src/script.rs | 16 +- crates/common/fmt/src/ui.rs | 513 +++++++++++++----- crates/common/src/transactions.rs | 2 +- crates/evm/core/Cargo.toml | 3 +- crates/evm/core/src/backend/mod.rs | 29 +- crates/evm/core/src/fork/init.rs | 15 +- crates/evm/core/src/fork/multi.rs | 3 +- crates/evm/core/src/opts.rs | 5 +- crates/evm/core/src/utils.rs | 13 +- crates/verify/src/bytecode.rs | 81 ++- crates/verify/src/etherscan/mod.rs | 7 +- crates/verify/src/utils.rs | 15 +- crates/wallets/src/wallet_signer.rs | 18 +- 48 files changed, 1124 insertions(+), 966 deletions(-) delete mode 100644 crates/anvil/core/src/eth/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 7451f42b7ba9..ebc0c4e0bbe1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,9 +86,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed961a48297c732a5d97ee321aa8bb5009ecadbcb077d8bec90cb54e651629" +checksum = "ae09ffd7c29062431dd86061deefe4e3c6f07fa0d674930095f8dcedb0baf02c" dependencies = [ "alloy-eips", "alloy-primitives", @@ -97,14 +97,15 @@ dependencies = [ "auto_impl", "c-kzg", "derive_more", + "k256", "serde", ] [[package]] name = "alloy-contract" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460ab80ce4bda1c80bcf96fe7460520476f2c7b734581c6567fac2708e2a60ef" +checksum = "66430a72d5bf5edead101c8c2f0a24bada5ec9f3cf9909b3e08b6d6899b4803e" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -157,9 +158,9 @@ dependencies = [ [[package]] name = "alloy-eip7702" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ffc577390ce50234e02d841214b3dc0bea6aaaae8e04bbf3cb82e9a45da9eb" +checksum = "5f6cee6a35793f3db8a5ffe60e86c695f321d081a567211245f503e8c498fce8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -172,9 +173,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69e06cf9c37be824b9d26d6d101114fdde6af0c87de2828b414c05c4b3daa71" +checksum = "5b6aa3961694b30ba53d41006131a2fca3bdab22e4c344e46db2c639e7c2dfdd" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -190,9 +191,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde15e14944a88bd6a57d325e9a49b75558746fe16aaccc79713ae50a6a9574c" +checksum = "e53f7877ded3921d18a0a9556d55bedf84535567198c9edab2aa23106da91855" dependencies = [ "alloy-primitives", "alloy-serde", @@ -213,9 +214,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af5979e0d5a7bf9c7eb79749121e8256e59021af611322aee56e77e20776b4b3" +checksum = "3694b7e480728c0b3e228384f223937f14c10caef5a4c766021190fc8f283d35" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -227,9 +228,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "204237129086ce5dc17a58025e93739b01b45313841f98fa339eb1d780511e57" +checksum = "ea94b8ceb5c75d7df0a93ba0acc53b55a22b47b532b600a800a87ef04eb5b0b4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -243,14 +244,16 @@ dependencies = [ "async-trait", "auto_impl", "futures-utils-wasm", + "serde", + "serde_json", "thiserror 1.0.69", ] [[package]] name = "alloy-network-primitives" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514f70ee2a953db21631cd817b13a1571474ec77ddc03d47616d5e8203489fde" +checksum = "df9f3e281005943944d15ee8491534a1c7b3cbf7a7de26f8c433b842b93eb5f9" dependencies = [ "alloy-consensus", "alloy-eips", @@ -293,9 +296,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4814d141ede360bb6cd1b4b064f1aab9de391e7c4d0d4d50ac89ea4bc1e25fbd" +checksum = "40c1f9eede27bf4c13c099e8e64d54efd7ce80ef6ea47478aa75d5d74e2dba3b" dependencies = [ "alloy-chains", "alloy-consensus", @@ -335,9 +338,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba46eb69ddf7a9925b81f15229cb74658e6eebe5dd30a5b74e2cd040380573" +checksum = "90f1f34232f77341076541c405482e4ae12f0ee7153d8f9969fc1691201b2247" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -376,9 +379,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc2bd1e7403463a5f2c61e955bcc9d3072b63aa177442b0f9aa6a6d22a941e3" +checksum = "374dbe0dc3abdc2c964f36b3d3edf9cdb3db29d16bda34aa123f03d810bec1dd" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -402,9 +405,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eea9bf1abdd506f985a53533f5ac01296bcd6102c5e139bbc5d40bc468d2c916" +checksum = "c74832aa474b670309c20fffc2a869fa141edab7c79ff7963fad0a08de60bae1" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -418,20 +421,21 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2382fc63fb0cf3e02818d547b80cb66cc49a31f8803d0c328402b2008bc13650" +checksum = "5ca97963132f78ddfc60e43a017348e6d52eea983925c23652f5b330e8e02291" dependencies = [ "alloy-primitives", + "alloy-rpc-types-eth", "alloy-serde", "serde", ] [[package]] name = "alloy-rpc-types-engine" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "886d22d41992287a235af2f3af4299b5ced2bcafb81eb835572ad35747476946" +checksum = "3f56294dce86af23ad6ee8df46cf8b0d292eb5d1ff67dc88a0886051e32b1faf" dependencies = [ "alloy-consensus", "alloy-eips", @@ -447,9 +451,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b034779a4850b4b03f5be5ea674a1cf7d746b2da762b34d1860ab45e48ca27" +checksum = "a8a477281940d82d29315846c7216db45b15e90bcd52309da9f54bcf7ad94a11" dependencies = [ "alloy-consensus", "alloy-eips", @@ -466,9 +470,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5fb6c5c401321f802f69dcdb95b932f30f8158f6798793f914baac5995628e" +checksum = "ecd8b4877ef520c138af702097477cdd19504a8e1e4675ba37e92ba40f2d3c6f" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -480,9 +484,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad066b49c3b1b5f64cdd2399177a19926a6a15db2dbf11e2098de621f9e7480" +checksum = "1d4ab49acf90a71f7fb894dc5fd485f1f07a1e348966c714c4d1e0b7478850a8" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -492,9 +496,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028e72eaa9703e4882344983cfe7636ce06d8cce104a78ea62fd19b46659efc4" +checksum = "4dfa4a7ccf15b2492bb68088692481fd6b2604ccbee1d0d6c44c21427ae4df83" dependencies = [ "alloy-primitives", "serde", @@ -503,9 +507,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "592c185d7100258c041afac51877660c7bf6213447999787197db4842f0e938e" +checksum = "2e10aec39d60dc27edcac447302c7803d2371946fb737245320a05b78eb2fafd" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -519,9 +523,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a406102908a4e51834f32c4e5c1b29aa2c407b3fd23a5cad129c28b56d85e1b8" +checksum = "0109e5b18079aec2a022e4bc9db1d74bcc046f8b66274ffa8b0e4322b44b2b44" dependencies = [ "alloy-consensus", "alloy-network", @@ -537,9 +541,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d363e12280cb43747d3b62a1e6f00d595bc1a56464bb20200c6b6ca5d68185" +checksum = "558651eb0d76bcf2224de694481e421112fa2cbc6fe6a413cc76fd67e14cf0d7" dependencies = [ "alloy-consensus", "alloy-network", @@ -555,9 +559,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a642c9f66ac73ae0d5398ce7ce3ce5bdfad5658d549abd48ea48962e585dca" +checksum = "29781b6a064b6235de4ec3cc0810f59fe227b8d31258f23a077570fc9525d7a6" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -575,9 +579,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6614f02fc1d5b079b2a4a5320018317b506fd0a6d67c1fd5542a71201724986c" +checksum = "d8396f6dff60700bc1d215ee03d86ff56de268af96e2bf833a14d0bafcab9882" dependencies = [ "alloy-consensus", "alloy-network", @@ -594,9 +598,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b754988ef4e1f5f7d55c132949bb2a10491ed6bbf1d35108269014f50da1823f" +checksum = "21267541177607141a5db6fd1abed5a46553b7a6d9363cf3d047721634705905" dependencies = [ "alloy-consensus", "alloy-network", @@ -684,9 +688,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be77579633ebbc1266ae6fd7694f75c408beb1aeb6865d0b18f22893c265a061" +checksum = "f99acddb34000d104961897dbb0240298e8b775a7efffb9fda2a1a3efedd65b3" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -704,9 +708,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fd1a5d0827939847983b46f2f79510361f901dc82f8e3c38ac7397af142c6e" +checksum = "5dc013132e34eeadaa0add7e74164c1503988bfba8bae885b32e0918ba85a8a6" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -719,9 +723,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8073d1186bfeeb8fbdd1292b6f1a0731f3aed8e21e1463905abfae0b96a887a6" +checksum = "063edc0660e81260653cc6a95777c29d54c2543a668aa5da2359fb450d25a1ba" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -740,9 +744,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.5.4" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f27837bb4a1d6c83a28231c94493e814882f0e9058648a97e908a5f3fc9fcf" +checksum = "abd170e600801116d5efe64f74a4fc073dbbb35c807013a7d0a388742aeebba0" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -935,6 +939,7 @@ dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-eips", + "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", @@ -3972,13 +3977,14 @@ dependencies = [ name = "foundry-evm-core" version = "0.2.0" dependencies = [ + "alloy-consensus", "alloy-dyn-abi", "alloy-genesis", "alloy-json-abi", + "alloy-network", "alloy-primitives", "alloy-provider", "alloy-rpc-types", - "alloy-serde", "alloy-sol-types", "alloy-transport", "auto_impl", @@ -4072,10 +4078,11 @@ dependencies = [ [[package]] name = "foundry-fork-db" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fe4a1e7e0f122b28afc156681687249bd5cf910d9bf873efb1181e4d41fbc3" +checksum = "5fea8068a6b7929229f72059296da09bf3c1d15569fdb4a351d2983450587c12" dependencies = [ + "alloy-consensus", "alloy-primitives", "alloy-provider", "alloy-rpc-types", @@ -6271,9 +6278,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "op-alloy-consensus" -version = "0.5.2" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26c3b35b7b3e36d15e0563eebffe13c1d9ca16b7aaffcb6a64354633547e16b" +checksum = "bff54d1d790eca1f3aedbd666162e9c42eceff90b9f9d24b352ed9c2df1e901a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6287,9 +6294,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" -version = "0.5.2" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bae9bf91b620e1e2c2291562e5998bc1247bd8ada011773e1997b31a95de99" +checksum = "981b7f8ab11fe85ba3c1723702f000429b8d0c16b5883c93d577895f262cbac6" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6297,6 +6304,7 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", "alloy-serde", + "derive_more", "op-alloy-consensus", "serde", "serde_json", @@ -7399,9 +7407,9 @@ dependencies = [ [[package]] name = "revm" -version = "17.1.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "055bee6a81aaeee8c2389ae31f0d4de87f44df24f4444a1116f9755fd87a76ad" +checksum = "15689a3c6a8d14b647b4666f2e236ef47b5a5133cdfd423f545947986fff7013" dependencies = [ "auto_impl", "cfg-if", @@ -7414,9 +7422,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e29c662f7887f3b659d4b0fd234673419a8fcbeaa1ecc29bf7034c0a75cc8ea" +checksum = "747291a18ad6726a08dd73f8b6a6b3a844db582ecae2063ccf0a04880c44f482" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -7432,9 +7440,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "13.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac2034454f8bc69dc7d3c94cdb1b57559e27f5ef0518771f1787de543d7d6a1" +checksum = "74e3f11d0fed049a4a10f79820c59113a79b38aed4ebec786a79d5c667bfeb51" dependencies = [ "revm-primitives", "serde", @@ -7442,9 +7450,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a88c8c7c5f9b988a9e65fc0990c6ce859cdb74114db705bd118a96d22d08027" +checksum = "e381060af24b750069a2b2d2c54bba273d84e8f5f9e8026fc9262298e26cc336" dependencies = [ "aurora-engine-modexp", "blst", @@ -7462,9 +7470,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "13.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d11fa1e195b0bebaf3fb18596f314a13ba3a4cb1fdd16d3465934d812fd921e" +checksum = "3702f132bb484f4f0d0ca4f6fbde3c82cfd745041abbedd6eda67730e1868ef0" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -9761,9 +9769,9 @@ dependencies = [ [[package]] name = "wasmtimer" -version = "0.2.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7ed9d8b15c7fb594d72bfb4b5a276f3d2029333cd93a932f376f5937f6f80ee" +checksum = "bb4f099acbc1043cc752b91615b24b02d7f6fcd975bd781fed9f50b3c3e15bf7" dependencies = [ "futures", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index 0cefa4cdb571..f5ec94b906bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -170,39 +170,39 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.9.0", default-features = false } foundry-compilers = { version = "0.12.3", default-features = false } -foundry-fork-db = "0.6.0" +foundry-fork-db = "0.7.0" solang-parser = "=0.3.3" ## revm -revm = { version = "17.1.0", default-features = false } -revm-primitives = { version = "13.0.0", default-features = false } -revm-inspectors = { version = "0.10.0", features = ["serde"] } +revm = { version = "18.0.0", default-features = false } +revm-primitives = { version = "14.0.0", default-features = false } +revm-inspectors = { version = "0.11.0", features = ["serde"] } ## ethers ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { version = "0.5.4", default-features = false } -alloy-contract = { version = "0.5.4", default-features = false } -alloy-eips = { version = "0.5.4", default-features = false } -alloy-genesis = { version = "0.5.4", default-features = false } -alloy-json-rpc = { version = "0.5.4", default-features = false } -alloy-network = { version = "0.5.4", default-features = false } -alloy-provider = { version = "0.5.4", default-features = false } -alloy-pubsub = { version = "0.5.4", default-features = false } -alloy-rpc-client = { version = "0.5.4", default-features = false } -alloy-rpc-types = { version = "0.5.4", default-features = true } -alloy-serde = { version = "0.5.4", default-features = false } -alloy-signer = { version = "0.5.4", default-features = false } -alloy-signer-aws = { version = "0.5.4", default-features = false } -alloy-signer-gcp = { version = "0.5.4", default-features = false } -alloy-signer-ledger = { version = "0.5.4", default-features = false } -alloy-signer-local = { version = "0.5.4", default-features = false } -alloy-signer-trezor = { version = "0.5.4", default-features = false } -alloy-transport = { version = "0.5.4", default-features = false } -alloy-transport-http = { version = "0.5.4", default-features = false } -alloy-transport-ipc = { version = "0.5.4", default-features = false } -alloy-transport-ws = { version = "0.5.4", default-features = false } +alloy-consensus = { version = "0.6.4", default-features = false } +alloy-contract = { version = "0.6.4", default-features = false } +alloy-eips = { version = "0.6.4", default-features = false } +alloy-genesis = { version = "0.6.4", default-features = false } +alloy-json-rpc = { version = "0.6.4", default-features = false } +alloy-network = { version = "0.6.4", default-features = false } +alloy-provider = { version = "0.6.4", default-features = false } +alloy-pubsub = { version = "0.6.4", default-features = false } +alloy-rpc-client = { version = "0.6.4", default-features = false } +alloy-rpc-types = { version = "0.6.4", default-features = true } +alloy-serde = { version = "0.6.4", default-features = false } +alloy-signer = { version = "0.6.4", default-features = false } +alloy-signer-aws = { version = "0.6.4", default-features = false } +alloy-signer-gcp = { version = "0.6.4", default-features = false } +alloy-signer-ledger = { version = "0.6.4", default-features = false } +alloy-signer-local = { version = "0.6.4", default-features = false } +alloy-signer-trezor = { version = "0.6.4", default-features = false } +alloy-transport = { version = "0.6.4", default-features = false } +alloy-transport-http = { version = "0.6.4", default-features = false } +alloy-transport-ipc = { version = "0.6.4", default-features = false } +alloy-transport-ws = { version = "0.6.4", default-features = false } ## alloy-core alloy-dyn-abi = "0.8.11" @@ -222,8 +222,8 @@ alloy-rlp = "0.3" alloy-trie = "0.6.0" ## op-alloy -op-alloy-rpc-types = "0.5.0" -op-alloy-consensus = "0.5.0" +op-alloy-rpc-types = "0.6.5" +op-alloy-consensus = "0.6.5" ## cli anstream = "0.6" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 4c3cbc4f7e8e..b3389d2eccae 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -67,7 +67,7 @@ alloy-transport.workspace = true alloy-chains.workspace = true alloy-genesis.workspace = true alloy-trie.workspace = true -op-alloy-consensus.workspace = true +op-alloy-consensus = { workspace = true, features = ["serde"] } # axum related axum.workspace = true diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index dcf8bf50857e..dacc7f20e0ea 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -22,7 +22,7 @@ revm = { workspace = true, default-features = false, features = [ "c-kzg", ] } -alloy-primitives = { workspace = true, features = ["serde"] } +alloy-primitives = { workspace = true, features = ["serde", "rlp"] } alloy-rpc-types = { workspace = true, features = ["anvil", "trace"] } alloy-serde.workspace = true alloy-rlp.workspace = true @@ -30,8 +30,8 @@ alloy-eips.workspace = true alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] } alloy-trie.workspace = true -op-alloy-consensus.workspace = true - +op-alloy-consensus = { workspace = true, features = ["serde"] } +alloy-network.workspace = true serde = { workspace = true, optional = true } serde_json.workspace = true bytes.workspace = true diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 33ffda6a701f..b1d3b85221be 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -18,7 +18,6 @@ pub mod proof; pub mod subscription; pub mod transaction; pub mod trie; -pub mod utils; pub mod wallet; #[cfg(feature = "serde")] diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 827f14a55402..8de659799a36 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -6,22 +6,23 @@ use alloy_consensus::{ eip4844::{TxEip4844, TxEip4844Variant, TxEip4844WithSidecar}, TxEip7702, }, - AnyReceiptEnvelope, Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, TxEip1559, TxEip2930, - TxEnvelope, TxLegacy, TxReceipt, TxType, + AnyReceiptEnvelope, Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, Transaction, TxEip1559, + TxEip2930, TxEnvelope, TxLegacy, TxReceipt, }; use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Encodable2718}; +use alloy_network::{AnyRpcTransaction, AnyTxEnvelope}; use alloy_primitives::{ - Address, Bloom, Bytes, Log, Parity, Signature, TxHash, TxKind, B256, U256, U64, + Address, Bloom, Bytes, Log, PrimitiveSignature, TxHash, TxKind, B256, U256, U64, }; use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; use alloy_rpc_types::{ request::TransactionRequest, trace::otterscan::OtsReceipt, AccessList, AnyTransactionReceipt, - ConversionError, Signature as RpcSignature, Transaction as RpcTransaction, TransactionReceipt, + ConversionError, Transaction as RpcTransaction, TransactionReceipt, }; use alloy_serde::{OtherFields, WithOtherFields}; use bytes::BufMut; use foundry_evm::traces::CallTraceNode; -use op_alloy_consensus::TxDeposit; +use op_alloy_consensus::{TxDeposit, DEPOSIT_TX_TYPE_ID}; use revm::{ interpreter::InstructionResult, primitives::{OptimismFields, TxEnv}, @@ -278,162 +279,64 @@ pub fn to_alloy_transaction_with_hash_and_sender( from: Address, ) -> RpcTransaction { match transaction { - TypedTransaction::Legacy(t) => RpcTransaction { - hash, - nonce: t.tx().nonce, - block_hash: None, - block_number: None, - transaction_index: None, - from, - to: t.tx().to.to().copied(), - value: t.tx().value, - gas_price: Some(t.tx().gas_price), - max_fee_per_gas: Some(t.tx().gas_price), - max_priority_fee_per_gas: Some(t.tx().gas_price), - gas: t.tx().gas_limit, - input: t.tx().input.clone(), - chain_id: t.tx().chain_id, - signature: Some(RpcSignature { - r: t.signature().r(), - s: t.signature().s(), - v: U256::from(t.signature().v().to_u64()), - y_parity: None, - }), - access_list: None, - transaction_type: Some(0), - max_fee_per_blob_gas: None, - blob_versioned_hashes: None, - authorization_list: None, - }, - TypedTransaction::EIP2930(t) => RpcTransaction { - hash, - nonce: t.tx().nonce, - block_hash: None, - block_number: None, - transaction_index: None, - from, - to: t.tx().to.to().copied(), - value: t.tx().value, - gas_price: Some(t.tx().gas_price), - max_fee_per_gas: Some(t.tx().gas_price), - max_priority_fee_per_gas: Some(t.tx().gas_price), - gas: t.tx().gas_limit, - input: t.tx().input.clone(), - chain_id: Some(t.tx().chain_id), - signature: Some(RpcSignature { - r: t.signature().r(), - s: t.signature().s(), - v: U256::from(t.signature().v().to_u64()), - y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), - }), - access_list: Some(t.tx().access_list.clone()), - transaction_type: Some(1), - max_fee_per_blob_gas: None, - blob_versioned_hashes: None, - authorization_list: None, - }, - TypedTransaction::EIP1559(t) => RpcTransaction { - hash, - nonce: t.tx().nonce, - block_hash: None, - block_number: None, - transaction_index: None, - from, - to: t.tx().to.to().copied(), - value: t.tx().value, - gas_price: None, - max_fee_per_gas: Some(t.tx().max_fee_per_gas), - max_priority_fee_per_gas: Some(t.tx().max_priority_fee_per_gas), - gas: t.tx().gas_limit, - input: t.tx().input.clone(), - chain_id: Some(t.tx().chain_id), - signature: Some(RpcSignature { - r: t.signature().r(), - s: t.signature().s(), - v: U256::from(t.signature().v().to_u64()), - y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), - }), - access_list: Some(t.tx().access_list.clone()), - transaction_type: Some(2), - max_fee_per_blob_gas: None, - blob_versioned_hashes: None, - authorization_list: None, - }, - TypedTransaction::EIP4844(t) => RpcTransaction { - hash, - nonce: t.tx().tx().nonce, - block_hash: None, - block_number: None, - transaction_index: None, - from, - to: Some(t.tx().tx().to), - value: t.tx().tx().value, - gas_price: Some(t.tx().tx().max_fee_per_gas), - max_fee_per_gas: Some(t.tx().tx().max_fee_per_gas), - max_priority_fee_per_gas: Some(t.tx().tx().max_priority_fee_per_gas), - gas: t.tx().tx().gas_limit, - input: t.tx().tx().input.clone(), - chain_id: Some(t.tx().tx().chain_id), - signature: Some(RpcSignature { - r: t.signature().r(), - s: t.signature().s(), - v: U256::from(t.signature().v().to_u64()), - y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), - }), - access_list: Some(t.tx().tx().access_list.clone()), - transaction_type: Some(3), - max_fee_per_blob_gas: Some(t.tx().tx().max_fee_per_blob_gas), - blob_versioned_hashes: Some(t.tx().tx().blob_versioned_hashes.clone()), - authorization_list: None, - }, - TypedTransaction::EIP7702(t) => RpcTransaction { - hash, - nonce: t.tx().nonce, - block_hash: None, - block_number: None, - transaction_index: None, - from, - to: Some(t.tx().to), - value: t.tx().value, - gas_price: Some(t.tx().max_fee_per_gas), - max_fee_per_gas: Some(t.tx().max_fee_per_gas), - max_priority_fee_per_gas: Some(t.tx().max_priority_fee_per_gas), - gas: t.tx().gas_limit, - input: t.tx().input.clone(), - chain_id: Some(t.tx().chain_id), - signature: Some(RpcSignature { - r: t.signature().r(), - s: t.signature().s(), - v: U256::from(t.signature().v().to_u64()), - y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), - }), - access_list: Some(t.tx().access_list.clone()), - transaction_type: Some(4), - authorization_list: Some(t.tx().authorization_list.clone()), - ..Default::default() - }, - TypedTransaction::Deposit(t) => RpcTransaction { - hash, - nonce: t.nonce, - block_hash: None, - block_number: None, - transaction_index: None, - from, - to: t.kind.to().copied(), - value: t.value, - gas_price: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - gas: t.gas_limit, - input: t.input.clone().0.into(), - chain_id: t.chain_id().map(u64::from), - signature: None, - access_list: None, - transaction_type: None, - max_fee_per_blob_gas: None, - blob_versioned_hashes: None, - authorization_list: None, - }, + TypedTransaction::Legacy(t) => { + let (tx, sig, _) = t.into_parts(); + RpcTransaction { + block_hash: None, + block_number: None, + transaction_index: None, + from, + effective_gas_price: None, + inner: TxEnvelope::Legacy(Signed::new_unchecked(tx, sig, hash)), + } + } + TypedTransaction::EIP2930(t) => { + let (tx, sig, _) = t.into_parts(); + RpcTransaction { + block_hash: None, + block_number: None, + transaction_index: None, + from, + effective_gas_price: None, + inner: TxEnvelope::Eip2930(Signed::new_unchecked(tx, sig, hash)), + } + } + TypedTransaction::EIP1559(t) => { + let (tx, sig, _) = t.into_parts(); + RpcTransaction { + block_hash: None, + block_number: None, + transaction_index: None, + from, + effective_gas_price: None, + inner: TxEnvelope::Eip1559(Signed::new_unchecked(tx, sig, hash)), + } + } + TypedTransaction::EIP4844(t) => { + let (tx, sig, _) = t.into_parts(); + RpcTransaction { + block_hash: None, + block_number: None, + transaction_index: None, + from, + effective_gas_price: None, + inner: TxEnvelope::Eip4844(Signed::new_unchecked(tx, sig, hash)), + } + } + TypedTransaction::EIP7702(t) => { + let (tx, sig, _) = t.into_parts(); + RpcTransaction { + block_hash: None, + block_number: None, + transaction_index: None, + from, + effective_gas_price: None, + inner: TxEnvelope::Eip7702(Signed::new_unchecked(tx, sig, hash)), + } + } + TypedTransaction::Deposit(_t) => { + unreachable!("cannot reach here, handled in `transaction_build` ") + } } } @@ -681,7 +584,8 @@ impl TryFrom for TransactionRequest { type Error = ConversionError; fn try_from(value: TypedTransaction) -> Result { - let from = value.recover().map_err(|_| ConversionError::InvalidSignature)?; + let from = + value.recover().map_err(|_| ConversionError::Custom("InvalidSignature".to_string()))?; let essentials = value.essentials(); let tx_type = value.r#type(); Ok(Self { @@ -702,8 +606,77 @@ impl TryFrom for TransactionRequest { } } +impl TryFrom for TypedTransaction { + type Error = ConversionError; + + fn try_from(value: AnyRpcTransaction) -> Result { + let AnyRpcTransaction { inner, .. } = value; + let from = inner.from; + match inner.inner { + AnyTxEnvelope::Ethereum(tx) => match tx { + TxEnvelope::Legacy(tx) => Ok(Self::Legacy(tx)), + TxEnvelope::Eip2930(tx) => Ok(Self::EIP2930(tx)), + TxEnvelope::Eip1559(tx) => Ok(Self::EIP1559(tx)), + TxEnvelope::Eip4844(tx) => Ok(Self::EIP4844(tx)), + TxEnvelope::Eip7702(tx) => Ok(Self::EIP7702(tx)), + _ => Err(ConversionError::Custom("UnsupportedTxType".to_string())), + }, + AnyTxEnvelope::Unknown(mut tx) => { + // Try to convert to deposit transaction + if tx.ty() == DEPOSIT_TX_TYPE_ID { + let nonce = get_field::(&tx.inner.fields, "nonce")?; + tx.inner.fields.insert("from".to_string(), serde_json::to_value(from).unwrap()); + let deposit_tx = + tx.inner.fields.deserialize_into::().map_err(|e| { + ConversionError::Custom(format!( + "Failed to deserialize deposit tx: {e}" + )) + })?; + + let TxDeposit { + source_hash, + is_system_transaction, + value, + gas_limit, + input, + mint, + from, + to, + } = deposit_tx; + + let deposit_tx = DepositTransaction { + nonce: nonce.to(), + source_hash, + from, + kind: to, + mint: mint.map(|m| U256::from(m)).unwrap_or_default(), + value, + gas_limit, + is_system_tx: is_system_transaction, + input, + }; + + return Ok(Self::Deposit(deposit_tx)); + }; + + Err(ConversionError::Custom("UnknownTxType".to_string())) + } + } + } +} + +fn get_field( + fields: &OtherFields, + key: &str, +) -> Result { + fields + .get_deserialized::(key) + .ok_or_else(|| ConversionError::Custom(format!("Missing{key}")))? + .map_err(|e| ConversionError::Custom(format!("Failed to deserialize {key}: {e}"))) +} + impl TypedTransaction { - /// Returns true if the transaction uses dynamic fees: EIP1559 or EIP4844 + /// Returns true if the transaction uses dynamic fees: EIP1559, EIP4844 or EIP7702 pub fn is_dynamic_fee(&self) -> bool { matches!(self, Self::EIP1559(_) | Self::EIP4844(_) | Self::EIP7702(_)) } @@ -992,168 +965,18 @@ impl TypedTransaction { } /// Returns the Signature of the transaction - pub fn signature(&self) -> Signature { + pub fn signature(&self) -> PrimitiveSignature { match self { Self::Legacy(tx) => *tx.signature(), Self::EIP2930(tx) => *tx.signature(), Self::EIP1559(tx) => *tx.signature(), Self::EIP4844(tx) => *tx.signature(), Self::EIP7702(tx) => *tx.signature(), - Self::Deposit(_) => Signature::from_scalars_and_parity( + Self::Deposit(_) => PrimitiveSignature::from_scalars_and_parity( B256::with_last_byte(1), B256::with_last_byte(1), - Parity::Parity(false), - ) - .unwrap(), - } - } -} - -impl TryFrom> for TypedTransaction { - type Error = ConversionError; - - fn try_from(tx: WithOtherFields) -> Result { - if tx.transaction_type.is_some_and(|t| t == 0x7E) { - let mint = tx - .other - .get_deserialized::("mint") - .ok_or(ConversionError::Custom("MissingMint".to_string()))? - .map_err(|_| ConversionError::Custom("Cannot deserialize mint".to_string()))?; - - let source_hash = tx - .other - .get_deserialized::("sourceHash") - .ok_or(ConversionError::Custom("MissingSourceHash".to_string()))? - .map_err(|_| { - ConversionError::Custom("Cannot deserialize source hash".to_string()) - })?; - - let deposit = DepositTransaction { - nonce: tx.nonce, - is_system_tx: true, - from: tx.from, - kind: tx.to.map(TxKind::Call).unwrap_or(TxKind::Create), - value: tx.value, - gas_limit: tx.gas, - input: tx.input.clone(), - mint, - source_hash, - }; - - return Ok(Self::Deposit(deposit)); - } - - let tx = tx.inner; - match tx.transaction_type.unwrap_or_default().try_into()? { - TxType::Legacy => { - let legacy = TxLegacy { - chain_id: tx.chain_id, - nonce: tx.nonce, - gas_price: tx.gas_price.ok_or(ConversionError::MissingGasPrice)?, - gas_limit: tx.gas, - value: tx.value, - input: tx.input, - to: tx.to.map_or(TxKind::Create, TxKind::Call), - }; - let signature = tx - .signature - .ok_or(ConversionError::MissingSignature)? - .try_into() - .map_err(ConversionError::SignatureError)?; - Ok(Self::Legacy(Signed::new_unchecked(legacy, signature, tx.hash))) - } - TxType::Eip1559 => { - let eip1559 = TxEip1559 { - chain_id: tx.chain_id.ok_or(ConversionError::MissingChainId)?, - nonce: tx.nonce, - max_fee_per_gas: tx - .max_fee_per_gas - .ok_or(ConversionError::MissingMaxFeePerGas)?, - max_priority_fee_per_gas: tx - .max_priority_fee_per_gas - .ok_or(ConversionError::MissingMaxPriorityFeePerGas)?, - gas_limit: tx.gas, - value: tx.value, - input: tx.input, - to: tx.to.map_or(TxKind::Create, TxKind::Call), - access_list: tx.access_list.ok_or(ConversionError::MissingAccessList)?, - }; - let signature = tx - .signature - .ok_or(ConversionError::MissingSignature)? - .try_into() - .map_err(ConversionError::SignatureError)?; - Ok(Self::EIP1559(Signed::new_unchecked(eip1559, signature, tx.hash))) - } - TxType::Eip2930 => { - let eip2930 = TxEip2930 { - chain_id: tx.chain_id.ok_or(ConversionError::MissingChainId)?, - nonce: tx.nonce, - gas_price: tx.gas_price.ok_or(ConversionError::MissingGasPrice)?, - gas_limit: tx.gas, - value: tx.value, - input: tx.input, - to: tx.to.map_or(TxKind::Create, TxKind::Call), - access_list: tx.access_list.ok_or(ConversionError::MissingAccessList)?, - }; - let signature = tx - .signature - .ok_or(ConversionError::MissingSignature)? - .try_into() - .map_err(ConversionError::SignatureError)?; - Ok(Self::EIP2930(Signed::new_unchecked(eip2930, signature, tx.hash))) - } - TxType::Eip4844 => { - let eip4844 = TxEip4844 { - chain_id: tx.chain_id.ok_or(ConversionError::MissingChainId)?, - nonce: tx.nonce, - gas_limit: tx.gas, - max_fee_per_gas: tx.gas_price.ok_or(ConversionError::MissingGasPrice)?, - max_priority_fee_per_gas: tx - .max_priority_fee_per_gas - .ok_or(ConversionError::MissingMaxPriorityFeePerGas)?, - max_fee_per_blob_gas: tx - .max_fee_per_blob_gas - .ok_or(ConversionError::MissingMaxFeePerBlobGas)?, - to: tx.to.ok_or(ConversionError::MissingTo)?, - value: tx.value, - access_list: tx.access_list.ok_or(ConversionError::MissingAccessList)?, - blob_versioned_hashes: tx - .blob_versioned_hashes - .ok_or(ConversionError::MissingBlobVersionedHashes)?, - input: tx.input, - }; - Ok(Self::EIP4844(Signed::new_unchecked( - TxEip4844Variant::TxEip4844(eip4844), - tx.signature - .ok_or(ConversionError::MissingSignature)? - .try_into() - .map_err(ConversionError::SignatureError)?, - tx.hash, - ))) - } - TxType::Eip7702 => { - let eip7702 = TxEip7702 { - chain_id: tx.chain_id.ok_or(ConversionError::MissingChainId)?, - nonce: tx.nonce, - gas_limit: tx.gas, - max_fee_per_gas: tx.gas_price.ok_or(ConversionError::MissingGasPrice)?, - max_priority_fee_per_gas: tx - .max_priority_fee_per_gas - .ok_or(ConversionError::MissingMaxPriorityFeePerGas)?, - to: tx.to.ok_or(ConversionError::MissingTo)?, - value: tx.value, - access_list: tx.access_list.ok_or(ConversionError::MissingAccessList)?, - input: tx.input, - authorization_list: tx.authorization_list.unwrap_or_default(), - }; - let signature = tx - .signature - .ok_or(ConversionError::MissingSignature)? - .try_into() - .map_err(ConversionError::SignatureError)?; - Ok(Self::EIP7702(Signed::new_unchecked(eip7702, signature, tx.hash))) - } + false, + ), } } } @@ -1200,10 +1023,7 @@ impl Encodable2718 for TypedTransaction { Self::EIP2930(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), Self::EIP1559(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), Self::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), - Self::EIP7702(tx) => { - let payload_length = tx.tx().fields_len() + tx.signature().rlp_vrs_len(); - Header { list: true, payload_length }.length() + payload_length + 1 - } + Self::EIP7702(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), Self::Deposit(tx) => 1 + tx.length(), } } @@ -1214,7 +1034,7 @@ impl Encodable2718 for TypedTransaction { Self::EIP2930(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), Self::EIP1559(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), Self::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), - Self::EIP7702(tx) => tx.tx().encode_with_signature(tx.signature(), out, false), + Self::EIP7702(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), Self::Deposit(tx) => { tx.encode_2718(out); } @@ -1224,15 +1044,14 @@ impl Encodable2718 for TypedTransaction { impl Decodable2718 for TypedTransaction { fn typed_decode(ty: u8, buf: &mut &[u8]) -> Result { - match ty { - 0x04 => return Ok(Self::EIP7702(TxEip7702::decode_signed_fields(buf)?)), - 0x7E => return Ok(Self::Deposit(DepositTransaction::decode(buf)?)), - _ => {} + if ty == 0x7E { + return Ok(Self::Deposit(DepositTransaction::decode(buf)?)) } match TxEnvelope::typed_decode(ty, buf)? { TxEnvelope::Eip2930(tx) => Ok(Self::EIP2930(tx)), TxEnvelope::Eip1559(tx) => Ok(Self::EIP1559(tx)), TxEnvelope::Eip4844(tx) => Ok(Self::EIP4844(tx)), + TxEnvelope::Eip7702(tx) => Ok(Self::EIP7702(tx)), _ => unreachable!(), } } @@ -1703,7 +1522,7 @@ mod tests { chain_id: Some(4), }; - let signature = Signature::from_str("0eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca182b").unwrap(); + let signature = PrimitiveSignature::from_str("0eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca182b").unwrap(); let tx = TypedTransaction::Legacy(Signed::new_unchecked( tx, diff --git a/crates/anvil/core/src/eth/utils.rs b/crates/anvil/core/src/eth/utils.rs deleted file mode 100644 index a60439280123..000000000000 --- a/crates/anvil/core/src/eth/utils.rs +++ /dev/null @@ -1,13 +0,0 @@ -use alloy_primitives::Parity; - -/// See -/// > If you do, then the v of the signature MUST be set to {0,1} + CHAIN_ID * 2 + 35 where -/// > {0,1} is the parity of the y value of the curve point for which r is the x-value in the -/// > secp256k1 signing process. -pub fn meets_eip155(chain_id: u64, v: Parity) -> bool { - let double_chain_id = chain_id.saturating_mul(2); - match v { - Parity::Eip155(v) => v == double_chain_id + 35 || v == double_chain_id + 36, - _ => false, - } -} diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 5561758182eb..714f91e6d7a8 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -15,12 +15,12 @@ use crate::{ mem::{self, in_memory_db::MemDb}, EthereumHardfork, FeeManager, PrecompileFactory, }; +use alloy_consensus::BlockHeader; use alloy_genesis::Genesis; -use alloy_network::AnyNetwork; +use alloy_network::{AnyNetwork, TransactionResponse}; use alloy_primitives::{hex, map::HashMap, utils::Unit, BlockNumber, TxHash, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{Block, BlockNumberOrTag, Transaction}; -use alloy_serde::WithOtherFields; +use alloy_rpc_types::{Block, BlockNumberOrTag}; use alloy_signer::Signer; use alloy_signer_local::{ coins_bip39::{English, Mnemonic}, @@ -1291,12 +1291,15 @@ latest block number: {latest_block}" /// we only use the gas limit value of the block if it is non-zero and the block gas /// limit is enabled, since there are networks where this is not used and is always /// `0x0` which would inevitably result in `OutOfGas` errors as soon as the evm is about to record gas, See also - pub(crate) fn fork_gas_limit(&self, block: &Block>) -> u128 { + pub(crate) fn fork_gas_limit( + &self, + block: &Block, + ) -> u128 { if !self.disable_block_gas_limit { if let Some(gas_limit) = self.gas_limit { return gas_limit; - } else if block.header.gas_limit > 0 { - return block.header.gas_limit as u128; + } else if block.header.gas_limit() > 0 { + return block.header.gas_limit() as u128; } } @@ -1335,19 +1338,21 @@ async fn derive_block_and_transactions( // Get the block pertaining to the fork transaction let transaction_block = provider - .get_block_by_number(transaction_block_number.into(), true) + .get_block_by_number( + transaction_block_number.into(), + alloy_rpc_types::BlockTransactionsKind::Full, + ) .await? .ok_or(eyre::eyre!("Failed to get fork block by number"))?; // Filter out transactions that are after the fork transaction - let filtered_transactions: Vec<&alloy_serde::WithOtherFields> = - transaction_block - .transactions - .as_transactions() - .ok_or(eyre::eyre!("Failed to get transactions from full fork block"))? - .iter() - .take_while_inclusive(|&transaction| transaction.hash != transaction_hash.0) - .collect(); + let filtered_transactions = transaction_block + .transactions + .as_transactions() + .ok_or(eyre::eyre!("Failed to get transactions from full fork block"))? + .iter() + .take_while_inclusive(|&transaction| transaction.tx_hash() != transaction_hash.0) + .collect::>(); // Convert the transactions to PoolTransactions let force_transactions = filtered_transactions diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 6ee7c6d6014a..daf9dc4a00f4 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -30,15 +30,16 @@ use crate::{ revm::primitives::{BlobExcessGasAndPrice, Output}, ClientFork, LoggingManager, Miner, MiningMode, StorageInfo, }; -use alloy_consensus::{transaction::eip4844::TxEip4844Variant, Account, TxEnvelope}; +use alloy_consensus::{transaction::eip4844::TxEip4844Variant, Account}; use alloy_dyn_abi::TypedData; use alloy_eips::eip2718::Encodable2718; use alloy_network::{ - eip2718::Decodable2718, BlockResponse, Ethereum, NetworkWallet, TransactionBuilder, + eip2718::Decodable2718, AnyRpcBlock, AnyRpcTransaction, BlockResponse, Ethereum, NetworkWallet, + TransactionBuilder, TransactionResponse, }; use alloy_primitives::{ map::{HashMap, HashSet}, - Address, Bytes, Parity, TxHash, TxKind, B256, B64, U256, U64, + Address, Bytes, PrimitiveSignature as Signature, TxHash, TxKind, B256, B64, U256, U64, }; use alloy_provider::utils::{ eip1559_default_estimator, EIP1559_FEE_ESTIMATION_PAST_BLOCKS, @@ -56,12 +57,10 @@ use alloy_rpc_types::{ parity::LocalizedTransactionTrace, }, txpool::{TxpoolContent, TxpoolInspect, TxpoolInspectSummary, TxpoolStatus}, - AccessList, AccessListResult, AnyNetworkBlock, BlockId, BlockNumberOrTag as BlockNumber, - BlockTransactions, EIP1186AccountProofResponse, FeeHistory, Filter, FilteredParams, Index, Log, - Transaction, + AccessList, AccessListResult, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, + EIP1186AccountProofResponse, FeeHistory, Filter, FilteredParams, Index, Log, }; use alloy_serde::WithOtherFields; -use alloy_signer::Signature; use alloy_transport::TransportErrorKind; use anvil_core::{ eth::{ @@ -475,13 +474,11 @@ impl EthApi { ) -> Result { match request { TypedTransactionRequest::Deposit(_) => { - let nil_signature: alloy_primitives::Signature = - alloy_primitives::Signature::from_scalars_and_parity( - B256::with_last_byte(1), - B256::with_last_byte(1), - Parity::Parity(false), - ) - .unwrap(); + let nil_signature = Signature::from_scalars_and_parity( + B256::with_last_byte(1), + B256::with_last_byte(1), + false, + ); return build_typed_transaction(request, nil_signature) } _ => { @@ -514,10 +511,7 @@ impl EthApi { match self.pool.get_transaction(hash) { Some(tx) => Ok(Some(tx.transaction.encoded_2718().into())), None => match self.backend.transaction_by_hash(hash).await? { - Some(tx) => TxEnvelope::try_from(tx.inner) - .map_or(Err(BlockchainError::FailedToDecodeTransaction), |tx| { - Ok(Some(tx.encoded_2718().into())) - }), + Some(tx) => Ok(Some(tx.inner.inner.encoded_2718().into())), None => Ok(None), }, } @@ -742,7 +736,7 @@ impl EthApi { /// Returns block with given hash. /// /// Handler for ETH RPC call: `eth_getBlockByHash` - pub async fn block_by_hash(&self, hash: B256) -> Result> { + pub async fn block_by_hash(&self, hash: B256) -> Result> { node_info!("eth_getBlockByHash"); self.backend.block_by_hash(hash).await } @@ -750,7 +744,7 @@ impl EthApi { /// Returns a _full_ block with given hash. /// /// Handler for ETH RPC call: `eth_getBlockByHash` - pub async fn block_by_hash_full(&self, hash: B256) -> Result> { + pub async fn block_by_hash_full(&self, hash: B256) -> Result> { node_info!("eth_getBlockByHash"); self.backend.block_by_hash_full(hash).await } @@ -758,7 +752,7 @@ impl EthApi { /// Returns block with given number. /// /// Handler for ETH RPC call: `eth_getBlockByNumber` - pub async fn block_by_number(&self, number: BlockNumber) -> Result> { + pub async fn block_by_number(&self, number: BlockNumber) -> Result> { node_info!("eth_getBlockByNumber"); if number == BlockNumber::Pending { return Ok(Some(self.pending_block().await)); @@ -770,10 +764,7 @@ impl EthApi { /// Returns a _full_ block with given number /// /// Handler for ETH RPC call: `eth_getBlockByNumber` - pub async fn block_by_number_full( - &self, - number: BlockNumber, - ) -> Result> { + pub async fn block_by_number_full(&self, number: BlockNumber) -> Result> { node_info!("eth_getBlockByNumber"); if number == BlockNumber::Pending { return Ok(self.pending_block_full().await); @@ -1185,10 +1176,7 @@ impl EthApi { /// this will also scan the mempool for a matching pending transaction /// /// Handler for ETH RPC call: `eth_getTransactionByHash` - pub async fn transaction_by_hash( - &self, - hash: B256, - ) -> Result>> { + pub async fn transaction_by_hash(&self, hash: B256) -> Result> { node_info!("eth_getTransactionByHash"); let mut tx = self.pool.get_transaction(hash).map(|pending| { let from = *pending.sender(); @@ -1218,7 +1206,7 @@ impl EthApi { &self, hash: B256, index: Index, - ) -> Result>> { + ) -> Result> { node_info!("eth_getTransactionByBlockHashAndIndex"); self.backend.transaction_by_block_hash_and_index(hash, index).await } @@ -1230,7 +1218,7 @@ impl EthApi { &self, block: BlockNumber, idx: Index, - ) -> Result>> { + ) -> Result> { node_info!("eth_getTransactionByBlockNumberAndIndex"); self.backend.transaction_by_block_number_and_index(block, idx).await } @@ -1262,7 +1250,7 @@ impl EthApi { &self, block_hash: B256, idx: Index, - ) -> Result> { + ) -> Result> { node_info!("eth_getUncleByBlockHashAndIndex"); let number = self.backend.ensure_block_number(Some(BlockId::Hash(block_hash.into()))).await?; @@ -1282,7 +1270,7 @@ impl EthApi { &self, block_number: BlockNumber, idx: Index, - ) -> Result> { + ) -> Result> { node_info!("eth_getUncleByBlockNumberAndIndex"); let number = self.backend.ensure_block_number(Some(BlockId::Number(block_number))).await?; if let Some(fork) = self.get_fork() { @@ -1544,7 +1532,7 @@ impl EthApi { ) -> Result> { node_info!("eth_getRawTransactionByBlockHashAndIndex"); match self.backend.transaction_by_block_hash_and_index(block_hash, index).await? { - Some(tx) => self.inner_raw_transaction(tx.hash).await, + Some(tx) => self.inner_raw_transaction(tx.tx_hash()).await, None => Ok(None), } } @@ -1559,7 +1547,7 @@ impl EthApi { ) -> Result> { node_info!("eth_getRawTransactionByBlockNumberAndIndex"); match self.backend.transaction_by_block_number_and_index(block_number, index).await? { - Some(tx) => self.inner_raw_transaction(tx.hash).await, + Some(tx) => self.inner_raw_transaction(tx.tx_hash()).await, None => Ok(None), } } @@ -2175,10 +2163,7 @@ impl EthApi { /// **Note**: This behaves exactly as [Self::evm_mine] but returns different output, for /// compatibility reasons, this is a separate call since `evm_mine` is not an anvil original. /// and `ganache` may change the `0x0` placeholder. - pub async fn evm_mine_detailed( - &self, - opts: Option, - ) -> Result> { + pub async fn evm_mine_detailed(&self, opts: Option) -> Result> { node_info!("evm_mine_detailed"); let mined_blocks = self.do_evm_mine(opts).await?; @@ -2196,7 +2181,7 @@ impl EthApi { BlockTransactions::Hashes(_) | BlockTransactions::Uncle => unreachable!(), }; for tx in block_txs.iter_mut() { - if let Some(receipt) = self.backend.mined_transaction_receipt(tx.hash) { + if let Some(receipt) = self.backend.mined_transaction_receipt(tx.tx_hash()) { if let Some(output) = receipt.out { // insert revert reason if failure if !receipt @@ -2359,10 +2344,10 @@ impl EthApi { /// See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content) for more details /// /// Handler for ETH RPC call: `txpool_inspect` - pub async fn txpool_content(&self) -> Result { + pub async fn txpool_content(&self) -> Result> { node_info!("txpool_content"); - let mut content = TxpoolContent::default(); - fn convert(tx: Arc) -> Transaction { + let mut content = TxpoolContent::::default(); + fn convert(tx: Arc) -> Result { let from = *tx.pending_transaction.sender(); let mut tx = transaction_build( Some(tx.hash()), @@ -2375,18 +2360,19 @@ impl EthApi { // we set the from field here explicitly to the set sender of the pending transaction, // in case the transaction is impersonated. tx.from = from; - tx.inner + + Ok(tx) } for pending in self.pool.ready_transactions() { let entry = content.pending.entry(*pending.pending_transaction.sender()).or_default(); let key = pending.pending_transaction.nonce().to_string(); - entry.insert(key, convert(pending)); + entry.insert(key, convert(pending)?); } for queued in self.pool.pending_transactions() { let entry = content.pending.entry(*queued.pending_transaction.sender()).or_default(); let key = queued.pending_transaction.nonce().to_string(); - entry.insert(key, convert(queued)); + entry.insert(key, convert(queued)?); } Ok(content) @@ -2803,14 +2789,14 @@ impl EthApi { } /// Returns the pending block with tx hashes - async fn pending_block(&self) -> AnyNetworkBlock { + async fn pending_block(&self) -> AnyRpcBlock { let transactions = self.pool.ready_transactions().collect::>(); let info = self.backend.pending_block(transactions).await; self.backend.convert_block(info.block) } /// Returns the full pending block with `Transaction` objects - async fn pending_block_full(&self) -> Option { + async fn pending_block_full(&self) -> Option { let transactions = self.pool.ready_transactions().collect::>(); let BlockInfo { block, transactions, receipts: _ } = self.backend.pending_block(transactions).await; @@ -2925,7 +2911,7 @@ impl EthApi { TypedTransactionRequest::Legacy(_) => Signature::from_scalars_and_parity( B256::with_last_byte(1), B256::with_last_byte(1), - Parity::NonEip155(false), + false, ), TypedTransactionRequest::EIP2930(_) | TypedTransactionRequest::EIP1559(_) | @@ -2933,10 +2919,9 @@ impl EthApi { TypedTransactionRequest::Deposit(_) => Signature::from_scalars_and_parity( B256::with_last_byte(1), B256::with_last_byte(1), - Parity::Parity(false), + false, ), } - .unwrap() } /// Returns the nonce of the `address` depending on the `block_number` diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 174933c3490a..db34d9c1995e 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -569,8 +569,8 @@ mod test { let block = r#"{ "header": { "parentHash": "0xceb0fe420d6f14a8eeec4319515b89acbb0bb4861cad9983d529ab4b1e4af929", - "ommersHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "beneficiary": "0x0000000000000000000000000000000000000000", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "miner": "0x0000000000000000000000000000000000000000", "stateRoot": "0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1", "transactionsRoot": "0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988", "receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa", diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 7d887f697572..64852c802ae1 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -3,7 +3,7 @@ use crate::eth::{backend::db::Db, error::BlockchainError, pool::transactions::PoolTransaction}; use alloy_consensus::Account; use alloy_eips::eip2930::AccessListResult; -use alloy_network::BlockResponse; +use alloy_network::{AnyRpcBlock, AnyRpcTransaction, BlockResponse, TransactionResponse}; use alloy_primitives::{ map::{FbHashMap, HashMap}, Address, Bytes, StorageValue, B256, U256, @@ -18,8 +18,8 @@ use alloy_rpc_types::{ geth::{GethDebugTracingOptions, GethTrace}, parity::LocalizedTransactionTrace as Trace, }, - AnyNetworkBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, - EIP1186AccountProofResponse, FeeHistory, Filter, Log, Transaction, + BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, + FeeHistory, Filter, Log, }; use alloy_serde::WithOtherFields; use alloy_transport::TransportError; @@ -291,7 +291,7 @@ impl ClientFork { &self, number: u64, index: usize, - ) -> Result>, TransportError> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_number(number).await? { match block.transactions() { BlockTransactions::Full(txs) => { @@ -315,7 +315,7 @@ impl ClientFork { &self, hash: B256, index: usize, - ) -> Result>, TransportError> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_hash(hash).await? { match block.transactions() { BlockTransactions::Full(txs) => { @@ -338,7 +338,7 @@ impl ClientFork { pub async fn transaction_by_hash( &self, hash: B256, - ) -> Result>, TransportError> { + ) -> Result, TransportError> { trace!(target: "backend::fork", "transaction_by_hash={:?}", hash); if let tx @ Some(_) = self.storage_read().transactions.get(&hash).cloned() { return Ok(tx); @@ -450,10 +450,7 @@ impl ClientFork { Ok(None) } - pub async fn block_by_hash( - &self, - hash: B256, - ) -> Result, TransportError> { + pub async fn block_by_hash(&self, hash: B256) -> Result, TransportError> { if let Some(mut block) = self.storage_read().blocks.get(&hash).cloned() { block.transactions.convert_to_hashes(); return Ok(Some(block)); @@ -468,7 +465,7 @@ impl ClientFork { pub async fn block_by_hash_full( &self, hash: B256, - ) -> Result, TransportError> { + ) -> Result, TransportError> { if let Some(block) = self.storage_read().blocks.get(&hash).cloned() { return Ok(Some(self.convert_to_full_block(block))); } @@ -478,7 +475,7 @@ impl ClientFork { pub async fn block_by_number( &self, block_number: u64, - ) -> Result, TransportError> { + ) -> Result, TransportError> { if let Some(mut block) = self .storage_read() .hashes @@ -499,7 +496,7 @@ impl ClientFork { pub async fn block_by_number_full( &self, block_number: u64, - ) -> Result, TransportError> { + ) -> Result, TransportError> { if let Some(block) = self .storage_read() .hashes @@ -516,7 +513,7 @@ impl ClientFork { async fn fetch_full_block( &self, block_id: impl Into, - ) -> Result, TransportError> { + ) -> Result, TransportError> { if let Some(block) = self.provider().get_block(block_id.into(), true.into()).await? { let hash = block.header.hash; let block_number = block.header.number; @@ -526,7 +523,7 @@ impl ClientFork { BlockTransactions::Full(txs) => txs.to_owned(), _ => vec![], }; - storage.transactions.extend(block_txs.iter().map(|tx| (tx.hash, tx.clone()))); + storage.transactions.extend(block_txs.iter().map(|tx| (tx.tx_hash(), tx.clone()))); storage.hashes.insert(block_number, hash); storage.blocks.insert(hash, block.clone()); return Ok(Some(block)); @@ -539,7 +536,7 @@ impl ClientFork { &self, hash: B256, index: usize, - ) -> Result, TransportError> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_hash(hash).await? { return self.uncles_by_block_and_index(block, index).await; } @@ -550,7 +547,7 @@ impl ClientFork { &self, number: u64, index: usize, - ) -> Result, TransportError> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_number(number).await? { return self.uncles_by_block_and_index(block, index).await; } @@ -559,9 +556,9 @@ impl ClientFork { async fn uncles_by_block_and_index( &self, - block: AnyNetworkBlock, + block: AnyRpcBlock, index: usize, - ) -> Result, TransportError> { + ) -> Result, TransportError> { let block_hash = block.header.hash; let block_number = block.header.number; if let Some(uncles) = self.storage_read().uncles.get(&block_hash) { @@ -582,7 +579,7 @@ impl ClientFork { } /// Converts a block of hashes into a full block - fn convert_to_full_block(&self, mut block: AnyNetworkBlock) -> AnyNetworkBlock { + fn convert_to_full_block(&self, mut block: AnyRpcBlock) -> AnyRpcBlock { let storage = self.storage.read(); let block_txs_len = match block.transactions { BlockTransactions::Full(ref txs) => txs.len(), @@ -684,10 +681,10 @@ impl ClientForkConfig { /// This is used as a cache so repeated requests to the same data are not sent to the remote client #[derive(Clone, Debug, Default)] pub struct ForkedStorage { - pub uncles: FbHashMap<32, Vec>, - pub blocks: FbHashMap<32, AnyNetworkBlock>, + pub uncles: FbHashMap<32, Vec>, + pub blocks: FbHashMap<32, AnyRpcBlock>, pub hashes: HashMap, - pub transactions: FbHashMap<32, WithOtherFields>, + pub transactions: FbHashMap<32, AnyRpcTransaction>, pub transaction_receipts: FbHashMap<32, ReceiptResponse>, pub transaction_traces: FbHashMap<32, Vec>, pub logs: HashMap>, diff --git a/crates/anvil/src/eth/backend/info.rs b/crates/anvil/src/eth/backend/info.rs index 3ac359619d83..0f539f9373dc 100644 --- a/crates/anvil/src/eth/backend/info.rs +++ b/crates/anvil/src/eth/backend/info.rs @@ -1,9 +1,8 @@ //! Handler that can get current storage related data use crate::mem::Backend; +use alloy_network::AnyRpcBlock; use alloy_primitives::B256; -use alloy_rpc_types::{Block as AlloyBlock, Transaction}; -use alloy_serde::WithOtherFields; use anvil_core::eth::{block::Block, transaction::TypedReceipt}; use std::{fmt, sync::Arc}; @@ -43,10 +42,7 @@ impl StorageInfo { } /// Returns the block with the given hash in the format of the ethereum API - pub fn eth_block( - &self, - hash: B256, - ) -> Option>>> { + pub fn eth_block(&self, hash: B256) -> Option { let block = self.block(hash)?; Some(self.backend.convert_block(block)) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index c7ff9422a6e4..0db343358fec 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -34,9 +34,14 @@ use crate::{ ForkChoice, NodeConfig, PrecompileFactory, }; use alloy_chains::NamedChain; -use alloy_consensus::{Account, Header, Receipt, ReceiptWithBloom}; +use alloy_consensus::{ + Account, Header, Receipt, ReceiptWithBloom, Signed, Transaction as TransactionTrait, TxEnvelope, +}; use alloy_eips::eip4844::MAX_BLOBS_PER_BLOCK; -use alloy_network::EthereumWallet; +use alloy_network::{ + AnyHeader, AnyRpcBlock, AnyRpcTransaction, AnyTxEnvelope, AnyTxType, EthereumWallet, + UnknownTxEnvelope, UnknownTypedTransaction, +}; use alloy_primitives::{ address, hex, keccak256, utils::Unit, Address, Bytes, TxHash, TxKind, B256, U256, U64, }; @@ -53,21 +58,19 @@ use alloy_rpc_types::{ }, parity::LocalizedTransactionTrace, }, - AccessList, AnyNetworkBlock, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, - BlockTransactions, EIP1186AccountProofResponse as AccountProof, - EIP1186StorageProof as StorageProof, Filter, FilteredParams, Header as AlloyHeader, Index, Log, - Transaction, TransactionReceipt, + AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, + EIP1186AccountProofResponse as AccountProof, EIP1186StorageProof as StorageProof, Filter, + FilteredParams, Header as AlloyHeader, Index, Log, Transaction, TransactionReceipt, }; -use alloy_serde::WithOtherFields; +use alloy_serde::{OtherFields, WithOtherFields}; use alloy_signer_local::PrivateKeySigner; use alloy_trie::{proof::ProofRetainer, HashBuilder, Nibbles}; use anvil_core::eth::{ block::{Block, BlockInfo}, transaction::{ - DepositReceipt, MaybeImpersonatedTransaction, PendingTransaction, ReceiptResponse, - TransactionInfo, TypedReceipt, TypedTransaction, + optimism::DepositTransaction, DepositReceipt, MaybeImpersonatedTransaction, + PendingTransaction, ReceiptResponse, TransactionInfo, TypedReceipt, TypedTransaction, }, - utils::meets_eip155, wallet::{Capabilities, DelegationCapability, WalletCapabilities}, }; use anvil_rpc::error::RpcError; @@ -89,6 +92,7 @@ use foundry_evm::{ traces::TracingInspectorConfig, }; use futures::channel::mpsc::{unbounded, UnboundedSender}; +use op_alloy_consensus::{TxDeposit, DEPOSIT_TX_TYPE_ID}; use parking_lot::{Mutex, RwLock}; use revm::{ db::WrapDatabaseRef, @@ -1709,10 +1713,7 @@ impl Backend { } } - pub async fn block_by_hash( - &self, - hash: B256, - ) -> Result, BlockchainError> { + pub async fn block_by_hash(&self, hash: B256) -> Result, BlockchainError> { trace!(target: "backend", "get block by hash {:?}", hash); if let tx @ Some(_) = self.mined_block_by_hash(hash) { return Ok(tx); @@ -1728,7 +1729,7 @@ impl Backend { pub async fn block_by_hash_full( &self, hash: B256, - ) -> Result, BlockchainError> { + ) -> Result, BlockchainError> { trace!(target: "backend", "get block by hash {:?}", hash); if let tx @ Some(_) = self.get_full_block(hash) { return Ok(tx); @@ -1741,7 +1742,7 @@ impl Backend { Ok(None) } - fn mined_block_by_hash(&self, hash: B256) -> Option { + fn mined_block_by_hash(&self, hash: B256) -> Option { let block = self.blockchain.get_block_by_hash(&hash)?; Some(self.convert_block(block)) } @@ -1749,7 +1750,7 @@ impl Backend { pub(crate) async fn mined_transactions_by_block_number( &self, number: BlockNumber, - ) -> Option>> { + ) -> Option> { if let Some(block) = self.get_block(number) { return self.mined_transactions_in_block(&block); } @@ -1760,7 +1761,7 @@ impl Backend { pub(crate) fn mined_transactions_in_block( &self, block: &Block, - ) -> Option>> { + ) -> Option> { let mut transactions = Vec::with_capacity(block.transactions.len()); let base_fee = block.header.base_fee_per_gas; let storage = self.blockchain.storage.read(); @@ -1777,7 +1778,7 @@ impl Backend { pub async fn block_by_number( &self, number: BlockNumber, - ) -> Result, BlockchainError> { + ) -> Result, BlockchainError> { trace!(target: "backend", "get block by number {:?}", number); if let tx @ Some(_) = self.mined_block_by_number(number) { return Ok(tx); @@ -1796,7 +1797,7 @@ impl Backend { pub async fn block_by_number_full( &self, number: BlockNumber, - ) -> Result, BlockchainError> { + ) -> Result, BlockchainError> { trace!(target: "backend", "get block by number {:?}", number); if let tx @ Some(_) = self.get_full_block(number) { return Ok(tx); @@ -1849,14 +1850,14 @@ impl Backend { self.blockchain.get_block_by_hash(&hash) } - pub fn mined_block_by_number(&self, number: BlockNumber) -> Option { + pub fn mined_block_by_number(&self, number: BlockNumber) -> Option { let block = self.get_block(number)?; let mut block = self.convert_block(block); block.transactions.convert_to_hashes(); Some(block) } - pub fn get_full_block(&self, id: impl Into) -> Option { + pub fn get_full_block(&self, id: impl Into) -> Option { let block = self.get_block(id)?; let transactions = self.mined_transactions_in_block(&block)?; let mut block = self.convert_block(block); @@ -1866,63 +1867,21 @@ impl Backend { } /// Takes a block as it's stored internally and returns the eth api conform block format. - pub fn convert_block(&self, block: Block) -> AnyNetworkBlock { + pub fn convert_block(&self, block: Block) -> AnyRpcBlock { let size = U256::from(alloy_rlp::encode(&block).len() as u32); let Block { header, transactions, .. } = block; let hash = header.hash_slow(); - let Header { - parent_hash, - ommers_hash, - beneficiary, - state_root, - transactions_root, - receipts_root, - logs_bloom, - difficulty, - number, - gas_limit, - gas_used, - timestamp, - requests_hash, - extra_data, - mix_hash, - nonce, - base_fee_per_gas, - withdrawals_root, - blob_gas_used, - excess_blob_gas, - parent_beacon_block_root, - } = header; + let Header { number, withdrawals_root, .. } = header; let block = AlloyBlock { header: AlloyHeader { + inner: AnyHeader::from(header), hash, - parent_hash, - uncles_hash: ommers_hash, - miner: beneficiary, - state_root, - transactions_root, - receipts_root, - number, - gas_used, - gas_limit, - extra_data: extra_data.0.into(), - logs_bloom, - timestamp, total_difficulty: Some(self.total_difficulty()), - difficulty, - mix_hash: Some(mix_hash), - nonce: Some(nonce), - base_fee_per_gas, - withdrawals_root, - blob_gas_used, - excess_blob_gas, - parent_beacon_block_root, - requests_hash, + size: Some(size), }, - size: Some(size), transactions: alloy_rpc_types::BlockTransactions::Hashes( transactions.into_iter().map(|tx| tx.hash()).collect(), ), @@ -2030,7 +1989,7 @@ impl Backend { if let Some(state) = self.states.write().get(&block_hash) { let block = BlockEnv { number: block_number, - coinbase: block.header.miner, + coinbase: block.header.beneficiary, timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, prevrandao: block.header.mix_hash, @@ -2469,7 +2428,7 @@ impl Backend { &self, number: BlockNumber, index: Index, - ) -> Result>, BlockchainError> { + ) -> Result, BlockchainError> { if let Some(block) = self.mined_block_by_number(number) { return Ok(self.mined_transaction_by_block_hash_and_index(block.header.hash, index)); } @@ -2488,7 +2447,7 @@ impl Backend { &self, hash: B256, index: Index, - ) -> Result>, BlockchainError> { + ) -> Result, BlockchainError> { if let tx @ Some(_) = self.mined_transaction_by_block_hash_and_index(hash, index) { return Ok(tx); } @@ -2504,7 +2463,7 @@ impl Backend { &self, block_hash: B256, index: Index, - ) -> Option> { + ) -> Option { let (info, block, tx) = { let storage = self.blockchain.storage.read(); let block = storage.blocks.get(&block_hash).cloned()?; @@ -2526,7 +2485,7 @@ impl Backend { pub async fn transaction_by_hash( &self, hash: B256, - ) -> Result>, BlockchainError> { + ) -> Result, BlockchainError> { trace!(target: "backend", "transaction_by_hash={:?}", hash); if let tx @ Some(_) = self.mined_transaction_by_hash(hash) { return Ok(tx); @@ -2539,7 +2498,7 @@ impl Backend { Ok(None) } - pub fn mined_transaction_by_hash(&self, hash: B256) -> Option> { + pub fn mined_transaction_by_hash(&self, hash: B256) -> Option { let (info, block) = { let storage = self.blockchain.storage.read(); let MinedTransaction { info, block_hash, .. } = @@ -2604,7 +2563,7 @@ impl Backend { .map(|(key, proof)| { let storage_key: U256 = key.into(); let value = account.storage.get(&storage_key).cloned().unwrap_or_default(); - StorageProof { key: JsonStorageKey(key), value, proof } + StorageProof { key: JsonStorageKey::Hash(key), value, proof } }) .collect(), }; @@ -2749,7 +2708,7 @@ impl TransactionValidator for Backend { if let Some(legacy) = tx.as_legacy() { // if env.handler_cfg.spec_id >= SpecId::SPURIOUS_DRAGON && - !meets_eip155(chain_id.to::(), legacy.signature().v()) + legacy.tx().chain_id.is_none() { warn!(target: "backend", ?chain_id, ?tx_chain_id, "incompatible EIP155-based V"); return Err(InvalidTransactionError::IncompatibleEIP155); @@ -2880,7 +2839,7 @@ impl TransactionValidator for Backend { } } -/// Creates a `Transaction` as it's expected for the `eth` RPC api from storage data +/// Creates a `AnyRpcTransaction` as it's expected for the `eth` RPC api from storage data #[allow(clippy::too_many_arguments)] pub fn transaction_build( tx_hash: Option, @@ -2888,56 +2847,134 @@ pub fn transaction_build( block: Option<&Block>, info: Option, base_fee: Option, -) -> WithOtherFields { - let mut transaction: Transaction = eth_transaction.clone().into(); - if info.is_some() && transaction.transaction_type == Some(0x7E) { - transaction.nonce = info.as_ref().unwrap().nonce; - } +) -> AnyRpcTransaction { + if let TypedTransaction::Deposit(ref deposit_tx) = eth_transaction.transaction { + let DepositTransaction { + nonce: _, + source_hash, + from, + kind, + mint, + gas_limit, + is_system_tx, + input, + value, + } = deposit_tx.clone(); + + let dep_tx = TxDeposit { + source_hash, + input, + from, + mint: Some(mint.to()), + to: kind, + is_system_transaction: is_system_tx, + value, + gas_limit, + }; - if eth_transaction.is_dynamic_fee() { - if block.is_none() && info.is_none() { - // transaction is not mined yet, gas price is considered just `max_fee_per_gas` - transaction.gas_price = transaction.max_fee_per_gas; - } else { - // if transaction is already mined, gas price is considered base fee + priority fee: the - // effective gas price. - let base_fee = base_fee.map_or(0u128, |g| g as u128); - let max_priority_fee_per_gas = transaction.max_priority_fee_per_gas.unwrap_or(0); - transaction.gas_price = Some(base_fee.saturating_add(max_priority_fee_per_gas)); + let ser = serde_json::to_value(&dep_tx).unwrap(); + let maybe_deposit_fields = OtherFields::try_from(ser); + + match maybe_deposit_fields { + Ok(fields) => { + let inner = UnknownTypedTransaction { + ty: AnyTxType(DEPOSIT_TX_TYPE_ID), + fields, + memo: Default::default(), + }; + + let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope { + hash: eth_transaction.hash(), + inner, + }); + + let tx = Transaction { + inner: envelope, + block_hash: block + .as_ref() + .map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))), + block_number: block.as_ref().map(|block| block.header.number), + transaction_index: info.as_ref().map(|info| info.transaction_index), + effective_gas_price: None, + from, + }; + + return WithOtherFields::new(tx); + } + Err(_) => { + error!(target: "backend", "failed to serialize deposit transaction"); + } } - } else { - transaction.max_fee_per_gas = None; - transaction.max_priority_fee_per_gas = None; } - transaction.block_hash = - block.as_ref().map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))); + let mut transaction: Transaction = eth_transaction.clone().into(); - transaction.block_number = block.as_ref().map(|block| block.header.number); + let effective_gas_price = if !eth_transaction.is_dynamic_fee() { + transaction.effective_gas_price(base_fee) + } else if block.is_none() && info.is_none() { + // transaction is not mined yet, gas price is considered just `max_fee_per_gas` + transaction.max_fee_per_gas() + } else { + // if transaction is already mined, gas price is considered base fee + priority + // fee: the effective gas price. + let base_fee = base_fee.map_or(0u128, |g| g as u128); + let max_priority_fee_per_gas = transaction.max_priority_fee_per_gas().unwrap_or(0); - transaction.transaction_index = info.as_ref().map(|info| info.transaction_index); + base_fee.saturating_add(max_priority_fee_per_gas) + }; - // need to check if the signature of the transaction is impersonated, if so then we - // can't recover the sender, instead we use the sender from the executed transaction and set the - // impersonated hash. - if eth_transaction.is_impersonated() { - transaction.from = info.as_ref().map(|info| info.from).unwrap_or_default(); - transaction.hash = eth_transaction.impersonated_hash(transaction.from); - } else { - transaction.from = eth_transaction.recover().expect("can recover signed tx"); - } + transaction.effective_gas_price = Some(effective_gas_price); - // if a specific hash was provided we update the transaction's hash - // This is important for impersonated transactions since they all use the `BYPASS_SIGNATURE` - // which would result in different hashes - // Note: for impersonated transactions this only concerns pending transactions because there's - // no `info` yet. - if let Some(tx_hash) = tx_hash { - transaction.hash = tx_hash; - } + let envelope = transaction.inner; - transaction.to = info.as_ref().map_or(eth_transaction.to(), |status| status.to); - WithOtherFields::new(transaction) + // if a specific hash was provided we update the transaction's hash + // This is important for impersonated transactions since they all use the + // `BYPASS_SIGNATURE` which would result in different hashes + // Note: for impersonated transactions this only concerns pending transactions because + // there's // no `info` yet. + let hash = tx_hash.unwrap_or(*envelope.tx_hash()); + + let envelope = match envelope { + TxEnvelope::Legacy(signed_tx) => { + let (t, sig, _) = signed_tx.into_parts(); + let new_signed = Signed::new_unchecked(t, sig, hash); + AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(new_signed)) + } + TxEnvelope::Eip1559(signed_tx) => { + let (t, sig, _) = signed_tx.into_parts(); + let new_signed = Signed::new_unchecked(t, sig, hash); + AnyTxEnvelope::Ethereum(TxEnvelope::Eip1559(new_signed)) + } + TxEnvelope::Eip2930(signed_tx) => { + let (t, sig, _) = signed_tx.into_parts(); + let new_signed = Signed::new_unchecked(t, sig, hash); + AnyTxEnvelope::Ethereum(TxEnvelope::Eip2930(new_signed)) + } + TxEnvelope::Eip4844(signed_tx) => { + let (t, sig, _) = signed_tx.into_parts(); + let new_signed = Signed::new_unchecked(t, sig, hash); + AnyTxEnvelope::Ethereum(TxEnvelope::Eip4844(new_signed)) + } + TxEnvelope::Eip7702(signed_tx) => { + let (t, sig, _) = signed_tx.into_parts(); + let new_signed = Signed::new_unchecked(t, sig, hash); + AnyTxEnvelope::Ethereum(TxEnvelope::Eip7702(new_signed)) + } + _ => unreachable!("unknown tx type"), + }; + + let tx = Transaction { + inner: envelope, + block_hash: block + .as_ref() + .map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))), + block_number: block.as_ref().map(|block| block.header.number), + transaction_index: info.as_ref().map(|info| info.transaction_index), + from: eth_transaction.recover().expect("can recover signed tx"), + // deprecated + effective_gas_price: Some(effective_gas_price), + }; + WithOtherFields::new(tx) } /// Prove a storage key's existence or nonexistence in the account's storage trie. diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 0c478ee061d9..394f33492e8d 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -90,6 +90,8 @@ pub enum BlockchainError { EIP7702TransactionUnsupportedAtHardfork, #[error("op-stack deposit tx received but is not supported.\n\nYou can use it by running anvil with '--optimism'.")] DepositTransactionUnsupported, + #[error("UnknownTransactionType not supported ")] + UnknownTransactionType, #[error("Excess blob gas not set.")] ExcessBlobGasNotSet, #[error("{0}")] @@ -463,6 +465,9 @@ impl ToRpcResponseResult for Result { RpcError::invalid_params(err.to_string()) } err @ BlockchainError::Message(_) => RpcError::internal_error_with(err.to_string()), + err @ BlockchainError::UnknownTransactionType => { + RpcError::invalid_params(err.to_string()) + } } .into(), } diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index e73fe4dd6647..617655444bef 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -3,7 +3,11 @@ use crate::eth::{ macros::node_info, EthApi, }; -use alloy_network::BlockResponse; +use alloy_consensus::Transaction as TransactionTrait; +use alloy_network::{ + AnyHeader, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope, BlockResponse, + TransactionResponse, +}; use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_rpc_types::{ trace::{ @@ -16,10 +20,8 @@ use alloy_rpc_types::{ RewardAction, TraceOutput, }, }, - AnyNetworkBlock, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, - Transaction, + Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, }; -use alloy_serde::WithOtherFields; use itertools::Itertools; use futures::future::join_all; @@ -90,7 +92,7 @@ impl EthApi { pub async fn erigon_get_header_by_number( &self, number: BlockNumber, - ) -> Result> { + ) -> Result> { node_info!("ots_getApiLevel"); self.backend.block_by_number(number).await @@ -146,7 +148,10 @@ impl EthApi { /// For simplicity purposes, we return the entire block instead of emptying the values that /// Otterscan doesn't want. This is the original purpose of the endpoint (to save bandwidth), /// but it doesn't seem necessary in the context of an anvil node - pub async fn ots_get_block_details(&self, number: BlockNumber) -> Result { + pub async fn ots_get_block_details( + &self, + number: BlockNumber, + ) -> Result> { node_info!("ots_getBlockDetails"); if let Some(block) = self.backend.block_by_number(number).await? { @@ -160,7 +165,10 @@ impl EthApi { /// For simplicity purposes, we return the entire block instead of emptying the values that /// Otterscan doesn't want. This is the original purpose of the endpoint (to save bandwidth), /// but it doesn't seem necessary in the context of an anvil node - pub async fn ots_get_block_details_by_hash(&self, hash: B256) -> Result { + pub async fn ots_get_block_details_by_hash( + &self, + hash: B256, + ) -> Result> { node_info!("ots_getBlockDetailsByHash"); if let Some(block) = self.backend.block_by_hash(hash).await? { @@ -178,7 +186,7 @@ impl EthApi { number: u64, page: usize, page_size: usize, - ) -> Result>> { + ) -> Result> { node_info!("ots_getBlockTransactions"); match self.backend.block_by_number_full(number.into()).await? { @@ -193,7 +201,7 @@ impl EthApi { address: Address, block_number: u64, page_size: usize, - ) -> Result { + ) -> Result>> { node_info!("ots_searchTransactionsBefore"); let best = self.backend.best_number(); @@ -236,7 +244,7 @@ impl EthApi { address: Address, block_number: u64, page_size: usize, - ) -> Result { + ) -> Result>> { node_info!("ots_searchTransactionsAfter"); let best = self.backend.best_number(); @@ -295,8 +303,8 @@ impl EthApi { for n in (from..=to).rev() { if let Some(txs) = self.backend.mined_transactions_by_block_number(n.into()).await { for tx in txs { - if U256::from(tx.nonce) == nonce && tx.from == address { - return Ok(Some(tx.hash)); + if U256::from(tx.nonce()) == nonce && tx.from == address { + return Ok(Some(tx.tx_hash())); } } } @@ -352,7 +360,10 @@ impl EthApi { /// based on the existing list. /// /// Therefore we keep it simple by keeping the data in the response - pub async fn build_ots_block_details(&self, block: AnyNetworkBlock) -> Result { + pub async fn build_ots_block_details( + &self, + block: AnyRpcBlock, + ) -> Result>> { if block.transactions.is_uncle() { return Err(BlockchainError::DataUnavailable); } @@ -375,15 +386,10 @@ impl EthApi { .iter() .fold(0, |acc, receipt| acc + receipt.gas_used * receipt.effective_gas_price); - let Block { header, uncles, transactions, size, withdrawals } = block.inner; + let Block { header, uncles, transactions, withdrawals } = block.inner; - let block = OtsSlimBlock { - header, - uncles, - transaction_count: transactions.len(), - size, - withdrawals, - }; + let block = + OtsSlimBlock { header, uncles, transaction_count: transactions.len(), withdrawals }; Ok(BlockDetails { block, @@ -399,10 +405,10 @@ impl EthApi { /// [`ots_getBlockTransactions`]: https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_getblockdetails pub async fn build_ots_block_tx( &self, - mut block: AnyNetworkBlock, + mut block: AnyRpcBlock, page: usize, page_size: usize, - ) -> Result>> { + ) -> Result> { if block.transactions.is_uncle() { return Err(BlockchainError::DataUnavailable); } @@ -436,8 +442,7 @@ impl EthApi { let transaction_count = block.transactions().len(); let fullblock = OtsBlock { block: block.inner, transaction_count }; - let ots_block_txs = - OtsBlockTransactions::> { fullblock, receipts }; + let ots_block_txs = OtsBlockTransactions { fullblock, receipts }; Ok(ots_block_txs) } @@ -447,7 +452,7 @@ impl EthApi { hashes: Vec, first_page: bool, last_page: bool, - ) -> Result { + ) -> Result>> { let txs_futs = hashes.iter().map(|hash| async { self.transaction_by_hash(*hash).await }); let txs = join_all(txs_futs) diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index 631064549ce7..36e421d7a50e 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -1,10 +1,9 @@ use crate::eth::{error::PoolError, util::hex_fmt_many}; +use alloy_network::AnyRpcTransaction; use alloy_primitives::{ map::{HashMap, HashSet}, Address, TxHash, }; -use alloy_rpc_types::Transaction as RpcTransaction; -use alloy_serde::WithOtherFields; use anvil_core::eth::transaction::{PendingTransaction, TypedTransaction}; use parking_lot::RwLock; use std::{cmp::Ordering, collections::BTreeSet, fmt, str::FromStr, sync::Arc, time::Instant}; @@ -113,10 +112,10 @@ impl fmt::Debug for PoolTransaction { } } -impl TryFrom> for PoolTransaction { +impl TryFrom for PoolTransaction { type Error = eyre::Error; - fn try_from(transaction: WithOtherFields) -> Result { - let typed_transaction = TypedTransaction::try_from(transaction)?; + fn try_from(value: AnyRpcTransaction) -> Result { + let typed_transaction = TypedTransaction::try_from(value)?; let pending_transaction = PendingTransaction::new(typed_transaction)?; Ok(Self { pending_transaction, diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index 5f99ef9ca17d..e2ea036a0caf 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -2,7 +2,7 @@ use crate::eth::error::BlockchainError; use alloy_consensus::SignableTransaction; use alloy_dyn_abi::TypedData; use alloy_network::TxSignerSync; -use alloy_primitives::{map::AddressHashMap, Address, Signature, B256, U256}; +use alloy_primitives::{map::AddressHashMap, Address, PrimitiveSignature as Signature, B256, U256}; use alloy_signer::Signer as AlloySigner; use alloy_signer_local::PrivateKeySigner; use anvil_core::eth::transaction::{ diff --git a/crates/anvil/src/tasks/mod.rs b/crates/anvil/src/tasks/mod.rs index f9ba36b60f6c..022e7dd97842 100644 --- a/crates/anvil/src/tasks/mod.rs +++ b/crates/anvil/src/tasks/mod.rs @@ -3,10 +3,10 @@ #![allow(rustdoc::private_doc_tests)] use crate::{shutdown::Shutdown, tasks::block_listener::BlockListener, EthApi}; -use alloy_network::AnyNetwork; +use alloy_network::{AnyHeader, AnyNetwork}; use alloy_primitives::B256; use alloy_provider::Provider; -use alloy_rpc_types::{anvil::Forking, AnyNetworkBlock}; +use alloy_rpc_types::anvil::Forking; use alloy_transport::Transport; use futures::StreamExt; use std::{fmt, future::Future}; @@ -129,13 +129,13 @@ impl TaskManager { P: Provider + 'static, T: Transport + Clone, { - self.spawn_block_subscription(provider, move |block| { + self.spawn_block_subscription(provider, move |header| { let api = api.clone(); async move { let _ = api .anvil_reset(Some(Forking { json_rpc_url: None, - block_number: Some(block.header.number), + block_number: Some(header.number), })) .await; } @@ -149,7 +149,7 @@ impl TaskManager { where P: Provider + 'static, T: Transport + Clone, - F: Fn(AnyNetworkBlock) -> Fut + Unpin + Send + Sync + 'static, + F: Fn(alloy_rpc_types::Header) -> Fut + Unpin + Send + Sync + 'static, Fut: Future + Send, { let shutdown = self.on_shutdown.clone(); diff --git a/crates/anvil/test-data/state-dump-legacy-stress.json b/crates/anvil/test-data/state-dump-legacy-stress.json index 50df9e03906e..f6605d5add4e 100644 --- a/crates/anvil/test-data/state-dump-legacy-stress.json +++ b/crates/anvil/test-data/state-dump-legacy-stress.json @@ -1 +1 @@ -{"block":{"number":"0x5","coinbase":"0x0000000000000000000000000000000000000000","timestamp":"0x66b200cb","gas_limit":"0x1c9c380","basefee":"0x12e09c7a","difficulty":"0x0","prevrandao":"0xe7ef87fc7c2090741a6749a087e4ca8092cb4d07136008799e4ebeac3b69e34a","blob_excess_gas_and_price":{"excess_blob_gas":0,"blob_gasprice":1}},"accounts":{"0x0000000000000000000000000000000000000000":{"nonce":0,"balance":"0x1088aa62285a00","code":"0x","storage":{}},"0x108f53faf774d7c4c56f5bce9ca6e605ce8aeadd":{"nonce":1,"balance":"0x0","code":"0x6080604052600080357fffffffff0000000000000000000000000000000000000000000000000000000016905060008160e01c610251565b60006379ba509782101561015e5781631627540c811461009857632a952b2d81146100b457633659cfe681146100d0576350c946fe81146100ec576353a47bb781146101085763625ca21c81146101245763718fe928811461014057610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc91505b5061024c565b816379ba509781146101a657638da5cb5b81146101c25763aaf10f4281146101de5763c7f62cda81146101fa5763daa250be81146102165763deba1b9881146102325761024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b738138ef7cf908021d117e542120b7a39065016107915061024a565b738138ef7cf908021d117e542120b7a3906501610791505b505b919050565b61025a81610037565b915050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036102ce57816040517fc2a825f50000000000000000000000000000000000000000000000000000000081526004016102c5919061032f565b60405180910390fd5b3660008037600080366000845af43d6000803e80600081146102ef573d6000f35b3d6000fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b610329816102f4565b82525050565b60006020820190506103446000830184610320565b9291505056fea264697066735822122017a4b7fdaaab3897a7b47abaed8d2ee92d558883d3bb2a8454f9601b2ab2c3db64736f6c63430008150033","storage":{}},"0x14dc79964da2c08b23698b3d3cc7ca32193d9955":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x19ba1fac55eea44d12a01372a8eb0c2ebbf9ca21":{"nonce":1,"balance":"0x21e19df7c2963f0ac6b","code":"0x","storage":{}},"0x19c6ab860dbe2bc433574193a4409770a8748bf6":{"nonce":1,"balance":"0x21e19df8da6b7bdc410","code":"0x","storage":{}},"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x40567ec443c1d1872af5155755ac3803cc3fe61e":{"nonce":1,"balance":"0x21e19da82562f921b40","code":"0x","storage":{}},"0x47d08dad17ccb558b3ea74b1a0e73a9cc804a9dc":{"nonce":1,"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100885760003560e01c806379ba50971161005b57806379ba5097146100ed5780638da5cb5b146100f7578063aaf10f4214610115578063c7f62cda1461013357610088565b80631627540c1461008d5780633659cfe6146100a957806353a47bb7146100c5578063718fe928146100e3575b600080fd5b6100a760048036038101906100a29190610d25565b61014f565b005b6100c360048036038101906100be9190610d25565b6102d0565b005b6100cd6102e4565b6040516100da9190610d61565b60405180910390f35b6100eb610317565b005b6100f56103fe565b005b6100ff61058b565b60405161010c9190610d61565b60405180910390f35b61011d6105be565b60405161012a9190610d61565b60405180910390f35b61014d60048036038101906101489190610d25565b6105f1565b005b61015761084c565b600061016161081b565b9050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036101c9576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610252576040517fa88ee57700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22826040516102c49190610d61565b60405180910390a15050565b6102d861084c565b6102e1816108c5565b50565b60006102ee61081b565b60010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600061032161081b565b90503373ffffffffffffffffffffffffffffffffffffffff168160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146103b757336040517fa0e5a0d70000000000000000000000000000000000000000000000000000000081526004016103ae9190610d61565b60405180910390fd5b60008160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600061040861081b565b905060008160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104a357336040517fa0e5a0d700000000000000000000000000000000000000000000000000000000815260040161049a9190610d61565b60405180910390fd5b7fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c8260000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16826040516104f8929190610d7c565b60405180910390a1808260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008260010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b600061059561081b565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006105c8610b05565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006105fb610b05565b905060018160000160146101000a81548160ff02191690831515021790555060008160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050828260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008373ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff16633659cfe6846040516024016106cc9190610d61565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161071b9190610e16565b600060405180830381855af49150503d8060008114610756576040519150601f19603f3d011682016040523d82523d6000602084013e61075b565b606091505b505090508015806107c357508173ffffffffffffffffffffffffffffffffffffffff16610786610b05565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b156107fa576040517fa1cfa5a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008360000160146101000a81548160ff0219169083151502179055600080fd5b60008060405160200161082d90610eb0565b6040516020818303038152906040528051906020012090508091505090565b610854610b36565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146108c357336040517f8e4a23d60000000000000000000000000000000000000000000000000000000081526004016108ba9190610d61565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361092b576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61093481610b69565b61097557806040517f8a8b41ec00000000000000000000000000000000000000000000000000000000815260040161096c9190610d61565b60405180910390fd5b600061097f610b05565b90508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610a0a576040517fa88ee57700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060000160149054906101000a900460ff16158015610a2e5750610a2d82610b7c565b5b15610a7057816040517f15504301000000000000000000000000000000000000000000000000000000008152600401610a679190610d61565b60405180910390fd5b818160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503073ffffffffffffffffffffffffffffffffffffffff167f5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c783604051610af99190610d61565b60405180910390a25050565b600080604051602001610b1790610f42565b6040516020818303038152906040528051906020012090508091505090565b6000610b4061081b565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080823b905060008111915050919050565b60008060003073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1663c7f62cda86604051602401610bc59190610d61565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610c149190610e16565b600060405180830381855af49150503d8060008114610c4f576040519150601f19603f3d011682016040523d82523d6000602084013e610c54565b606091505b509150915081158015610cb9575063a1cfa5a860e01b604051602001610c7a9190610faf565b6040516020818303038152906040528051906020012081604051602001610ca19190610e16565b60405160208183030381529060405280519060200120145b92505050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cf282610cc7565b9050919050565b610d0281610ce7565b8114610d0d57600080fd5b50565b600081359050610d1f81610cf9565b92915050565b600060208284031215610d3b57610d3a610cc2565b5b6000610d4984828501610d10565b91505092915050565b610d5b81610ce7565b82525050565b6000602082019050610d766000830184610d52565b92915050565b6000604082019050610d916000830185610d52565b610d9e6020830184610d52565b9392505050565b600081519050919050565b600081905092915050565b60005b83811015610dd9578082015181840152602081019050610dbe565b60008484015250505050565b6000610df082610da5565b610dfa8185610db0565b9350610e0a818560208601610dbb565b80840191505092915050565b6000610e228284610de5565b915081905092915050565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b6000610e9a602383610e2d565b9150610ea582610e3e565b604082019050919050565b60006020820190508181036000830152610ec981610e8d565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b6000610f2c602183610e2d565b9150610f3782610ed0565b604082019050919050565b60006020820190508181036000830152610f5b81610f1f565b9050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6000819050919050565b610fa9610fa482610f62565b610f8e565b82525050565b6000610fbb8284610f98565b6004820191508190509291505056fea264697066735822122023a7c33d7b91dce35ffbcf8837693364ab22a3905d0fc00016833e5fac45ca2f64736f6c63430008110033","storage":{"0x5c7865864a2a990d80b5bb5c40e7b73a029960dc711fbb56120dfab976e92ea3":"0x0"}},"0x4e59b44847b379578588920ca78fbf26c0b4956c":{"nonce":2,"balance":"0x0","code":"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3","storage":{}},"0x70997970c51812dc3a010c7d01b50e0d17dc79c8":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x8138ef7cf908021d117e542120b7a39065016107":{"nonce":1,"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100575760003560e01c80632a952b2d1461005c57806350c946fe14610085578063625ca21c146100a5578063daa250be146100c6578063deba1b98146100d9575b600080fd5b61006f61006a366004613a63565b6100ec565b60405161007c9190613a7c565b60405180910390f35b610098610093366004613a63565b61011c565b60405161007c9190613b21565b6100b86100b3366004613c92565b610276565b60405190815260200161007c565b61006f6100d4366004613d5f565b6102bb565b6100b86100e7366004613c92565b6102d6565b6100f46139e4565b6040805160008082526020820190815281830190925261011691849190610310565b92915050565b6101416040805160608101909152806000815260200160608152602001606081525090565b61014a82610ab6565b60408051606081019091528154909190829060ff16600981111561017057610170613aa7565b600981111561018157610181613aa7565b815260200160018201805461019590613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546101c190613dc2565b801561020e5780601f106101e35761010080835404028352916020019161020e565b820191906000526020600020905b8154815290600101906020018083116101f157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561026657602002820191906000526020600020905b815481526020019060010190808311610252575b5050505050815250509050919050565b600080604051806060016040528086600981111561029657610296613aa7565b81526020018581526020018481525090506102b081610ac1565b9150505b9392505050565b6102c36139e4565b6102ce848484610310565b949350505050565b60008060405180606001604052808660098111156102f6576102f6613aa7565b81526020018581526020018481525090506102b081610acc565b6103186139e4565b81518351146103a05760408051634bab873760e11b81526004810191909152600d60448201526c72756e74696d6556616c75657360981b606482015260806024820152602260848201527f6d7573742062652073616d65206c656e6774682061732072756e74696d654b6560a482015261797360f01b60c482015260e4015b60405180910390fd5b60006103ab85610c26565b805490915060ff1660018160098111156103c7576103c7613aa7565b036104755761046c6103da838787610c84565b8360010180546103e990613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461041590613dc2565b80156104625780601f1061043757610100808354040283529160200191610462565b820191906000526020600020905b81548152906001019060200180831161044557829003601f168201915b5050505050610d46565b925050506102b4565b600281600981111561048957610489613aa7565b036105305761046c61049c838787610c84565b8360010180546104ab90613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546104d790613dc2565b80156105245780601f106104f957610100808354040283529160200191610524565b820191906000526020600020905b81548152906001019060200180831161050757829003601f168201915b50505050508787610ebb565b600381600981111561054457610544613aa7565b036105de5761046c82600101805461055b90613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461058790613dc2565b80156105d45780601f106105a9576101008083540402835291602001916105d4565b820191906000526020600020905b8154815290600101906020018083116105b757829003601f168201915b5050505050610f59565b60048160098111156105f2576105f2613aa7565b0361068c5761046c82600101805461060990613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461063590613dc2565b80156106825780601f1061065757610100808354040283529160200191610682565b820191906000526020600020905b81548152906001019060200180831161066557829003601f168201915b5050505050611087565b60058160098111156106a0576106a0613aa7565b0361073a5761046c8260010180546106b790613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546106e390613dc2565b80156107305780601f1061070557610100808354040283529160200191610730565b820191906000526020600020905b81548152906001019060200180831161071357829003601f168201915b505050505061131e565b600981600981111561074e5761074e613aa7565b036107ea5761046c82600101805461076590613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461079190613dc2565b80156107de5780601f106107b3576101008083540402835291602001916107de565b820191906000526020600020905b8154815290600101906020018083116107c157829003601f168201915b505050505086866114b5565b60068160098111156107fe576107fe613aa7565b036108a35761046c610811838787610c84565b83600101805461082090613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461084c90613dc2565b80156108995780601f1061086e57610100808354040283529160200191610899565b820191906000526020600020905b81548152906001019060200180831161087c57829003601f168201915b50505050506115c7565b60078160098111156108b7576108b7613aa7565b036109ec576040805160608101909152825461046c91908490829060ff1660098111156108e6576108e6613aa7565b60098111156108f7576108f7613aa7565b815260200160018201805461090b90613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461093790613dc2565b80156109845780601f1061095957610100808354040283529160200191610984565b820191906000526020600020905b81548152906001019060200180831161096757829003601f168201915b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156109dc57602002820191906000526020600020905b8154815260200190600101908083116109c8575b5050505050815250508686611728565b6008816009811115610a0057610a00613aa7565b03610a9a5761046c826001018054610a1790613dc2565b80601f0160208091040260200160405190810160405280929190818152602001828054610a4390613dc2565b8015610a905780601f10610a6557610100808354040283529160200191610a90565b820191906000526020600020905b815481529060010190602001808311610a7357829003601f168201915b50505050506118a5565b6040516323a9bbc960e01b815260048101879052602401610397565b600061011682610c26565b6000610116826118ea565b6000610ad782610ac1565b9050610ae28161192a565b15610b35577fcb64985827770858ec421ad26da7e558c757541643036ce44d6b4eb9e8e5dc5e81836000015184602001518560400151604051610b289493929190613e32565b60405180910390a1919050565b610b3e82611a8c565b610b5d578160405163382bbbc960e11b81526004016103979190613b21565b60005b826040015151811015610bd957610b9383604001518281518110610b8657610b86613e6a565b602002602001015161192a565b610bd15782604001518181518110610bad57610bad613e6a565b6020026020010151604051632f19f96160e11b815260040161039791815260200190565b600101610b60565b50610be382611c31565b8351602085015160408087015190519395507fcb64985827770858ec421ad26da7e558c757541643036ce44d6b4eb9e8e5dc5e9450610b28938693929190613e32565b604080516020808201839052606082018190527f696f2e73796e7468657469782e6f7261636c652d6d616e616765722e4e6f6465608080840191909152828401949094528251808303909401845260a0909101909152815191012090565b600283015460609067ffffffffffffffff811115610ca457610ca4613b9a565b604051908082528060200260200182016040528015610cdd57816020015b610cca6139e4565b815260200190600190039081610cc25790505b50905060005b6002850154811015610d3e57610d19856002018281548110610d0757610d07613e6a565b90600052602060002001548585610310565b828281518110610d2b57610d2b613e6a565b6020908102919091010152600101610ce3565b509392505050565b610d4e6139e4565b600082806020019051810190610d649190613e80565b90506000816008811115610d7a57610d7a613aa7565b03610d9057610d8884611ca5565b915050610116565b6001816008811115610da457610da4613aa7565b03610db257610d8884611d0d565b6002816008811115610dc657610dc6613aa7565b03610dd457610d8884611d90565b6003816008811115610de857610de8613aa7565b03610df657610d8884611e13565b6004816008811115610e0a57610e0a613aa7565b03610e1857610d8884611ec9565b6005816008811115610e2c57610e2c613aa7565b03610e3a57610d8884612009565b6006816008811115610e4e57610e4e613aa7565b03610e5c57610d88846120e4565b6007816008811115610e7057610e70613aa7565b03610e7e57610d888461220c565b6008816008811115610e9257610e92613aa7565b03610ea057610d88846122ce565b80604051631be413d360e11b81526004016103979190613ea1565b610ec36139e4565b600084806020019051810190610ed99190613ed3565b604051631ecba7c360e31b81529091506001600160a01b0382169063f65d3e1890610f0e908990899089908990600401613ef0565b608060405180830381865afa158015610f2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4f9190613f91565b9695505050505050565b610f616139e4565b600080600084806020019051810190610f7a9190613fe8565b92509250925060008390506000806000836001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610fc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fec9190614041565b509350509250925060008660001461100f5761100a8585858a6123c7565b611011565b825b905060128660ff161161103b5761103661102f60ff881660126140a7565b82906124c2565b611053565b61105361104c601260ff89166140a7565b82906124dc565b9050604051806080016040528082815260200183815260200160008152602001600081525098505050505050505050919050565b61108f6139e4565b600080600080600080878060200190518101906110ac91906140ba565b604080516002808252606082018352979d50959b50939950919750955093506000929060208301908036833701905050905081816000815181106110f2576110f2613e6a565b602002602001019063ffffffff16908163ffffffff168152505060008160018151811061112157611121613e6a565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526000906001600160a01b0385169063883bdbfd90611165908590600401614143565b600060405180830381865afa158015611182573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526111aa91908101906141f5565b5090506000816000815181106111c2576111c2613e6a565b6020026020010151826001815181106111dd576111dd613e6a565b60200260200101516111ef91906142c1565b9050600061121761120563ffffffff87166124f6565b61120f9084614304565b60060b61252d565b905060008260060b12801561124c575061123b63ffffffff8616612569565b612569565b8260060b6112499190614342565b15155b1561125f578061125b81614356565b9150505b600061126d6012600a61445d565b9050600061128061123684848f8f612593565b905060006112908a60ff16612569565b61129c8c60ff16612569565b6112a6919061446c565b905060008082136112d1576112cc6112c56112c084614493565b612686565b84906124dc565b6112e4565b6112e46112dd83612686565b84906124c2565b905060405180608001604052808281526020014281526020016000815260200160008152509e505050505050505050505050505050919050565b6113266139e4565b60008060008480602001905181019061133f91906144bf565b91945092509050826000826113bc576040516396834ad360e01b8152600481018590526001600160a01b038316906396834ad390602401608060405180830381865afa158015611393573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b791906144f5565b611425565b604051639474f45b60e01b8152600481018590526001600160a01b03831690639474f45b90602401608060405180830381865afa158015611401573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142591906144f5565b90506000816040015160030b601261143d919061456f565b90506000808213611467576114626114576112c084614493565b845160070b906124dc565b61147e565b61147e61147383612686565b845160070b906124c2565b9050604051806080016040528082815260200184606001518152602001600081526020016000815250975050505050505050919050565b6114bd6139e4565b6000806000868060200190518101906114d69190614597565b92509250925060005b8651811015611545578681815181106114fa576114fa613e6a565b6020026020010151717374616c656e657373546f6c6572616e636560701b0361153d5785818151811061152f5761152f613e6a565b602002602001015160001c91505b6001016114df565b5060408051600180825281830190925260009160208083019080368337019050509050828160008151811061157c5761157c613e6a565b602002602001018181525050836001838360405160200161159f939291906145ce565b60408051601f198184030181529082905263cf2cabdf60e01b82526103979291600401614603565b6115cf6139e4565b6000828060200190518101906115e59190614627565b90506000846000815181106115fc576115fc613e6a565b602002602001015160000151905060008560018151811061161f5761161f613e6a565b6020026020010151600001519050808214611702576000611653601261164d611648858761446c565b6126a9565b906124c2565b905082158061167b5750611666836126a9565b6116709082614640565b61167985612569565b125b15611700576002875111156116b0578660028151811061169d5761169d613e6a565b6020026020010151945050505050610116565b826000036116d15760405163014cc07160e01b815260040160405180910390fd5b6116da836126a9565b6116e49082614640565b60405163dcac091960e01b815260040161039791815260200190565b505b8560008151811061171557611715613e6a565b6020026020010151935050505092915050565b6117306139e4565b6000846020015180602001905181019061174a9190614627565b905060005b84518110156117bc5784818151811061176a5761176a613e6a565b6020026020010151717374616c656e657373546f6c6572616e636560701b036117b4576117ad8482815181106117a2576117a2613e6a565b602002602001015190565b91506117bc565b60010161174f565b50600085604001516000815181106117d6576117d6613e6a565b6020026020010151905060006117ed828787610310565b60208101519091506117ff84426140a7565b1161180e5792506102b4915050565b86604001515160010361187157866040015160008151811061183257611832613e6a565b602002602001015181600001518260200151604051631808066560e21b8152600401610397939291909283526020830191909152604082015260600190565b61189a876040015160018151811061188b5761188b613e6a565b60200260200101518787610310565b979650505050505050565b6118ad6139e4565b6040518060800160405280838060200190518101906118cc9190614627565b81526020014281526020016000815260200160008152509050919050565b600081600001518260200151836040015160405160200161190d9392919061466e565b604051602081830303815290604052805190602001209050919050565b60008061193683610c26565b60408051606081019091528154909190829060ff16600981111561195c5761195c613aa7565b600981111561196d5761196d613aa7565b815260200160018201805461198190613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546119ad90613dc2565b80156119fa5780601f106119cf576101008083540402835291602001916119fa565b820191906000526020600020905b8154815290600101906020018083116119dd57829003601f168201915b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015611a5257602002820191906000526020600020905b815481526020019060010190808311611a3e575b505050505081525050905060006009811115611a7057611a70613aa7565b81516009811115611a8357611a83613aa7565b14159392505050565b6000600182516009811115611aa357611aa3613aa7565b1480611ac15750600682516009811115611abf57611abf613aa7565b145b80611ade5750600782516009811115611adc57611adc613aa7565b145b15611aee57611aec826126c1565b505b600182516009811115611b0357611b03613aa7565b03611b11576101168261284a565b600282516009811115611b2657611b26613aa7565b03611b3457610116826128a5565b600382516009811115611b4957611b49613aa7565b03611b575761011682612973565b600482516009811115611b6c57611b6c613aa7565b03611b7a5761011682612aae565b600582516009811115611b8f57611b8f613aa7565b03611b9d5761011682612e92565b600982516009811115611bb257611bb2613aa7565b03611bc05761011682612fcb565b600682516009811115611bd557611bd5613aa7565b03611be3576101168261300e565b600782516009811115611bf857611bf8613aa7565b03611c065761011682613052565b600882516009811115611c1b57611c1b613aa7565b03611c295761011682613078565b506000919050565b600080611c3d836118ea565b9050611c4881610c26565b8351815491935090839060ff19166001836009811115611c6a57611c6a613aa7565b021790555060208301516001830190611c8390826146ed565b5060408301518051611c9f916002850191602090910190613a0c565b50915091565b611cad6139e4565b60005b8251811015611d07578160200151838281518110611cd057611cd0613e6a565b6020026020010151602001511115611cff57828181518110611cf457611cf4613e6a565b602002602001015191505b600101611cb0565b50919050565b611d156139e4565b81600081518110611d2857611d28613e6a565b602002602001015190506000600190505b8251811015611d07578160000151838281518110611d5957611d59613e6a565b6020026020010151600001511215611d8857828181518110611d7d57611d7d613e6a565b602002602001015191505b600101611d39565b611d986139e4565b81600081518110611dab57611dab613e6a565b602002602001015190506000600190505b8251811015611d07578160000151838281518110611ddc57611ddc613e6a565b6020026020010151600001511315611e0b57828181518110611e0057611e00613e6a565b602002602001015191505b600101611dbc565b611e1b6139e4565b60005b8251811015611e9557828181518110611e3957611e39613e6a565b60200260200101516000015182600001818151611e56919061456f565b9052508251839082908110611e6d57611e6d613e6a565b60200260200101516020015182602001818151611e8a91906147ad565b905250600101611e1e565b50611ea08251612569565b8151611eac9190614640565b815281516020820151611ebf91906147c0565b6020820152919050565b611ed16139e4565b611eed826000611ee86001865161123691906140a7565b6130a4565b60028251611efb91906147d4565b600003611fd65760408051600280825260608201909252600091816020015b611f226139e4565b815260200190600190039081611f1a57905050905082600160028551611f4891906147c0565b611f5291906140a7565b81518110611f6257611f62613e6a565b602002602001015181600081518110611f7d57611f7d613e6a565b60200260200101819052508260028451611f9791906147c0565b81518110611fa757611fa7613e6a565b602002602001015181600181518110611fc257611fc2613e6a565b60200260200101819052506102b481611e13565b8160028351611fe591906147c0565b81518110611ff557611ff5613e6a565b60200260200101519050919050565b919050565b6120116139e4565b8160008151811061202457612024613e6a565b60209081029190910101515181528151829060009061204557612045613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061207657612076613e6a565b6020026020010151600001518260000181815161209391906147e8565b90525082518390829081106120aa576120aa613e6a565b602002602001015160200151826020018181516120c791906147ad565b90525060010161205b565b5081518160200151611ebf91906147c0565b6120ec6139e4565b816000815181106120ff576120ff613e6a565b60209081029190910101515181528151829060009061212057612120613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061215157612151613e6a565b60200260200101516000015160000361219e5782818151811061217657612176613e6a565b6020026020010151600001516040516338ee04a760e01b815260040161039791815260200190565b8281815181106121b0576121b0613e6a565b602002602001015160000151826000018181516121cd9190614640565b90525082518390829081106121e4576121e4613e6a565b6020026020010151602001518260200181815161220191906147ad565b905250600101612136565b6122146139e4565b8160008151811061222757612227613e6a565b60209081029190910101515181528151829060009061224857612248613e6a565b6020908102919091018101518101519082015260015b82518110156120d25761229083828151811061227c5761227c613e6a565b602090810291909101015151835190613264565b825282518390829081106122a6576122a6613e6a565b602002602001015160200151826020018181516122c391906147ad565b90525060010161225e565b6122d66139e4565b816000815181106122e9576122e9613e6a565b60209081029190910101515181528151829060009061230a5761230a613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061233b5761233b613e6a565b6020026020010151600001516000036123605782818151811061217657612176613e6a565b61238983828151811061237557612375613e6a565b602090810291909101015151835190613283565b8252825183908290811061239f5761239f613e6a565b602002602001015160200151826020018181516123bc91906147ad565b905250600101612320565b6000826001826123d785426140a7565b90505b69ffffffffffffffffffff8716156124a3576001600160a01b038816639a6fc8f561240489614818565b6040516001600160e01b031960e084901b16815269ffffffffffffffffffff8216600482015290995060240160a060405180830381865afa925050508015612469575060408051601f3d908101601f1916820190925261246691810190614041565b60015b156124a357858210156124805750505050506124a3565b61248a848961456f565b97508661249681614834565b97505050505050506123da565b6124ac82612569565b6124b69084614640565b98975050505050505050565b60006124d261123683600a61484d565b6102b490846147e8565b60006124ec61123683600a61484d565b6102b49084614640565b6000667fffffffffffff66ffffffffffffff83161115612529576040516329d2678160e21b815260040160405180910390fd5b5090565b6000627fffff19600683900b128061254b5750627fffff600683900b135b1561252957604051630d962f7960e21b815260040160405180910390fd5b60006001600160ff1b038211156125295760405163677c430560e11b815260040160405180910390fd5b60008061259f86613298565b90506fffffffffffffffffffffffffffffffff6001600160a01b0382161161261c5760006125d66001600160a01b03831680614859565b9050836001600160a01b0316856001600160a01b03161061260557612600600160c01b87836136cd565b612614565b6126148187600160c01b6136cd565b92505061267d565b600061263b6001600160a01b03831680680100000000000000006136cd565b9050836001600160a01b0316856001600160a01b03161061266a57612665600160801b87836136cd565b612679565b6126798187600160801b6136cd565b9250505b50949350505050565b6000808212156125295760405163029f024d60e31b815260040160405180910390fd5b600080821215612529576126bc82614493565b610116565b6000805b8260400151518110156128415760006126fa846040015183815181106126ed576126ed613e6a565b6020026020010151610ab6565b60408051606081019091528154909190829060ff16600981111561272057612720613aa7565b600981111561273157612731613aa7565b815260200160018201805461274590613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461277190613dc2565b80156127be5780601f10612793576101008083540402835291602001916127be565b820191906000526020600020905b8154815290600101906020018083116127a157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561281657602002820191906000526020600020905b815481526020019060010190808311612802575b505050505081525050905061282a81611a8c565b612838575060009392505050565b506001016126c5565b50600192915050565b60006002826040015151101561286257506000919050565b81602001515160201461287757506000919050565b600082602001518060200190518101906128919190614627565b905060088111156128415750600092915050565b6000602082602001515110156128bd57506000919050565b600082602001518060200190518101906128d79190613ed3565b90506128ea816306e7ea3960e21b6138e2565b6128f75750600092915050565b604051633b70a5bf60e21b81526001600160a01b0382169063edc296fc90612923908690600401613b21565b6020604051808303816000875af1158015612942573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129669190614870565b6128415750600092915050565b6040810151516000901561298957506000919050565b81602001515160601461299e57506000919050565b60008083602001518060200190518101906129b99190613fe8565b92505091506000829050806001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015612a01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a259190614041565b5050505050806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8c919061488b565b60ff168260ff1614612aa357506000949350505050565b506001949350505050565b60408101515160009015612ac457506000919050565b81602001515160c014612ad957506000919050565b6000806000806000808760200151806020019051810190612afa91906140ba565b9550955095509550955095508360ff16866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6c919061488b565b60ff1614612b8257506000979650505050505050565b8260ff16856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612be8919061488b565b60ff1614612bfe57506000979650505050505050565b6000826001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c629190613ed3565b90506000836001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ca4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cc89190613ed3565b9050876001600160a01b0316826001600160a01b0316148015612cfc5750866001600160a01b0316816001600160a01b0316145b158015612d385750866001600160a01b0316826001600160a01b0316148015612d365750876001600160a01b0316816001600160a01b0316145b155b15612d4d575060009998505050505050505050565b60128660ff161180612d62575060128560ff16115b15612d77575060009998505050505050505050565b8263ffffffff16600003612d95575060009998505050505050505050565b6040805160028082526060820183526000926020830190803683370190505090508381600081518110612dca57612dca613e6a565b602002602001019063ffffffff16908163ffffffff1681525050600081600181518110612df957612df9613e6a565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526001600160a01b0386169063883bdbfd90612e3a908490600401614143565b600060405180830381865afa158015612e57573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612e7f91908101906141f5565b5060019c9b505050505050505050505050565b60408101515160009015612ea857506000919050565b816020015151606014612ebd57506000919050565b60008060008460200151806020019051810190612eda91906144bf565b919450925090508281612f55576040516396834ad360e01b8152600481018490526001600160a01b038216906396834ad390602401608060405180830381865afa158015612f2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f5091906144f5565b612fbe565b604051639474f45b60e01b8152600481018490526001600160a01b03821690639474f45b90602401608060405180830381865afa158015612f9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fbe91906144f5565b5060019695505050505050565b60408101515160009015612fe157506000919050565b816020015151606014612ff657506000919050565b8160200151806020019051810190612aa39190614597565b60008160400151516002148061302957508160400151516003145b61303557506000919050565b81602001515160201461304a57506000919050565b506001919050565b600081604001515160011480613029575081604001515160021461303557506000919050565b6040810151516000901561308e57506000919050565b6020826020015151101561304a57506000919050565b81818082036130b4575050505050565b6000856130da60026130c6888861446c565b6130d09190614640565b6112c0908861456f565b815181106130ea576130ea613e6a565b60200260200101516000015190505b818313613236575b808661310c85612686565b8151811061311c5761311c613e6a565b60200260200101516000015112156131405782613138816148a6565b935050613101565b8561314a83612686565b8151811061315a5761315a613e6a565b60200260200101516000015181121561317f5781613177816148be565b925050613140565b818313613231578561319083612686565b815181106131a0576131a0613e6a565b6020026020010151866131b285612686565b815181106131c2576131c2613e6a565b6020026020010151876131d486612686565b815181106131e4576131e4613e6a565b60200260200101886131f586612686565b8151811061320557613205613e6a565b602002602001018290528290525050828061321f906148a6565b935050818061322d906148be565b9250505b6130f9565b81851215613249576132498686846130a4565b8383121561325c5761325c8684866130a4565b505050505050565b6000670de0b6b3a764000061327983856147e8565b6102b49190614640565b600081613279670de0b6b3a7640000856147e8565b60008060008360020b126132b8576132b3600284900b612686565b6132c8565b6132c86112c0600285900b614493565b90506132e36112c06132dd620d89e7196148db565b60020b90565b8111156133165760405162461bcd60e51b81526020600482015260016024820152601560fa1b6044820152606401610397565b60008160011660000361332d57600160801b61333f565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff169050600282161561337e576080613379826ffff97272373d413259a46990580e213a614859565b901c90505b60048216156133a85760806133a3826ffff2e50f5f656932ef12357cf3c7fdcc614859565b901c90505b60088216156133d25760806133cd826fffe5caca7e10e4e61c3624eaa0941cd0614859565b901c90505b60108216156133fc5760806133f7826fffcb9843d60f6159c9db58835c926644614859565b901c90505b6020821615613426576080613421826fff973b41fa98c081472e6896dfb254c0614859565b901c90505b604082161561345057608061344b826fff2ea16466c96a3843ec78b326b52861614859565b901c90505b608082161561347a576080613475826ffe5dee046a99a2a811c461f1969c3053614859565b901c90505b6101008216156134a55760806134a0826ffcbe86c7900a88aedcffc83b479aa3a4614859565b901c90505b6102008216156134d05760806134cb826ff987a7253ac413176f2b074cf7815e54614859565b901c90505b6104008216156134fb5760806134f6826ff3392b0822b70005940c7a398e4b70f3614859565b901c90505b610800821615613526576080613521826fe7159475a2c29b7443b29c7fa6e889d9614859565b901c90505b61100082161561355157608061354c826fd097f3bdfd2022b8845ad8f792aa5825614859565b901c90505b61200082161561357c576080613577826fa9f746462d870fdf8a65dc1f90e061e5614859565b901c90505b6140008216156135a75760806135a2826f70d869a156d2a1b890bb3df62baf32f7614859565b901c90505b6180008216156135d25760806135cd826f31be135f97d08fd981231505542fcfa6614859565b901c90505b620100008216156135fe5760806135f9826f09aa508b5b7a84e1c677de54f3e99bc9614859565b901c90505b62020000821615613629576080613624826e5d6af8dedb81196699c329225ee604614859565b901c90505b6204000082161561365357608061364e826d2216e584f5fa1ea926041bedfe98614859565b901c90505b6208000082161561367b576080613676826b048a170391f7dc42444e8fa2614859565b901c90505b60008460020b131561369657613693816000196147c0565b90505b6102ce6136a8640100000000836147d4565b156136b45760016136b7565b60005b6136c89060ff16602084901c6147ad565b6139ba565b6000808060001985870985870292508281108382030391505080600003613749576000841161373e5760405162461bcd60e51b815260206004820152601960248201527f48616e646c65206e6f6e2d6f766572666c6f77206361736573000000000000006044820152606401610397565b5082900490506102b4565b8084116137985760405162461bcd60e51b815260206004820152601960248201527f70726576656e74732064656e6f6d696e61746f72203d3d2030000000000000006044820152606401610397565b60008486880980840393811190920391905060006137d06137b887612569565b6137c188612569565b6137ca90614493565b16612686565b9586900495938490049360008190030460010190506137ef8184614859565b909317926000613800876003614859565b600218905061380f8188614859565b61381a9060026140a7565b6138249082614859565b90506138308188614859565b61383b9060026140a7565b6138459082614859565b90506138518188614859565b61385c9060026140a7565b6138669082614859565b90506138728188614859565b61387d9060026140a7565b6138879082614859565b90506138938188614859565b61389e9060026140a7565b6138a89082614859565b90506138b48188614859565b6138bf9060026140a7565b6138c99082614859565b90506138d58186614859565b9998505050505050505050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b1790529051600091829182916001600160a01b0387169161394391906148fd565b6000604051808303816000865af19150503d8060008114613980576040519150601f19603f3d011682016040523d82523d6000602084013e613985565b606091505b50915091508161399a57600092505050610116565b80516000036139ae57600092505050610116565b60200151949350505050565b60006001600160a01b038211156125295760405163dccde8ed60e01b815260040160405180910390fd5b6040518060800160405280600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215613a47579160200282015b82811115613a47578251825591602001919060010190613a2c565b506125299291505b808211156125295760008155600101613a4f565b600060208284031215613a7557600080fd5b5035919050565b8151815260208083015190820152604080830151908201526060808301519082015260808101610116565b634e487b7160e01b600052602160045260246000fd5b600a8110613acd57613acd613aa7565b9052565b60005b83811015613aec578181015183820152602001613ad4565b50506000910152565b60008151808452613b0d816020860160208601613ad1565b601f01601f19169290920160200192915050565b60006020808352613b358184018551613abd565b8084015160606040850152613b4d6080850182613af5565b6040860151858203601f19016060870152805180835290840192506000918401905b80831015613b8f5783518252928401926001929092019190840190613b6f565b509695505050505050565b634e487b7160e01b600052604160045260246000fd5b6040516080810167ffffffffffffffff81118282101715613bd357613bd3613b9a565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715613c0257613c02613b9a565b604052919050565b600067ffffffffffffffff821115613c2457613c24613b9a565b5060051b60200190565b600082601f830112613c3f57600080fd5b81356020613c54613c4f83613c0a565b613bd9565b8083825260208201915060208460051b870101935086841115613c7657600080fd5b602086015b84811015613b8f5780358352918301918301613c7b565b600080600060608486031215613ca757600080fd5b8335600a8110613cb657600080fd5b925060208481013567ffffffffffffffff80821115613cd457600080fd5b818701915087601f830112613ce857600080fd5b813581811115613cfa57613cfa613b9a565b613d0c601f8201601f19168501613bd9565b8181528985838601011115613d2057600080fd5b818585018683013760009181019094015291935060408601359180831115613d4757600080fd5b5050613d5586828701613c2e565b9150509250925092565b600080600060608486031215613d7457600080fd5b83359250602084013567ffffffffffffffff80821115613d9357600080fd5b613d9f87838801613c2e565b93506040860135915080821115613db557600080fd5b50613d5586828701613c2e565b600181811c90821680613dd657607f821691505b602082108103611d0757634e487b7160e01b600052602260045260246000fd5b60008151808452602080850194506020840160005b83811015613e2757815187529582019590820190600101613e0b565b509495945050505050565b848152613e426020820185613abd565b608060408201526000613e586080830185613af5565b828103606084015261189a8185613df6565b634e487b7160e01b600052603260045260246000fd5b600060208284031215613e9257600080fd5b8151600981106102b457600080fd5b6020810160098310613eb557613eb5613aa7565b91905290565b6001600160a01b0381168114613ed057600080fd5b50565b600060208284031215613ee557600080fd5b81516102b481613ebb565b608080825285518282018190526000919060209060a0850190828a01855b82811015613f5257613f42848351805182526020810151602083015260408101516040830152606081015160608301525050565b9285019290840190600101613f0e565b5050508481036020860152613f678189613af5565b925050508281036040840152613f7d8186613df6565b9050828103606084015261189a8185613df6565b600060808284031215613fa357600080fd5b613fab613bb0565b825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b805160ff8116811461200457600080fd5b600080600060608486031215613ffd57600080fd5b835161400881613ebb565b6020850151909350915061401e60408501613fd7565b90509250925092565b805169ffffffffffffffffffff8116811461200457600080fd5b600080600080600060a0868803121561405957600080fd5b61406286614027565b945060208601519350604086015192506060860151915061408560808701614027565b90509295509295909350565b634e487b7160e01b600052601160045260246000fd5b8181038181111561011657610116614091565b60008060008060008060c087890312156140d357600080fd5b86516140de81613ebb565b60208801519096506140ef81613ebb565b94506140fd60408801613fd7565b935061410b60608801613fd7565b9250608087015161411b81613ebb565b60a088015190925063ffffffff8116811461413557600080fd5b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b8181101561418157835163ffffffff168352928401929184019160010161415f565b50909695505050505050565b600082601f83011261419e57600080fd5b815160206141ae613c4f83613c0a565b8083825260208201915060208460051b8701019350868411156141d057600080fd5b602086015b84811015613b8f5780516141e881613ebb565b83529183019183016141d5565b6000806040838503121561420857600080fd5b825167ffffffffffffffff8082111561422057600080fd5b818501915085601f83011261423457600080fd5b81516020614244613c4f83613c0a565b82815260059290921b8401810191818101908984111561426357600080fd5b948201945b838610156142915785518060060b81146142825760008081fd5b82529482019490820190614268565b918801519196509093505050808211156142aa57600080fd5b506142b78582860161418d565b9150509250929050565b600682810b9082900b03667fffffffffffff198112667fffffffffffff8213171561011657610116614091565b634e487b7160e01b600052601260045260246000fd5b60008160060b8360060b8061431b5761431b6142ee565b667fffffffffffff1982146000198214161561433957614339614091565b90059392505050565b600082614351576143516142ee565b500790565b60008160020b627fffff19810361436f5761436f614091565b6000190192915050565b600181815b808511156143b457816000190482111561439a5761439a614091565b808516156143a757918102915b93841c939080029061437e565b509250929050565b6000826143cb57506001610116565b816143d857506000610116565b81600181146143ee57600281146143f857614414565b6001915050610116565b60ff84111561440957614409614091565b50506001821b610116565b5060208310610133831016604e8410600b8410161715614437575081810a610116565b6144418383614379565b806000190482111561445557614455614091565b029392505050565b60006102b460ff8416836143bc565b818103600083128015838313168383128216171561448c5761448c614091565b5092915050565b6000600160ff1b82016144a8576144a8614091565b5060000390565b8051801515811461200457600080fd5b6000806000606084860312156144d457600080fd5b83516144df81613ebb565b6020850151909350915061401e604085016144af565b60006080828403121561450757600080fd5b61450f613bb0565b82518060070b811461452057600080fd5b8152602083015167ffffffffffffffff8116811461453d57600080fd5b60208201526040830151600381900b811461455757600080fd5b60408201526060928301519281019290925250919050565b808201828112600083128015821682158216171561458f5761458f614091565b505092915050565b6000806000606084860312156145ac57600080fd5b83516145b781613ebb565b602085015160409095015190969495509392505050565b60ff8416815267ffffffffffffffff831660208201526060604082015260006145fa6060830184613df6565b95945050505050565b6001600160a01b03831681526040602082018190526000906102ce90830184613af5565b60006020828403121561463957600080fd5b5051919050565b60008261464f5761464f6142ee565b600160ff1b82146000198414161561466957614669614091565b500590565b6146788185613abd565b60606020820152600061468e6060830185613af5565b8281036040840152610f4f8185613df6565b601f8211156146e8576000816000526020600020601f850160051c810160208610156146c95750805b601f850160051c820191505b8181101561325c578281556001016146d5565b505050565b815167ffffffffffffffff81111561470757614707613b9a565b61471b816147158454613dc2565b846146a0565b602080601f83116001811461475057600084156147385750858301515b600019600386901b1c1916600185901b17855561325c565b600085815260208120601f198616915b8281101561477f57888601518255948401946001909101908401614760565b508582101561479d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b8082018082111561011657610116614091565b6000826147cf576147cf6142ee565b500490565b6000826147e3576147e36142ee565b500690565b80820260008212600160ff1b8414161561480457614804614091565b818105831482151761011657610116614091565b600069ffffffffffffffffffff82168061436f5761436f614091565b60006001820161484657614846614091565b5060010190565b60006102b483836143bc565b808202811582820484141761011657610116614091565b60006020828403121561488257600080fd5b6102b4826144af565b60006020828403121561489d57600080fd5b6102b482613fd7565b60006001600160ff1b01820161484657614846614091565b6000600160ff1b82016148d3576148d3614091565b506000190190565b60008160020b627fffff1981036148f4576148f4614091565b60000392915050565b6000825161490f818460208701613ad1565b919091019291505056fea264697066735822122074f32fef384fdc296b0859f1c1f941c8e736c6cb972aa9e2b894956ebd6a80b364736f6c63430008160033","storage":{}},"0x83a0444b93927c3afcbe46e522280390f748e171":{"nonce":1,"balance":"0x0","code":"0x6080604052366100135761001161001d565b005b61001b61001d565b005b6000610027610093565b90503660008037600080366000845af43d6000803e806000811461004a573d6000f35b3d6000fd5b600080823b905060008111915050919050565b6000806040516020016100749061017a565b6040516020818303038152906040528051906020012090508091505090565b600061009d6100c6565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000806040516020016100d89061020c565b6040516020818303038152906040528051906020012090508091505090565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b60006101646023836100f7565b915061016f82610108565b604082019050919050565b6000602082019050818103600083015261019381610157565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b60006101f66021836100f7565b91506102018261019a565b604082019050919050565b60006020820190508181036000830152610225816101e9565b905091905056fea2646970667358221220800da1f73cebd5e4afa07496d9bca6b6c4f526bdd3f4014ec15c70fe3a1c441364736f6c63430008110033","storage":{"0x5a648c35a2f5512218b4683cf10e03f5b7c9dc7346e1bf77d304ae97f60f592b":"0x108f53faf774d7c4c56f5bce9ca6e605ce8aeadd","0x5c7865864a2a990d80b5bb5c40e7b73a029960dc711fbb56120dfab976e92ea3":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"}},"0x90f79bf6eb2c4f870365e785982e1f101e93b906":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x976ea74026e726554db657fa54763abd0c3a0aa9":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xa0ee7a142d267c1f36714e4a8f75612f20a79720":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xc67e2bd3108604cf0168c0e5ef9cd6d78b9bb14b":{"nonce":1,"balance":"0x21e19c6edb7e2445f20","code":"0x","storage":{}},"0xeb045d78d273107348b0300c01d29b7552d622ab":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266":{"nonce":1,"balance":"0x21e19e08b86820a43ea","code":"0x","storage":{}}},"best_block_number":"0x5","blocks":[{"header":{"parentHash":"0x08abe6e453727534d8dd708843a7522b7d500338bdfe2402ca105dcdb05eebe9","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0xcd346446ed010523161f40a5f2b512def549bfb79e165b4354488738416481f2","transactionsRoot":"0xb3a4689832e0b599260ae70362ffcf224b60571b35ff8836904a3d81e2675d66","receiptsRoot":"0x2d13fdc120ab90536fed583939de7fb68b64926a306c1f629593ca9c2c93b198","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x3ea90d","timestamp":"0x66b200ca","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x2e0b6260","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gas":"0x3ea90d","maxFeePerGas":"0x83215600","maxPriorityFeePerGas":"0x3b9aca00","value":"0x0","accessList":[],"input":"0x608060405234801561001057600080fd5b5061494f806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80632a952b2d1461005c57806350c946fe14610085578063625ca21c146100a5578063daa250be146100c6578063deba1b98146100d9575b600080fd5b61006f61006a366004613a63565b6100ec565b60405161007c9190613a7c565b60405180910390f35b610098610093366004613a63565b61011c565b60405161007c9190613b21565b6100b86100b3366004613c92565b610276565b60405190815260200161007c565b61006f6100d4366004613d5f565b6102bb565b6100b86100e7366004613c92565b6102d6565b6100f46139e4565b6040805160008082526020820190815281830190925261011691849190610310565b92915050565b6101416040805160608101909152806000815260200160608152602001606081525090565b61014a82610ab6565b60408051606081019091528154909190829060ff16600981111561017057610170613aa7565b600981111561018157610181613aa7565b815260200160018201805461019590613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546101c190613dc2565b801561020e5780601f106101e35761010080835404028352916020019161020e565b820191906000526020600020905b8154815290600101906020018083116101f157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561026657602002820191906000526020600020905b815481526020019060010190808311610252575b5050505050815250509050919050565b600080604051806060016040528086600981111561029657610296613aa7565b81526020018581526020018481525090506102b081610ac1565b9150505b9392505050565b6102c36139e4565b6102ce848484610310565b949350505050565b60008060405180606001604052808660098111156102f6576102f6613aa7565b81526020018581526020018481525090506102b081610acc565b6103186139e4565b81518351146103a05760408051634bab873760e11b81526004810191909152600d60448201526c72756e74696d6556616c75657360981b606482015260806024820152602260848201527f6d7573742062652073616d65206c656e6774682061732072756e74696d654b6560a482015261797360f01b60c482015260e4015b60405180910390fd5b60006103ab85610c26565b805490915060ff1660018160098111156103c7576103c7613aa7565b036104755761046c6103da838787610c84565b8360010180546103e990613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461041590613dc2565b80156104625780601f1061043757610100808354040283529160200191610462565b820191906000526020600020905b81548152906001019060200180831161044557829003601f168201915b5050505050610d46565b925050506102b4565b600281600981111561048957610489613aa7565b036105305761046c61049c838787610c84565b8360010180546104ab90613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546104d790613dc2565b80156105245780601f106104f957610100808354040283529160200191610524565b820191906000526020600020905b81548152906001019060200180831161050757829003601f168201915b50505050508787610ebb565b600381600981111561054457610544613aa7565b036105de5761046c82600101805461055b90613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461058790613dc2565b80156105d45780601f106105a9576101008083540402835291602001916105d4565b820191906000526020600020905b8154815290600101906020018083116105b757829003601f168201915b5050505050610f59565b60048160098111156105f2576105f2613aa7565b0361068c5761046c82600101805461060990613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461063590613dc2565b80156106825780601f1061065757610100808354040283529160200191610682565b820191906000526020600020905b81548152906001019060200180831161066557829003601f168201915b5050505050611087565b60058160098111156106a0576106a0613aa7565b0361073a5761046c8260010180546106b790613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546106e390613dc2565b80156107305780601f1061070557610100808354040283529160200191610730565b820191906000526020600020905b81548152906001019060200180831161071357829003601f168201915b505050505061131e565b600981600981111561074e5761074e613aa7565b036107ea5761046c82600101805461076590613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461079190613dc2565b80156107de5780601f106107b3576101008083540402835291602001916107de565b820191906000526020600020905b8154815290600101906020018083116107c157829003601f168201915b505050505086866114b5565b60068160098111156107fe576107fe613aa7565b036108a35761046c610811838787610c84565b83600101805461082090613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461084c90613dc2565b80156108995780601f1061086e57610100808354040283529160200191610899565b820191906000526020600020905b81548152906001019060200180831161087c57829003601f168201915b50505050506115c7565b60078160098111156108b7576108b7613aa7565b036109ec576040805160608101909152825461046c91908490829060ff1660098111156108e6576108e6613aa7565b60098111156108f7576108f7613aa7565b815260200160018201805461090b90613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461093790613dc2565b80156109845780601f1061095957610100808354040283529160200191610984565b820191906000526020600020905b81548152906001019060200180831161096757829003601f168201915b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156109dc57602002820191906000526020600020905b8154815260200190600101908083116109c8575b5050505050815250508686611728565b6008816009811115610a0057610a00613aa7565b03610a9a5761046c826001018054610a1790613dc2565b80601f0160208091040260200160405190810160405280929190818152602001828054610a4390613dc2565b8015610a905780601f10610a6557610100808354040283529160200191610a90565b820191906000526020600020905b815481529060010190602001808311610a7357829003601f168201915b50505050506118a5565b6040516323a9bbc960e01b815260048101879052602401610397565b600061011682610c26565b6000610116826118ea565b6000610ad782610ac1565b9050610ae28161192a565b15610b35577fcb64985827770858ec421ad26da7e558c757541643036ce44d6b4eb9e8e5dc5e81836000015184602001518560400151604051610b289493929190613e32565b60405180910390a1919050565b610b3e82611a8c565b610b5d578160405163382bbbc960e11b81526004016103979190613b21565b60005b826040015151811015610bd957610b9383604001518281518110610b8657610b86613e6a565b602002602001015161192a565b610bd15782604001518181518110610bad57610bad613e6a565b6020026020010151604051632f19f96160e11b815260040161039791815260200190565b600101610b60565b50610be382611c31565b8351602085015160408087015190519395507fcb64985827770858ec421ad26da7e558c757541643036ce44d6b4eb9e8e5dc5e9450610b28938693929190613e32565b604080516020808201839052606082018190527f696f2e73796e7468657469782e6f7261636c652d6d616e616765722e4e6f6465608080840191909152828401949094528251808303909401845260a0909101909152815191012090565b600283015460609067ffffffffffffffff811115610ca457610ca4613b9a565b604051908082528060200260200182016040528015610cdd57816020015b610cca6139e4565b815260200190600190039081610cc25790505b50905060005b6002850154811015610d3e57610d19856002018281548110610d0757610d07613e6a565b90600052602060002001548585610310565b828281518110610d2b57610d2b613e6a565b6020908102919091010152600101610ce3565b509392505050565b610d4e6139e4565b600082806020019051810190610d649190613e80565b90506000816008811115610d7a57610d7a613aa7565b03610d9057610d8884611ca5565b915050610116565b6001816008811115610da457610da4613aa7565b03610db257610d8884611d0d565b6002816008811115610dc657610dc6613aa7565b03610dd457610d8884611d90565b6003816008811115610de857610de8613aa7565b03610df657610d8884611e13565b6004816008811115610e0a57610e0a613aa7565b03610e1857610d8884611ec9565b6005816008811115610e2c57610e2c613aa7565b03610e3a57610d8884612009565b6006816008811115610e4e57610e4e613aa7565b03610e5c57610d88846120e4565b6007816008811115610e7057610e70613aa7565b03610e7e57610d888461220c565b6008816008811115610e9257610e92613aa7565b03610ea057610d88846122ce565b80604051631be413d360e11b81526004016103979190613ea1565b610ec36139e4565b600084806020019051810190610ed99190613ed3565b604051631ecba7c360e31b81529091506001600160a01b0382169063f65d3e1890610f0e908990899089908990600401613ef0565b608060405180830381865afa158015610f2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4f9190613f91565b9695505050505050565b610f616139e4565b600080600084806020019051810190610f7a9190613fe8565b92509250925060008390506000806000836001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610fc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fec9190614041565b509350509250925060008660001461100f5761100a8585858a6123c7565b611011565b825b905060128660ff161161103b5761103661102f60ff881660126140a7565b82906124c2565b611053565b61105361104c601260ff89166140a7565b82906124dc565b9050604051806080016040528082815260200183815260200160008152602001600081525098505050505050505050919050565b61108f6139e4565b600080600080600080878060200190518101906110ac91906140ba565b604080516002808252606082018352979d50959b50939950919750955093506000929060208301908036833701905050905081816000815181106110f2576110f2613e6a565b602002602001019063ffffffff16908163ffffffff168152505060008160018151811061112157611121613e6a565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526000906001600160a01b0385169063883bdbfd90611165908590600401614143565b600060405180830381865afa158015611182573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526111aa91908101906141f5565b5090506000816000815181106111c2576111c2613e6a565b6020026020010151826001815181106111dd576111dd613e6a565b60200260200101516111ef91906142c1565b9050600061121761120563ffffffff87166124f6565b61120f9084614304565b60060b61252d565b905060008260060b12801561124c575061123b63ffffffff8616612569565b612569565b8260060b6112499190614342565b15155b1561125f578061125b81614356565b9150505b600061126d6012600a61445d565b9050600061128061123684848f8f612593565b905060006112908a60ff16612569565b61129c8c60ff16612569565b6112a6919061446c565b905060008082136112d1576112cc6112c56112c084614493565b612686565b84906124dc565b6112e4565b6112e46112dd83612686565b84906124c2565b905060405180608001604052808281526020014281526020016000815260200160008152509e505050505050505050505050505050919050565b6113266139e4565b60008060008480602001905181019061133f91906144bf565b91945092509050826000826113bc576040516396834ad360e01b8152600481018590526001600160a01b038316906396834ad390602401608060405180830381865afa158015611393573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b791906144f5565b611425565b604051639474f45b60e01b8152600481018590526001600160a01b03831690639474f45b90602401608060405180830381865afa158015611401573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142591906144f5565b90506000816040015160030b601261143d919061456f565b90506000808213611467576114626114576112c084614493565b845160070b906124dc565b61147e565b61147e61147383612686565b845160070b906124c2565b9050604051806080016040528082815260200184606001518152602001600081526020016000815250975050505050505050919050565b6114bd6139e4565b6000806000868060200190518101906114d69190614597565b92509250925060005b8651811015611545578681815181106114fa576114fa613e6a565b6020026020010151717374616c656e657373546f6c6572616e636560701b0361153d5785818151811061152f5761152f613e6a565b602002602001015160001c91505b6001016114df565b5060408051600180825281830190925260009160208083019080368337019050509050828160008151811061157c5761157c613e6a565b602002602001018181525050836001838360405160200161159f939291906145ce565b60408051601f198184030181529082905263cf2cabdf60e01b82526103979291600401614603565b6115cf6139e4565b6000828060200190518101906115e59190614627565b90506000846000815181106115fc576115fc613e6a565b602002602001015160000151905060008560018151811061161f5761161f613e6a565b6020026020010151600001519050808214611702576000611653601261164d611648858761446c565b6126a9565b906124c2565b905082158061167b5750611666836126a9565b6116709082614640565b61167985612569565b125b15611700576002875111156116b0578660028151811061169d5761169d613e6a565b6020026020010151945050505050610116565b826000036116d15760405163014cc07160e01b815260040160405180910390fd5b6116da836126a9565b6116e49082614640565b60405163dcac091960e01b815260040161039791815260200190565b505b8560008151811061171557611715613e6a565b6020026020010151935050505092915050565b6117306139e4565b6000846020015180602001905181019061174a9190614627565b905060005b84518110156117bc5784818151811061176a5761176a613e6a565b6020026020010151717374616c656e657373546f6c6572616e636560701b036117b4576117ad8482815181106117a2576117a2613e6a565b602002602001015190565b91506117bc565b60010161174f565b50600085604001516000815181106117d6576117d6613e6a565b6020026020010151905060006117ed828787610310565b60208101519091506117ff84426140a7565b1161180e5792506102b4915050565b86604001515160010361187157866040015160008151811061183257611832613e6a565b602002602001015181600001518260200151604051631808066560e21b8152600401610397939291909283526020830191909152604082015260600190565b61189a876040015160018151811061188b5761188b613e6a565b60200260200101518787610310565b979650505050505050565b6118ad6139e4565b6040518060800160405280838060200190518101906118cc9190614627565b81526020014281526020016000815260200160008152509050919050565b600081600001518260200151836040015160405160200161190d9392919061466e565b604051602081830303815290604052805190602001209050919050565b60008061193683610c26565b60408051606081019091528154909190829060ff16600981111561195c5761195c613aa7565b600981111561196d5761196d613aa7565b815260200160018201805461198190613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546119ad90613dc2565b80156119fa5780601f106119cf576101008083540402835291602001916119fa565b820191906000526020600020905b8154815290600101906020018083116119dd57829003601f168201915b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015611a5257602002820191906000526020600020905b815481526020019060010190808311611a3e575b505050505081525050905060006009811115611a7057611a70613aa7565b81516009811115611a8357611a83613aa7565b14159392505050565b6000600182516009811115611aa357611aa3613aa7565b1480611ac15750600682516009811115611abf57611abf613aa7565b145b80611ade5750600782516009811115611adc57611adc613aa7565b145b15611aee57611aec826126c1565b505b600182516009811115611b0357611b03613aa7565b03611b11576101168261284a565b600282516009811115611b2657611b26613aa7565b03611b3457610116826128a5565b600382516009811115611b4957611b49613aa7565b03611b575761011682612973565b600482516009811115611b6c57611b6c613aa7565b03611b7a5761011682612aae565b600582516009811115611b8f57611b8f613aa7565b03611b9d5761011682612e92565b600982516009811115611bb257611bb2613aa7565b03611bc05761011682612fcb565b600682516009811115611bd557611bd5613aa7565b03611be3576101168261300e565b600782516009811115611bf857611bf8613aa7565b03611c065761011682613052565b600882516009811115611c1b57611c1b613aa7565b03611c295761011682613078565b506000919050565b600080611c3d836118ea565b9050611c4881610c26565b8351815491935090839060ff19166001836009811115611c6a57611c6a613aa7565b021790555060208301516001830190611c8390826146ed565b5060408301518051611c9f916002850191602090910190613a0c565b50915091565b611cad6139e4565b60005b8251811015611d07578160200151838281518110611cd057611cd0613e6a565b6020026020010151602001511115611cff57828181518110611cf457611cf4613e6a565b602002602001015191505b600101611cb0565b50919050565b611d156139e4565b81600081518110611d2857611d28613e6a565b602002602001015190506000600190505b8251811015611d07578160000151838281518110611d5957611d59613e6a565b6020026020010151600001511215611d8857828181518110611d7d57611d7d613e6a565b602002602001015191505b600101611d39565b611d986139e4565b81600081518110611dab57611dab613e6a565b602002602001015190506000600190505b8251811015611d07578160000151838281518110611ddc57611ddc613e6a565b6020026020010151600001511315611e0b57828181518110611e0057611e00613e6a565b602002602001015191505b600101611dbc565b611e1b6139e4565b60005b8251811015611e9557828181518110611e3957611e39613e6a565b60200260200101516000015182600001818151611e56919061456f565b9052508251839082908110611e6d57611e6d613e6a565b60200260200101516020015182602001818151611e8a91906147ad565b905250600101611e1e565b50611ea08251612569565b8151611eac9190614640565b815281516020820151611ebf91906147c0565b6020820152919050565b611ed16139e4565b611eed826000611ee86001865161123691906140a7565b6130a4565b60028251611efb91906147d4565b600003611fd65760408051600280825260608201909252600091816020015b611f226139e4565b815260200190600190039081611f1a57905050905082600160028551611f4891906147c0565b611f5291906140a7565b81518110611f6257611f62613e6a565b602002602001015181600081518110611f7d57611f7d613e6a565b60200260200101819052508260028451611f9791906147c0565b81518110611fa757611fa7613e6a565b602002602001015181600181518110611fc257611fc2613e6a565b60200260200101819052506102b481611e13565b8160028351611fe591906147c0565b81518110611ff557611ff5613e6a565b60200260200101519050919050565b919050565b6120116139e4565b8160008151811061202457612024613e6a565b60209081029190910101515181528151829060009061204557612045613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061207657612076613e6a565b6020026020010151600001518260000181815161209391906147e8565b90525082518390829081106120aa576120aa613e6a565b602002602001015160200151826020018181516120c791906147ad565b90525060010161205b565b5081518160200151611ebf91906147c0565b6120ec6139e4565b816000815181106120ff576120ff613e6a565b60209081029190910101515181528151829060009061212057612120613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061215157612151613e6a565b60200260200101516000015160000361219e5782818151811061217657612176613e6a565b6020026020010151600001516040516338ee04a760e01b815260040161039791815260200190565b8281815181106121b0576121b0613e6a565b602002602001015160000151826000018181516121cd9190614640565b90525082518390829081106121e4576121e4613e6a565b6020026020010151602001518260200181815161220191906147ad565b905250600101612136565b6122146139e4565b8160008151811061222757612227613e6a565b60209081029190910101515181528151829060009061224857612248613e6a565b6020908102919091018101518101519082015260015b82518110156120d25761229083828151811061227c5761227c613e6a565b602090810291909101015151835190613264565b825282518390829081106122a6576122a6613e6a565b602002602001015160200151826020018181516122c391906147ad565b90525060010161225e565b6122d66139e4565b816000815181106122e9576122e9613e6a565b60209081029190910101515181528151829060009061230a5761230a613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061233b5761233b613e6a565b6020026020010151600001516000036123605782818151811061217657612176613e6a565b61238983828151811061237557612375613e6a565b602090810291909101015151835190613283565b8252825183908290811061239f5761239f613e6a565b602002602001015160200151826020018181516123bc91906147ad565b905250600101612320565b6000826001826123d785426140a7565b90505b69ffffffffffffffffffff8716156124a3576001600160a01b038816639a6fc8f561240489614818565b6040516001600160e01b031960e084901b16815269ffffffffffffffffffff8216600482015290995060240160a060405180830381865afa925050508015612469575060408051601f3d908101601f1916820190925261246691810190614041565b60015b156124a357858210156124805750505050506124a3565b61248a848961456f565b97508661249681614834565b97505050505050506123da565b6124ac82612569565b6124b69084614640565b98975050505050505050565b60006124d261123683600a61484d565b6102b490846147e8565b60006124ec61123683600a61484d565b6102b49084614640565b6000667fffffffffffff66ffffffffffffff83161115612529576040516329d2678160e21b815260040160405180910390fd5b5090565b6000627fffff19600683900b128061254b5750627fffff600683900b135b1561252957604051630d962f7960e21b815260040160405180910390fd5b60006001600160ff1b038211156125295760405163677c430560e11b815260040160405180910390fd5b60008061259f86613298565b90506fffffffffffffffffffffffffffffffff6001600160a01b0382161161261c5760006125d66001600160a01b03831680614859565b9050836001600160a01b0316856001600160a01b03161061260557612600600160c01b87836136cd565b612614565b6126148187600160c01b6136cd565b92505061267d565b600061263b6001600160a01b03831680680100000000000000006136cd565b9050836001600160a01b0316856001600160a01b03161061266a57612665600160801b87836136cd565b612679565b6126798187600160801b6136cd565b9250505b50949350505050565b6000808212156125295760405163029f024d60e31b815260040160405180910390fd5b600080821215612529576126bc82614493565b610116565b6000805b8260400151518110156128415760006126fa846040015183815181106126ed576126ed613e6a565b6020026020010151610ab6565b60408051606081019091528154909190829060ff16600981111561272057612720613aa7565b600981111561273157612731613aa7565b815260200160018201805461274590613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461277190613dc2565b80156127be5780601f10612793576101008083540402835291602001916127be565b820191906000526020600020905b8154815290600101906020018083116127a157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561281657602002820191906000526020600020905b815481526020019060010190808311612802575b505050505081525050905061282a81611a8c565b612838575060009392505050565b506001016126c5565b50600192915050565b60006002826040015151101561286257506000919050565b81602001515160201461287757506000919050565b600082602001518060200190518101906128919190614627565b905060088111156128415750600092915050565b6000602082602001515110156128bd57506000919050565b600082602001518060200190518101906128d79190613ed3565b90506128ea816306e7ea3960e21b6138e2565b6128f75750600092915050565b604051633b70a5bf60e21b81526001600160a01b0382169063edc296fc90612923908690600401613b21565b6020604051808303816000875af1158015612942573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129669190614870565b6128415750600092915050565b6040810151516000901561298957506000919050565b81602001515160601461299e57506000919050565b60008083602001518060200190518101906129b99190613fe8565b92505091506000829050806001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015612a01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a259190614041565b5050505050806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8c919061488b565b60ff168260ff1614612aa357506000949350505050565b506001949350505050565b60408101515160009015612ac457506000919050565b81602001515160c014612ad957506000919050565b6000806000806000808760200151806020019051810190612afa91906140ba565b9550955095509550955095508360ff16866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6c919061488b565b60ff1614612b8257506000979650505050505050565b8260ff16856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612be8919061488b565b60ff1614612bfe57506000979650505050505050565b6000826001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c629190613ed3565b90506000836001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ca4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cc89190613ed3565b9050876001600160a01b0316826001600160a01b0316148015612cfc5750866001600160a01b0316816001600160a01b0316145b158015612d385750866001600160a01b0316826001600160a01b0316148015612d365750876001600160a01b0316816001600160a01b0316145b155b15612d4d575060009998505050505050505050565b60128660ff161180612d62575060128560ff16115b15612d77575060009998505050505050505050565b8263ffffffff16600003612d95575060009998505050505050505050565b6040805160028082526060820183526000926020830190803683370190505090508381600081518110612dca57612dca613e6a565b602002602001019063ffffffff16908163ffffffff1681525050600081600181518110612df957612df9613e6a565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526001600160a01b0386169063883bdbfd90612e3a908490600401614143565b600060405180830381865afa158015612e57573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612e7f91908101906141f5565b5060019c9b505050505050505050505050565b60408101515160009015612ea857506000919050565b816020015151606014612ebd57506000919050565b60008060008460200151806020019051810190612eda91906144bf565b919450925090508281612f55576040516396834ad360e01b8152600481018490526001600160a01b038216906396834ad390602401608060405180830381865afa158015612f2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f5091906144f5565b612fbe565b604051639474f45b60e01b8152600481018490526001600160a01b03821690639474f45b90602401608060405180830381865afa158015612f9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fbe91906144f5565b5060019695505050505050565b60408101515160009015612fe157506000919050565b816020015151606014612ff657506000919050565b8160200151806020019051810190612aa39190614597565b60008160400151516002148061302957508160400151516003145b61303557506000919050565b81602001515160201461304a57506000919050565b506001919050565b600081604001515160011480613029575081604001515160021461303557506000919050565b6040810151516000901561308e57506000919050565b6020826020015151101561304a57506000919050565b81818082036130b4575050505050565b6000856130da60026130c6888861446c565b6130d09190614640565b6112c0908861456f565b815181106130ea576130ea613e6a565b60200260200101516000015190505b818313613236575b808661310c85612686565b8151811061311c5761311c613e6a565b60200260200101516000015112156131405782613138816148a6565b935050613101565b8561314a83612686565b8151811061315a5761315a613e6a565b60200260200101516000015181121561317f5781613177816148be565b925050613140565b818313613231578561319083612686565b815181106131a0576131a0613e6a565b6020026020010151866131b285612686565b815181106131c2576131c2613e6a565b6020026020010151876131d486612686565b815181106131e4576131e4613e6a565b60200260200101886131f586612686565b8151811061320557613205613e6a565b602002602001018290528290525050828061321f906148a6565b935050818061322d906148be565b9250505b6130f9565b81851215613249576132498686846130a4565b8383121561325c5761325c8684866130a4565b505050505050565b6000670de0b6b3a764000061327983856147e8565b6102b49190614640565b600081613279670de0b6b3a7640000856147e8565b60008060008360020b126132b8576132b3600284900b612686565b6132c8565b6132c86112c0600285900b614493565b90506132e36112c06132dd620d89e7196148db565b60020b90565b8111156133165760405162461bcd60e51b81526020600482015260016024820152601560fa1b6044820152606401610397565b60008160011660000361332d57600160801b61333f565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff169050600282161561337e576080613379826ffff97272373d413259a46990580e213a614859565b901c90505b60048216156133a85760806133a3826ffff2e50f5f656932ef12357cf3c7fdcc614859565b901c90505b60088216156133d25760806133cd826fffe5caca7e10e4e61c3624eaa0941cd0614859565b901c90505b60108216156133fc5760806133f7826fffcb9843d60f6159c9db58835c926644614859565b901c90505b6020821615613426576080613421826fff973b41fa98c081472e6896dfb254c0614859565b901c90505b604082161561345057608061344b826fff2ea16466c96a3843ec78b326b52861614859565b901c90505b608082161561347a576080613475826ffe5dee046a99a2a811c461f1969c3053614859565b901c90505b6101008216156134a55760806134a0826ffcbe86c7900a88aedcffc83b479aa3a4614859565b901c90505b6102008216156134d05760806134cb826ff987a7253ac413176f2b074cf7815e54614859565b901c90505b6104008216156134fb5760806134f6826ff3392b0822b70005940c7a398e4b70f3614859565b901c90505b610800821615613526576080613521826fe7159475a2c29b7443b29c7fa6e889d9614859565b901c90505b61100082161561355157608061354c826fd097f3bdfd2022b8845ad8f792aa5825614859565b901c90505b61200082161561357c576080613577826fa9f746462d870fdf8a65dc1f90e061e5614859565b901c90505b6140008216156135a75760806135a2826f70d869a156d2a1b890bb3df62baf32f7614859565b901c90505b6180008216156135d25760806135cd826f31be135f97d08fd981231505542fcfa6614859565b901c90505b620100008216156135fe5760806135f9826f09aa508b5b7a84e1c677de54f3e99bc9614859565b901c90505b62020000821615613629576080613624826e5d6af8dedb81196699c329225ee604614859565b901c90505b6204000082161561365357608061364e826d2216e584f5fa1ea926041bedfe98614859565b901c90505b6208000082161561367b576080613676826b048a170391f7dc42444e8fa2614859565b901c90505b60008460020b131561369657613693816000196147c0565b90505b6102ce6136a8640100000000836147d4565b156136b45760016136b7565b60005b6136c89060ff16602084901c6147ad565b6139ba565b6000808060001985870985870292508281108382030391505080600003613749576000841161373e5760405162461bcd60e51b815260206004820152601960248201527f48616e646c65206e6f6e2d6f766572666c6f77206361736573000000000000006044820152606401610397565b5082900490506102b4565b8084116137985760405162461bcd60e51b815260206004820152601960248201527f70726576656e74732064656e6f6d696e61746f72203d3d2030000000000000006044820152606401610397565b60008486880980840393811190920391905060006137d06137b887612569565b6137c188612569565b6137ca90614493565b16612686565b9586900495938490049360008190030460010190506137ef8184614859565b909317926000613800876003614859565b600218905061380f8188614859565b61381a9060026140a7565b6138249082614859565b90506138308188614859565b61383b9060026140a7565b6138459082614859565b90506138518188614859565b61385c9060026140a7565b6138669082614859565b90506138728188614859565b61387d9060026140a7565b6138879082614859565b90506138938188614859565b61389e9060026140a7565b6138a89082614859565b90506138b48188614859565b6138bf9060026140a7565b6138c99082614859565b90506138d58186614859565b9998505050505050505050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b1790529051600091829182916001600160a01b0387169161394391906148fd565b6000604051808303816000865af19150503d8060008114613980576040519150601f19603f3d011682016040523d82523d6000602084013e613985565b606091505b50915091508161399a57600092505050610116565b80516000036139ae57600092505050610116565b60200151949350505050565b60006001600160a01b038211156125295760405163dccde8ed60e01b815260040160405180910390fd5b6040518060800160405280600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215613a47579160200282015b82811115613a47578251825591602001919060010190613a2c565b506125299291505b808211156125295760008155600101613a4f565b600060208284031215613a7557600080fd5b5035919050565b8151815260208083015190820152604080830151908201526060808301519082015260808101610116565b634e487b7160e01b600052602160045260246000fd5b600a8110613acd57613acd613aa7565b9052565b60005b83811015613aec578181015183820152602001613ad4565b50506000910152565b60008151808452613b0d816020860160208601613ad1565b601f01601f19169290920160200192915050565b60006020808352613b358184018551613abd565b8084015160606040850152613b4d6080850182613af5565b6040860151858203601f19016060870152805180835290840192506000918401905b80831015613b8f5783518252928401926001929092019190840190613b6f565b509695505050505050565b634e487b7160e01b600052604160045260246000fd5b6040516080810167ffffffffffffffff81118282101715613bd357613bd3613b9a565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715613c0257613c02613b9a565b604052919050565b600067ffffffffffffffff821115613c2457613c24613b9a565b5060051b60200190565b600082601f830112613c3f57600080fd5b81356020613c54613c4f83613c0a565b613bd9565b8083825260208201915060208460051b870101935086841115613c7657600080fd5b602086015b84811015613b8f5780358352918301918301613c7b565b600080600060608486031215613ca757600080fd5b8335600a8110613cb657600080fd5b925060208481013567ffffffffffffffff80821115613cd457600080fd5b818701915087601f830112613ce857600080fd5b813581811115613cfa57613cfa613b9a565b613d0c601f8201601f19168501613bd9565b8181528985838601011115613d2057600080fd5b818585018683013760009181019094015291935060408601359180831115613d4757600080fd5b5050613d5586828701613c2e565b9150509250925092565b600080600060608486031215613d7457600080fd5b83359250602084013567ffffffffffffffff80821115613d9357600080fd5b613d9f87838801613c2e565b93506040860135915080821115613db557600080fd5b50613d5586828701613c2e565b600181811c90821680613dd657607f821691505b602082108103611d0757634e487b7160e01b600052602260045260246000fd5b60008151808452602080850194506020840160005b83811015613e2757815187529582019590820190600101613e0b565b509495945050505050565b848152613e426020820185613abd565b608060408201526000613e586080830185613af5565b828103606084015261189a8185613df6565b634e487b7160e01b600052603260045260246000fd5b600060208284031215613e9257600080fd5b8151600981106102b457600080fd5b6020810160098310613eb557613eb5613aa7565b91905290565b6001600160a01b0381168114613ed057600080fd5b50565b600060208284031215613ee557600080fd5b81516102b481613ebb565b608080825285518282018190526000919060209060a0850190828a01855b82811015613f5257613f42848351805182526020810151602083015260408101516040830152606081015160608301525050565b9285019290840190600101613f0e565b5050508481036020860152613f678189613af5565b925050508281036040840152613f7d8186613df6565b9050828103606084015261189a8185613df6565b600060808284031215613fa357600080fd5b613fab613bb0565b825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b805160ff8116811461200457600080fd5b600080600060608486031215613ffd57600080fd5b835161400881613ebb565b6020850151909350915061401e60408501613fd7565b90509250925092565b805169ffffffffffffffffffff8116811461200457600080fd5b600080600080600060a0868803121561405957600080fd5b61406286614027565b945060208601519350604086015192506060860151915061408560808701614027565b90509295509295909350565b634e487b7160e01b600052601160045260246000fd5b8181038181111561011657610116614091565b60008060008060008060c087890312156140d357600080fd5b86516140de81613ebb565b60208801519096506140ef81613ebb565b94506140fd60408801613fd7565b935061410b60608801613fd7565b9250608087015161411b81613ebb565b60a088015190925063ffffffff8116811461413557600080fd5b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b8181101561418157835163ffffffff168352928401929184019160010161415f565b50909695505050505050565b600082601f83011261419e57600080fd5b815160206141ae613c4f83613c0a565b8083825260208201915060208460051b8701019350868411156141d057600080fd5b602086015b84811015613b8f5780516141e881613ebb565b83529183019183016141d5565b6000806040838503121561420857600080fd5b825167ffffffffffffffff8082111561422057600080fd5b818501915085601f83011261423457600080fd5b81516020614244613c4f83613c0a565b82815260059290921b8401810191818101908984111561426357600080fd5b948201945b838610156142915785518060060b81146142825760008081fd5b82529482019490820190614268565b918801519196509093505050808211156142aa57600080fd5b506142b78582860161418d565b9150509250929050565b600682810b9082900b03667fffffffffffff198112667fffffffffffff8213171561011657610116614091565b634e487b7160e01b600052601260045260246000fd5b60008160060b8360060b8061431b5761431b6142ee565b667fffffffffffff1982146000198214161561433957614339614091565b90059392505050565b600082614351576143516142ee565b500790565b60008160020b627fffff19810361436f5761436f614091565b6000190192915050565b600181815b808511156143b457816000190482111561439a5761439a614091565b808516156143a757918102915b93841c939080029061437e565b509250929050565b6000826143cb57506001610116565b816143d857506000610116565b81600181146143ee57600281146143f857614414565b6001915050610116565b60ff84111561440957614409614091565b50506001821b610116565b5060208310610133831016604e8410600b8410161715614437575081810a610116565b6144418383614379565b806000190482111561445557614455614091565b029392505050565b60006102b460ff8416836143bc565b818103600083128015838313168383128216171561448c5761448c614091565b5092915050565b6000600160ff1b82016144a8576144a8614091565b5060000390565b8051801515811461200457600080fd5b6000806000606084860312156144d457600080fd5b83516144df81613ebb565b6020850151909350915061401e604085016144af565b60006080828403121561450757600080fd5b61450f613bb0565b82518060070b811461452057600080fd5b8152602083015167ffffffffffffffff8116811461453d57600080fd5b60208201526040830151600381900b811461455757600080fd5b60408201526060928301519281019290925250919050565b808201828112600083128015821682158216171561458f5761458f614091565b505092915050565b6000806000606084860312156145ac57600080fd5b83516145b781613ebb565b602085015160409095015190969495509392505050565b60ff8416815267ffffffffffffffff831660208201526060604082015260006145fa6060830184613df6565b95945050505050565b6001600160a01b03831681526040602082018190526000906102ce90830184613af5565b60006020828403121561463957600080fd5b5051919050565b60008261464f5761464f6142ee565b600160ff1b82146000198414161561466957614669614091565b500590565b6146788185613abd565b60606020820152600061468e6060830185613af5565b8281036040840152610f4f8185613df6565b601f8211156146e8576000816000526020600020601f850160051c810160208610156146c95750805b601f850160051c820191505b8181101561325c578281556001016146d5565b505050565b815167ffffffffffffffff81111561470757614707613b9a565b61471b816147158454613dc2565b846146a0565b602080601f83116001811461475057600084156147385750858301515b600019600386901b1c1916600185901b17855561325c565b600085815260208120601f198616915b8281101561477f57888601518255948401946001909101908401614760565b508582101561479d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b8082018082111561011657610116614091565b6000826147cf576147cf6142ee565b500490565b6000826147e3576147e36142ee565b500690565b80820260008212600160ff1b8414161561480457614804614091565b818105831482151761011657610116614091565b600069ffffffffffffffffffff82168061436f5761436f614091565b60006001820161484657614846614091565b5060010190565b60006102b483836143bc565b808202811582820484141761011657610116614091565b60006020828403121561488257600080fd5b6102b4826144af565b60006020828403121561489d57600080fd5b6102b482613fd7565b60006001600160ff1b01820161484657614846614091565b6000600160ff1b82016148d3576148d3614091565b506000190190565b60008160020b627fffff1981036148f4576148f4614091565b60000392915050565b6000825161490f818460208701613ad1565b919091019291505056fea264697066735822122074f32fef384fdc296b0859f1c1f941c8e736c6cb972aa9e2b894956ebd6a80b364736f6c63430008160033","r":"0x1","s":"0x1","yParity":"0x0","hash":"0xbc73db80bf4b8784ba10a8910a0b7ef85f6846d102b41dd990969ea205335354"}}],"ommers":[]},{"header":{"parentHash":"0x026ae0c6ae91f186a9befa1ac8be30eea35e30e77de51a731085221e5cd39209","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0xb6003e7ba07a15a9e35f63daa484728ec4ceeded0c4d10ac1b04e9552d412b3c","transactionsRoot":"0x6e4969a136061ca7a390d12830d47a151585325a8d396819fb2b958ff85e9f8f","receiptsRoot":"0xc3e81df67d3e2a6c8345a954ef250cfcc41abcc2292a5aa263071124533fc9ad","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x3","gasLimit":"0x1c9c380","gasUsed":"0x3c0f6","timestamp":"0x66b200ce","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x18993a68","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gas":"0x3c0f6","maxFeePerGas":"0x5d4285cd","maxPriorityFeePerGas":"0x3b9aca00","value":"0x0","accessList":[],"input":"0x608060405234801561001057600080fd5b50610380806100206000396000f3fe6080604052600080357fffffffff0000000000000000000000000000000000000000000000000000000016905060008160e01c610251565b60006379ba509782101561015e5781631627540c811461009857632a952b2d81146100b457633659cfe681146100d0576350c946fe81146100ec576353a47bb781146101085763625ca21c81146101245763718fe928811461014057610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc91505b5061024c565b816379ba509781146101a657638da5cb5b81146101c25763aaf10f4281146101de5763c7f62cda81146101fa5763daa250be81146102165763deba1b9881146102325761024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b738138ef7cf908021d117e542120b7a39065016107915061024a565b738138ef7cf908021d117e542120b7a3906501610791505b505b919050565b61025a81610037565b915050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036102ce57816040517fc2a825f50000000000000000000000000000000000000000000000000000000081526004016102c5919061032f565b60405180910390fd5b3660008037600080366000845af43d6000803e80600081146102ef573d6000f35b3d6000fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b610329816102f4565b82525050565b60006020820190506103446000830184610320565b9291505056fea264697066735822122017a4b7fdaaab3897a7b47abaed8d2ee92d558883d3bb2a8454f9601b2ab2c3db64736f6c63430008150033","r":"0x1","s":"0x1","yParity":"0x0","hash":"0x2476e039803622aeb040f924f04c493f559aed3d6c9372ab405cb33c8c695328"}}],"ommers":[]},{"header":{"parentHash":"0x3d22100ac0ee8d5cde334f7f926191a861b0648971ebc179547df28a0224c6d0","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x9511d4711e5c30a72b0bff38a261daa75dcc5ba8b772d970a5c742244b4c861b","transactionsRoot":"0xba5fff578d3d6c2cd63acbe9bca353eaa6fe22a5c408956eff49106e0a96c507","receiptsRoot":"0xbae111f01cb07677e3a8c5031546138407c01bc964d3493d732dc4edf47d36d3","logsBloom":"0x00000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000020000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000001000000000000000000000400000001000010000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x5","gasLimit":"0x1c9c380","gasUsed":"0xcae7","timestamp":"0x66b200cb","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x12e09c7a","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gas":"0xcc4d","maxFeePerGas":"0x557e5ec4","maxPriorityFeePerGas":"0x3b9aca00","to":"0x83a0444b93927c3afcbe46e522280390f748e171","value":"0x0","accessList":[],"input":"0x3659cfe6000000000000000000000000108f53faf774d7c4c56f5bce9ca6e605ce8aeadd","r":"0x1","s":"0x1","yParity":"0x0","hash":"0xf88e7b19ee347145c257e0cf7ac4ecc2bae83ca79d7edaa231e71d3213aeb151"}}],"ommers":[]},{"header":{"parentHash":"0x08abe6e453727534d8dd708843a7522b7d500338bdfe2402ca105dcdb05eebe9","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x9c8eaf493f8b4edce2ba1647343eadcc0989cf461e712c0a6253ff2ca1842bb7","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200ca","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0xdd07c07470e1deff3749831f0f1ad8d4b6e35505e83b3c6ea14181716197cd8a","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x29aa352e71b139e83b397bdd3dcf9b65d74770edaf3a9624d0dbc4f96f868680","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200cb","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x24a1ab52","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x0","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200c9","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0xf6930be4847cac5017bbcbec2756eed19f36b4196526a98a88e311c296e3a9be","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x29aa352e71b139e83b397bdd3dcf9b65d74770edaf3a9624d0dbc4f96f868680","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200cc","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x200d75e8","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x08abe6e453727534d8dd708843a7522b7d500338bdfe2402ca105dcdb05eebe9","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0xb6003e7ba07a15a9e35f63daa484728ec4ceeded0c4d10ac1b04e9552d412b3c","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x4","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200ca","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x1592fbf9","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x149d41e3b89d8324cef3feff98ef308e97bafe8745cc8461c60172bc7d4c44ba","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x510f2275449c013534a25ad0b13c867caf720947b68bcbcd4863f7b172a5d023","transactionsRoot":"0x0b44110186e52ff0ceb6b0776ca2992c94144a4ed712eef65ea038260ef0fcc7","receiptsRoot":"0xc2823b8eb4730d9f2657137cc2ddc2c4f22ab68e0ab826236cf6a1551ca2b3a5","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0xe61f9","timestamp":"0x66b200cb","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x342770c0","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gas":"0xe94d1","maxFeePerGas":"0x83215600","maxPriorityFeePerGas":"0x3b9aca00","to":"0x4e59b44847b379578588920ca78fbf26c0b4956c","value":"0x0","accessList":[],"input":"0x4786e4342646b3ba97c1790b6cf5a55087a36240b22570f5d3a5d6bcc929d93b608060405234801561001057600080fd5b5060008061002661006d60201b61081b1760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610141565b60008060405160200161007f90610121565b6040516020818303038152906040528051906020012090508091505090565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b600061010b60238361009e565b9150610116826100af565b604082019050919050565b6000602082019050818103600083015261013a816100fe565b9050919050565b611000806101506000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806379ba50971161005b57806379ba5097146100ed5780638da5cb5b146100f7578063aaf10f4214610115578063c7f62cda1461013357610088565b80631627540c1461008d5780633659cfe6146100a957806353a47bb7146100c5578063718fe928146100e3575b600080fd5b6100a760048036038101906100a29190610d25565b61014f565b005b6100c360048036038101906100be9190610d25565b6102d0565b005b6100cd6102e4565b6040516100da9190610d61565b60405180910390f35b6100eb610317565b005b6100f56103fe565b005b6100ff61058b565b60405161010c9190610d61565b60405180910390f35b61011d6105be565b60405161012a9190610d61565b60405180910390f35b61014d60048036038101906101489190610d25565b6105f1565b005b61015761084c565b600061016161081b565b9050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036101c9576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610252576040517fa88ee57700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22826040516102c49190610d61565b60405180910390a15050565b6102d861084c565b6102e1816108c5565b50565b60006102ee61081b565b60010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600061032161081b565b90503373ffffffffffffffffffffffffffffffffffffffff168160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146103b757336040517fa0e5a0d70000000000000000000000000000000000000000000000000000000081526004016103ae9190610d61565b60405180910390fd5b60008160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600061040861081b565b905060008160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104a357336040517fa0e5a0d700000000000000000000000000000000000000000000000000000000815260040161049a9190610d61565b60405180910390fd5b7fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c8260000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16826040516104f8929190610d7c565b60405180910390a1808260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008260010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b600061059561081b565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006105c8610b05565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006105fb610b05565b905060018160000160146101000a81548160ff02191690831515021790555060008160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050828260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008373ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff16633659cfe6846040516024016106cc9190610d61565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161071b9190610e16565b600060405180830381855af49150503d8060008114610756576040519150601f19603f3d011682016040523d82523d6000602084013e61075b565b606091505b505090508015806107c357508173ffffffffffffffffffffffffffffffffffffffff16610786610b05565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b156107fa576040517fa1cfa5a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008360000160146101000a81548160ff0219169083151502179055600080fd5b60008060405160200161082d90610eb0565b6040516020818303038152906040528051906020012090508091505090565b610854610b36565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146108c357336040517f8e4a23d60000000000000000000000000000000000000000000000000000000081526004016108ba9190610d61565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361092b576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61093481610b69565b61097557806040517f8a8b41ec00000000000000000000000000000000000000000000000000000000815260040161096c9190610d61565b60405180910390fd5b600061097f610b05565b90508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610a0a576040517fa88ee57700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060000160149054906101000a900460ff16158015610a2e5750610a2d82610b7c565b5b15610a7057816040517f15504301000000000000000000000000000000000000000000000000000000008152600401610a679190610d61565b60405180910390fd5b818160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503073ffffffffffffffffffffffffffffffffffffffff167f5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c783604051610af99190610d61565b60405180910390a25050565b600080604051602001610b1790610f42565b6040516020818303038152906040528051906020012090508091505090565b6000610b4061081b565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080823b905060008111915050919050565b60008060003073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1663c7f62cda86604051602401610bc59190610d61565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610c149190610e16565b600060405180830381855af49150503d8060008114610c4f576040519150601f19603f3d011682016040523d82523d6000602084013e610c54565b606091505b509150915081158015610cb9575063a1cfa5a860e01b604051602001610c7a9190610faf565b6040516020818303038152906040528051906020012081604051602001610ca19190610e16565b60405160208183030381529060405280519060200120145b92505050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cf282610cc7565b9050919050565b610d0281610ce7565b8114610d0d57600080fd5b50565b600081359050610d1f81610cf9565b92915050565b600060208284031215610d3b57610d3a610cc2565b5b6000610d4984828501610d10565b91505092915050565b610d5b81610ce7565b82525050565b6000602082019050610d766000830184610d52565b92915050565b6000604082019050610d916000830185610d52565b610d9e6020830184610d52565b9392505050565b600081519050919050565b600081905092915050565b60005b83811015610dd9578082015181840152602081019050610dbe565b60008484015250505050565b6000610df082610da5565b610dfa8185610db0565b9350610e0a818560208601610dbb565b80840191505092915050565b6000610e228284610de5565b915081905092915050565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b6000610e9a602383610e2d565b9150610ea582610e3e565b604082019050919050565b60006020820190508181036000830152610ec981610e8d565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b6000610f2c602183610e2d565b9150610f3782610ed0565b604082019050919050565b60006020820190508181036000830152610f5b81610f1f565b9050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6000819050919050565b610fa9610fa482610f62565b610f8e565b82525050565b6000610fbb8284610f98565b6004820191508190509291505056fea264697066735822122023a7c33d7b91dce35ffbcf8837693364ab22a3905d0fc00016833e5fac45ca2f64736f6c63430008110033","r":"0x1","s":"0x1","yParity":"0x0","hash":"0x4feae6769d748b4f0f7c9bf21d782236c88f13906789a3ec602961296e4c3e43"}}],"ommers":[]},{"header":{"parentHash":"0xb3535af5103fd1c2bbd6dc7ff23f0799037a6542c231ebcb85abd776560fa512","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x23d74fb99ff6e42cbb5c33f92b078e37be6af2b6092459b103ff7059a6517ebc","transactionsRoot":"0x9eab45eca206fe11c107ea985c7d02fcfa442836aea3e04ba11dc4df587d5aa6","receiptsRoot":"0xe25abcfa973db8c55f73292137c626430de130a382ad4466337fefb0f7c8fde0","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x3ce3f","timestamp":"0x66b200cd","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x1c0bc72b","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gas":"0x3d8a8","maxFeePerGas":"0x6211577c","maxPriorityFeePerGas":"0x3b9aca00","to":"0x4e59b44847b379578588920ca78fbf26c0b4956c","value":"0x0","accessList":[],"input":"0x4786e4342646b3ba97c1790b6cf5a55087a36240b22570f5d3a5d6bcc929d93b608060405234801561001057600080fd5b5060405161068538038061068583398181016040528101906100329190610275565b818181600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361009b576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6100ae8161019d60201b61004f1760201c565b6100ef57806040517f8a8b41ec0000000000000000000000000000000000000000000000000000000081526004016100e691906102c4565b60405180910390fd5b806100fe6101b060201b60201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050806101536101e160201b6100621760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505050610414565b600080823b905060008111915050919050565b6000806040516020016101c290610362565b6040516020818303038152906040528051906020012090508091505090565b6000806040516020016101f3906103f4565b6040516020818303038152906040528051906020012090508091505090565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061024282610217565b9050919050565b61025281610237565b811461025d57600080fd5b50565b60008151905061026f81610249565b92915050565b6000806040838503121561028c5761028b610212565b5b600061029a85828601610260565b92505060206102ab85828601610260565b9150509250929050565b6102be81610237565b82525050565b60006020820190506102d960008301846102b5565b92915050565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b600061034c6021836102df565b9150610357826102f0565b604082019050919050565b6000602082019050818103600083015261037b8161033f565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b60006103de6023836102df565b91506103e982610382565b604082019050919050565b6000602082019050818103600083015261040d816103d1565b9050919050565b610262806104236000396000f3fe6080604052366100135761001161001d565b005b61001b61001d565b005b6000610027610093565b90503660008037600080366000845af43d6000803e806000811461004a573d6000f35b3d6000fd5b600080823b905060008111915050919050565b6000806040516020016100749061017a565b6040516020818303038152906040528051906020012090508091505090565b600061009d6100c6565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000806040516020016100d89061020c565b6040516020818303038152906040528051906020012090508091505090565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b60006101646023836100f7565b915061016f82610108565b604082019050919050565b6000602082019050818103600083015261019381610157565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b60006101f66021836100f7565b91506102018261019a565b604082019050919050565b60006020820190508181036000830152610225816101e9565b905091905056fea2646970667358221220800da1f73cebd5e4afa07496d9bca6b6c4f526bdd3f4014ec15c70fe3a1c441364736f6c6343000811003300000000000000000000000047d08dad17ccb558b3ea74b1a0e73a9cc804a9dc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266","r":"0x1","s":"0x1","yParity":"0x0","hash":"0xb6794d5c7abed6f91d447e8efb72ef2580595a6d7c8dee57ba1dbb330970146a"}}],"ommers":[]},{"header":{"parentHash":"0x08abe6e453727534d8dd708843a7522b7d500338bdfe2402ca105dcdb05eebe9","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x510f2275449c013534a25ad0b13c867caf720947b68bcbcd4863f7b172a5d023","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x3","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200ca","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x29dd5614","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]}]} \ No newline at end of file +{"block":{"number":"0x5","coinbase":"0x0000000000000000000000000000000000000000","timestamp":"0x66b200cb","gas_limit":"0x1c9c380","basefee":"0x12e09c7a","difficulty":"0x0","prevrandao":"0xe7ef87fc7c2090741a6749a087e4ca8092cb4d07136008799e4ebeac3b69e34a","blob_excess_gas_and_price":{"excess_blob_gas":0,"blob_gasprice":1}},"accounts":{"0x0000000000000000000000000000000000000000":{"nonce":0,"balance":"0x1088aa62285a00","code":"0x","storage":{}},"0x108f53faf774d7c4c56f5bce9ca6e605ce8aeadd":{"nonce":1,"balance":"0x0","code":"0x6080604052600080357fffffffff0000000000000000000000000000000000000000000000000000000016905060008160e01c610251565b60006379ba509782101561015e5781631627540c811461009857632a952b2d81146100b457633659cfe681146100d0576350c946fe81146100ec576353a47bb781146101085763625ca21c81146101245763718fe928811461014057610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc91505b5061024c565b816379ba509781146101a657638da5cb5b81146101c25763aaf10f4281146101de5763c7f62cda81146101fa5763daa250be81146102165763deba1b9881146102325761024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b738138ef7cf908021d117e542120b7a39065016107915061024a565b738138ef7cf908021d117e542120b7a3906501610791505b505b919050565b61025a81610037565b915050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036102ce57816040517fc2a825f50000000000000000000000000000000000000000000000000000000081526004016102c5919061032f565b60405180910390fd5b3660008037600080366000845af43d6000803e80600081146102ef573d6000f35b3d6000fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b610329816102f4565b82525050565b60006020820190506103446000830184610320565b9291505056fea264697066735822122017a4b7fdaaab3897a7b47abaed8d2ee92d558883d3bb2a8454f9601b2ab2c3db64736f6c63430008150033","storage":{}},"0x14dc79964da2c08b23698b3d3cc7ca32193d9955":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x19ba1fac55eea44d12a01372a8eb0c2ebbf9ca21":{"nonce":1,"balance":"0x21e19df7c2963f0ac6b","code":"0x","storage":{}},"0x19c6ab860dbe2bc433574193a4409770a8748bf6":{"nonce":1,"balance":"0x21e19df8da6b7bdc410","code":"0x","storage":{}},"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x40567ec443c1d1872af5155755ac3803cc3fe61e":{"nonce":1,"balance":"0x21e19da82562f921b40","code":"0x","storage":{}},"0x47d08dad17ccb558b3ea74b1a0e73a9cc804a9dc":{"nonce":1,"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100885760003560e01c806379ba50971161005b57806379ba5097146100ed5780638da5cb5b146100f7578063aaf10f4214610115578063c7f62cda1461013357610088565b80631627540c1461008d5780633659cfe6146100a957806353a47bb7146100c5578063718fe928146100e3575b600080fd5b6100a760048036038101906100a29190610d25565b61014f565b005b6100c360048036038101906100be9190610d25565b6102d0565b005b6100cd6102e4565b6040516100da9190610d61565b60405180910390f35b6100eb610317565b005b6100f56103fe565b005b6100ff61058b565b60405161010c9190610d61565b60405180910390f35b61011d6105be565b60405161012a9190610d61565b60405180910390f35b61014d60048036038101906101489190610d25565b6105f1565b005b61015761084c565b600061016161081b565b9050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036101c9576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610252576040517fa88ee57700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22826040516102c49190610d61565b60405180910390a15050565b6102d861084c565b6102e1816108c5565b50565b60006102ee61081b565b60010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600061032161081b565b90503373ffffffffffffffffffffffffffffffffffffffff168160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146103b757336040517fa0e5a0d70000000000000000000000000000000000000000000000000000000081526004016103ae9190610d61565b60405180910390fd5b60008160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600061040861081b565b905060008160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104a357336040517fa0e5a0d700000000000000000000000000000000000000000000000000000000815260040161049a9190610d61565b60405180910390fd5b7fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c8260000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16826040516104f8929190610d7c565b60405180910390a1808260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008260010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b600061059561081b565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006105c8610b05565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006105fb610b05565b905060018160000160146101000a81548160ff02191690831515021790555060008160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050828260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008373ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff16633659cfe6846040516024016106cc9190610d61565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161071b9190610e16565b600060405180830381855af49150503d8060008114610756576040519150601f19603f3d011682016040523d82523d6000602084013e61075b565b606091505b505090508015806107c357508173ffffffffffffffffffffffffffffffffffffffff16610786610b05565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b156107fa576040517fa1cfa5a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008360000160146101000a81548160ff0219169083151502179055600080fd5b60008060405160200161082d90610eb0565b6040516020818303038152906040528051906020012090508091505090565b610854610b36565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146108c357336040517f8e4a23d60000000000000000000000000000000000000000000000000000000081526004016108ba9190610d61565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361092b576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61093481610b69565b61097557806040517f8a8b41ec00000000000000000000000000000000000000000000000000000000815260040161096c9190610d61565b60405180910390fd5b600061097f610b05565b90508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610a0a576040517fa88ee57700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060000160149054906101000a900460ff16158015610a2e5750610a2d82610b7c565b5b15610a7057816040517f15504301000000000000000000000000000000000000000000000000000000008152600401610a679190610d61565b60405180910390fd5b818160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503073ffffffffffffffffffffffffffffffffffffffff167f5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c783604051610af99190610d61565b60405180910390a25050565b600080604051602001610b1790610f42565b6040516020818303038152906040528051906020012090508091505090565b6000610b4061081b565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080823b905060008111915050919050565b60008060003073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1663c7f62cda86604051602401610bc59190610d61565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610c149190610e16565b600060405180830381855af49150503d8060008114610c4f576040519150601f19603f3d011682016040523d82523d6000602084013e610c54565b606091505b509150915081158015610cb9575063a1cfa5a860e01b604051602001610c7a9190610faf565b6040516020818303038152906040528051906020012081604051602001610ca19190610e16565b60405160208183030381529060405280519060200120145b92505050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cf282610cc7565b9050919050565b610d0281610ce7565b8114610d0d57600080fd5b50565b600081359050610d1f81610cf9565b92915050565b600060208284031215610d3b57610d3a610cc2565b5b6000610d4984828501610d10565b91505092915050565b610d5b81610ce7565b82525050565b6000602082019050610d766000830184610d52565b92915050565b6000604082019050610d916000830185610d52565b610d9e6020830184610d52565b9392505050565b600081519050919050565b600081905092915050565b60005b83811015610dd9578082015181840152602081019050610dbe565b60008484015250505050565b6000610df082610da5565b610dfa8185610db0565b9350610e0a818560208601610dbb565b80840191505092915050565b6000610e228284610de5565b915081905092915050565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b6000610e9a602383610e2d565b9150610ea582610e3e565b604082019050919050565b60006020820190508181036000830152610ec981610e8d565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b6000610f2c602183610e2d565b9150610f3782610ed0565b604082019050919050565b60006020820190508181036000830152610f5b81610f1f565b9050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6000819050919050565b610fa9610fa482610f62565b610f8e565b82525050565b6000610fbb8284610f98565b6004820191508190509291505056fea264697066735822122023a7c33d7b91dce35ffbcf8837693364ab22a3905d0fc00016833e5fac45ca2f64736f6c63430008110033","storage":{"0x5c7865864a2a990d80b5bb5c40e7b73a029960dc711fbb56120dfab976e92ea3":"0x0"}},"0x4e59b44847b379578588920ca78fbf26c0b4956c":{"nonce":2,"balance":"0x0","code":"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3","storage":{}},"0x70997970c51812dc3a010c7d01b50e0d17dc79c8":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x8138ef7cf908021d117e542120b7a39065016107":{"nonce":1,"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100575760003560e01c80632a952b2d1461005c57806350c946fe14610085578063625ca21c146100a5578063daa250be146100c6578063deba1b98146100d9575b600080fd5b61006f61006a366004613a63565b6100ec565b60405161007c9190613a7c565b60405180910390f35b610098610093366004613a63565b61011c565b60405161007c9190613b21565b6100b86100b3366004613c92565b610276565b60405190815260200161007c565b61006f6100d4366004613d5f565b6102bb565b6100b86100e7366004613c92565b6102d6565b6100f46139e4565b6040805160008082526020820190815281830190925261011691849190610310565b92915050565b6101416040805160608101909152806000815260200160608152602001606081525090565b61014a82610ab6565b60408051606081019091528154909190829060ff16600981111561017057610170613aa7565b600981111561018157610181613aa7565b815260200160018201805461019590613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546101c190613dc2565b801561020e5780601f106101e35761010080835404028352916020019161020e565b820191906000526020600020905b8154815290600101906020018083116101f157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561026657602002820191906000526020600020905b815481526020019060010190808311610252575b5050505050815250509050919050565b600080604051806060016040528086600981111561029657610296613aa7565b81526020018581526020018481525090506102b081610ac1565b9150505b9392505050565b6102c36139e4565b6102ce848484610310565b949350505050565b60008060405180606001604052808660098111156102f6576102f6613aa7565b81526020018581526020018481525090506102b081610acc565b6103186139e4565b81518351146103a05760408051634bab873760e11b81526004810191909152600d60448201526c72756e74696d6556616c75657360981b606482015260806024820152602260848201527f6d7573742062652073616d65206c656e6774682061732072756e74696d654b6560a482015261797360f01b60c482015260e4015b60405180910390fd5b60006103ab85610c26565b805490915060ff1660018160098111156103c7576103c7613aa7565b036104755761046c6103da838787610c84565b8360010180546103e990613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461041590613dc2565b80156104625780601f1061043757610100808354040283529160200191610462565b820191906000526020600020905b81548152906001019060200180831161044557829003601f168201915b5050505050610d46565b925050506102b4565b600281600981111561048957610489613aa7565b036105305761046c61049c838787610c84565b8360010180546104ab90613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546104d790613dc2565b80156105245780601f106104f957610100808354040283529160200191610524565b820191906000526020600020905b81548152906001019060200180831161050757829003601f168201915b50505050508787610ebb565b600381600981111561054457610544613aa7565b036105de5761046c82600101805461055b90613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461058790613dc2565b80156105d45780601f106105a9576101008083540402835291602001916105d4565b820191906000526020600020905b8154815290600101906020018083116105b757829003601f168201915b5050505050610f59565b60048160098111156105f2576105f2613aa7565b0361068c5761046c82600101805461060990613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461063590613dc2565b80156106825780601f1061065757610100808354040283529160200191610682565b820191906000526020600020905b81548152906001019060200180831161066557829003601f168201915b5050505050611087565b60058160098111156106a0576106a0613aa7565b0361073a5761046c8260010180546106b790613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546106e390613dc2565b80156107305780601f1061070557610100808354040283529160200191610730565b820191906000526020600020905b81548152906001019060200180831161071357829003601f168201915b505050505061131e565b600981600981111561074e5761074e613aa7565b036107ea5761046c82600101805461076590613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461079190613dc2565b80156107de5780601f106107b3576101008083540402835291602001916107de565b820191906000526020600020905b8154815290600101906020018083116107c157829003601f168201915b505050505086866114b5565b60068160098111156107fe576107fe613aa7565b036108a35761046c610811838787610c84565b83600101805461082090613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461084c90613dc2565b80156108995780601f1061086e57610100808354040283529160200191610899565b820191906000526020600020905b81548152906001019060200180831161087c57829003601f168201915b50505050506115c7565b60078160098111156108b7576108b7613aa7565b036109ec576040805160608101909152825461046c91908490829060ff1660098111156108e6576108e6613aa7565b60098111156108f7576108f7613aa7565b815260200160018201805461090b90613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461093790613dc2565b80156109845780601f1061095957610100808354040283529160200191610984565b820191906000526020600020905b81548152906001019060200180831161096757829003601f168201915b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156109dc57602002820191906000526020600020905b8154815260200190600101908083116109c8575b5050505050815250508686611728565b6008816009811115610a0057610a00613aa7565b03610a9a5761046c826001018054610a1790613dc2565b80601f0160208091040260200160405190810160405280929190818152602001828054610a4390613dc2565b8015610a905780601f10610a6557610100808354040283529160200191610a90565b820191906000526020600020905b815481529060010190602001808311610a7357829003601f168201915b50505050506118a5565b6040516323a9bbc960e01b815260048101879052602401610397565b600061011682610c26565b6000610116826118ea565b6000610ad782610ac1565b9050610ae28161192a565b15610b35577fcb64985827770858ec421ad26da7e558c757541643036ce44d6b4eb9e8e5dc5e81836000015184602001518560400151604051610b289493929190613e32565b60405180910390a1919050565b610b3e82611a8c565b610b5d578160405163382bbbc960e11b81526004016103979190613b21565b60005b826040015151811015610bd957610b9383604001518281518110610b8657610b86613e6a565b602002602001015161192a565b610bd15782604001518181518110610bad57610bad613e6a565b6020026020010151604051632f19f96160e11b815260040161039791815260200190565b600101610b60565b50610be382611c31565b8351602085015160408087015190519395507fcb64985827770858ec421ad26da7e558c757541643036ce44d6b4eb9e8e5dc5e9450610b28938693929190613e32565b604080516020808201839052606082018190527f696f2e73796e7468657469782e6f7261636c652d6d616e616765722e4e6f6465608080840191909152828401949094528251808303909401845260a0909101909152815191012090565b600283015460609067ffffffffffffffff811115610ca457610ca4613b9a565b604051908082528060200260200182016040528015610cdd57816020015b610cca6139e4565b815260200190600190039081610cc25790505b50905060005b6002850154811015610d3e57610d19856002018281548110610d0757610d07613e6a565b90600052602060002001548585610310565b828281518110610d2b57610d2b613e6a565b6020908102919091010152600101610ce3565b509392505050565b610d4e6139e4565b600082806020019051810190610d649190613e80565b90506000816008811115610d7a57610d7a613aa7565b03610d9057610d8884611ca5565b915050610116565b6001816008811115610da457610da4613aa7565b03610db257610d8884611d0d565b6002816008811115610dc657610dc6613aa7565b03610dd457610d8884611d90565b6003816008811115610de857610de8613aa7565b03610df657610d8884611e13565b6004816008811115610e0a57610e0a613aa7565b03610e1857610d8884611ec9565b6005816008811115610e2c57610e2c613aa7565b03610e3a57610d8884612009565b6006816008811115610e4e57610e4e613aa7565b03610e5c57610d88846120e4565b6007816008811115610e7057610e70613aa7565b03610e7e57610d888461220c565b6008816008811115610e9257610e92613aa7565b03610ea057610d88846122ce565b80604051631be413d360e11b81526004016103979190613ea1565b610ec36139e4565b600084806020019051810190610ed99190613ed3565b604051631ecba7c360e31b81529091506001600160a01b0382169063f65d3e1890610f0e908990899089908990600401613ef0565b608060405180830381865afa158015610f2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4f9190613f91565b9695505050505050565b610f616139e4565b600080600084806020019051810190610f7a9190613fe8565b92509250925060008390506000806000836001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610fc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fec9190614041565b509350509250925060008660001461100f5761100a8585858a6123c7565b611011565b825b905060128660ff161161103b5761103661102f60ff881660126140a7565b82906124c2565b611053565b61105361104c601260ff89166140a7565b82906124dc565b9050604051806080016040528082815260200183815260200160008152602001600081525098505050505050505050919050565b61108f6139e4565b600080600080600080878060200190518101906110ac91906140ba565b604080516002808252606082018352979d50959b50939950919750955093506000929060208301908036833701905050905081816000815181106110f2576110f2613e6a565b602002602001019063ffffffff16908163ffffffff168152505060008160018151811061112157611121613e6a565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526000906001600160a01b0385169063883bdbfd90611165908590600401614143565b600060405180830381865afa158015611182573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526111aa91908101906141f5565b5090506000816000815181106111c2576111c2613e6a565b6020026020010151826001815181106111dd576111dd613e6a565b60200260200101516111ef91906142c1565b9050600061121761120563ffffffff87166124f6565b61120f9084614304565b60060b61252d565b905060008260060b12801561124c575061123b63ffffffff8616612569565b612569565b8260060b6112499190614342565b15155b1561125f578061125b81614356565b9150505b600061126d6012600a61445d565b9050600061128061123684848f8f612593565b905060006112908a60ff16612569565b61129c8c60ff16612569565b6112a6919061446c565b905060008082136112d1576112cc6112c56112c084614493565b612686565b84906124dc565b6112e4565b6112e46112dd83612686565b84906124c2565b905060405180608001604052808281526020014281526020016000815260200160008152509e505050505050505050505050505050919050565b6113266139e4565b60008060008480602001905181019061133f91906144bf565b91945092509050826000826113bc576040516396834ad360e01b8152600481018590526001600160a01b038316906396834ad390602401608060405180830381865afa158015611393573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b791906144f5565b611425565b604051639474f45b60e01b8152600481018590526001600160a01b03831690639474f45b90602401608060405180830381865afa158015611401573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142591906144f5565b90506000816040015160030b601261143d919061456f565b90506000808213611467576114626114576112c084614493565b845160070b906124dc565b61147e565b61147e61147383612686565b845160070b906124c2565b9050604051806080016040528082815260200184606001518152602001600081526020016000815250975050505050505050919050565b6114bd6139e4565b6000806000868060200190518101906114d69190614597565b92509250925060005b8651811015611545578681815181106114fa576114fa613e6a565b6020026020010151717374616c656e657373546f6c6572616e636560701b0361153d5785818151811061152f5761152f613e6a565b602002602001015160001c91505b6001016114df565b5060408051600180825281830190925260009160208083019080368337019050509050828160008151811061157c5761157c613e6a565b602002602001018181525050836001838360405160200161159f939291906145ce565b60408051601f198184030181529082905263cf2cabdf60e01b82526103979291600401614603565b6115cf6139e4565b6000828060200190518101906115e59190614627565b90506000846000815181106115fc576115fc613e6a565b602002602001015160000151905060008560018151811061161f5761161f613e6a565b6020026020010151600001519050808214611702576000611653601261164d611648858761446c565b6126a9565b906124c2565b905082158061167b5750611666836126a9565b6116709082614640565b61167985612569565b125b15611700576002875111156116b0578660028151811061169d5761169d613e6a565b6020026020010151945050505050610116565b826000036116d15760405163014cc07160e01b815260040160405180910390fd5b6116da836126a9565b6116e49082614640565b60405163dcac091960e01b815260040161039791815260200190565b505b8560008151811061171557611715613e6a565b6020026020010151935050505092915050565b6117306139e4565b6000846020015180602001905181019061174a9190614627565b905060005b84518110156117bc5784818151811061176a5761176a613e6a565b6020026020010151717374616c656e657373546f6c6572616e636560701b036117b4576117ad8482815181106117a2576117a2613e6a565b602002602001015190565b91506117bc565b60010161174f565b50600085604001516000815181106117d6576117d6613e6a565b6020026020010151905060006117ed828787610310565b60208101519091506117ff84426140a7565b1161180e5792506102b4915050565b86604001515160010361187157866040015160008151811061183257611832613e6a565b602002602001015181600001518260200151604051631808066560e21b8152600401610397939291909283526020830191909152604082015260600190565b61189a876040015160018151811061188b5761188b613e6a565b60200260200101518787610310565b979650505050505050565b6118ad6139e4565b6040518060800160405280838060200190518101906118cc9190614627565b81526020014281526020016000815260200160008152509050919050565b600081600001518260200151836040015160405160200161190d9392919061466e565b604051602081830303815290604052805190602001209050919050565b60008061193683610c26565b60408051606081019091528154909190829060ff16600981111561195c5761195c613aa7565b600981111561196d5761196d613aa7565b815260200160018201805461198190613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546119ad90613dc2565b80156119fa5780601f106119cf576101008083540402835291602001916119fa565b820191906000526020600020905b8154815290600101906020018083116119dd57829003601f168201915b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015611a5257602002820191906000526020600020905b815481526020019060010190808311611a3e575b505050505081525050905060006009811115611a7057611a70613aa7565b81516009811115611a8357611a83613aa7565b14159392505050565b6000600182516009811115611aa357611aa3613aa7565b1480611ac15750600682516009811115611abf57611abf613aa7565b145b80611ade5750600782516009811115611adc57611adc613aa7565b145b15611aee57611aec826126c1565b505b600182516009811115611b0357611b03613aa7565b03611b11576101168261284a565b600282516009811115611b2657611b26613aa7565b03611b3457610116826128a5565b600382516009811115611b4957611b49613aa7565b03611b575761011682612973565b600482516009811115611b6c57611b6c613aa7565b03611b7a5761011682612aae565b600582516009811115611b8f57611b8f613aa7565b03611b9d5761011682612e92565b600982516009811115611bb257611bb2613aa7565b03611bc05761011682612fcb565b600682516009811115611bd557611bd5613aa7565b03611be3576101168261300e565b600782516009811115611bf857611bf8613aa7565b03611c065761011682613052565b600882516009811115611c1b57611c1b613aa7565b03611c295761011682613078565b506000919050565b600080611c3d836118ea565b9050611c4881610c26565b8351815491935090839060ff19166001836009811115611c6a57611c6a613aa7565b021790555060208301516001830190611c8390826146ed565b5060408301518051611c9f916002850191602090910190613a0c565b50915091565b611cad6139e4565b60005b8251811015611d07578160200151838281518110611cd057611cd0613e6a565b6020026020010151602001511115611cff57828181518110611cf457611cf4613e6a565b602002602001015191505b600101611cb0565b50919050565b611d156139e4565b81600081518110611d2857611d28613e6a565b602002602001015190506000600190505b8251811015611d07578160000151838281518110611d5957611d59613e6a565b6020026020010151600001511215611d8857828181518110611d7d57611d7d613e6a565b602002602001015191505b600101611d39565b611d986139e4565b81600081518110611dab57611dab613e6a565b602002602001015190506000600190505b8251811015611d07578160000151838281518110611ddc57611ddc613e6a565b6020026020010151600001511315611e0b57828181518110611e0057611e00613e6a565b602002602001015191505b600101611dbc565b611e1b6139e4565b60005b8251811015611e9557828181518110611e3957611e39613e6a565b60200260200101516000015182600001818151611e56919061456f565b9052508251839082908110611e6d57611e6d613e6a565b60200260200101516020015182602001818151611e8a91906147ad565b905250600101611e1e565b50611ea08251612569565b8151611eac9190614640565b815281516020820151611ebf91906147c0565b6020820152919050565b611ed16139e4565b611eed826000611ee86001865161123691906140a7565b6130a4565b60028251611efb91906147d4565b600003611fd65760408051600280825260608201909252600091816020015b611f226139e4565b815260200190600190039081611f1a57905050905082600160028551611f4891906147c0565b611f5291906140a7565b81518110611f6257611f62613e6a565b602002602001015181600081518110611f7d57611f7d613e6a565b60200260200101819052508260028451611f9791906147c0565b81518110611fa757611fa7613e6a565b602002602001015181600181518110611fc257611fc2613e6a565b60200260200101819052506102b481611e13565b8160028351611fe591906147c0565b81518110611ff557611ff5613e6a565b60200260200101519050919050565b919050565b6120116139e4565b8160008151811061202457612024613e6a565b60209081029190910101515181528151829060009061204557612045613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061207657612076613e6a565b6020026020010151600001518260000181815161209391906147e8565b90525082518390829081106120aa576120aa613e6a565b602002602001015160200151826020018181516120c791906147ad565b90525060010161205b565b5081518160200151611ebf91906147c0565b6120ec6139e4565b816000815181106120ff576120ff613e6a565b60209081029190910101515181528151829060009061212057612120613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061215157612151613e6a565b60200260200101516000015160000361219e5782818151811061217657612176613e6a565b6020026020010151600001516040516338ee04a760e01b815260040161039791815260200190565b8281815181106121b0576121b0613e6a565b602002602001015160000151826000018181516121cd9190614640565b90525082518390829081106121e4576121e4613e6a565b6020026020010151602001518260200181815161220191906147ad565b905250600101612136565b6122146139e4565b8160008151811061222757612227613e6a565b60209081029190910101515181528151829060009061224857612248613e6a565b6020908102919091018101518101519082015260015b82518110156120d25761229083828151811061227c5761227c613e6a565b602090810291909101015151835190613264565b825282518390829081106122a6576122a6613e6a565b602002602001015160200151826020018181516122c391906147ad565b90525060010161225e565b6122d66139e4565b816000815181106122e9576122e9613e6a565b60209081029190910101515181528151829060009061230a5761230a613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061233b5761233b613e6a565b6020026020010151600001516000036123605782818151811061217657612176613e6a565b61238983828151811061237557612375613e6a565b602090810291909101015151835190613283565b8252825183908290811061239f5761239f613e6a565b602002602001015160200151826020018181516123bc91906147ad565b905250600101612320565b6000826001826123d785426140a7565b90505b69ffffffffffffffffffff8716156124a3576001600160a01b038816639a6fc8f561240489614818565b6040516001600160e01b031960e084901b16815269ffffffffffffffffffff8216600482015290995060240160a060405180830381865afa925050508015612469575060408051601f3d908101601f1916820190925261246691810190614041565b60015b156124a357858210156124805750505050506124a3565b61248a848961456f565b97508661249681614834565b97505050505050506123da565b6124ac82612569565b6124b69084614640565b98975050505050505050565b60006124d261123683600a61484d565b6102b490846147e8565b60006124ec61123683600a61484d565b6102b49084614640565b6000667fffffffffffff66ffffffffffffff83161115612529576040516329d2678160e21b815260040160405180910390fd5b5090565b6000627fffff19600683900b128061254b5750627fffff600683900b135b1561252957604051630d962f7960e21b815260040160405180910390fd5b60006001600160ff1b038211156125295760405163677c430560e11b815260040160405180910390fd5b60008061259f86613298565b90506fffffffffffffffffffffffffffffffff6001600160a01b0382161161261c5760006125d66001600160a01b03831680614859565b9050836001600160a01b0316856001600160a01b03161061260557612600600160c01b87836136cd565b612614565b6126148187600160c01b6136cd565b92505061267d565b600061263b6001600160a01b03831680680100000000000000006136cd565b9050836001600160a01b0316856001600160a01b03161061266a57612665600160801b87836136cd565b612679565b6126798187600160801b6136cd565b9250505b50949350505050565b6000808212156125295760405163029f024d60e31b815260040160405180910390fd5b600080821215612529576126bc82614493565b610116565b6000805b8260400151518110156128415760006126fa846040015183815181106126ed576126ed613e6a565b6020026020010151610ab6565b60408051606081019091528154909190829060ff16600981111561272057612720613aa7565b600981111561273157612731613aa7565b815260200160018201805461274590613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461277190613dc2565b80156127be5780601f10612793576101008083540402835291602001916127be565b820191906000526020600020905b8154815290600101906020018083116127a157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561281657602002820191906000526020600020905b815481526020019060010190808311612802575b505050505081525050905061282a81611a8c565b612838575060009392505050565b506001016126c5565b50600192915050565b60006002826040015151101561286257506000919050565b81602001515160201461287757506000919050565b600082602001518060200190518101906128919190614627565b905060088111156128415750600092915050565b6000602082602001515110156128bd57506000919050565b600082602001518060200190518101906128d79190613ed3565b90506128ea816306e7ea3960e21b6138e2565b6128f75750600092915050565b604051633b70a5bf60e21b81526001600160a01b0382169063edc296fc90612923908690600401613b21565b6020604051808303816000875af1158015612942573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129669190614870565b6128415750600092915050565b6040810151516000901561298957506000919050565b81602001515160601461299e57506000919050565b60008083602001518060200190518101906129b99190613fe8565b92505091506000829050806001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015612a01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a259190614041565b5050505050806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8c919061488b565b60ff168260ff1614612aa357506000949350505050565b506001949350505050565b60408101515160009015612ac457506000919050565b81602001515160c014612ad957506000919050565b6000806000806000808760200151806020019051810190612afa91906140ba565b9550955095509550955095508360ff16866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6c919061488b565b60ff1614612b8257506000979650505050505050565b8260ff16856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612be8919061488b565b60ff1614612bfe57506000979650505050505050565b6000826001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c629190613ed3565b90506000836001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ca4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cc89190613ed3565b9050876001600160a01b0316826001600160a01b0316148015612cfc5750866001600160a01b0316816001600160a01b0316145b158015612d385750866001600160a01b0316826001600160a01b0316148015612d365750876001600160a01b0316816001600160a01b0316145b155b15612d4d575060009998505050505050505050565b60128660ff161180612d62575060128560ff16115b15612d77575060009998505050505050505050565b8263ffffffff16600003612d95575060009998505050505050505050565b6040805160028082526060820183526000926020830190803683370190505090508381600081518110612dca57612dca613e6a565b602002602001019063ffffffff16908163ffffffff1681525050600081600181518110612df957612df9613e6a565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526001600160a01b0386169063883bdbfd90612e3a908490600401614143565b600060405180830381865afa158015612e57573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612e7f91908101906141f5565b5060019c9b505050505050505050505050565b60408101515160009015612ea857506000919050565b816020015151606014612ebd57506000919050565b60008060008460200151806020019051810190612eda91906144bf565b919450925090508281612f55576040516396834ad360e01b8152600481018490526001600160a01b038216906396834ad390602401608060405180830381865afa158015612f2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f5091906144f5565b612fbe565b604051639474f45b60e01b8152600481018490526001600160a01b03821690639474f45b90602401608060405180830381865afa158015612f9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fbe91906144f5565b5060019695505050505050565b60408101515160009015612fe157506000919050565b816020015151606014612ff657506000919050565b8160200151806020019051810190612aa39190614597565b60008160400151516002148061302957508160400151516003145b61303557506000919050565b81602001515160201461304a57506000919050565b506001919050565b600081604001515160011480613029575081604001515160021461303557506000919050565b6040810151516000901561308e57506000919050565b6020826020015151101561304a57506000919050565b81818082036130b4575050505050565b6000856130da60026130c6888861446c565b6130d09190614640565b6112c0908861456f565b815181106130ea576130ea613e6a565b60200260200101516000015190505b818313613236575b808661310c85612686565b8151811061311c5761311c613e6a565b60200260200101516000015112156131405782613138816148a6565b935050613101565b8561314a83612686565b8151811061315a5761315a613e6a565b60200260200101516000015181121561317f5781613177816148be565b925050613140565b818313613231578561319083612686565b815181106131a0576131a0613e6a565b6020026020010151866131b285612686565b815181106131c2576131c2613e6a565b6020026020010151876131d486612686565b815181106131e4576131e4613e6a565b60200260200101886131f586612686565b8151811061320557613205613e6a565b602002602001018290528290525050828061321f906148a6565b935050818061322d906148be565b9250505b6130f9565b81851215613249576132498686846130a4565b8383121561325c5761325c8684866130a4565b505050505050565b6000670de0b6b3a764000061327983856147e8565b6102b49190614640565b600081613279670de0b6b3a7640000856147e8565b60008060008360020b126132b8576132b3600284900b612686565b6132c8565b6132c86112c0600285900b614493565b90506132e36112c06132dd620d89e7196148db565b60020b90565b8111156133165760405162461bcd60e51b81526020600482015260016024820152601560fa1b6044820152606401610397565b60008160011660000361332d57600160801b61333f565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff169050600282161561337e576080613379826ffff97272373d413259a46990580e213a614859565b901c90505b60048216156133a85760806133a3826ffff2e50f5f656932ef12357cf3c7fdcc614859565b901c90505b60088216156133d25760806133cd826fffe5caca7e10e4e61c3624eaa0941cd0614859565b901c90505b60108216156133fc5760806133f7826fffcb9843d60f6159c9db58835c926644614859565b901c90505b6020821615613426576080613421826fff973b41fa98c081472e6896dfb254c0614859565b901c90505b604082161561345057608061344b826fff2ea16466c96a3843ec78b326b52861614859565b901c90505b608082161561347a576080613475826ffe5dee046a99a2a811c461f1969c3053614859565b901c90505b6101008216156134a55760806134a0826ffcbe86c7900a88aedcffc83b479aa3a4614859565b901c90505b6102008216156134d05760806134cb826ff987a7253ac413176f2b074cf7815e54614859565b901c90505b6104008216156134fb5760806134f6826ff3392b0822b70005940c7a398e4b70f3614859565b901c90505b610800821615613526576080613521826fe7159475a2c29b7443b29c7fa6e889d9614859565b901c90505b61100082161561355157608061354c826fd097f3bdfd2022b8845ad8f792aa5825614859565b901c90505b61200082161561357c576080613577826fa9f746462d870fdf8a65dc1f90e061e5614859565b901c90505b6140008216156135a75760806135a2826f70d869a156d2a1b890bb3df62baf32f7614859565b901c90505b6180008216156135d25760806135cd826f31be135f97d08fd981231505542fcfa6614859565b901c90505b620100008216156135fe5760806135f9826f09aa508b5b7a84e1c677de54f3e99bc9614859565b901c90505b62020000821615613629576080613624826e5d6af8dedb81196699c329225ee604614859565b901c90505b6204000082161561365357608061364e826d2216e584f5fa1ea926041bedfe98614859565b901c90505b6208000082161561367b576080613676826b048a170391f7dc42444e8fa2614859565b901c90505b60008460020b131561369657613693816000196147c0565b90505b6102ce6136a8640100000000836147d4565b156136b45760016136b7565b60005b6136c89060ff16602084901c6147ad565b6139ba565b6000808060001985870985870292508281108382030391505080600003613749576000841161373e5760405162461bcd60e51b815260206004820152601960248201527f48616e646c65206e6f6e2d6f766572666c6f77206361736573000000000000006044820152606401610397565b5082900490506102b4565b8084116137985760405162461bcd60e51b815260206004820152601960248201527f70726576656e74732064656e6f6d696e61746f72203d3d2030000000000000006044820152606401610397565b60008486880980840393811190920391905060006137d06137b887612569565b6137c188612569565b6137ca90614493565b16612686565b9586900495938490049360008190030460010190506137ef8184614859565b909317926000613800876003614859565b600218905061380f8188614859565b61381a9060026140a7565b6138249082614859565b90506138308188614859565b61383b9060026140a7565b6138459082614859565b90506138518188614859565b61385c9060026140a7565b6138669082614859565b90506138728188614859565b61387d9060026140a7565b6138879082614859565b90506138938188614859565b61389e9060026140a7565b6138a89082614859565b90506138b48188614859565b6138bf9060026140a7565b6138c99082614859565b90506138d58186614859565b9998505050505050505050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b1790529051600091829182916001600160a01b0387169161394391906148fd565b6000604051808303816000865af19150503d8060008114613980576040519150601f19603f3d011682016040523d82523d6000602084013e613985565b606091505b50915091508161399a57600092505050610116565b80516000036139ae57600092505050610116565b60200151949350505050565b60006001600160a01b038211156125295760405163dccde8ed60e01b815260040160405180910390fd5b6040518060800160405280600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215613a47579160200282015b82811115613a47578251825591602001919060010190613a2c565b506125299291505b808211156125295760008155600101613a4f565b600060208284031215613a7557600080fd5b5035919050565b8151815260208083015190820152604080830151908201526060808301519082015260808101610116565b634e487b7160e01b600052602160045260246000fd5b600a8110613acd57613acd613aa7565b9052565b60005b83811015613aec578181015183820152602001613ad4565b50506000910152565b60008151808452613b0d816020860160208601613ad1565b601f01601f19169290920160200192915050565b60006020808352613b358184018551613abd565b8084015160606040850152613b4d6080850182613af5565b6040860151858203601f19016060870152805180835290840192506000918401905b80831015613b8f5783518252928401926001929092019190840190613b6f565b509695505050505050565b634e487b7160e01b600052604160045260246000fd5b6040516080810167ffffffffffffffff81118282101715613bd357613bd3613b9a565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715613c0257613c02613b9a565b604052919050565b600067ffffffffffffffff821115613c2457613c24613b9a565b5060051b60200190565b600082601f830112613c3f57600080fd5b81356020613c54613c4f83613c0a565b613bd9565b8083825260208201915060208460051b870101935086841115613c7657600080fd5b602086015b84811015613b8f5780358352918301918301613c7b565b600080600060608486031215613ca757600080fd5b8335600a8110613cb657600080fd5b925060208481013567ffffffffffffffff80821115613cd457600080fd5b818701915087601f830112613ce857600080fd5b813581811115613cfa57613cfa613b9a565b613d0c601f8201601f19168501613bd9565b8181528985838601011115613d2057600080fd5b818585018683013760009181019094015291935060408601359180831115613d4757600080fd5b5050613d5586828701613c2e565b9150509250925092565b600080600060608486031215613d7457600080fd5b83359250602084013567ffffffffffffffff80821115613d9357600080fd5b613d9f87838801613c2e565b93506040860135915080821115613db557600080fd5b50613d5586828701613c2e565b600181811c90821680613dd657607f821691505b602082108103611d0757634e487b7160e01b600052602260045260246000fd5b60008151808452602080850194506020840160005b83811015613e2757815187529582019590820190600101613e0b565b509495945050505050565b848152613e426020820185613abd565b608060408201526000613e586080830185613af5565b828103606084015261189a8185613df6565b634e487b7160e01b600052603260045260246000fd5b600060208284031215613e9257600080fd5b8151600981106102b457600080fd5b6020810160098310613eb557613eb5613aa7565b91905290565b6001600160a01b0381168114613ed057600080fd5b50565b600060208284031215613ee557600080fd5b81516102b481613ebb565b608080825285518282018190526000919060209060a0850190828a01855b82811015613f5257613f42848351805182526020810151602083015260408101516040830152606081015160608301525050565b9285019290840190600101613f0e565b5050508481036020860152613f678189613af5565b925050508281036040840152613f7d8186613df6565b9050828103606084015261189a8185613df6565b600060808284031215613fa357600080fd5b613fab613bb0565b825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b805160ff8116811461200457600080fd5b600080600060608486031215613ffd57600080fd5b835161400881613ebb565b6020850151909350915061401e60408501613fd7565b90509250925092565b805169ffffffffffffffffffff8116811461200457600080fd5b600080600080600060a0868803121561405957600080fd5b61406286614027565b945060208601519350604086015192506060860151915061408560808701614027565b90509295509295909350565b634e487b7160e01b600052601160045260246000fd5b8181038181111561011657610116614091565b60008060008060008060c087890312156140d357600080fd5b86516140de81613ebb565b60208801519096506140ef81613ebb565b94506140fd60408801613fd7565b935061410b60608801613fd7565b9250608087015161411b81613ebb565b60a088015190925063ffffffff8116811461413557600080fd5b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b8181101561418157835163ffffffff168352928401929184019160010161415f565b50909695505050505050565b600082601f83011261419e57600080fd5b815160206141ae613c4f83613c0a565b8083825260208201915060208460051b8701019350868411156141d057600080fd5b602086015b84811015613b8f5780516141e881613ebb565b83529183019183016141d5565b6000806040838503121561420857600080fd5b825167ffffffffffffffff8082111561422057600080fd5b818501915085601f83011261423457600080fd5b81516020614244613c4f83613c0a565b82815260059290921b8401810191818101908984111561426357600080fd5b948201945b838610156142915785518060060b81146142825760008081fd5b82529482019490820190614268565b918801519196509093505050808211156142aa57600080fd5b506142b78582860161418d565b9150509250929050565b600682810b9082900b03667fffffffffffff198112667fffffffffffff8213171561011657610116614091565b634e487b7160e01b600052601260045260246000fd5b60008160060b8360060b8061431b5761431b6142ee565b667fffffffffffff1982146000198214161561433957614339614091565b90059392505050565b600082614351576143516142ee565b500790565b60008160020b627fffff19810361436f5761436f614091565b6000190192915050565b600181815b808511156143b457816000190482111561439a5761439a614091565b808516156143a757918102915b93841c939080029061437e565b509250929050565b6000826143cb57506001610116565b816143d857506000610116565b81600181146143ee57600281146143f857614414565b6001915050610116565b60ff84111561440957614409614091565b50506001821b610116565b5060208310610133831016604e8410600b8410161715614437575081810a610116565b6144418383614379565b806000190482111561445557614455614091565b029392505050565b60006102b460ff8416836143bc565b818103600083128015838313168383128216171561448c5761448c614091565b5092915050565b6000600160ff1b82016144a8576144a8614091565b5060000390565b8051801515811461200457600080fd5b6000806000606084860312156144d457600080fd5b83516144df81613ebb565b6020850151909350915061401e604085016144af565b60006080828403121561450757600080fd5b61450f613bb0565b82518060070b811461452057600080fd5b8152602083015167ffffffffffffffff8116811461453d57600080fd5b60208201526040830151600381900b811461455757600080fd5b60408201526060928301519281019290925250919050565b808201828112600083128015821682158216171561458f5761458f614091565b505092915050565b6000806000606084860312156145ac57600080fd5b83516145b781613ebb565b602085015160409095015190969495509392505050565b60ff8416815267ffffffffffffffff831660208201526060604082015260006145fa6060830184613df6565b95945050505050565b6001600160a01b03831681526040602082018190526000906102ce90830184613af5565b60006020828403121561463957600080fd5b5051919050565b60008261464f5761464f6142ee565b600160ff1b82146000198414161561466957614669614091565b500590565b6146788185613abd565b60606020820152600061468e6060830185613af5565b8281036040840152610f4f8185613df6565b601f8211156146e8576000816000526020600020601f850160051c810160208610156146c95750805b601f850160051c820191505b8181101561325c578281556001016146d5565b505050565b815167ffffffffffffffff81111561470757614707613b9a565b61471b816147158454613dc2565b846146a0565b602080601f83116001811461475057600084156147385750858301515b600019600386901b1c1916600185901b17855561325c565b600085815260208120601f198616915b8281101561477f57888601518255948401946001909101908401614760565b508582101561479d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b8082018082111561011657610116614091565b6000826147cf576147cf6142ee565b500490565b6000826147e3576147e36142ee565b500690565b80820260008212600160ff1b8414161561480457614804614091565b818105831482151761011657610116614091565b600069ffffffffffffffffffff82168061436f5761436f614091565b60006001820161484657614846614091565b5060010190565b60006102b483836143bc565b808202811582820484141761011657610116614091565b60006020828403121561488257600080fd5b6102b4826144af565b60006020828403121561489d57600080fd5b6102b482613fd7565b60006001600160ff1b01820161484657614846614091565b6000600160ff1b82016148d3576148d3614091565b506000190190565b60008160020b627fffff1981036148f4576148f4614091565b60000392915050565b6000825161490f818460208701613ad1565b919091019291505056fea264697066735822122074f32fef384fdc296b0859f1c1f941c8e736c6cb972aa9e2b894956ebd6a80b364736f6c63430008160033","storage":{}},"0x83a0444b93927c3afcbe46e522280390f748e171":{"nonce":1,"balance":"0x0","code":"0x6080604052366100135761001161001d565b005b61001b61001d565b005b6000610027610093565b90503660008037600080366000845af43d6000803e806000811461004a573d6000f35b3d6000fd5b600080823b905060008111915050919050565b6000806040516020016100749061017a565b6040516020818303038152906040528051906020012090508091505090565b600061009d6100c6565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000806040516020016100d89061020c565b6040516020818303038152906040528051906020012090508091505090565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b60006101646023836100f7565b915061016f82610108565b604082019050919050565b6000602082019050818103600083015261019381610157565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b60006101f66021836100f7565b91506102018261019a565b604082019050919050565b60006020820190508181036000830152610225816101e9565b905091905056fea2646970667358221220800da1f73cebd5e4afa07496d9bca6b6c4f526bdd3f4014ec15c70fe3a1c441364736f6c63430008110033","storage":{"0x5a648c35a2f5512218b4683cf10e03f5b7c9dc7346e1bf77d304ae97f60f592b":"0x108f53faf774d7c4c56f5bce9ca6e605ce8aeadd","0x5c7865864a2a990d80b5bb5c40e7b73a029960dc711fbb56120dfab976e92ea3":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"}},"0x90f79bf6eb2c4f870365e785982e1f101e93b906":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x976ea74026e726554db657fa54763abd0c3a0aa9":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xa0ee7a142d267c1f36714e4a8f75612f20a79720":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xc67e2bd3108604cf0168c0e5ef9cd6d78b9bb14b":{"nonce":1,"balance":"0x21e19c6edb7e2445f20","code":"0x","storage":{}},"0xeb045d78d273107348b0300c01d29b7552d622ab":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266":{"nonce":1,"balance":"0x21e19e08b86820a43ea","code":"0x","storage":{}}},"best_block_number":"0x5","blocks":[{"header":{"parentHash":"0x08abe6e453727534d8dd708843a7522b7d500338bdfe2402ca105dcdb05eebe9","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0xcd346446ed010523161f40a5f2b512def549bfb79e165b4354488738416481f2","transactionsRoot":"0xb3a4689832e0b599260ae70362ffcf224b60571b35ff8836904a3d81e2675d66","receiptsRoot":"0x2d13fdc120ab90536fed583939de7fb68b64926a306c1f629593ca9c2c93b198","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x3ea90d","timestamp":"0x66b200ca","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x2e0b6260","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gas":"0x3ea90d","maxFeePerGas":"0x83215600","maxPriorityFeePerGas":"0x3b9aca00","value":"0x0","accessList":[],"input":"0x608060405234801561001057600080fd5b5061494f806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80632a952b2d1461005c57806350c946fe14610085578063625ca21c146100a5578063daa250be146100c6578063deba1b98146100d9575b600080fd5b61006f61006a366004613a63565b6100ec565b60405161007c9190613a7c565b60405180910390f35b610098610093366004613a63565b61011c565b60405161007c9190613b21565b6100b86100b3366004613c92565b610276565b60405190815260200161007c565b61006f6100d4366004613d5f565b6102bb565b6100b86100e7366004613c92565b6102d6565b6100f46139e4565b6040805160008082526020820190815281830190925261011691849190610310565b92915050565b6101416040805160608101909152806000815260200160608152602001606081525090565b61014a82610ab6565b60408051606081019091528154909190829060ff16600981111561017057610170613aa7565b600981111561018157610181613aa7565b815260200160018201805461019590613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546101c190613dc2565b801561020e5780601f106101e35761010080835404028352916020019161020e565b820191906000526020600020905b8154815290600101906020018083116101f157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561026657602002820191906000526020600020905b815481526020019060010190808311610252575b5050505050815250509050919050565b600080604051806060016040528086600981111561029657610296613aa7565b81526020018581526020018481525090506102b081610ac1565b9150505b9392505050565b6102c36139e4565b6102ce848484610310565b949350505050565b60008060405180606001604052808660098111156102f6576102f6613aa7565b81526020018581526020018481525090506102b081610acc565b6103186139e4565b81518351146103a05760408051634bab873760e11b81526004810191909152600d60448201526c72756e74696d6556616c75657360981b606482015260806024820152602260848201527f6d7573742062652073616d65206c656e6774682061732072756e74696d654b6560a482015261797360f01b60c482015260e4015b60405180910390fd5b60006103ab85610c26565b805490915060ff1660018160098111156103c7576103c7613aa7565b036104755761046c6103da838787610c84565b8360010180546103e990613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461041590613dc2565b80156104625780601f1061043757610100808354040283529160200191610462565b820191906000526020600020905b81548152906001019060200180831161044557829003601f168201915b5050505050610d46565b925050506102b4565b600281600981111561048957610489613aa7565b036105305761046c61049c838787610c84565b8360010180546104ab90613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546104d790613dc2565b80156105245780601f106104f957610100808354040283529160200191610524565b820191906000526020600020905b81548152906001019060200180831161050757829003601f168201915b50505050508787610ebb565b600381600981111561054457610544613aa7565b036105de5761046c82600101805461055b90613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461058790613dc2565b80156105d45780601f106105a9576101008083540402835291602001916105d4565b820191906000526020600020905b8154815290600101906020018083116105b757829003601f168201915b5050505050610f59565b60048160098111156105f2576105f2613aa7565b0361068c5761046c82600101805461060990613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461063590613dc2565b80156106825780601f1061065757610100808354040283529160200191610682565b820191906000526020600020905b81548152906001019060200180831161066557829003601f168201915b5050505050611087565b60058160098111156106a0576106a0613aa7565b0361073a5761046c8260010180546106b790613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546106e390613dc2565b80156107305780601f1061070557610100808354040283529160200191610730565b820191906000526020600020905b81548152906001019060200180831161071357829003601f168201915b505050505061131e565b600981600981111561074e5761074e613aa7565b036107ea5761046c82600101805461076590613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461079190613dc2565b80156107de5780601f106107b3576101008083540402835291602001916107de565b820191906000526020600020905b8154815290600101906020018083116107c157829003601f168201915b505050505086866114b5565b60068160098111156107fe576107fe613aa7565b036108a35761046c610811838787610c84565b83600101805461082090613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461084c90613dc2565b80156108995780601f1061086e57610100808354040283529160200191610899565b820191906000526020600020905b81548152906001019060200180831161087c57829003601f168201915b50505050506115c7565b60078160098111156108b7576108b7613aa7565b036109ec576040805160608101909152825461046c91908490829060ff1660098111156108e6576108e6613aa7565b60098111156108f7576108f7613aa7565b815260200160018201805461090b90613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461093790613dc2565b80156109845780601f1061095957610100808354040283529160200191610984565b820191906000526020600020905b81548152906001019060200180831161096757829003601f168201915b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156109dc57602002820191906000526020600020905b8154815260200190600101908083116109c8575b5050505050815250508686611728565b6008816009811115610a0057610a00613aa7565b03610a9a5761046c826001018054610a1790613dc2565b80601f0160208091040260200160405190810160405280929190818152602001828054610a4390613dc2565b8015610a905780601f10610a6557610100808354040283529160200191610a90565b820191906000526020600020905b815481529060010190602001808311610a7357829003601f168201915b50505050506118a5565b6040516323a9bbc960e01b815260048101879052602401610397565b600061011682610c26565b6000610116826118ea565b6000610ad782610ac1565b9050610ae28161192a565b15610b35577fcb64985827770858ec421ad26da7e558c757541643036ce44d6b4eb9e8e5dc5e81836000015184602001518560400151604051610b289493929190613e32565b60405180910390a1919050565b610b3e82611a8c565b610b5d578160405163382bbbc960e11b81526004016103979190613b21565b60005b826040015151811015610bd957610b9383604001518281518110610b8657610b86613e6a565b602002602001015161192a565b610bd15782604001518181518110610bad57610bad613e6a565b6020026020010151604051632f19f96160e11b815260040161039791815260200190565b600101610b60565b50610be382611c31565b8351602085015160408087015190519395507fcb64985827770858ec421ad26da7e558c757541643036ce44d6b4eb9e8e5dc5e9450610b28938693929190613e32565b604080516020808201839052606082018190527f696f2e73796e7468657469782e6f7261636c652d6d616e616765722e4e6f6465608080840191909152828401949094528251808303909401845260a0909101909152815191012090565b600283015460609067ffffffffffffffff811115610ca457610ca4613b9a565b604051908082528060200260200182016040528015610cdd57816020015b610cca6139e4565b815260200190600190039081610cc25790505b50905060005b6002850154811015610d3e57610d19856002018281548110610d0757610d07613e6a565b90600052602060002001548585610310565b828281518110610d2b57610d2b613e6a565b6020908102919091010152600101610ce3565b509392505050565b610d4e6139e4565b600082806020019051810190610d649190613e80565b90506000816008811115610d7a57610d7a613aa7565b03610d9057610d8884611ca5565b915050610116565b6001816008811115610da457610da4613aa7565b03610db257610d8884611d0d565b6002816008811115610dc657610dc6613aa7565b03610dd457610d8884611d90565b6003816008811115610de857610de8613aa7565b03610df657610d8884611e13565b6004816008811115610e0a57610e0a613aa7565b03610e1857610d8884611ec9565b6005816008811115610e2c57610e2c613aa7565b03610e3a57610d8884612009565b6006816008811115610e4e57610e4e613aa7565b03610e5c57610d88846120e4565b6007816008811115610e7057610e70613aa7565b03610e7e57610d888461220c565b6008816008811115610e9257610e92613aa7565b03610ea057610d88846122ce565b80604051631be413d360e11b81526004016103979190613ea1565b610ec36139e4565b600084806020019051810190610ed99190613ed3565b604051631ecba7c360e31b81529091506001600160a01b0382169063f65d3e1890610f0e908990899089908990600401613ef0565b608060405180830381865afa158015610f2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4f9190613f91565b9695505050505050565b610f616139e4565b600080600084806020019051810190610f7a9190613fe8565b92509250925060008390506000806000836001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610fc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fec9190614041565b509350509250925060008660001461100f5761100a8585858a6123c7565b611011565b825b905060128660ff161161103b5761103661102f60ff881660126140a7565b82906124c2565b611053565b61105361104c601260ff89166140a7565b82906124dc565b9050604051806080016040528082815260200183815260200160008152602001600081525098505050505050505050919050565b61108f6139e4565b600080600080600080878060200190518101906110ac91906140ba565b604080516002808252606082018352979d50959b50939950919750955093506000929060208301908036833701905050905081816000815181106110f2576110f2613e6a565b602002602001019063ffffffff16908163ffffffff168152505060008160018151811061112157611121613e6a565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526000906001600160a01b0385169063883bdbfd90611165908590600401614143565b600060405180830381865afa158015611182573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526111aa91908101906141f5565b5090506000816000815181106111c2576111c2613e6a565b6020026020010151826001815181106111dd576111dd613e6a565b60200260200101516111ef91906142c1565b9050600061121761120563ffffffff87166124f6565b61120f9084614304565b60060b61252d565b905060008260060b12801561124c575061123b63ffffffff8616612569565b612569565b8260060b6112499190614342565b15155b1561125f578061125b81614356565b9150505b600061126d6012600a61445d565b9050600061128061123684848f8f612593565b905060006112908a60ff16612569565b61129c8c60ff16612569565b6112a6919061446c565b905060008082136112d1576112cc6112c56112c084614493565b612686565b84906124dc565b6112e4565b6112e46112dd83612686565b84906124c2565b905060405180608001604052808281526020014281526020016000815260200160008152509e505050505050505050505050505050919050565b6113266139e4565b60008060008480602001905181019061133f91906144bf565b91945092509050826000826113bc576040516396834ad360e01b8152600481018590526001600160a01b038316906396834ad390602401608060405180830381865afa158015611393573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b791906144f5565b611425565b604051639474f45b60e01b8152600481018590526001600160a01b03831690639474f45b90602401608060405180830381865afa158015611401573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142591906144f5565b90506000816040015160030b601261143d919061456f565b90506000808213611467576114626114576112c084614493565b845160070b906124dc565b61147e565b61147e61147383612686565b845160070b906124c2565b9050604051806080016040528082815260200184606001518152602001600081526020016000815250975050505050505050919050565b6114bd6139e4565b6000806000868060200190518101906114d69190614597565b92509250925060005b8651811015611545578681815181106114fa576114fa613e6a565b6020026020010151717374616c656e657373546f6c6572616e636560701b0361153d5785818151811061152f5761152f613e6a565b602002602001015160001c91505b6001016114df565b5060408051600180825281830190925260009160208083019080368337019050509050828160008151811061157c5761157c613e6a565b602002602001018181525050836001838360405160200161159f939291906145ce565b60408051601f198184030181529082905263cf2cabdf60e01b82526103979291600401614603565b6115cf6139e4565b6000828060200190518101906115e59190614627565b90506000846000815181106115fc576115fc613e6a565b602002602001015160000151905060008560018151811061161f5761161f613e6a565b6020026020010151600001519050808214611702576000611653601261164d611648858761446c565b6126a9565b906124c2565b905082158061167b5750611666836126a9565b6116709082614640565b61167985612569565b125b15611700576002875111156116b0578660028151811061169d5761169d613e6a565b6020026020010151945050505050610116565b826000036116d15760405163014cc07160e01b815260040160405180910390fd5b6116da836126a9565b6116e49082614640565b60405163dcac091960e01b815260040161039791815260200190565b505b8560008151811061171557611715613e6a565b6020026020010151935050505092915050565b6117306139e4565b6000846020015180602001905181019061174a9190614627565b905060005b84518110156117bc5784818151811061176a5761176a613e6a565b6020026020010151717374616c656e657373546f6c6572616e636560701b036117b4576117ad8482815181106117a2576117a2613e6a565b602002602001015190565b91506117bc565b60010161174f565b50600085604001516000815181106117d6576117d6613e6a565b6020026020010151905060006117ed828787610310565b60208101519091506117ff84426140a7565b1161180e5792506102b4915050565b86604001515160010361187157866040015160008151811061183257611832613e6a565b602002602001015181600001518260200151604051631808066560e21b8152600401610397939291909283526020830191909152604082015260600190565b61189a876040015160018151811061188b5761188b613e6a565b60200260200101518787610310565b979650505050505050565b6118ad6139e4565b6040518060800160405280838060200190518101906118cc9190614627565b81526020014281526020016000815260200160008152509050919050565b600081600001518260200151836040015160405160200161190d9392919061466e565b604051602081830303815290604052805190602001209050919050565b60008061193683610c26565b60408051606081019091528154909190829060ff16600981111561195c5761195c613aa7565b600981111561196d5761196d613aa7565b815260200160018201805461198190613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546119ad90613dc2565b80156119fa5780601f106119cf576101008083540402835291602001916119fa565b820191906000526020600020905b8154815290600101906020018083116119dd57829003601f168201915b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015611a5257602002820191906000526020600020905b815481526020019060010190808311611a3e575b505050505081525050905060006009811115611a7057611a70613aa7565b81516009811115611a8357611a83613aa7565b14159392505050565b6000600182516009811115611aa357611aa3613aa7565b1480611ac15750600682516009811115611abf57611abf613aa7565b145b80611ade5750600782516009811115611adc57611adc613aa7565b145b15611aee57611aec826126c1565b505b600182516009811115611b0357611b03613aa7565b03611b11576101168261284a565b600282516009811115611b2657611b26613aa7565b03611b3457610116826128a5565b600382516009811115611b4957611b49613aa7565b03611b575761011682612973565b600482516009811115611b6c57611b6c613aa7565b03611b7a5761011682612aae565b600582516009811115611b8f57611b8f613aa7565b03611b9d5761011682612e92565b600982516009811115611bb257611bb2613aa7565b03611bc05761011682612fcb565b600682516009811115611bd557611bd5613aa7565b03611be3576101168261300e565b600782516009811115611bf857611bf8613aa7565b03611c065761011682613052565b600882516009811115611c1b57611c1b613aa7565b03611c295761011682613078565b506000919050565b600080611c3d836118ea565b9050611c4881610c26565b8351815491935090839060ff19166001836009811115611c6a57611c6a613aa7565b021790555060208301516001830190611c8390826146ed565b5060408301518051611c9f916002850191602090910190613a0c565b50915091565b611cad6139e4565b60005b8251811015611d07578160200151838281518110611cd057611cd0613e6a565b6020026020010151602001511115611cff57828181518110611cf457611cf4613e6a565b602002602001015191505b600101611cb0565b50919050565b611d156139e4565b81600081518110611d2857611d28613e6a565b602002602001015190506000600190505b8251811015611d07578160000151838281518110611d5957611d59613e6a565b6020026020010151600001511215611d8857828181518110611d7d57611d7d613e6a565b602002602001015191505b600101611d39565b611d986139e4565b81600081518110611dab57611dab613e6a565b602002602001015190506000600190505b8251811015611d07578160000151838281518110611ddc57611ddc613e6a565b6020026020010151600001511315611e0b57828181518110611e0057611e00613e6a565b602002602001015191505b600101611dbc565b611e1b6139e4565b60005b8251811015611e9557828181518110611e3957611e39613e6a565b60200260200101516000015182600001818151611e56919061456f565b9052508251839082908110611e6d57611e6d613e6a565b60200260200101516020015182602001818151611e8a91906147ad565b905250600101611e1e565b50611ea08251612569565b8151611eac9190614640565b815281516020820151611ebf91906147c0565b6020820152919050565b611ed16139e4565b611eed826000611ee86001865161123691906140a7565b6130a4565b60028251611efb91906147d4565b600003611fd65760408051600280825260608201909252600091816020015b611f226139e4565b815260200190600190039081611f1a57905050905082600160028551611f4891906147c0565b611f5291906140a7565b81518110611f6257611f62613e6a565b602002602001015181600081518110611f7d57611f7d613e6a565b60200260200101819052508260028451611f9791906147c0565b81518110611fa757611fa7613e6a565b602002602001015181600181518110611fc257611fc2613e6a565b60200260200101819052506102b481611e13565b8160028351611fe591906147c0565b81518110611ff557611ff5613e6a565b60200260200101519050919050565b919050565b6120116139e4565b8160008151811061202457612024613e6a565b60209081029190910101515181528151829060009061204557612045613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061207657612076613e6a565b6020026020010151600001518260000181815161209391906147e8565b90525082518390829081106120aa576120aa613e6a565b602002602001015160200151826020018181516120c791906147ad565b90525060010161205b565b5081518160200151611ebf91906147c0565b6120ec6139e4565b816000815181106120ff576120ff613e6a565b60209081029190910101515181528151829060009061212057612120613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061215157612151613e6a565b60200260200101516000015160000361219e5782818151811061217657612176613e6a565b6020026020010151600001516040516338ee04a760e01b815260040161039791815260200190565b8281815181106121b0576121b0613e6a565b602002602001015160000151826000018181516121cd9190614640565b90525082518390829081106121e4576121e4613e6a565b6020026020010151602001518260200181815161220191906147ad565b905250600101612136565b6122146139e4565b8160008151811061222757612227613e6a565b60209081029190910101515181528151829060009061224857612248613e6a565b6020908102919091018101518101519082015260015b82518110156120d25761229083828151811061227c5761227c613e6a565b602090810291909101015151835190613264565b825282518390829081106122a6576122a6613e6a565b602002602001015160200151826020018181516122c391906147ad565b90525060010161225e565b6122d66139e4565b816000815181106122e9576122e9613e6a565b60209081029190910101515181528151829060009061230a5761230a613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061233b5761233b613e6a565b6020026020010151600001516000036123605782818151811061217657612176613e6a565b61238983828151811061237557612375613e6a565b602090810291909101015151835190613283565b8252825183908290811061239f5761239f613e6a565b602002602001015160200151826020018181516123bc91906147ad565b905250600101612320565b6000826001826123d785426140a7565b90505b69ffffffffffffffffffff8716156124a3576001600160a01b038816639a6fc8f561240489614818565b6040516001600160e01b031960e084901b16815269ffffffffffffffffffff8216600482015290995060240160a060405180830381865afa925050508015612469575060408051601f3d908101601f1916820190925261246691810190614041565b60015b156124a357858210156124805750505050506124a3565b61248a848961456f565b97508661249681614834565b97505050505050506123da565b6124ac82612569565b6124b69084614640565b98975050505050505050565b60006124d261123683600a61484d565b6102b490846147e8565b60006124ec61123683600a61484d565b6102b49084614640565b6000667fffffffffffff66ffffffffffffff83161115612529576040516329d2678160e21b815260040160405180910390fd5b5090565b6000627fffff19600683900b128061254b5750627fffff600683900b135b1561252957604051630d962f7960e21b815260040160405180910390fd5b60006001600160ff1b038211156125295760405163677c430560e11b815260040160405180910390fd5b60008061259f86613298565b90506fffffffffffffffffffffffffffffffff6001600160a01b0382161161261c5760006125d66001600160a01b03831680614859565b9050836001600160a01b0316856001600160a01b03161061260557612600600160c01b87836136cd565b612614565b6126148187600160c01b6136cd565b92505061267d565b600061263b6001600160a01b03831680680100000000000000006136cd565b9050836001600160a01b0316856001600160a01b03161061266a57612665600160801b87836136cd565b612679565b6126798187600160801b6136cd565b9250505b50949350505050565b6000808212156125295760405163029f024d60e31b815260040160405180910390fd5b600080821215612529576126bc82614493565b610116565b6000805b8260400151518110156128415760006126fa846040015183815181106126ed576126ed613e6a565b6020026020010151610ab6565b60408051606081019091528154909190829060ff16600981111561272057612720613aa7565b600981111561273157612731613aa7565b815260200160018201805461274590613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461277190613dc2565b80156127be5780601f10612793576101008083540402835291602001916127be565b820191906000526020600020905b8154815290600101906020018083116127a157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561281657602002820191906000526020600020905b815481526020019060010190808311612802575b505050505081525050905061282a81611a8c565b612838575060009392505050565b506001016126c5565b50600192915050565b60006002826040015151101561286257506000919050565b81602001515160201461287757506000919050565b600082602001518060200190518101906128919190614627565b905060088111156128415750600092915050565b6000602082602001515110156128bd57506000919050565b600082602001518060200190518101906128d79190613ed3565b90506128ea816306e7ea3960e21b6138e2565b6128f75750600092915050565b604051633b70a5bf60e21b81526001600160a01b0382169063edc296fc90612923908690600401613b21565b6020604051808303816000875af1158015612942573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129669190614870565b6128415750600092915050565b6040810151516000901561298957506000919050565b81602001515160601461299e57506000919050565b60008083602001518060200190518101906129b99190613fe8565b92505091506000829050806001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015612a01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a259190614041565b5050505050806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8c919061488b565b60ff168260ff1614612aa357506000949350505050565b506001949350505050565b60408101515160009015612ac457506000919050565b81602001515160c014612ad957506000919050565b6000806000806000808760200151806020019051810190612afa91906140ba565b9550955095509550955095508360ff16866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6c919061488b565b60ff1614612b8257506000979650505050505050565b8260ff16856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612be8919061488b565b60ff1614612bfe57506000979650505050505050565b6000826001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c629190613ed3565b90506000836001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ca4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cc89190613ed3565b9050876001600160a01b0316826001600160a01b0316148015612cfc5750866001600160a01b0316816001600160a01b0316145b158015612d385750866001600160a01b0316826001600160a01b0316148015612d365750876001600160a01b0316816001600160a01b0316145b155b15612d4d575060009998505050505050505050565b60128660ff161180612d62575060128560ff16115b15612d77575060009998505050505050505050565b8263ffffffff16600003612d95575060009998505050505050505050565b6040805160028082526060820183526000926020830190803683370190505090508381600081518110612dca57612dca613e6a565b602002602001019063ffffffff16908163ffffffff1681525050600081600181518110612df957612df9613e6a565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526001600160a01b0386169063883bdbfd90612e3a908490600401614143565b600060405180830381865afa158015612e57573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612e7f91908101906141f5565b5060019c9b505050505050505050505050565b60408101515160009015612ea857506000919050565b816020015151606014612ebd57506000919050565b60008060008460200151806020019051810190612eda91906144bf565b919450925090508281612f55576040516396834ad360e01b8152600481018490526001600160a01b038216906396834ad390602401608060405180830381865afa158015612f2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f5091906144f5565b612fbe565b604051639474f45b60e01b8152600481018490526001600160a01b03821690639474f45b90602401608060405180830381865afa158015612f9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fbe91906144f5565b5060019695505050505050565b60408101515160009015612fe157506000919050565b816020015151606014612ff657506000919050565b8160200151806020019051810190612aa39190614597565b60008160400151516002148061302957508160400151516003145b61303557506000919050565b81602001515160201461304a57506000919050565b506001919050565b600081604001515160011480613029575081604001515160021461303557506000919050565b6040810151516000901561308e57506000919050565b6020826020015151101561304a57506000919050565b81818082036130b4575050505050565b6000856130da60026130c6888861446c565b6130d09190614640565b6112c0908861456f565b815181106130ea576130ea613e6a565b60200260200101516000015190505b818313613236575b808661310c85612686565b8151811061311c5761311c613e6a565b60200260200101516000015112156131405782613138816148a6565b935050613101565b8561314a83612686565b8151811061315a5761315a613e6a565b60200260200101516000015181121561317f5781613177816148be565b925050613140565b818313613231578561319083612686565b815181106131a0576131a0613e6a565b6020026020010151866131b285612686565b815181106131c2576131c2613e6a565b6020026020010151876131d486612686565b815181106131e4576131e4613e6a565b60200260200101886131f586612686565b8151811061320557613205613e6a565b602002602001018290528290525050828061321f906148a6565b935050818061322d906148be565b9250505b6130f9565b81851215613249576132498686846130a4565b8383121561325c5761325c8684866130a4565b505050505050565b6000670de0b6b3a764000061327983856147e8565b6102b49190614640565b600081613279670de0b6b3a7640000856147e8565b60008060008360020b126132b8576132b3600284900b612686565b6132c8565b6132c86112c0600285900b614493565b90506132e36112c06132dd620d89e7196148db565b60020b90565b8111156133165760405162461bcd60e51b81526020600482015260016024820152601560fa1b6044820152606401610397565b60008160011660000361332d57600160801b61333f565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff169050600282161561337e576080613379826ffff97272373d413259a46990580e213a614859565b901c90505b60048216156133a85760806133a3826ffff2e50f5f656932ef12357cf3c7fdcc614859565b901c90505b60088216156133d25760806133cd826fffe5caca7e10e4e61c3624eaa0941cd0614859565b901c90505b60108216156133fc5760806133f7826fffcb9843d60f6159c9db58835c926644614859565b901c90505b6020821615613426576080613421826fff973b41fa98c081472e6896dfb254c0614859565b901c90505b604082161561345057608061344b826fff2ea16466c96a3843ec78b326b52861614859565b901c90505b608082161561347a576080613475826ffe5dee046a99a2a811c461f1969c3053614859565b901c90505b6101008216156134a55760806134a0826ffcbe86c7900a88aedcffc83b479aa3a4614859565b901c90505b6102008216156134d05760806134cb826ff987a7253ac413176f2b074cf7815e54614859565b901c90505b6104008216156134fb5760806134f6826ff3392b0822b70005940c7a398e4b70f3614859565b901c90505b610800821615613526576080613521826fe7159475a2c29b7443b29c7fa6e889d9614859565b901c90505b61100082161561355157608061354c826fd097f3bdfd2022b8845ad8f792aa5825614859565b901c90505b61200082161561357c576080613577826fa9f746462d870fdf8a65dc1f90e061e5614859565b901c90505b6140008216156135a75760806135a2826f70d869a156d2a1b890bb3df62baf32f7614859565b901c90505b6180008216156135d25760806135cd826f31be135f97d08fd981231505542fcfa6614859565b901c90505b620100008216156135fe5760806135f9826f09aa508b5b7a84e1c677de54f3e99bc9614859565b901c90505b62020000821615613629576080613624826e5d6af8dedb81196699c329225ee604614859565b901c90505b6204000082161561365357608061364e826d2216e584f5fa1ea926041bedfe98614859565b901c90505b6208000082161561367b576080613676826b048a170391f7dc42444e8fa2614859565b901c90505b60008460020b131561369657613693816000196147c0565b90505b6102ce6136a8640100000000836147d4565b156136b45760016136b7565b60005b6136c89060ff16602084901c6147ad565b6139ba565b6000808060001985870985870292508281108382030391505080600003613749576000841161373e5760405162461bcd60e51b815260206004820152601960248201527f48616e646c65206e6f6e2d6f766572666c6f77206361736573000000000000006044820152606401610397565b5082900490506102b4565b8084116137985760405162461bcd60e51b815260206004820152601960248201527f70726576656e74732064656e6f6d696e61746f72203d3d2030000000000000006044820152606401610397565b60008486880980840393811190920391905060006137d06137b887612569565b6137c188612569565b6137ca90614493565b16612686565b9586900495938490049360008190030460010190506137ef8184614859565b909317926000613800876003614859565b600218905061380f8188614859565b61381a9060026140a7565b6138249082614859565b90506138308188614859565b61383b9060026140a7565b6138459082614859565b90506138518188614859565b61385c9060026140a7565b6138669082614859565b90506138728188614859565b61387d9060026140a7565b6138879082614859565b90506138938188614859565b61389e9060026140a7565b6138a89082614859565b90506138b48188614859565b6138bf9060026140a7565b6138c99082614859565b90506138d58186614859565b9998505050505050505050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b1790529051600091829182916001600160a01b0387169161394391906148fd565b6000604051808303816000865af19150503d8060008114613980576040519150601f19603f3d011682016040523d82523d6000602084013e613985565b606091505b50915091508161399a57600092505050610116565b80516000036139ae57600092505050610116565b60200151949350505050565b60006001600160a01b038211156125295760405163dccde8ed60e01b815260040160405180910390fd5b6040518060800160405280600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215613a47579160200282015b82811115613a47578251825591602001919060010190613a2c565b506125299291505b808211156125295760008155600101613a4f565b600060208284031215613a7557600080fd5b5035919050565b8151815260208083015190820152604080830151908201526060808301519082015260808101610116565b634e487b7160e01b600052602160045260246000fd5b600a8110613acd57613acd613aa7565b9052565b60005b83811015613aec578181015183820152602001613ad4565b50506000910152565b60008151808452613b0d816020860160208601613ad1565b601f01601f19169290920160200192915050565b60006020808352613b358184018551613abd565b8084015160606040850152613b4d6080850182613af5565b6040860151858203601f19016060870152805180835290840192506000918401905b80831015613b8f5783518252928401926001929092019190840190613b6f565b509695505050505050565b634e487b7160e01b600052604160045260246000fd5b6040516080810167ffffffffffffffff81118282101715613bd357613bd3613b9a565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715613c0257613c02613b9a565b604052919050565b600067ffffffffffffffff821115613c2457613c24613b9a565b5060051b60200190565b600082601f830112613c3f57600080fd5b81356020613c54613c4f83613c0a565b613bd9565b8083825260208201915060208460051b870101935086841115613c7657600080fd5b602086015b84811015613b8f5780358352918301918301613c7b565b600080600060608486031215613ca757600080fd5b8335600a8110613cb657600080fd5b925060208481013567ffffffffffffffff80821115613cd457600080fd5b818701915087601f830112613ce857600080fd5b813581811115613cfa57613cfa613b9a565b613d0c601f8201601f19168501613bd9565b8181528985838601011115613d2057600080fd5b818585018683013760009181019094015291935060408601359180831115613d4757600080fd5b5050613d5586828701613c2e565b9150509250925092565b600080600060608486031215613d7457600080fd5b83359250602084013567ffffffffffffffff80821115613d9357600080fd5b613d9f87838801613c2e565b93506040860135915080821115613db557600080fd5b50613d5586828701613c2e565b600181811c90821680613dd657607f821691505b602082108103611d0757634e487b7160e01b600052602260045260246000fd5b60008151808452602080850194506020840160005b83811015613e2757815187529582019590820190600101613e0b565b509495945050505050565b848152613e426020820185613abd565b608060408201526000613e586080830185613af5565b828103606084015261189a8185613df6565b634e487b7160e01b600052603260045260246000fd5b600060208284031215613e9257600080fd5b8151600981106102b457600080fd5b6020810160098310613eb557613eb5613aa7565b91905290565b6001600160a01b0381168114613ed057600080fd5b50565b600060208284031215613ee557600080fd5b81516102b481613ebb565b608080825285518282018190526000919060209060a0850190828a01855b82811015613f5257613f42848351805182526020810151602083015260408101516040830152606081015160608301525050565b9285019290840190600101613f0e565b5050508481036020860152613f678189613af5565b925050508281036040840152613f7d8186613df6565b9050828103606084015261189a8185613df6565b600060808284031215613fa357600080fd5b613fab613bb0565b825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b805160ff8116811461200457600080fd5b600080600060608486031215613ffd57600080fd5b835161400881613ebb565b6020850151909350915061401e60408501613fd7565b90509250925092565b805169ffffffffffffffffffff8116811461200457600080fd5b600080600080600060a0868803121561405957600080fd5b61406286614027565b945060208601519350604086015192506060860151915061408560808701614027565b90509295509295909350565b634e487b7160e01b600052601160045260246000fd5b8181038181111561011657610116614091565b60008060008060008060c087890312156140d357600080fd5b86516140de81613ebb565b60208801519096506140ef81613ebb565b94506140fd60408801613fd7565b935061410b60608801613fd7565b9250608087015161411b81613ebb565b60a088015190925063ffffffff8116811461413557600080fd5b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b8181101561418157835163ffffffff168352928401929184019160010161415f565b50909695505050505050565b600082601f83011261419e57600080fd5b815160206141ae613c4f83613c0a565b8083825260208201915060208460051b8701019350868411156141d057600080fd5b602086015b84811015613b8f5780516141e881613ebb565b83529183019183016141d5565b6000806040838503121561420857600080fd5b825167ffffffffffffffff8082111561422057600080fd5b818501915085601f83011261423457600080fd5b81516020614244613c4f83613c0a565b82815260059290921b8401810191818101908984111561426357600080fd5b948201945b838610156142915785518060060b81146142825760008081fd5b82529482019490820190614268565b918801519196509093505050808211156142aa57600080fd5b506142b78582860161418d565b9150509250929050565b600682810b9082900b03667fffffffffffff198112667fffffffffffff8213171561011657610116614091565b634e487b7160e01b600052601260045260246000fd5b60008160060b8360060b8061431b5761431b6142ee565b667fffffffffffff1982146000198214161561433957614339614091565b90059392505050565b600082614351576143516142ee565b500790565b60008160020b627fffff19810361436f5761436f614091565b6000190192915050565b600181815b808511156143b457816000190482111561439a5761439a614091565b808516156143a757918102915b93841c939080029061437e565b509250929050565b6000826143cb57506001610116565b816143d857506000610116565b81600181146143ee57600281146143f857614414565b6001915050610116565b60ff84111561440957614409614091565b50506001821b610116565b5060208310610133831016604e8410600b8410161715614437575081810a610116565b6144418383614379565b806000190482111561445557614455614091565b029392505050565b60006102b460ff8416836143bc565b818103600083128015838313168383128216171561448c5761448c614091565b5092915050565b6000600160ff1b82016144a8576144a8614091565b5060000390565b8051801515811461200457600080fd5b6000806000606084860312156144d457600080fd5b83516144df81613ebb565b6020850151909350915061401e604085016144af565b60006080828403121561450757600080fd5b61450f613bb0565b82518060070b811461452057600080fd5b8152602083015167ffffffffffffffff8116811461453d57600080fd5b60208201526040830151600381900b811461455757600080fd5b60408201526060928301519281019290925250919050565b808201828112600083128015821682158216171561458f5761458f614091565b505092915050565b6000806000606084860312156145ac57600080fd5b83516145b781613ebb565b602085015160409095015190969495509392505050565b60ff8416815267ffffffffffffffff831660208201526060604082015260006145fa6060830184613df6565b95945050505050565b6001600160a01b03831681526040602082018190526000906102ce90830184613af5565b60006020828403121561463957600080fd5b5051919050565b60008261464f5761464f6142ee565b600160ff1b82146000198414161561466957614669614091565b500590565b6146788185613abd565b60606020820152600061468e6060830185613af5565b8281036040840152610f4f8185613df6565b601f8211156146e8576000816000526020600020601f850160051c810160208610156146c95750805b601f850160051c820191505b8181101561325c578281556001016146d5565b505050565b815167ffffffffffffffff81111561470757614707613b9a565b61471b816147158454613dc2565b846146a0565b602080601f83116001811461475057600084156147385750858301515b600019600386901b1c1916600185901b17855561325c565b600085815260208120601f198616915b8281101561477f57888601518255948401946001909101908401614760565b508582101561479d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b8082018082111561011657610116614091565b6000826147cf576147cf6142ee565b500490565b6000826147e3576147e36142ee565b500690565b80820260008212600160ff1b8414161561480457614804614091565b818105831482151761011657610116614091565b600069ffffffffffffffffffff82168061436f5761436f614091565b60006001820161484657614846614091565b5060010190565b60006102b483836143bc565b808202811582820484141761011657610116614091565b60006020828403121561488257600080fd5b6102b4826144af565b60006020828403121561489d57600080fd5b6102b482613fd7565b60006001600160ff1b01820161484657614846614091565b6000600160ff1b82016148d3576148d3614091565b506000190190565b60008160020b627fffff1981036148f4576148f4614091565b60000392915050565b6000825161490f818460208701613ad1565b919091019291505056fea264697066735822122074f32fef384fdc296b0859f1c1f941c8e736c6cb972aa9e2b894956ebd6a80b364736f6c63430008160033","r":"0x1","s":"0x1","yParity":"0x0","hash":"0xbc73db80bf4b8784ba10a8910a0b7ef85f6846d102b41dd990969ea205335354"}}],"ommers":[]},{"header":{"parentHash":"0x026ae0c6ae91f186a9befa1ac8be30eea35e30e77de51a731085221e5cd39209","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0xb6003e7ba07a15a9e35f63daa484728ec4ceeded0c4d10ac1b04e9552d412b3c","transactionsRoot":"0x6e4969a136061ca7a390d12830d47a151585325a8d396819fb2b958ff85e9f8f","receiptsRoot":"0xc3e81df67d3e2a6c8345a954ef250cfcc41abcc2292a5aa263071124533fc9ad","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x3","gasLimit":"0x1c9c380","gasUsed":"0x3c0f6","timestamp":"0x66b200ce","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x18993a68","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gas":"0x3c0f6","maxFeePerGas":"0x5d4285cd","maxPriorityFeePerGas":"0x3b9aca00","value":"0x0","accessList":[],"input":"0x608060405234801561001057600080fd5b50610380806100206000396000f3fe6080604052600080357fffffffff0000000000000000000000000000000000000000000000000000000016905060008160e01c610251565b60006379ba509782101561015e5781631627540c811461009857632a952b2d81146100b457633659cfe681146100d0576350c946fe81146100ec576353a47bb781146101085763625ca21c81146101245763718fe928811461014057610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc91505b5061024c565b816379ba509781146101a657638da5cb5b81146101c25763aaf10f4281146101de5763c7f62cda81146101fa5763daa250be81146102165763deba1b9881146102325761024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b738138ef7cf908021d117e542120b7a39065016107915061024a565b738138ef7cf908021d117e542120b7a3906501610791505b505b919050565b61025a81610037565b915050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036102ce57816040517fc2a825f50000000000000000000000000000000000000000000000000000000081526004016102c5919061032f565b60405180910390fd5b3660008037600080366000845af43d6000803e80600081146102ef573d6000f35b3d6000fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b610329816102f4565b82525050565b60006020820190506103446000830184610320565b9291505056fea264697066735822122017a4b7fdaaab3897a7b47abaed8d2ee92d558883d3bb2a8454f9601b2ab2c3db64736f6c63430008150033","r":"0x1","s":"0x1","yParity":"0x0","hash":"0x2476e039803622aeb040f924f04c493f559aed3d6c9372ab405cb33c8c695328"}}],"ommers":[]},{"header":{"parentHash":"0x3d22100ac0ee8d5cde334f7f926191a861b0648971ebc179547df28a0224c6d0","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x9511d4711e5c30a72b0bff38a261daa75dcc5ba8b772d970a5c742244b4c861b","transactionsRoot":"0xba5fff578d3d6c2cd63acbe9bca353eaa6fe22a5c408956eff49106e0a96c507","receiptsRoot":"0xbae111f01cb07677e3a8c5031546138407c01bc964d3493d732dc4edf47d36d3","logsBloom":"0x00000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000020000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000001000000000000000000000400000001000010000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x5","gasLimit":"0x1c9c380","gasUsed":"0xcae7","timestamp":"0x66b200cb","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x12e09c7a","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gas":"0xcc4d","maxFeePerGas":"0x557e5ec4","maxPriorityFeePerGas":"0x3b9aca00","to":"0x83a0444b93927c3afcbe46e522280390f748e171","value":"0x0","accessList":[],"input":"0x3659cfe6000000000000000000000000108f53faf774d7c4c56f5bce9ca6e605ce8aeadd","r":"0x1","s":"0x1","yParity":"0x0","hash":"0xf88e7b19ee347145c257e0cf7ac4ecc2bae83ca79d7edaa231e71d3213aeb151"}}],"ommers":[]},{"header":{"parentHash":"0x08abe6e453727534d8dd708843a7522b7d500338bdfe2402ca105dcdb05eebe9","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x9c8eaf493f8b4edce2ba1647343eadcc0989cf461e712c0a6253ff2ca1842bb7","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200ca","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0xdd07c07470e1deff3749831f0f1ad8d4b6e35505e83b3c6ea14181716197cd8a","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x29aa352e71b139e83b397bdd3dcf9b65d74770edaf3a9624d0dbc4f96f868680","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200cb","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x24a1ab52","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x0","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200c9","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0xf6930be4847cac5017bbcbec2756eed19f36b4196526a98a88e311c296e3a9be","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x29aa352e71b139e83b397bdd3dcf9b65d74770edaf3a9624d0dbc4f96f868680","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200cc","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x200d75e8","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x08abe6e453727534d8dd708843a7522b7d500338bdfe2402ca105dcdb05eebe9","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0xb6003e7ba07a15a9e35f63daa484728ec4ceeded0c4d10ac1b04e9552d412b3c","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x4","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200ca","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x1592fbf9","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x149d41e3b89d8324cef3feff98ef308e97bafe8745cc8461c60172bc7d4c44ba","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x510f2275449c013534a25ad0b13c867caf720947b68bcbcd4863f7b172a5d023","transactionsRoot":"0x0b44110186e52ff0ceb6b0776ca2992c94144a4ed712eef65ea038260ef0fcc7","receiptsRoot":"0xc2823b8eb4730d9f2657137cc2ddc2c4f22ab68e0ab826236cf6a1551ca2b3a5","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0xe61f9","timestamp":"0x66b200cb","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x342770c0","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gas":"0xe94d1","maxFeePerGas":"0x83215600","maxPriorityFeePerGas":"0x3b9aca00","to":"0x4e59b44847b379578588920ca78fbf26c0b4956c","value":"0x0","accessList":[],"input":"0x4786e4342646b3ba97c1790b6cf5a55087a36240b22570f5d3a5d6bcc929d93b608060405234801561001057600080fd5b5060008061002661006d60201b61081b1760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610141565b60008060405160200161007f90610121565b6040516020818303038152906040528051906020012090508091505090565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b600061010b60238361009e565b9150610116826100af565b604082019050919050565b6000602082019050818103600083015261013a816100fe565b9050919050565b611000806101506000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806379ba50971161005b57806379ba5097146100ed5780638da5cb5b146100f7578063aaf10f4214610115578063c7f62cda1461013357610088565b80631627540c1461008d5780633659cfe6146100a957806353a47bb7146100c5578063718fe928146100e3575b600080fd5b6100a760048036038101906100a29190610d25565b61014f565b005b6100c360048036038101906100be9190610d25565b6102d0565b005b6100cd6102e4565b6040516100da9190610d61565b60405180910390f35b6100eb610317565b005b6100f56103fe565b005b6100ff61058b565b60405161010c9190610d61565b60405180910390f35b61011d6105be565b60405161012a9190610d61565b60405180910390f35b61014d60048036038101906101489190610d25565b6105f1565b005b61015761084c565b600061016161081b565b9050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036101c9576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610252576040517fa88ee57700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22826040516102c49190610d61565b60405180910390a15050565b6102d861084c565b6102e1816108c5565b50565b60006102ee61081b565b60010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600061032161081b565b90503373ffffffffffffffffffffffffffffffffffffffff168160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146103b757336040517fa0e5a0d70000000000000000000000000000000000000000000000000000000081526004016103ae9190610d61565b60405180910390fd5b60008160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600061040861081b565b905060008160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104a357336040517fa0e5a0d700000000000000000000000000000000000000000000000000000000815260040161049a9190610d61565b60405180910390fd5b7fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c8260000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16826040516104f8929190610d7c565b60405180910390a1808260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008260010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b600061059561081b565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006105c8610b05565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006105fb610b05565b905060018160000160146101000a81548160ff02191690831515021790555060008160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050828260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008373ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff16633659cfe6846040516024016106cc9190610d61565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161071b9190610e16565b600060405180830381855af49150503d8060008114610756576040519150601f19603f3d011682016040523d82523d6000602084013e61075b565b606091505b505090508015806107c357508173ffffffffffffffffffffffffffffffffffffffff16610786610b05565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b156107fa576040517fa1cfa5a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008360000160146101000a81548160ff0219169083151502179055600080fd5b60008060405160200161082d90610eb0565b6040516020818303038152906040528051906020012090508091505090565b610854610b36565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146108c357336040517f8e4a23d60000000000000000000000000000000000000000000000000000000081526004016108ba9190610d61565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361092b576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61093481610b69565b61097557806040517f8a8b41ec00000000000000000000000000000000000000000000000000000000815260040161096c9190610d61565b60405180910390fd5b600061097f610b05565b90508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610a0a576040517fa88ee57700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060000160149054906101000a900460ff16158015610a2e5750610a2d82610b7c565b5b15610a7057816040517f15504301000000000000000000000000000000000000000000000000000000008152600401610a679190610d61565b60405180910390fd5b818160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503073ffffffffffffffffffffffffffffffffffffffff167f5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c783604051610af99190610d61565b60405180910390a25050565b600080604051602001610b1790610f42565b6040516020818303038152906040528051906020012090508091505090565b6000610b4061081b565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080823b905060008111915050919050565b60008060003073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1663c7f62cda86604051602401610bc59190610d61565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610c149190610e16565b600060405180830381855af49150503d8060008114610c4f576040519150601f19603f3d011682016040523d82523d6000602084013e610c54565b606091505b509150915081158015610cb9575063a1cfa5a860e01b604051602001610c7a9190610faf565b6040516020818303038152906040528051906020012081604051602001610ca19190610e16565b60405160208183030381529060405280519060200120145b92505050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cf282610cc7565b9050919050565b610d0281610ce7565b8114610d0d57600080fd5b50565b600081359050610d1f81610cf9565b92915050565b600060208284031215610d3b57610d3a610cc2565b5b6000610d4984828501610d10565b91505092915050565b610d5b81610ce7565b82525050565b6000602082019050610d766000830184610d52565b92915050565b6000604082019050610d916000830185610d52565b610d9e6020830184610d52565b9392505050565b600081519050919050565b600081905092915050565b60005b83811015610dd9578082015181840152602081019050610dbe565b60008484015250505050565b6000610df082610da5565b610dfa8185610db0565b9350610e0a818560208601610dbb565b80840191505092915050565b6000610e228284610de5565b915081905092915050565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b6000610e9a602383610e2d565b9150610ea582610e3e565b604082019050919050565b60006020820190508181036000830152610ec981610e8d565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b6000610f2c602183610e2d565b9150610f3782610ed0565b604082019050919050565b60006020820190508181036000830152610f5b81610f1f565b9050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6000819050919050565b610fa9610fa482610f62565b610f8e565b82525050565b6000610fbb8284610f98565b6004820191508190509291505056fea264697066735822122023a7c33d7b91dce35ffbcf8837693364ab22a3905d0fc00016833e5fac45ca2f64736f6c63430008110033","r":"0x1","s":"0x1","yParity":"0x0","hash":"0x4feae6769d748b4f0f7c9bf21d782236c88f13906789a3ec602961296e4c3e43"}}],"ommers":[]},{"header":{"parentHash":"0xb3535af5103fd1c2bbd6dc7ff23f0799037a6542c231ebcb85abd776560fa512","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x23d74fb99ff6e42cbb5c33f92b078e37be6af2b6092459b103ff7059a6517ebc","transactionsRoot":"0x9eab45eca206fe11c107ea985c7d02fcfa442836aea3e04ba11dc4df587d5aa6","receiptsRoot":"0xe25abcfa973db8c55f73292137c626430de130a382ad4466337fefb0f7c8fde0","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x3ce3f","timestamp":"0x66b200cd","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x1c0bc72b","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gas":"0x3d8a8","maxFeePerGas":"0x6211577c","maxPriorityFeePerGas":"0x3b9aca00","to":"0x4e59b44847b379578588920ca78fbf26c0b4956c","value":"0x0","accessList":[],"input":"0x4786e4342646b3ba97c1790b6cf5a55087a36240b22570f5d3a5d6bcc929d93b608060405234801561001057600080fd5b5060405161068538038061068583398181016040528101906100329190610275565b818181600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361009b576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6100ae8161019d60201b61004f1760201c565b6100ef57806040517f8a8b41ec0000000000000000000000000000000000000000000000000000000081526004016100e691906102c4565b60405180910390fd5b806100fe6101b060201b60201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050806101536101e160201b6100621760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505050610414565b600080823b905060008111915050919050565b6000806040516020016101c290610362565b6040516020818303038152906040528051906020012090508091505090565b6000806040516020016101f3906103f4565b6040516020818303038152906040528051906020012090508091505090565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061024282610217565b9050919050565b61025281610237565b811461025d57600080fd5b50565b60008151905061026f81610249565b92915050565b6000806040838503121561028c5761028b610212565b5b600061029a85828601610260565b92505060206102ab85828601610260565b9150509250929050565b6102be81610237565b82525050565b60006020820190506102d960008301846102b5565b92915050565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b600061034c6021836102df565b9150610357826102f0565b604082019050919050565b6000602082019050818103600083015261037b8161033f565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b60006103de6023836102df565b91506103e982610382565b604082019050919050565b6000602082019050818103600083015261040d816103d1565b9050919050565b610262806104236000396000f3fe6080604052366100135761001161001d565b005b61001b61001d565b005b6000610027610093565b90503660008037600080366000845af43d6000803e806000811461004a573d6000f35b3d6000fd5b600080823b905060008111915050919050565b6000806040516020016100749061017a565b6040516020818303038152906040528051906020012090508091505090565b600061009d6100c6565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000806040516020016100d89061020c565b6040516020818303038152906040528051906020012090508091505090565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b60006101646023836100f7565b915061016f82610108565b604082019050919050565b6000602082019050818103600083015261019381610157565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b60006101f66021836100f7565b91506102018261019a565b604082019050919050565b60006020820190508181036000830152610225816101e9565b905091905056fea2646970667358221220800da1f73cebd5e4afa07496d9bca6b6c4f526bdd3f4014ec15c70fe3a1c441364736f6c6343000811003300000000000000000000000047d08dad17ccb558b3ea74b1a0e73a9cc804a9dc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266","r":"0x1","s":"0x1","yParity":"0x0","hash":"0xb6794d5c7abed6f91d447e8efb72ef2580595a6d7c8dee57ba1dbb330970146a"}}],"ommers":[]},{"header":{"parentHash":"0x08abe6e453727534d8dd708843a7522b7d500338bdfe2402ca105dcdb05eebe9","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x510f2275449c013534a25ad0b13c867caf720947b68bcbcd4863f7b172a5d023","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x3","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200ca","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x29dd5614","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]}]} \ No newline at end of file diff --git a/crates/anvil/test-data/state-dump-legacy.json b/crates/anvil/test-data/state-dump-legacy.json index 0641b2f7be91..273442701292 100644 --- a/crates/anvil/test-data/state-dump-legacy.json +++ b/crates/anvil/test-data/state-dump-legacy.json @@ -1 +1 @@ -{"block":{"number":"0x2","coinbase":"0x0000000000000000000000000000000000000000","timestamp":"0x66cdc823","gas_limit":"0x1c9c380","basefee":"0x342a1c58","difficulty":"0x0","prevrandao":"0xb92480171c0235f8c6710a4047d7ee14a3be58c630839fb4422826ff3a013e44","blob_excess_gas_and_price":{"excess_blob_gas":0,"blob_gasprice":1}},"accounts":{"0x0000000000000000000000000000000000000000":{"nonce":0,"balance":"0xa410","code":"0x","storage":{}},"0x14dc79964da2c08b23698b3d3cc7ca32193d9955":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x4e59b44847b379578588920ca78fbf26c0b4956c":{"nonce":0,"balance":"0x0","code":"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3","storage":{}},"0x70997970c51812dc3a010c7d01b50e0d17dc79c8":{"nonce":1,"balance":"0x21e19e0b90393da9b38","code":"0x","storage":{}},"0x90f79bf6eb2c4f870365e785982e1f101e93b906":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x976ea74026e726554db657fa54763abd0c3a0aa9":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xa0ee7a142d267c1f36714e4a8f75612f20a79720":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266":{"nonce":1,"balance":"0x21e19e0b6a140b55df8","code":"0x","storage":{}}},"best_block_number":"0x2","blocks":[{"header":{"parentHash":"0xceb0fe420d6f14a8eeec4319515b89acbb0bb4861cad9983d529ab4b1e4af929","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1","transactionsRoot":"0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdc823","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x342a1c58","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x7a69","nonce":"0x0","gas":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","value":"0x0","accessList":[],"input":"0x","r":"0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0","s":"0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd","yParity":"0x0","hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515"}}],"ommers":[]},{"header":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x0","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66cdc80e","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0xa00dc0c9ee9a888e67ea32d8772f8cc28eff62448c9ec985ee941fcbc921ba59","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x6e5f60b37eeaece7dedfc42cc394731a0ae3ed3d3be93c402780b2e23e141175","transactionsRoot":"0x9ceaeb1b16b924afbf4bf4df4c2c49dc9cfbe23ac7a40bf26a704158ea2d352f","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdc814","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x7a69","nonce":"0x0","gas":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","value":"0x0","accessList":[],"input":"0x","r":"0x703a4b4d6dbff2fa2345df73263df2098faa7214863b5ec82c4c07162d87b853","s":"0x17dea762c4ce600ad1d9d2c9ae6dd35b9e526d03c875f868ad0792fd4fad72e0","yParity":"0x0","hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3"}}],"ommers":[]}]} \ No newline at end of file +{"block":{"number":"0x2","coinbase":"0x0000000000000000000000000000000000000000","timestamp":"0x66cdc823","gas_limit":"0x1c9c380","basefee":"0x342a1c58","difficulty":"0x0","prevrandao":"0xb92480171c0235f8c6710a4047d7ee14a3be58c630839fb4422826ff3a013e44","blob_excess_gas_and_price":{"excess_blob_gas":0,"blob_gasprice":1}},"accounts":{"0x0000000000000000000000000000000000000000":{"nonce":0,"balance":"0xa410","code":"0x","storage":{}},"0x14dc79964da2c08b23698b3d3cc7ca32193d9955":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x4e59b44847b379578588920ca78fbf26c0b4956c":{"nonce":0,"balance":"0x0","code":"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3","storage":{}},"0x70997970c51812dc3a010c7d01b50e0d17dc79c8":{"nonce":1,"balance":"0x21e19e0b90393da9b38","code":"0x","storage":{}},"0x90f79bf6eb2c4f870365e785982e1f101e93b906":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x976ea74026e726554db657fa54763abd0c3a0aa9":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xa0ee7a142d267c1f36714e4a8f75612f20a79720":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266":{"nonce":1,"balance":"0x21e19e0b6a140b55df8","code":"0x","storage":{}}},"best_block_number":"0x2","blocks":[{"header":{"parentHash":"0xceb0fe420d6f14a8eeec4319515b89acbb0bb4861cad9983d529ab4b1e4af929","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1","transactionsRoot":"0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdc823","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x342a1c58","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x7a69","nonce":"0x0","gas":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","value":"0x0","accessList":[],"input":"0x","r":"0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0","s":"0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd","yParity":"0x0","hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515"}}],"ommers":[]},{"header":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x0","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66cdc80e","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0xa00dc0c9ee9a888e67ea32d8772f8cc28eff62448c9ec985ee941fcbc921ba59","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x6e5f60b37eeaece7dedfc42cc394731a0ae3ed3d3be93c402780b2e23e141175","transactionsRoot":"0x9ceaeb1b16b924afbf4bf4df4c2c49dc9cfbe23ac7a40bf26a704158ea2d352f","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdc814","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x7a69","nonce":"0x0","gas":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","value":"0x0","accessList":[],"input":"0x","r":"0x703a4b4d6dbff2fa2345df73263df2098faa7214863b5ec82c4c07162d87b853","s":"0x17dea762c4ce600ad1d9d2c9ae6dd35b9e526d03c875f868ad0792fd4fad72e0","yParity":"0x0","hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3"}}],"ommers":[]}]} \ No newline at end of file diff --git a/crates/anvil/test-data/state-dump.json b/crates/anvil/test-data/state-dump.json index 3a3c478cfffe..e868bf2efea5 100644 --- a/crates/anvil/test-data/state-dump.json +++ b/crates/anvil/test-data/state-dump.json @@ -1 +1 @@ -{"block":{"number":"0x2","coinbase":"0x0000000000000000000000000000000000000000","timestamp":"0x66cdcc2b","gas_limit":"0x1c9c380","basefee":"0x342a1c58","difficulty":"0x0","prevrandao":"0xdb639d7f8af4f0ff2aa9cc49861820e72f5f8bfeeed677d1e3569f6b1625df4a","blob_excess_gas_and_price":{"excess_blob_gas":0,"blob_gasprice":1}},"accounts":{"0x0000000000000000000000000000000000000000":{"nonce":0,"balance":"0xa410","code":"0x","storage":{}},"0x14dc79964da2c08b23698b3d3cc7ca32193d9955":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x4e59b44847b379578588920ca78fbf26c0b4956c":{"nonce":0,"balance":"0x0","code":"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3","storage":{}},"0x70997970c51812dc3a010c7d01b50e0d17dc79c8":{"nonce":1,"balance":"0x21e19e0b90393da9b38","code":"0x","storage":{}},"0x90f79bf6eb2c4f870365e785982e1f101e93b906":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x976ea74026e726554db657fa54763abd0c3a0aa9":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xa0ee7a142d267c1f36714e4a8f75612f20a79720":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266":{"nonce":1,"balance":"0x21e19e0b6a140b55df8","code":"0x","storage":{}}},"best_block_number":"0x2","blocks":[{"header":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x0","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66cdcc25","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x3a52101c98a4319c419681131d3585d70a6cf13a9af25136be20d451eed5480a","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x6e5f60b37eeaece7dedfc42cc394731a0ae3ed3d3be93c402780b2e23e141175","transactionsRoot":"0x9ceaeb1b16b924afbf4bf4df4c2c49dc9cfbe23ac7a40bf26a704158ea2d352f","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdcc29","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"transaction":{"EIP1559":{"chainId":"0x7a69","nonce":"0x0","gas":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","value":"0x0","accessList":[],"input":"0x","r":"0x703a4b4d6dbff2fa2345df73263df2098faa7214863b5ec82c4c07162d87b853","s":"0x17dea762c4ce600ad1d9d2c9ae6dd35b9e526d03c875f868ad0792fd4fad72e0","yParity":"0x0","hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3"}},"impersonated_sender":null}],"ommers":[]},{"header":{"parentHash":"0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1","transactionsRoot":"0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdcc2b","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x342a1c58","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"transaction":{"EIP1559":{"chainId":"0x7a69","nonce":"0x0","gas":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","value":"0x0","accessList":[],"input":"0x","r":"0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0","s":"0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd","yParity":"0x0","hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515"}},"impersonated_sender":null}],"ommers":[]}],"transactions":[{"info":{"transaction_hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3","transaction_index":0,"from":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","contract_address":null,"traces":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","maybe_precompile":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}],"exit":"Stop","out":"0x","nonce":0,"gas_used":21000},"receipt":{"type":"0x2","status":"0x1","cumulativeGasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"block_hash":"0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70","block_number":1},{"info":{"transaction_hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515","transaction_index":0,"from":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","contract_address":null,"traces":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","address":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","maybe_precompile":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}],"exit":"Stop","out":"0x","nonce":0,"gas_used":21000},"receipt":{"type":"0x2","status":"0x1","cumulativeGasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"block_hash":"0x1f435a603c1bf6d544a90156b572b96d7a1730028422d800839bae78bb3506d0","block_number":2}]} \ No newline at end of file +{"block":{"number":"0x2","coinbase":"0x0000000000000000000000000000000000000000","timestamp":"0x66cdcc2b","gas_limit":"0x1c9c380","basefee":"0x342a1c58","difficulty":"0x0","prevrandao":"0xdb639d7f8af4f0ff2aa9cc49861820e72f5f8bfeeed677d1e3569f6b1625df4a","blob_excess_gas_and_price":{"excess_blob_gas":0,"blob_gasprice":1}},"accounts":{"0x0000000000000000000000000000000000000000":{"nonce":0,"balance":"0xa410","code":"0x","storage":{}},"0x14dc79964da2c08b23698b3d3cc7ca32193d9955":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x4e59b44847b379578588920ca78fbf26c0b4956c":{"nonce":0,"balance":"0x0","code":"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3","storage":{}},"0x70997970c51812dc3a010c7d01b50e0d17dc79c8":{"nonce":1,"balance":"0x21e19e0b90393da9b38","code":"0x","storage":{}},"0x90f79bf6eb2c4f870365e785982e1f101e93b906":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x976ea74026e726554db657fa54763abd0c3a0aa9":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xa0ee7a142d267c1f36714e4a8f75612f20a79720":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266":{"nonce":1,"balance":"0x21e19e0b6a140b55df8","code":"0x","storage":{}}},"best_block_number":"0x2","blocks":[{"header":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x0","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66cdcc25","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x3a52101c98a4319c419681131d3585d70a6cf13a9af25136be20d451eed5480a","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x6e5f60b37eeaece7dedfc42cc394731a0ae3ed3d3be93c402780b2e23e141175","transactionsRoot":"0x9ceaeb1b16b924afbf4bf4df4c2c49dc9cfbe23ac7a40bf26a704158ea2d352f","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdcc29","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"transaction":{"EIP1559":{"chainId":"0x7a69","nonce":"0x0","gas":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","value":"0x0","accessList":[],"input":"0x","r":"0x703a4b4d6dbff2fa2345df73263df2098faa7214863b5ec82c4c07162d87b853","s":"0x17dea762c4ce600ad1d9d2c9ae6dd35b9e526d03c875f868ad0792fd4fad72e0","yParity":"0x0","hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3"}},"impersonated_sender":null}],"ommers":[]},{"header":{"parentHash":"0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1","transactionsRoot":"0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdcc2b","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x342a1c58","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"transaction":{"EIP1559":{"chainId":"0x7a69","nonce":"0x0","gas":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","value":"0x0","accessList":[],"input":"0x","r":"0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0","s":"0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd","yParity":"0x0","hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515"}},"impersonated_sender":null}],"ommers":[]}],"transactions":[{"info":{"transaction_hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3","transaction_index":0,"from":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","contract_address":null,"traces":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","maybe_precompile":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}],"exit":"Stop","out":"0x","nonce":0,"gas_used":21000},"receipt":{"type":"0x2","status":"0x1","cumulativeGasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"block_hash":"0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70","block_number":1},{"info":{"transaction_hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515","transaction_index":0,"from":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","contract_address":null,"traces":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","address":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","maybe_precompile":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}],"exit":"Stop","out":"0x","nonce":0,"gas_used":21000},"receipt":{"type":"0x2","status":"0x1","cumulativeGasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"block_hash":"0x1f435a603c1bf6d544a90156b572b96d7a1730028422d800839bae78bb3506d0","block_number":2}]} \ No newline at end of file diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index ce78d72ca59a..b75b088b0e1f 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -13,7 +13,7 @@ use alloy_rpc_types::{ anvil::{ ForkedNetwork, Forking, Metadata, MineOptions, NodeEnvironment, NodeForkConfig, NodeInfo, }, - BlockId, BlockNumberOrTag, TransactionRequest, + BlockId, BlockNumberOrTag, BlockTransactionsKind, TransactionRequest, }; use alloy_serde::WithOtherFields; use anvil::{ @@ -643,7 +643,7 @@ async fn test_fork_revert_call_latest_block_timestamp() { let Multicall::getCurrentBlockCoinbaseReturn { coinbase } = multicall_contract.getCurrentBlockCoinbase().call().await.unwrap(); - assert_eq!(coinbase, latest_block.header.miner); + assert_eq!(coinbase, latest_block.header.beneficiary); } #[tokio::test(flavor = "multi_thread")] @@ -718,14 +718,16 @@ async fn test_reorg() { // The first 3 reorged blocks should have 5 transactions each for num in 14..17 { - let block = provider.get_block_by_number(num.into(), true).await.unwrap(); + let block = + provider.get_block_by_number(num.into(), BlockTransactionsKind::Full).await.unwrap(); let block = block.unwrap(); assert_eq!(block.transactions.len(), 5); } // Verify that historic blocks are still accessible for num in (0..14).rev() { - let _ = provider.get_block_by_number(num.into(), true).await.unwrap(); + let _ = + provider.get_block_by_number(num.into(), BlockTransactionsKind::Full).await.unwrap(); } // Send a few more transaction to verify the chain can still progress @@ -777,7 +779,7 @@ async fn test_reorg() { let signature = accounts[5].sign_transaction_sync(&mut tx).unwrap(); let tx = tx.into_signed(signature); let mut encoded = vec![]; - tx.tx().encode_with_signature(tx.signature(), &mut encoded, false); + tx.eip2718_encode(&mut encoded); let pre_bal = provider.get_balance(accounts[5].address()).await.unwrap(); api.anvil_reorg(ReorgOptions { diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index c4172b265be1..946118af8c07 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -266,7 +266,7 @@ async fn can_call_on_pending_block() { .call() .await .unwrap(); - assert_eq!(block.header.miner, ret_coinbase); + assert_eq!(block.header.beneficiary, ret_coinbase); } } diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 2b965087b4c1..ea195f000b6d 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -1,10 +1,10 @@ use crate::utils::{http_provider, http_provider_with_signer}; -use alloy_consensus::{SidecarBuilder, SimpleCoder}; +use alloy_consensus::{SidecarBuilder, SimpleCoder, Transaction}; use alloy_eips::eip4844::{BLOB_TX_MIN_BLOB_GASPRICE, DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}; use alloy_network::{EthereumWallet, TransactionBuilder, TransactionBuilder4844}; use alloy_primitives::U256; use alloy_provider::Provider; -use alloy_rpc_types::{BlockId, TransactionRequest}; +use alloy_rpc_types::{BlockId, BlockTransactionsKind, TransactionRequest}; use alloy_serde::WithOtherFields; use anvil::{spawn, EthereumHardfork, NodeConfig}; @@ -176,8 +176,14 @@ async fn can_mine_blobs_when_exceeds_max_blobs() { let second_receipt = second_tx.get_receipt().await.unwrap(); let (first_block, second_block) = tokio::join!( - provider.get_block_by_number(first_receipt.block_number.unwrap().into(), false), - provider.get_block_by_number(second_receipt.block_number.unwrap().into(), false) + provider.get_block_by_number( + first_receipt.block_number.unwrap().into(), + BlockTransactionsKind::Hashes + ), + provider.get_block_by_number( + second_receipt.block_number.unwrap().into(), + BlockTransactionsKind::Hashes + ) ); assert_eq!( first_block.unwrap().unwrap().header.blob_gas_used, @@ -239,7 +245,7 @@ async fn can_correctly_estimate_blob_gas_with_recommended_fillers() { receipt.block_number.expect("Failed to get block number") ); - assert!(tx.max_fee_per_blob_gas.unwrap() >= BLOB_TX_MIN_BLOB_GASPRICE); + assert!(tx.max_fee_per_blob_gas().unwrap() >= BLOB_TX_MIN_BLOB_GASPRICE); assert_eq!(receipt.from, alice); assert_eq!(receipt.to, Some(bob)); assert_eq!( @@ -285,7 +291,7 @@ async fn can_correctly_estimate_blob_gas_with_recommended_fillers_with_signer() receipt.block_number.expect("Failed to get block number") ); - assert!(tx.max_fee_per_blob_gas.unwrap() >= BLOB_TX_MIN_BLOB_GASPRICE); + assert!(tx.max_fee_per_blob_gas().unwrap() >= BLOB_TX_MIN_BLOB_GASPRICE); assert_eq!(receipt.from, alice); assert_eq!(receipt.to, Some(bob)); assert_eq!( diff --git a/crates/anvil/tests/it/eip7702.rs b/crates/anvil/tests/it/eip7702.rs index ab787e4ebc76..e10633d6c14d 100644 --- a/crates/anvil/tests/it/eip7702.rs +++ b/crates/anvil/tests/it/eip7702.rs @@ -66,7 +66,7 @@ async fn can_send_eip7702_tx() { let tx = tx.into_signed(signature); let mut encoded = Vec::new(); - tx.tx().encode_with_signature(tx.signature(), &mut encoded, false); + tx.eip2718_encode(&mut encoded); let receipt = provider.send_raw_transaction(&encoded).await.unwrap().get_receipt().await.unwrap(); diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 821395417fa3..3d470894b2b3 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -5,7 +5,7 @@ use crate::{ utils::{http_provider, http_provider_with_signer}, }; use alloy_chains::NamedChain; -use alloy_network::{EthereumWallet, ReceiptResponse, TransactionBuilder}; +use alloy_network::{EthereumWallet, ReceiptResponse, TransactionBuilder, TransactionResponse}; use alloy_primitives::{address, b256, bytes, uint, Address, Bytes, TxHash, TxKind, U256, U64}; use alloy_provider::Provider; use alloy_rpc_types::{ @@ -1407,7 +1407,7 @@ async fn test_immutable_fork_transaction_hash() { api.backend.mined_transaction_by_block_hash_and_index(hash, expected.1.into()) }) .unwrap(); - assert_eq!(tx.inner.hash.to_string(), expected.0.to_string()); + assert_eq!(tx.tx_hash().to_string(), expected.0.to_string()); } } diff --git a/crates/anvil/tests/it/ipc.rs b/crates/anvil/tests/it/ipc.rs index 4f13f8aaf0a1..e5d99e01b333 100644 --- a/crates/anvil/tests/it/ipc.rs +++ b/crates/anvil/tests/it/ipc.rs @@ -54,7 +54,7 @@ async fn test_sub_new_heads_ipc() { let blocks = provider.subscribe_blocks().await.unwrap().into_stream(); let blocks = blocks.take(3).collect::>().await; - let block_numbers = blocks.into_iter().map(|b| b.header.number).collect::>(); + let block_numbers = blocks.into_iter().map(|b| b.number).collect::>(); assert_eq!(block_numbers, vec![1, 2, 3]); } diff --git a/crates/anvil/tests/it/logs.rs b/crates/anvil/tests/it/logs.rs index 3bf09493d8aa..ac644e6e2476 100644 --- a/crates/anvil/tests/it/logs.rs +++ b/crates/anvil/tests/it/logs.rs @@ -7,7 +7,7 @@ use crate::{ use alloy_network::EthereumWallet; use alloy_primitives::{map::B256HashSet, B256}; use alloy_provider::Provider; -use alloy_rpc_types::{BlockNumberOrTag, Filter}; +use alloy_rpc_types::{BlockNumberOrTag, BlockTransactionsKind, Filter}; use anvil::{spawn, NodeConfig}; use futures::StreamExt; @@ -55,7 +55,7 @@ async fn get_past_events() { // and we can fetch the events at a block hash // let hash = provider.get_block(1).await.unwrap().unwrap().hash.unwrap(); let hash = provider - .get_block_by_number(BlockNumberOrTag::from(1), false) + .get_block_by_number(BlockNumberOrTag::from(1), BlockTransactionsKind::Hashes) .await .unwrap() .unwrap() @@ -191,7 +191,10 @@ async fn watch_events() { assert_eq!(log.1.block_number.unwrap(), starting_block_number + i + 1); let hash = provider - .get_block_by_number(BlockNumberOrTag::from(starting_block_number + i + 1), false) + .get_block_by_number( + BlockNumberOrTag::from(starting_block_number + i + 1), + false.into(), + ) .await .unwrap() .unwrap() diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index e839c9986a1f..37d21a29e0b7 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -1,6 +1,7 @@ //! Tests for otterscan endpoints. use crate::abi::Multicall; +use alloy_network::TransactionResponse; use alloy_primitives::{address, Address, Bytes, U256}; use alloy_provider::Provider; use alloy_rpc_types::{ @@ -409,7 +410,7 @@ async fn ots_search_transactions_before() { // check each individual hash result.txs.iter().for_each(|tx| { - assert_eq!(hashes.pop(), Some(tx.hash)); + assert_eq!(hashes.pop(), Some(tx.tx_hash())); }); block = result.txs.last().unwrap().block_number.unwrap(); @@ -444,7 +445,7 @@ async fn ots_search_transactions_after() { // check each individual hash result.txs.iter().rev().for_each(|tx| { - assert_eq!(hashes.pop_back(), Some(tx.hash)); + assert_eq!(hashes.pop_back(), Some(tx.tx_hash())); }); block = result.txs.first().unwrap().block_number.unwrap(); diff --git a/crates/anvil/tests/it/pubsub.rs b/crates/anvil/tests/it/pubsub.rs index ecfb9b1005f0..948456055f24 100644 --- a/crates/anvil/tests/it/pubsub.rs +++ b/crates/anvil/tests/it/pubsub.rs @@ -23,7 +23,7 @@ async fn test_sub_new_heads() { api.anvil_set_interval_mining(1).unwrap(); let blocks = blocks.into_stream().take(3).collect::>().await; - let block_numbers = blocks.into_iter().map(|b| b.header.number).collect::>(); + let block_numbers = blocks.into_iter().map(|b| b.number).collect::>(); assert_eq!(block_numbers, vec![1, 2, 3]); } @@ -262,7 +262,7 @@ async fn test_sub_new_heads_fast() { let mut block_numbers = Vec::new(); for _ in 0..num { api.mine_one().await; - let block_number = blocks.next().await.unwrap().header.number; + let block_number = blocks.next().await.unwrap().number; block_numbers.push(block_number); } diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 07c120d1ce52..0afce986b5e8 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -2,7 +2,7 @@ use crate::{ abi::{Greeter, Multicall, SimpleStorage}, utils::{connect_pubsub, http_provider_with_signer}, }; -use alloy_network::{EthereumWallet, TransactionBuilder}; +use alloy_network::{EthereumWallet, TransactionBuilder, TransactionResponse}; use alloy_primitives::{map::B256HashSet, Address, Bytes, FixedBytes, U256}; use alloy_provider::Provider; use alloy_rpc_types::{ @@ -718,7 +718,7 @@ async fn can_get_pending_transaction() { api.mine_one().await; let mined = provider.get_transaction_by_hash(*tx.tx_hash()).await.unwrap().unwrap(); - assert_eq!(mined.hash, pending.unwrap().unwrap().hash); + assert_eq!(mined.tx_hash(), pending.unwrap().unwrap().tx_hash()); } #[tokio::test(flavor = "multi_thread")] diff --git a/crates/cast/bin/cmd/creation_code.rs b/crates/cast/bin/cmd/creation_code.rs index 04531b6167f3..b444bff3209a 100644 --- a/crates/cast/bin/cmd/creation_code.rs +++ b/crates/cast/bin/cmd/creation_code.rs @@ -1,3 +1,4 @@ +use alloy_consensus::Transaction; use alloy_primitives::{Address, Bytes}; use alloy_provider::{ext::TraceApi, Provider}; use alloy_rpc_types::trace::parity::{Action, CreateAction, CreateOutput, TraceOutput}; @@ -143,9 +144,9 @@ pub async fn fetch_creation_code( let tx_data = provider.get_transaction_by_hash(creation_tx_hash).await?; let tx_data = tx_data.ok_or_eyre("Could not find creation tx data.")?; - let bytecode = if tx_data.inner.to.is_none() { + let bytecode = if tx_data.to().is_none() { // Contract was created using a standard transaction - tx_data.inner.input + tx_data.input().clone() } else { // Contract was created using a factory pattern or create2 // Extract creation code from tx traces diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 880b2fe6f080..79083fa8db9e 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -1,3 +1,5 @@ +use alloy_consensus::Transaction; +use alloy_network::TransactionResponse; use alloy_primitives::U256; use alloy_provider::Provider; use alloy_rpc_types::BlockTransactions; @@ -115,10 +117,11 @@ impl RunArgs { .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))?; // check if the tx is a system transaction - if is_known_system_sender(tx.from) || tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) { + if is_known_system_sender(tx.from) || tx.transaction_type() == Some(SYSTEM_TRANSACTION_TYPE) + { return Err(eyre::eyre!( "{:?} is a system transaction.\nReplaying system transactions is currently not supported.", - tx.hash + tx.tx_hash() )); } @@ -140,7 +143,7 @@ impl RunArgs { if let Some(block) = &block { env.block.timestamp = U256::from(block.header.timestamp); - env.block.coinbase = block.header.miner; + env.block.coinbase = block.header.beneficiary; env.block.difficulty = block.header.difficulty; env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); env.block.basefee = U256::from(block.header.base_fee_per_gas.unwrap_or_default()); @@ -184,27 +187,28 @@ impl RunArgs { // we skip them otherwise this would cause // reverts if is_known_system_sender(tx.from) || - tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) + tx.transaction_type() == Some(SYSTEM_TRANSACTION_TYPE) { pb.set_position((index + 1) as u64); continue; } - if tx.hash == tx_hash { + if tx.tx_hash() == tx_hash { break; } configure_tx_env(&mut env, &tx.inner); - if let Some(to) = tx.to { - trace!(tx=?tx.hash,?to, "executing previous call transaction"); + if let Some(to) = Transaction::to(tx) { + trace!(tx=?tx.tx_hash(),?to, "executing previous call transaction"); executor.transact_with_env(env.clone()).wrap_err_with(|| { format!( "Failed to execute transaction: {:?} in block {}", - tx.hash, env.block.number + tx.tx_hash(), + env.block.number ) })?; } else { - trace!(tx=?tx.hash, "executing previous create transaction"); + trace!(tx=?tx.tx_hash(), "executing previous create transaction"); if let Err(error) = executor.deploy_with_env(env.clone(), None) { match error { // Reverted transactions should be skipped @@ -213,7 +217,8 @@ impl RunArgs { return Err(error).wrap_err_with(|| { format!( "Failed to deploy transaction: {:?} in block {}", - tx.hash, env.block.number + tx.tx_hash(), + env.block.number ) }) } @@ -232,11 +237,11 @@ impl RunArgs { configure_tx_env(&mut env, &tx.inner); - if let Some(to) = tx.to { - trace!(tx=?tx.hash, to=?to, "executing call transaction"); + if let Some(to) = Transaction::to(&tx) { + trace!(tx=?tx.tx_hash(), to=?to, "executing call transaction"); TraceResult::try_from(executor.transact_with_env(env))? } else { - trace!(tx=?tx.hash, "executing create transaction"); + trace!(tx=?tx.tx_hash(), "executing create transaction"); TraceResult::try_from(executor.deploy_with_env(env, None))? } }; diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 75272b3b1a99..39e821dc91f1 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -731,9 +731,9 @@ where .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))?; Ok(if raw { - format!("0x{}", hex::encode(TxEnvelope::try_from(tx.inner)?.encoded_2718())) + format!("0x{}", hex::encode(tx.inner.inner.encoded_2718())) } else if let Some(field) = field { - get_pretty_tx_attr(&tx, field.as_str()) + get_pretty_tx_attr(&tx.inner, field.as_str()) .ok_or_else(|| eyre::eyre!("invalid tx field: {}", field.to_string()))? } else if shell::is_json() { // to_value first to sort json object keys @@ -995,7 +995,7 @@ where Either::Right(futures::future::pending()) } => { if let (Some(block), Some(to_block)) = (block, to_block_number) { - if block.header.number > to_block { + if block.number > to_block { break; } } diff --git a/crates/cheatcodes/src/crypto.rs b/crates/cheatcodes/src/crypto.rs index cdb07720c328..3fd13d9b06e9 100644 --- a/crates/cheatcodes/src/crypto.rs +++ b/crates/cheatcodes/src/crypto.rs @@ -15,7 +15,9 @@ use k256::{ ecdsa::SigningKey, elliptic_curve::{bigint::ArrayEncoding, sec1::ToEncodedPoint}, }; -use p256::ecdsa::{signature::hazmat::PrehashSigner, Signature, SigningKey as P256SigningKey}; +use p256::ecdsa::{ + signature::hazmat::PrehashSigner, Signature as P256Signature, SigningKey as P256SigningKey, +}; /// The BIP32 default derivation path prefix. const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/"; @@ -215,23 +217,23 @@ fn create_wallet(private_key: &U256, label: Option<&str>, state: &mut Cheatcodes .abi_encode()) } -fn encode_full_sig(sig: alloy_primitives::Signature) -> Vec { +fn encode_full_sig(sig: alloy_primitives::PrimitiveSignature) -> Vec { // Retrieve v, r and s from signature. - let v = U256::from(sig.v().y_parity_byte_non_eip155().unwrap_or(sig.v().y_parity_byte())); + let v = U256::from(sig.v() as u64 + 27); let r = B256::from(sig.r()); let s = B256::from(sig.s()); (v, r, s).abi_encode() } -fn encode_compact_sig(sig: alloy_primitives::Signature) -> Vec { +fn encode_compact_sig(sig: alloy_primitives::PrimitiveSignature) -> Vec { // Implement EIP-2098 compact signature. let r = B256::from(sig.r()); let mut vs = sig.s(); - vs.set_bit(255, sig.v().y_parity()); + vs.set_bit(255, sig.v()); (r, vs).abi_encode() } -fn sign(private_key: &U256, digest: &B256) -> Result { +fn sign(private_key: &U256, digest: &B256) -> Result { // The `ecrecover` precompile does not use EIP-155. No chain ID is needed. let wallet = parse_wallet(private_key)?; let sig = wallet.sign_hash_sync(digest)?; @@ -243,7 +245,7 @@ fn sign_with_wallet( state: &mut Cheatcodes, signer: Option
, digest: &B256, -) -> Result { +) -> Result { if state.wallets().is_empty() { bail!("no wallets available"); } @@ -273,7 +275,7 @@ fn sign_with_wallet( fn sign_p256(private_key: &U256, digest: &B256) -> Result { let signing_key = parse_private_key_p256(private_key)?; - let signature: Signature = signing_key.sign_prehash(digest.as_slice())?; + let signature: P256Signature = signing_key.sign_prehash(digest.as_slice())?; let r_bytes: [u8; 32] = signature.r().to_bytes().into(); let s_bytes: [u8; 32] = signature.s().to_bytes().into(); @@ -403,7 +405,7 @@ mod tests { let result = sign_p256(&pk_u256, &digest).unwrap(); let result_bytes: [u8; 64] = result.try_into().unwrap(); - let signature = Signature::from_bytes(&result_bytes.into()).unwrap(); + let signature = P256Signature::from_bytes(&result_bytes.into()).unwrap(); let verifying_key = VerifyingKey::from(&signing_key); assert!(verifying_key.verify_prehash(digest.as_slice(), &signature).is_ok()); } diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 3e1239237e83..b28141ae002e 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -1,9 +1,9 @@ //! Implementations of [`Scripting`](spec::Group::Scripting) cheatcodes. use crate::{Cheatcode, CheatsCtxt, Result, Vm::*}; -use alloy_primitives::{Address, B256, U256}; +use alloy_primitives::{Address, PrimitiveSignature, B256, U256}; use alloy_rpc_types::Authorization; -use alloy_signer::{Signature, SignerSync}; +use alloy_signer::SignerSync; use alloy_signer_local::PrivateKeySigner; use alloy_sol_types::SolValue; use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner}; @@ -104,9 +104,13 @@ fn write_delegation(ccx: &mut CheatsCtxt, auth: SignedAuthorization) -> Result<( Ok(()) } -fn sig_to_delegation(sig: Signature, nonce: u64, implementation: Address) -> SignedDelegation { +fn sig_to_delegation( + sig: PrimitiveSignature, + nonce: u64, + implementation: Address, +) -> SignedDelegation { SignedDelegation { - v: sig.v().y_parity() as u8, + v: sig.v() as u8, r: sig.r().into(), s: sig.s().into(), nonce, @@ -114,8 +118,8 @@ fn sig_to_delegation(sig: Signature, nonce: u64, implementation: Address) -> Sig } } -fn sig_to_auth(sig: Signature, auth: Authorization) -> SignedAuthorization { - SignedAuthorization::new_unchecked(auth, sig.v().y_parity() as u8, sig.r(), sig.s()) +fn sig_to_auth(sig: PrimitiveSignature, auth: Authorization) -> SignedAuthorization { + SignedAuthorization::new_unchecked(auth, sig.v() as u8, sig.r(), sig.s()) } impl Cheatcode for startBroadcast_0Call { diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index 5534e72d8355..a82d2cdc3d3c 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -1,11 +1,14 @@ //! Helper trait and functions to format Ethereum types. -use alloy_consensus::{AnyReceiptEnvelope, Eip658Value, Receipt, ReceiptWithBloom, TxType}; -use alloy_network::ReceiptResponse; -use alloy_primitives::{hex, Address, Bloom, Bytes, FixedBytes, Uint, B256, I256, U256, U64}; +use alloy_consensus::{ + AnyReceiptEnvelope, Eip658Value, Receipt, ReceiptWithBloom, Transaction as TxTrait, TxEnvelope, + TxType, +}; +use alloy_network::{AnyHeader, AnyRpcBlock, AnyTxEnvelope, ReceiptResponse}; +use alloy_primitives::{hex, Address, Bloom, Bytes, FixedBytes, Uint, I256, U256, U64, U8}; use alloy_rpc_types::{ - AccessListItem, AnyNetworkBlock, AnyTransactionReceipt, Block, BlockTransactions, Header, Log, - Transaction, TransactionReceipt, + AccessListItem, AnyTransactionReceipt, Block, BlockTransactions, Header, Log, Transaction, + TransactionReceipt, }; use alloy_serde::{OtherFields, WithOtherFields}; use serde::Deserialize; @@ -267,7 +270,7 @@ transactionIndex: {}", } } -impl UIfmt for Block { +impl UIfmt for Block> { fn pretty(&self) -> String { format!( " @@ -317,10 +320,210 @@ impl UIfmt for AccessListItem { } } +impl UIfmt for TxEnvelope { + fn pretty(&self) -> String { + match &self { + Self::Eip2930(tx) => format!( + " +accessList {} +chainId {} +gasLimit {} +gasPrice {} +hash {} +input {} +nonce {} +r {} +s {} +to {} +type {} +value {} +yParity {}", + self.access_list() + .map(|a| a.iter().collect::>()) + .unwrap_or_default() + .pretty(), + self.chain_id().pretty(), + self.gas_limit().pretty(), + self.gas_price().pretty(), + self.tx_hash().pretty(), + self.input().pretty(), + self.nonce().pretty(), + FixedBytes::from(tx.signature().r()).pretty(), + FixedBytes::from(tx.signature().s()).pretty(), + self.to().pretty(), + self.ty(), + self.value().pretty(), + (if tx.signature().v() { 1u64 } else { 0 }).pretty(), + ), + Self::Eip1559(tx) => format!( + " +accessList {} +chainId {} +gasLimit {} +hash {} +input {} +maxFeePerGas {} +maxPriorityFeePerGas {} +nonce {} +r {} +s {} +to {} +type {} +value {} +yParity {}", + self.access_list() + .map(|a| a.iter().collect::>()) + .unwrap_or_default() + .pretty(), + self.chain_id().pretty(), + self.gas_limit().pretty(), + self.tx_hash().pretty(), + self.input().pretty(), + self.max_fee_per_gas().pretty(), + self.max_priority_fee_per_gas().pretty(), + self.nonce().pretty(), + FixedBytes::from(tx.signature().r()).pretty(), + FixedBytes::from(tx.signature().s()).pretty(), + self.to().pretty(), + self.ty(), + self.value().pretty(), + (if tx.signature().v() { 1u64 } else { 0 }).pretty(), + ), + Self::Eip4844(tx) => format!( + " +accessList {} +blobVersionedHashes {} +chainId {} +gasLimit {} +hash {} +input {} +maxFeePerBlobGas {} +maxFeePerGas {} +maxPriorityFeePerGas {} +nonce {} +r {} +s {} +to {} +type {} +value {} +yParity {}", + self.access_list() + .map(|a| a.iter().collect::>()) + .unwrap_or_default() + .pretty(), + self.blob_versioned_hashes().unwrap_or(&[]).pretty(), + self.chain_id().pretty(), + self.gas_limit().pretty(), + self.tx_hash().pretty(), + self.input().pretty(), + self.max_fee_per_blob_gas().pretty(), + self.max_fee_per_gas().pretty(), + self.max_priority_fee_per_gas().pretty(), + self.nonce().pretty(), + FixedBytes::from(tx.signature().r()).pretty(), + FixedBytes::from(tx.signature().s()).pretty(), + self.to().pretty(), + self.ty(), + self.value().pretty(), + (if tx.signature().v() { 1u64 } else { 0 }).pretty(), + ), + Self::Eip7702(tx) => format!( + " +accessList {} +authorizationList {} +chainId {} +gasLimit {} +hash {} +input {} +maxFeePerGas {} +maxPriorityFeePerGas {} +nonce {} +r {} +s {} +to {} +type {} +value {} +yParity {}", + self.access_list() + .map(|a| a.iter().collect::>()) + .unwrap_or_default() + .pretty(), + self.authorization_list() + .as_ref() + .map(|l| serde_json::to_string(&l).unwrap()) + .unwrap_or_default(), + self.chain_id().pretty(), + self.gas_limit().pretty(), + self.tx_hash().pretty(), + self.input().pretty(), + self.max_fee_per_gas().pretty(), + self.max_priority_fee_per_gas().pretty(), + self.nonce().pretty(), + FixedBytes::from(tx.signature().r()).pretty(), + FixedBytes::from(tx.signature().s()).pretty(), + self.to().pretty(), + self.ty(), + self.value().pretty(), + (if tx.signature().v() { 1u64 } else { 0 }).pretty(), + ), + _ => format!( + " +gas {} +gasPrice {} +hash {} +input {} +nonce {} +r {} +s {} +to {} +type {} +v {} +value {}", + self.gas_limit().pretty(), + self.gas_price().pretty(), + self.tx_hash().pretty(), + self.input().pretty(), + self.nonce().pretty(), + self.as_legacy() + .map(|tx| FixedBytes::from(tx.signature().r()).pretty()) + .unwrap_or_default(), + self.as_legacy() + .map(|tx| FixedBytes::from(tx.signature().s()).pretty()) + .unwrap_or_default(), + self.to().pretty(), + self.ty(), + self.as_legacy() + .map(|tx| (if tx.signature().v() { 1u64 } else { 0 }).pretty()) + .unwrap_or_default(), + self.value().pretty(), + ), + } + } +} + +impl UIfmt for AnyTxEnvelope { + fn pretty(&self) -> String { + match self { + Self::Ethereum(envelop) => envelop.pretty(), + Self::Unknown(tx) => { + format!( + " +hash {} +type {} +{} + ", + tx.hash.pretty(), + tx.ty(), + tx.inner.fields.pretty(), + ) + } + } + } +} impl UIfmt for Transaction { fn pretty(&self) -> String { - match self.transaction_type { - Some(1) => format!( + match &self.inner { + TxEnvelope::Eip2930(tx) => format!( " accessList {} blockHash {} @@ -339,25 +542,29 @@ transactionIndex {} type {} value {} yParity {}", - self.access_list.as_deref().map(Vec::as_slice).unwrap_or(&[]).pretty(), + self.inner + .access_list() + .map(|a| a.iter().collect::>()) + .unwrap_or_default() + .pretty(), self.block_hash.pretty(), self.block_number.pretty(), - self.chain_id.pretty(), + self.chain_id().pretty(), self.from.pretty(), - self.gas.pretty(), - self.gas_price.pretty(), - self.hash.pretty(), - self.input.pretty(), - self.nonce.pretty(), - self.signature.map(|s| s.r.to_be_bytes_vec()).pretty(), - self.signature.map(|s| s.s.to_be_bytes_vec()).pretty(), - self.to.pretty(), + self.gas_limit().pretty(), + self.gas_price().pretty(), + self.inner.tx_hash().pretty(), + self.input().pretty(), + self.nonce().pretty(), + FixedBytes::from(tx.signature().r()).pretty(), + FixedBytes::from(tx.signature().s()).pretty(), + self.to().pretty(), self.transaction_index.pretty(), - self.transaction_type.unwrap(), - self.value.pretty(), - self.signature.map(|s| s.v).pretty(), + self.inner.ty(), + self.value().pretty(), + (if tx.signature().v() { 1u64 } else { 0 }).pretty(), ), - Some(2) => format!( + TxEnvelope::Eip1559(tx) => format!( " accessList {} blockHash {} @@ -377,26 +584,30 @@ transactionIndex {} type {} value {} yParity {}", - self.access_list.as_deref().map(Vec::as_slice).unwrap_or(&[]).pretty(), + self.inner + .access_list() + .map(|a| a.iter().collect::>()) + .unwrap_or_default() + .pretty(), self.block_hash.pretty(), self.block_number.pretty(), - self.chain_id.pretty(), + self.chain_id().pretty(), self.from.pretty(), - self.gas.pretty(), - self.hash.pretty(), - self.input.pretty(), - self.max_fee_per_gas.pretty(), - self.max_priority_fee_per_gas.pretty(), - self.nonce.pretty(), - self.signature.map(|s| s.r.to_be_bytes_vec()).pretty(), - self.signature.map(|s| s.s.to_be_bytes_vec()).pretty(), - self.to.pretty(), + self.gas_limit().pretty(), + tx.hash().pretty(), + self.input().pretty(), + self.max_fee_per_gas().pretty(), + self.max_priority_fee_per_gas().pretty(), + self.nonce().pretty(), + FixedBytes::from(tx.signature().r()).pretty(), + FixedBytes::from(tx.signature().s()).pretty(), + self.to().pretty(), self.transaction_index.pretty(), - self.transaction_type.unwrap(), - self.value.pretty(), - self.signature.map(|s| s.v).pretty(), + self.inner.ty(), + self.value().pretty(), + (if tx.signature().v() { 1u64 } else { 0 }).pretty(), ), - Some(3) => format!( + TxEnvelope::Eip4844(tx) => format!( " accessList {} blobVersionedHashes {} @@ -418,28 +629,32 @@ transactionIndex {} type {} value {} yParity {}", - self.access_list.as_deref().map(Vec::as_slice).unwrap_or(&[]).pretty(), - self.blob_versioned_hashes.as_deref().unwrap_or(&[]).pretty(), + self.inner + .access_list() + .map(|a| a.iter().collect::>()) + .unwrap_or_default() + .pretty(), + self.blob_versioned_hashes().unwrap_or(&[]).pretty(), self.block_hash.pretty(), self.block_number.pretty(), - self.chain_id.pretty(), + self.chain_id().pretty(), self.from.pretty(), - self.gas.pretty(), - self.hash.pretty(), - self.input.pretty(), - self.max_fee_per_blob_gas.pretty(), - self.max_fee_per_gas.pretty(), - self.max_priority_fee_per_gas.pretty(), - self.nonce.pretty(), - self.signature.map(|s| s.r.to_be_bytes_vec()).pretty(), - self.signature.map(|s| s.s.to_be_bytes_vec()).pretty(), - self.to.pretty(), + self.gas_limit().pretty(), + tx.hash().pretty(), + self.input().pretty(), + self.max_fee_per_blob_gas().pretty(), + self.max_fee_per_gas().pretty(), + self.max_priority_fee_per_gas().pretty(), + self.nonce().pretty(), + FixedBytes::from(tx.signature().r()).pretty(), + FixedBytes::from(tx.signature().s()).pretty(), + self.to().pretty(), self.transaction_index.pretty(), - self.transaction_type.unwrap(), - self.value.pretty(), - self.signature.map(|s| s.v).pretty(), + self.inner.ty(), + self.value().pretty(), + (if tx.signature().v() { 1u64 } else { 0 }).pretty(), ), - Some(4) => format!( + TxEnvelope::Eip7702(tx) => format!( " accessList {} authorizationList {} @@ -460,28 +675,32 @@ transactionIndex {} type {} value {} yParity {}", - self.access_list.as_deref().map(Vec::as_slice).unwrap_or(&[]).pretty(), - self.authorization_list + self.inner + .access_list() + .map(|a| a.iter().collect::>()) + .unwrap_or_default() + .pretty(), + self.authorization_list() .as_ref() .map(|l| serde_json::to_string(&l).unwrap()) .unwrap_or_default(), self.block_hash.pretty(), self.block_number.pretty(), - self.chain_id.pretty(), + self.chain_id().pretty(), self.from.pretty(), - self.gas.pretty(), - self.hash.pretty(), - self.input.pretty(), - self.max_fee_per_gas.pretty(), - self.max_priority_fee_per_gas.pretty(), - self.nonce.pretty(), - self.signature.map(|s| s.r.to_be_bytes_vec()).pretty(), - self.signature.map(|s| s.s.to_be_bytes_vec()).pretty(), - self.to.pretty(), + self.gas_limit().pretty(), + tx.hash().pretty(), + self.input().pretty(), + self.max_fee_per_gas().pretty(), + self.max_priority_fee_per_gas().pretty(), + self.nonce().pretty(), + FixedBytes::from(tx.signature().r()).pretty(), + FixedBytes::from(tx.signature().s()).pretty(), + self.to().pretty(), self.transaction_index.pretty(), - self.transaction_type.unwrap(), - self.value.pretty(), - self.signature.map(|s| s.v).pretty(), + self.inner.ty(), + self.value().pretty(), + (if tx.signature().v() { 1u64 } else { 0 }).pretty(), ), _ => format!( " @@ -502,22 +721,52 @@ value {}", self.block_hash.pretty(), self.block_number.pretty(), self.from.pretty(), - self.gas.pretty(), - self.gas_price.pretty(), - self.hash.pretty(), - self.input.pretty(), - self.nonce, - self.signature.map(|s| s.r.to_be_bytes_vec()).pretty(), - self.signature.map(|s| s.s.to_be_bytes_vec()).pretty(), - self.to.pretty(), + self.gas_limit().pretty(), + self.gas_price().pretty(), + self.inner.tx_hash().pretty(), + self.input().pretty(), + self.nonce().pretty(), + self.inner + .as_legacy() + .map(|tx| FixedBytes::from(tx.signature().r()).pretty()) + .unwrap_or_default(), + self.inner + .as_legacy() + .map(|tx| FixedBytes::from(tx.signature().s()).pretty()) + .unwrap_or_default(), + self.to().pretty(), self.transaction_index.pretty(), - self.signature.map(|s| s.v).pretty(), - self.value.pretty(), + self.inner + .as_legacy() + .map(|tx| (if tx.signature().v() { 1u64 } else { 0 }).pretty()) + .unwrap_or_default(), + self.value().pretty(), ), } } } +impl UIfmt for Transaction { + fn pretty(&self) -> String { + format!( + " +blockHash {} +blockNumber {} +from {} +transactionIndex {} +effectiveGasPrice {} +{} + ", + self.block_hash.pretty(), + self.block_number.pretty(), + self.from.pretty(), + self.transaction_index.pretty(), + self.effective_gas_price.pretty(), + self.inner.pretty(), + ) + } +} + impl UIfmt for WithOtherFields { fn pretty(&self) -> String { format!("{}{}", self.inner.pretty(), self.other.pretty()) @@ -555,28 +804,39 @@ impl UIfmt for EthValue { } /// Returns the `UiFmt::pretty()` formatted attribute of the transactions -pub fn get_pretty_tx_attr(transaction: &Transaction, attr: &str) -> Option { +pub fn get_pretty_tx_attr(transaction: &Transaction, attr: &str) -> Option { + let sig = match &transaction.inner { + AnyTxEnvelope::Ethereum(envelope) => match &envelope { + TxEnvelope::Eip2930(tx) => Some(tx.signature()), + TxEnvelope::Eip1559(tx) => Some(tx.signature()), + TxEnvelope::Eip4844(tx) => Some(tx.signature()), + TxEnvelope::Eip7702(tx) => Some(tx.signature()), + TxEnvelope::Legacy(tx) => Some(tx.signature()), + _ => None, + }, + _ => None, + }; match attr { "blockHash" | "block_hash" => Some(transaction.block_hash.pretty()), "blockNumber" | "block_number" => Some(transaction.block_number.pretty()), "from" => Some(transaction.from.pretty()), - "gas" => Some(transaction.gas.pretty()), - "gasPrice" | "gas_price" => Some(transaction.gas_price.pretty()), - "hash" => Some(transaction.hash.pretty()), - "input" => Some(transaction.input.pretty()), - "nonce" => Some(transaction.nonce.to_string()), - "s" => transaction.signature.map(|s| B256::from(s.s).pretty()), - "r" => transaction.signature.map(|s| B256::from(s.r).pretty()), - "to" => Some(transaction.to.pretty()), + "gas" => Some(transaction.gas_limit().pretty()), + "gasPrice" | "gas_price" => Some(Transaction::gas_price(transaction).pretty()), + "hash" => Some(alloy_network::TransactionResponse::tx_hash(transaction).pretty()), + "input" => Some(transaction.input().pretty()), + "nonce" => Some(transaction.nonce().to_string()), + "s" => sig.map(|s| FixedBytes::from(s.s()).pretty()), + "r" => sig.map(|s| FixedBytes::from(s.r()).pretty()), + "to" => Some(transaction.to().pretty()), "transactionIndex" | "transaction_index" => Some(transaction.transaction_index.pretty()), - "v" => transaction.signature.map(|s| s.v.pretty()), - "value" => Some(transaction.value.pretty()), + "v" => sig.map(|s| U8::from_be_slice(&s.as_bytes()[64..]).pretty()), + "value" => Some(transaction.value().pretty()), _ => None, } } /// Returns the `UiFmt::pretty()` formatted attribute of the given block -pub fn get_pretty_block_attr(block: &AnyNetworkBlock, attr: &str) -> Option { +pub fn get_pretty_block_attr(block: &AnyRpcBlock, attr: &str) -> Option { match attr { "baseFeePerGas" | "base_fee_per_gas" => Some(block.header.base_fee_per_gas.pretty()), "difficulty" => Some(block.header.difficulty.pretty()), @@ -585,15 +845,15 @@ pub fn get_pretty_block_attr(block: &AnyNetworkBlock, attr: &str) -> Option Some(block.header.gas_used.pretty()), "hash" => Some(block.header.hash.pretty()), "logsBloom" | "logs_bloom" => Some(block.header.logs_bloom.pretty()), - "miner" | "author" => Some(block.header.miner.pretty()), + "miner" | "author" => Some(block.header.inner.beneficiary.pretty()), "mixHash" | "mix_hash" => Some(block.header.mix_hash.pretty()), "nonce" => Some(block.header.nonce.pretty()), "number" => Some(block.header.number.pretty()), "parentHash" | "parent_hash" => Some(block.header.parent_hash.pretty()), "transactionsRoot" | "transactions_root" => Some(block.header.transactions_root.pretty()), "receiptsRoot" | "receipts_root" => Some(block.header.receipts_root.pretty()), - "sha3Uncles" | "sha_3_uncles" => Some(block.header.uncles_hash.pretty()), - "size" => Some(block.size.pretty()), + "sha3Uncles" | "sha_3_uncles" => Some(block.header.ommers_hash.pretty()), + "size" => Some(block.header.size.pretty()), "stateRoot" | "state_root" => Some(block.header.state_root.pretty()), "timestamp" => Some(block.header.timestamp.pretty()), "totalDifficulty" | "total_difficult" => Some(block.header.total_difficulty.pretty()), @@ -610,37 +870,40 @@ pub fn get_pretty_block_attr(block: &AnyNetworkBlock, attr: &str) -> Option(block: &Block) -> String { +fn pretty_block_basics(block: &Block>) -> String { let Block { header: Header { hash, - parent_hash, - uncles_hash, - miner, - state_root, - transactions_root, - receipts_root, - logs_bloom, - difficulty, - number, - gas_limit, - gas_used, - timestamp, + size, total_difficulty, - extra_data, - mix_hash, - nonce, - base_fee_per_gas, - withdrawals_root, - blob_gas_used, - excess_blob_gas, - parent_beacon_block_root, - requests_hash, + inner: + AnyHeader { + parent_hash, + ommers_hash, + beneficiary, + state_root, + transactions_root, + receipts_root, + logs_bloom, + difficulty, + number, + gas_limit, + gas_used, + timestamp, + extra_data, + mix_hash, + nonce, + base_fee_per_gas, + withdrawals_root, + blob_gas_used, + excess_blob_gas, + parent_beacon_block_root, + requests_hash, + }, }, uncles: _, transactions: _, - size, withdrawals: _, } = block; format!( @@ -676,7 +939,7 @@ requestsHash {}", gas_used.pretty(), hash.pretty(), logs_bloom.pretty(), - miner.pretty(), + beneficiary.pretty(), mix_hash.pretty(), nonce.pretty(), number.pretty(), @@ -684,7 +947,7 @@ requestsHash {}", parent_beacon_block_root.pretty(), transactions_root.pretty(), receipts_root.pretty(), - uncles_hash.pretty(), + ommers_hash.pretty(), size.pretty(), state_root.pretty(), timestamp.pretty(), @@ -702,6 +965,7 @@ requestsHash {}", #[cfg(test)] mod tests { use super::*; + use alloy_primitives::B256; use similar_asserts::assert_eq; use std::str::FromStr; @@ -764,7 +1028,7 @@ r 0x6fca94073a0cf3381978662d46cf890602d3e9ccf6a31e4b69e8ecbd9 s 0x0e804161a2b56a37ca1f6f4c4b8bce926587afa0d9b1acc5165e6556c959d583 to 0x4a16A42407AA491564643E1dfc1fd50af29794eF transactionIndex 0 -v 56 +v 1 value 0 index 435 l1BlockNumber 12691036 @@ -973,7 +1237,7 @@ r 0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99 s 0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e to 0xdca8ce283150AB773BCbeB8d38289bdB5661dE1e transactionIndex 0 -v 37 +v 0 value 0".to_string(); let txs = match block.transactions { BlockTransactions::Full(txs) => txs, @@ -1027,11 +1291,12 @@ value 0".to_string(); #[test] fn test_pretty_tx_attr() { let block = r#"{"number":"0x3","hash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","parentHash":"0x689c70c080ca22bc0e681694fa803c1aba16a69c8b6368fed5311d279eb9de90","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x7270c1c4440180f2bd5215809ee3d545df042b67329499e1ab97eb759d31610d","stateRoot":"0x29f32984517a7d25607da485b23cefabfd443751422ca7e603395e1de9bc8a4b","receiptsRoot":"0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2","miner":"0x0000000000000000000000000000000000000000","difficulty":"0x0","totalDifficulty":"0x0","extraData":"0x","size":"0x3e8","gasLimit":"0x6691b7","gasUsed":"0x5208","timestamp":"0x5ecedbb9","transactions":[{"hash":"0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067","nonce":"0x2","blockHash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","blockNumber":"0x3","transactionIndex":"0x0","from":"0xfdcedc3bfca10ecb0890337fbdd1977aba84807a","to":"0xdca8ce283150ab773bcbeb8d38289bdb5661de1e","value":"0x0","gas":"0x15f90","gasPrice":"0x4a817c800","input":"0x","v":"0x25","r":"0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88","s":"0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e"}],"uncles":[]}"#; - let block: Block = serde_json::from_str(block).unwrap(); + let block: Block> = serde_json::from_str(block).unwrap(); let txs = match block.transactions { BlockTransactions::Full(txes) => txes, _ => panic!("not full transactions"), }; + assert_eq!(None, get_pretty_tx_attr(&txs[0], "")); assert_eq!(Some("3".to_string()), get_pretty_tx_attr(&txs[0], "blockNumber")); assert_eq!( @@ -1059,7 +1324,7 @@ value 0".to_string(); get_pretty_tx_attr(&txs[0], "to") ); assert_eq!(Some("0".to_string()), get_pretty_tx_attr(&txs[0], "transactionIndex")); - assert_eq!(Some("37".to_string()), get_pretty_tx_attr(&txs[0], "v")); + assert_eq!(Some("27".to_string()), get_pretty_tx_attr(&txs[0], "v")); assert_eq!(Some("0".to_string()), get_pretty_tx_attr(&txs[0], "value")); } @@ -1096,7 +1361,7 @@ value 0".to_string(); } ); - let block: AnyNetworkBlock = serde_json::from_value(json).unwrap(); + let block: AnyRpcBlock = serde_json::from_value(json).unwrap(); assert_eq!(None, get_pretty_block_attr(&block, "")); assert_eq!(Some("7".to_string()), get_pretty_block_attr(&block, "baseFeePerGas")); diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index f0ed0b54f8bd..b319da0d877b 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -58,7 +58,7 @@ impl TransactionReceiptWithRevertReason { if let Some(block_hash) = self.receipt.block_hash { match provider - .call(&WithOtherFields::new(transaction.inner.into())) + .call(&transaction.inner.inner.into()) .block(BlockId::Hash(block_hash.into())) .await { diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index c9f5131d7f34..2abf9b22fed5 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -29,8 +29,9 @@ alloy-primitives = { workspace = true, features = [ "rlp", ] } alloy-provider.workspace = true +alloy-network.workspace = true +alloy-consensus.workspace = true alloy-rpc-types.workspace = true -alloy-serde.workspace = true alloy-sol-types.workspace = true alloy-transport.workspace = true foundry-fork-db.workspace = true diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index e70468ef944c..2db34ad290b0 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -7,10 +7,11 @@ use crate::{ utils::{configure_tx_env, configure_tx_req_env, new_evm_with_inspector}, InspectorExt, }; +use alloy_consensus::Transaction as TransactionTrait; use alloy_genesis::GenesisAccount; +use alloy_network::{AnyRpcBlock, AnyTxEnvelope, TransactionResponse}; use alloy_primitives::{keccak256, uint, Address, TxKind, B256, U256}; -use alloy_rpc_types::{Block, BlockNumberOrTag, Transaction, TransactionRequest}; -use alloy_serde::WithOtherFields; +use alloy_rpc_types::{BlockNumberOrTag, Transaction, TransactionRequest}; use eyre::Context; use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; pub use foundry_fork_db::{cache::BlockchainDbMeta, BlockchainDb, SharedBackend}; @@ -839,7 +840,7 @@ impl Backend { &self, id: LocalForkId, transaction: B256, - ) -> eyre::Result<(u64, Block>)> { + ) -> eyre::Result<(u64, AnyRpcBlock)> { let fork = self.inner.get_fork_by_id(id)?; let tx = fork.db.db.get_transaction(transaction)?; @@ -850,14 +851,12 @@ impl Backend { // we need to subtract 1 here because we want the state before the transaction // was mined let fork_block = tx_block - 1; - Ok((fork_block, block.inner)) + Ok((fork_block, block)) } else { let block = fork.db.db.get_full_block(BlockNumberOrTag::Latest)?; let number = block.header.number; - let block = block.inner; - Ok((number, block)) } } @@ -871,7 +870,7 @@ impl Backend { env: Env, tx_hash: B256, journaled_state: &mut JournaledState, - ) -> eyre::Result> { + ) -> eyre::Result>> { trace!(?id, ?tx_hash, "replay until transaction"); let persistent_accounts = self.inner.persistent_accounts.clone(); @@ -885,17 +884,17 @@ impl Backend { // System transactions such as on L2s don't contain any pricing info so we skip them // otherwise this would cause reverts if is_known_system_sender(tx.from) || - tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) + tx.transaction_type() == Some(SYSTEM_TRANSACTION_TYPE) { - trace!(tx=?tx.hash, "skipping system transaction"); + trace!(tx=?tx.tx_hash(), "skipping system transaction"); continue; } - if tx.hash == tx_hash { + if tx.tx_hash() == tx_hash { // found the target transaction return Ok(Some(tx.inner)) } - trace!(tx=?tx.hash, "committing transaction"); + trace!(tx=?tx.tx_hash(), "committing transaction"); commit_transaction( &tx.inner, @@ -1914,9 +1913,9 @@ fn is_contract_in_state(journaled_state: &JournaledState, acc: Address) -> bool } /// Updates the env's block with the block's data -fn update_env_block(env: &mut Env, block: &Block) { +fn update_env_block(env: &mut Env, block: &AnyRpcBlock) { env.block.timestamp = U256::from(block.header.timestamp); - env.block.coinbase = block.header.miner; + env.block.coinbase = block.header.beneficiary; env.block.difficulty = block.header.difficulty; env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); env.block.basefee = U256::from(block.header.base_fee_per_gas.unwrap_or_default()); @@ -1930,7 +1929,7 @@ fn update_env_block(env: &mut Env, block: &Block) { /// Executes the given transaction and commits state changes to the database _and_ the journaled /// state, with an inspector. fn commit_transaction( - tx: &Transaction, + tx: &Transaction, mut env: EnvWithHandlerCfg, journaled_state: &mut JournaledState, fork: &mut Fork, @@ -1940,7 +1939,7 @@ fn commit_transaction( ) -> eyre::Result<()> { // TODO: Remove after https://github.com/foundry-rs/foundry/pull/9131 // if the tx has the blob_versioned_hashes field, we assume it's a Cancun block - if tx.blob_versioned_hashes.is_some() { + if tx.blob_versioned_hashes().is_some() { env.handler_cfg.spec_id = SpecId::CANCUN; } diff --git a/crates/evm/core/src/fork/init.rs b/crates/evm/core/src/fork/init.rs index b32fece5a037..ae3f9b7a04b1 100644 --- a/crates/evm/core/src/fork/init.rs +++ b/crates/evm/core/src/fork/init.rs @@ -1,10 +1,8 @@ use crate::utils::apply_chain_and_block_specific_env_changes; +use alloy_consensus::BlockHeader; use alloy_primitives::{Address, U256}; -use alloy_provider::{ - network::{BlockResponse, HeaderResponse}, - Network, Provider, -}; -use alloy_rpc_types::BlockNumberOrTag; +use alloy_provider::{network::BlockResponse, Network, Provider}; +use alloy_rpc_types::{BlockNumberOrTag, BlockTransactionsKind}; use alloy_transport::Transport; use eyre::WrapErr; use foundry_common::NON_ARCHIVE_NODE_WARNING; @@ -30,7 +28,10 @@ pub async fn environment>( let (fork_gas_price, rpc_chain_id, block) = tokio::try_join!( provider.get_gas_price(), provider.get_chain_id(), - provider.get_block_by_number(BlockNumberOrTag::Number(block_number), false) + provider.get_block_by_number( + BlockNumberOrTag::Number(block_number), + BlockTransactionsKind::Hashes + ) )?; let block = if let Some(block) = block { block @@ -66,7 +67,7 @@ pub async fn environment>( block: BlockEnv { number: U256::from(block.header().number()), timestamp: U256::from(block.header().timestamp()), - coinbase: block.header().coinbase(), + coinbase: block.header().beneficiary(), difficulty: block.header().difficulty(), prevrandao: block.header().mix_hash(), basefee: U256::from(block.header().base_fee_per_gas().unwrap_or_default()), diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index 14fa59aafc1f..112e1eeda7d4 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -4,8 +4,9 @@ //! concurrently active pairs at once. use super::CreateFork; +use alloy_consensus::BlockHeader; use alloy_primitives::{map::HashMap, U256}; -use alloy_provider::network::{BlockResponse, HeaderResponse}; +use alloy_provider::network::BlockResponse; use alloy_transport::layers::RetryBackoffService; use foundry_common::provider::{ runtime_transport::RuntimeTransport, ProviderBuilder, RetryProvider, diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 99cc8dabaf07..9849fd1cef9c 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -1,8 +1,7 @@ use super::fork::environment; use crate::fork::CreateFork; use alloy_primitives::{Address, B256, U256}; -use alloy_provider::Provider; -use alloy_rpc_types::AnyNetworkBlock; +use alloy_provider::{network::AnyRpcBlock, Provider}; use eyre::WrapErr; use foundry_common::{provider::ProviderBuilder, ALCHEMY_FREE_TIER_CUPS}; use foundry_config::{Chain, Config}; @@ -87,7 +86,7 @@ impl EvmOpts { pub async fn fork_evm_env( &self, fork_url: impl AsRef, - ) -> eyre::Result<(revm::primitives::Env, AnyNetworkBlock)> { + ) -> eyre::Result<(revm::primitives::Env, AnyRpcBlock)> { let fork_url = fork_url.as_ref(); let provider = ProviderBuilder::new(fork_url) .compute_units_per_second(self.get_compute_units_per_second()) diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index d76cd087ce16..be9660c72ac4 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -3,12 +3,11 @@ use crate::{ backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER, precompiles::ALPHANET_P256, InspectorExt, }; +use alloy_consensus::BlockHeader; use alloy_json_abi::{Function, JsonAbi}; +use alloy_network::AnyTxEnvelope; use alloy_primitives::{Address, Selector, TxKind, U256}; -use alloy_provider::{ - network::{BlockResponse, HeaderResponse}, - Network, -}; +use alloy_provider::{network::BlockResponse, Network}; use alloy_rpc_types::{Transaction, TransactionRequest}; use foundry_config::NamedChain; use foundry_fork_db::DatabaseError; @@ -85,8 +84,10 @@ pub fn get_function<'a>( } /// Configures the env for the given RPC transaction. -pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction) { - configure_tx_req_env(env, &tx.clone().into()).expect("cannot fail"); +pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction) { + if let AnyTxEnvelope::Ethereum(tx) = &tx.inner { + configure_tx_req_env(env, &tx.clone().into()).expect("cannot fail"); + } } /// Configures the env for the given RPC transaction request. diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index cd86c4200625..d3079fae72cf 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -8,10 +8,13 @@ use crate::{ verify::VerifierArgs, }; use alloy_primitives::{hex, Address, Bytes, U256}; -use alloy_provider::Provider; -use alloy_rpc_types::{BlockId, BlockNumberOrTag, Transaction}; +use alloy_provider::{ + network::{AnyTxEnvelope, TransactionBuilder}, + Provider, +}; +use alloy_rpc_types::{BlockId, BlockNumberOrTag, TransactionInput, TransactionRequest}; use clap::{Parser, ValueHint}; -use eyre::{OptionExt, Result}; +use eyre::{Context, OptionExt, Result}; use foundry_cli::{ opts::EtherscanOpts, utils::{self, read_constructor_args_file, LoadConfig}, @@ -19,8 +22,8 @@ use foundry_cli::{ use foundry_common::shell; use foundry_compilers::{artifacts::EvmVersion, info::ContractInfo}; use foundry_config::{figment, impl_figment_convert, Config}; -use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, utils::configure_tx_env}; -use revm_primitives::AccountInfo; +use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, utils::configure_tx_req_env}; +use revm_primitives::{AccountInfo, TxKind}; use std::path::PathBuf; impl_figment_convert!(VerifyBytecodeArgs); @@ -244,21 +247,22 @@ impl VerifyBytecodeArgs { // Setup genesis tx and env. let deployer = Address::with_last_byte(0x1); - let mut gen_tx = Transaction { - from: deployer, - to: None, - input: Bytes::from(local_bytecode_vec), - ..Default::default() - }; + let mut gen_tx_req = TransactionRequest::default() + .with_from(deployer) + .with_input(Bytes::from(local_bytecode_vec)) + .into_create(); if let Some(ref block) = genesis_block { configure_env_block(&mut env, block); - gen_tx.max_fee_per_gas = block.header.base_fee_per_gas.map(|g| g as u128); - gen_tx.gas = block.header.gas_limit; - gen_tx.gas_price = block.header.base_fee_per_gas.map(|g| g as u128); + gen_tx_req.max_fee_per_gas = block.header.base_fee_per_gas.map(|g| g as u128); + gen_tx_req.gas = Some(block.header.gas_limit); + gen_tx_req.gas_price = block.header.base_fee_per_gas.map(|g| g as u128); } - configure_tx_env(&mut env, &gen_tx); + // configure_tx_rq_env(&mut env, &gen_tx); + + configure_tx_req_env(&mut env, &gen_tx_req) + .wrap_err("Failed to configure tx request env")?; // Seed deployer account with funds let account_info = AccountInfo { @@ -268,8 +272,12 @@ impl VerifyBytecodeArgs { }; executor.backend_mut().insert_account_info(deployer, account_info); - let fork_address = - crate::utils::deploy_contract(&mut executor, &env, config.evm_spec_id(), &gen_tx)?; + let fork_address = crate::utils::deploy_contract( + &mut executor, + &env, + config.evm_spec_id(), + gen_tx_req.to, + )?; // Compare runtime bytecode let (deployed_bytecode, onchain_runtime_code) = crate::utils::get_runtime_codes( @@ -308,7 +316,7 @@ impl VerifyBytecodeArgs { let creation_data = creation_data.unwrap(); // Get transaction and receipt. trace!(creation_tx_hash = ?creation_data.transaction_hash); - let mut transaction = provider + let transaction = provider .get_transaction_by_hash(creation_data.transaction_hash) .await .or_else(|e| eyre::bail!("Couldn't fetch transaction from RPC: {:?}", e))? @@ -328,12 +336,23 @@ impl VerifyBytecodeArgs { ); }; + let mut transaction: TransactionRequest = match transaction.inner.inner { + AnyTxEnvelope::Ethereum(tx) => tx.into(), + AnyTxEnvelope::Unknown(_) => unreachable!("Unknown transaction type"), + }; + // Extract creation code from creation tx input. let maybe_creation_code = if receipt.to.is_none() && receipt.contract_address == Some(self.address) { - &transaction.input + match &transaction.input.input { + Some(input) => &input[..], + None => unreachable!("creation tx input is None"), + } } else if receipt.to == Some(DEFAULT_CREATE2_DEPLOYER) { - &transaction.input[32..] + match &transaction.input.input { + Some(input) => &input[32..], + None => unreachable!("creation tx input is None"), + } } else { eyre::bail!( "Could not extract the creation code for contract at address {}", @@ -434,35 +453,39 @@ impl VerifyBytecodeArgs { // Use `transaction.from` instead of `creation_data.contract_creator` to resolve // blockscout creation data discrepancy in case of CREATE2. - let prev_block_nonce = - provider.get_transaction_count(transaction.from).block_id(prev_block_id).await?; - transaction.nonce = prev_block_nonce; + let prev_block_nonce = provider + .get_transaction_count(transaction.from.unwrap()) + .block_id(prev_block_id) + .await?; + transaction.set_nonce(prev_block_nonce); if let Some(ref block) = block { configure_env_block(&mut env, block) } // Replace the `input` with local creation code in the creation tx. - if let Some(to) = transaction.to { + if let Some(TxKind::Call(to)) = transaction.kind() { if to == DEFAULT_CREATE2_DEPLOYER { - let mut input = transaction.input[..32].to_vec(); // Salt + let mut input = transaction.input.input.unwrap()[..32].to_vec(); // Salt input.extend_from_slice(&local_bytecode_vec); - transaction.input = Bytes::from(input); + transaction.input = TransactionInput::both(Bytes::from(input)); // Deploy default CREATE2 deployer executor.deploy_create2_deployer()?; } } else { - transaction.input = Bytes::from(local_bytecode_vec); + transaction.input = TransactionInput::both(Bytes::from(local_bytecode_vec)); } - configure_tx_env(&mut env, &transaction.inner); + // configure_req__env(&mut env, &transaction.inner); + configure_tx_req_env(&mut env, &transaction) + .wrap_err("Failed to configure tx request env")?; let fork_address = crate::utils::deploy_contract( &mut executor, &env, config.evm_spec_id(), - &transaction, + transaction.to, )?; // State committed using deploy_with_env, now get the runtime bytecode from the db. diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index ae1443962af6..ca90129d543b 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -6,6 +6,7 @@ use crate::{ use alloy_json_abi::Function; use alloy_primitives::hex; use alloy_provider::Provider; +use alloy_rpc_types::TransactionTrait; use eyre::{eyre, Context, OptionExt, Result}; use foundry_block_explorers::{ errors::EtherscanError, @@ -402,9 +403,9 @@ impl EtherscanVerificationProvider { .ok_or_eyre("Couldn't fetch transaction receipt from RPC")?; let maybe_creation_code = if receipt.contract_address == Some(args.address) { - &transaction.input - } else if transaction.to == Some(DEFAULT_CREATE2_DEPLOYER) { - &transaction.input[32..] + transaction.inner.inner.input() + } else if transaction.to() == Some(DEFAULT_CREATE2_DEPLOYER) { + &transaction.inner.inner.input()[32..] } else { eyre::bail!("Fetching of constructor arguments is not supported for contracts created by contracts") }; diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index 19c63e7fb3ec..a14d6af6df96 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -1,8 +1,8 @@ use crate::{bytecode::VerifyBytecodeArgs, types::VerificationType}; use alloy_dyn_abi::DynSolValue; use alloy_primitives::{Address, Bytes, U256}; -use alloy_provider::Provider; -use alloy_rpc_types::{AnyNetworkBlock, BlockId, Transaction}; +use alloy_provider::{network::AnyRpcBlock, Provider}; +use alloy_rpc_types::BlockId; use clap::ValueEnum; use eyre::{OptionExt, Result}; use foundry_block_explorers::{ @@ -17,7 +17,7 @@ use reqwest::Url; use revm_primitives::{ db::Database, env::{EnvWithHandlerCfg, HandlerCfg}, - Bytecode, Env, SpecId, + Bytecode, Env, SpecId, TxKind, }; use semver::Version; use serde::{Deserialize, Serialize}; @@ -340,9 +340,9 @@ pub async fn get_tracing_executor( Ok((env, executor)) } -pub fn configure_env_block(env: &mut Env, block: &AnyNetworkBlock) { +pub fn configure_env_block(env: &mut Env, block: &AnyRpcBlock) { env.block.timestamp = U256::from(block.header.timestamp); - env.block.coinbase = block.header.miner; + env.block.coinbase = block.header.beneficiary; env.block.difficulty = block.header.difficulty; env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); env.block.basefee = U256::from(block.header.base_fee_per_gas.unwrap_or_default()); @@ -353,11 +353,12 @@ pub fn deploy_contract( executor: &mut TracingExecutor, env: &Env, spec_id: SpecId, - transaction: &Transaction, + to: Option, ) -> Result { let env_with_handler = EnvWithHandlerCfg::new(Box::new(env.clone()), HandlerCfg::new(spec_id)); - if let Some(to) = transaction.to { + if to.is_some_and(|to| to.is_call()) { + let TxKind::Call(to) = to.unwrap() else { unreachable!() }; if to != DEFAULT_CREATE2_DEPLOYER { eyre::bail!("Transaction `to` address is not the default create2 deployer i.e the tx is not a contract creation tx."); } diff --git a/crates/wallets/src/wallet_signer.rs b/crates/wallets/src/wallet_signer.rs index a7bc1a312a36..812df986300b 100644 --- a/crates/wallets/src/wallet_signer.rs +++ b/crates/wallets/src/wallet_signer.rs @@ -2,8 +2,8 @@ use crate::error::WalletSignerError; use alloy_consensus::SignableTransaction; use alloy_dyn_abi::TypedData; use alloy_network::TxSigner; -use alloy_primitives::{hex, Address, ChainId, B256}; -use alloy_signer::{Signature, Signer}; +use alloy_primitives::{hex, Address, ChainId, PrimitiveSignature, B256}; +use alloy_signer::Signer; use alloy_signer_ledger::{HDPath as LedgerHDPath, LedgerSigner}; use alloy_signer_local::{coins_bip39::English, MnemonicBuilder, PrivateKeySigner}; use alloy_signer_trezor::{HDPath as TrezorHDPath, TrezorSigner}; @@ -198,11 +198,11 @@ macro_rules! delegate { #[async_trait] impl Signer for WalletSigner { /// Signs the given hash. - async fn sign_hash(&self, hash: &B256) -> alloy_signer::Result { + async fn sign_hash(&self, hash: &B256) -> alloy_signer::Result { delegate!(self, inner => inner.sign_hash(hash)).await } - async fn sign_message(&self, message: &[u8]) -> alloy_signer::Result { + async fn sign_message(&self, message: &[u8]) -> alloy_signer::Result { delegate!(self, inner => inner.sign_message(message)).await } @@ -222,7 +222,7 @@ impl Signer for WalletSigner { &self, payload: &T, domain: &Eip712Domain, - ) -> alloy_signer::Result + ) -> alloy_signer::Result where Self: Sized, { @@ -232,21 +232,21 @@ impl Signer for WalletSigner { async fn sign_dynamic_typed_data( &self, payload: &TypedData, - ) -> alloy_signer::Result { + ) -> alloy_signer::Result { delegate!(self, inner => inner.sign_dynamic_typed_data(payload)).await } } #[async_trait] -impl TxSigner for WalletSigner { +impl TxSigner for WalletSigner { fn address(&self) -> Address { delegate!(self, inner => alloy_signer::Signer::address(inner)) } async fn sign_transaction( &self, - tx: &mut dyn SignableTransaction, - ) -> alloy_signer::Result { + tx: &mut dyn SignableTransaction, + ) -> alloy_signer::Result { delegate!(self, inner => inner.sign_transaction(tx)).await } } From 057c8ac20d2c2580237ed24557df846b48ab35b2 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:32:32 +0530 Subject: [PATCH 32/68] feat(`anvil`): `--cache-path` (#9343) * feat(`anvil`): --cache-path * nits * test * nit * run with tempdir * nit --- Cargo.lock | 18 +++++++++++ Cargo.toml | 1 + crates/anvil/Cargo.toml | 1 + crates/anvil/src/cmd.rs | 7 ++++- crates/anvil/src/config.rs | 11 +++++++ crates/anvil/src/eth/backend/mem/cache.rs | 4 +++ crates/anvil/src/eth/backend/mem/mod.rs | 9 ++++-- crates/anvil/src/eth/backend/mem/storage.rs | 8 ++++- crates/anvil/tests/it/anvil.rs | 34 ++++++++++++++++++++- 9 files changed, 88 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ebc0c4e0bbe1..bb06adac6ca3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -262,6 +262,23 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-node-bindings" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27444ea67d360508753022807cdd0b49a95c878924c9c5f8f32668b7d7768245" +dependencies = [ + "alloy-genesis", + "alloy-primitives", + "k256", + "rand", + "serde_json", + "tempfile", + "thiserror 1.0.69", + "tracing", + "url", +] + [[package]] name = "alloy-primitives" version = "0.8.12" @@ -875,6 +892,7 @@ dependencies = [ "alloy-json-abi", "alloy-json-rpc", "alloy-network", + "alloy-node-bindings", "alloy-primitives", "alloy-provider", "alloy-pubsub", diff --git a/Cargo.toml b/Cargo.toml index f5ec94b906bc..b2008ad5b64d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -203,6 +203,7 @@ alloy-transport = { version = "0.6.4", default-features = false } alloy-transport-http = { version = "0.6.4", default-features = false } alloy-transport-ipc = { version = "0.6.4", default-features = false } alloy-transport-ws = { version = "0.6.4", default-features = false } +alloy-node-bindings = { version = "0.6.4", default-features = false } ## alloy-core alloy-dyn-abi = "0.8.11" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index b3389d2eccae..39a8bc649dcb 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -117,6 +117,7 @@ alloy-rpc-client = { workspace = true, features = ["pubsub"] } alloy-transport-ipc = { workspace = true, features = ["mock"] } alloy-provider = { workspace = true, features = ["txpool-api"] } alloy-transport-ws.workspace = true +alloy-node-bindings.workspace = true alloy-json-rpc.workspace = true alloy-pubsub.workspace = true foundry-test-utils.workspace = true diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 839dae0160b2..eda009418c14 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -189,6 +189,10 @@ pub struct NodeArgs { #[command(flatten)] pub server_config: ServerConfig, + + /// Path to the cache directory where states are stored. + #[arg(long, value_name = "PATH")] + pub cache_path: Option, } #[cfg(windows)] @@ -274,7 +278,8 @@ impl NodeArgs { .with_alphanet(self.evm_opts.alphanet) .with_disable_default_create2_deployer(self.evm_opts.disable_default_create2_deployer) .with_slots_in_an_epoch(self.slots_in_an_epoch) - .with_memory_limit(self.evm_opts.memory_limit)) + .with_memory_limit(self.evm_opts.memory_limit) + .with_cache_path(self.cache_path)) } fn account_generator(&self) -> AccountGenerator { diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 714f91e6d7a8..ada48232904b 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -189,6 +189,8 @@ pub struct NodeConfig { pub alphanet: bool, /// Do not print log messages. pub silent: bool, + /// The path where states are cached. + pub cache_path: Option, } impl NodeConfig { @@ -465,6 +467,7 @@ impl Default for NodeConfig { precompile_factory: None, alphanet: false, silent: false, + cache_path: None, } } } @@ -969,6 +972,13 @@ impl NodeConfig { self } + /// Sets the path where states are cached + #[must_use] + pub fn with_cache_path(mut self, cache_path: Option) -> Self { + self.cache_path = cache_path; + self + } + /// Configures everything related to env, backend and database and returns the /// [Backend](mem::Backend) /// @@ -1051,6 +1061,7 @@ impl NodeConfig { self.max_persisted_states, self.transaction_block_keeper, self.block_time, + self.cache_path.clone(), Arc::new(tokio::sync::RwLock::new(self.clone())), ) .await; diff --git a/crates/anvil/src/eth/backend/mem/cache.rs b/crates/anvil/src/eth/backend/mem/cache.rs index e51aaae7e1ae..51b92c3d65d7 100644 --- a/crates/anvil/src/eth/backend/mem/cache.rs +++ b/crates/anvil/src/eth/backend/mem/cache.rs @@ -18,6 +18,10 @@ pub struct DiskStateCache { } impl DiskStateCache { + /// Specify the path where to create the tempdir in + pub fn with_path(self, temp_path: PathBuf) -> Self { + Self { temp_path: Some(temp_path), temp_dir: None } + } /// Returns the cache file for the given hash fn with_cache_file(&mut self, hash: B256, f: F) -> Option where diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 0db343358fec..83718ad821aa 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -103,12 +103,12 @@ use revm::{ use std::{ collections::BTreeMap, io::{Read, Write}, + path::PathBuf, sync::Arc, time::Duration, }; use storage::{Blockchain, MinedTransaction, DEFAULT_HISTORY_LIMIT}; use tokio::sync::RwLock as AsyncRwLock; - pub mod cache; pub mod fork_db; pub mod in_memory_db; @@ -227,6 +227,7 @@ impl Backend { max_persisted_states: Option, transaction_block_keeper: Option, automine_block_time: Option, + cache_path: Option, node_config: Arc>, ) -> Self { // if this is a fork then adjust the blockchain storage @@ -249,7 +250,7 @@ impl Backend { genesis.timestamp }; - let states = if prune_state_history_config.is_config_enabled() { + let mut states = if prune_state_history_config.is_config_enabled() { // if prune state history is enabled, configure the state cache only for memory prune_state_history_config .max_memory_history @@ -264,6 +265,10 @@ impl Backend { Default::default() }; + if let Some(cache_path) = cache_path { + states = states.disk_path(cache_path); + } + let (slots_in_an_epoch, precompile_factory) = { let cfg = node_config.read().await; (cfg.slots_in_an_epoch, cfg.precompile_factory.clone()) diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index d5a72fccbbb6..056b886277c9 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -41,7 +41,7 @@ use foundry_evm::{ }; use parking_lot::RwLock; use revm::primitives::SpecId; -use std::{collections::VecDeque, fmt, sync::Arc, time::Duration}; +use std::{collections::VecDeque, fmt, path::PathBuf, sync::Arc, time::Duration}; // use yansi::Paint; // === various limits in number of blocks === @@ -94,6 +94,12 @@ impl InMemoryBlockStates { self } + /// Configures the path on disk where the states will cached. + pub fn disk_path(mut self, path: PathBuf) -> Self { + self.disk_cache = self.disk_cache.with_path(path); + self + } + /// This modifies the `limit` what to keep stored in memory. /// /// This will ensure the new limit adjusts based on the block time. diff --git a/crates/anvil/tests/it/anvil.rs b/crates/anvil/tests/it/anvil.rs index 65eeac70baf7..b5ed0c85312f 100644 --- a/crates/anvil/tests/it/anvil.rs +++ b/crates/anvil/tests/it/anvil.rs @@ -2,9 +2,11 @@ use alloy_consensus::EMPTY_ROOT_HASH; use alloy_eips::BlockNumberOrTag; -use alloy_primitives::Address; +use alloy_node_bindings::utils::run_with_tempdir; +use alloy_primitives::{Address, U256}; use alloy_provider::Provider; use anvil::{spawn, EthereumHardfork, NodeConfig}; +use std::time::Duration; #[tokio::test(flavor = "multi_thread")] async fn test_can_change_mining_mode() { @@ -118,3 +120,33 @@ async fn test_cancun_fields() { assert!(block.header.blob_gas_used.is_some()); assert!(block.header.excess_blob_gas.is_some()); } + +#[tokio::test(flavor = "multi_thread")] +#[cfg(not(windows))] +async fn test_cache_path() { + run_with_tempdir("custom-anvil-cache", |tmp_dir| async move { + let cache_path = tmp_dir.join("cache"); + let (api, _handle) = spawn( + NodeConfig::test() + .with_cache_path(Some(cache_path.clone())) + .with_max_persisted_states(Some(5_usize)) + .with_blocktime(Some(Duration::from_millis(1))), + ) + .await; + + api.anvil_mine(Some(U256::from(1000)), None).await.unwrap(); + + // sleep to ensure the cache is written + tokio::time::sleep(Duration::from_secs(2)).await; + + assert!(cache_path.exists()); + assert!(cache_path.read_dir().unwrap().count() > 0); + + // Clean the directory, this is to prevent an error when temp_dir is dropped. + let _ = std::fs::remove_dir_all(cache_path); + + //sleep to ensure OS file handles are released + tokio::time::sleep(Duration::from_secs(1)).await; + }) + .await; +} From 9504f78cb7bf9c4fc77de9a18f6d8383896f8238 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:43:21 +0530 Subject: [PATCH 33/68] chore: rm flaky cache-path test (#9372) --- Cargo.lock | 18 ------------------ Cargo.toml | 1 - crates/anvil/Cargo.toml | 1 - crates/anvil/tests/it/anvil.rs | 34 +--------------------------------- 4 files changed, 1 insertion(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb06adac6ca3..ebc0c4e0bbe1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -262,23 +262,6 @@ dependencies = [ "serde", ] -[[package]] -name = "alloy-node-bindings" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27444ea67d360508753022807cdd0b49a95c878924c9c5f8f32668b7d7768245" -dependencies = [ - "alloy-genesis", - "alloy-primitives", - "k256", - "rand", - "serde_json", - "tempfile", - "thiserror 1.0.69", - "tracing", - "url", -] - [[package]] name = "alloy-primitives" version = "0.8.12" @@ -892,7 +875,6 @@ dependencies = [ "alloy-json-abi", "alloy-json-rpc", "alloy-network", - "alloy-node-bindings", "alloy-primitives", "alloy-provider", "alloy-pubsub", diff --git a/Cargo.toml b/Cargo.toml index b2008ad5b64d..f5ec94b906bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -203,7 +203,6 @@ alloy-transport = { version = "0.6.4", default-features = false } alloy-transport-http = { version = "0.6.4", default-features = false } alloy-transport-ipc = { version = "0.6.4", default-features = false } alloy-transport-ws = { version = "0.6.4", default-features = false } -alloy-node-bindings = { version = "0.6.4", default-features = false } ## alloy-core alloy-dyn-abi = "0.8.11" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 39a8bc649dcb..b3389d2eccae 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -117,7 +117,6 @@ alloy-rpc-client = { workspace = true, features = ["pubsub"] } alloy-transport-ipc = { workspace = true, features = ["mock"] } alloy-provider = { workspace = true, features = ["txpool-api"] } alloy-transport-ws.workspace = true -alloy-node-bindings.workspace = true alloy-json-rpc.workspace = true alloy-pubsub.workspace = true foundry-test-utils.workspace = true diff --git a/crates/anvil/tests/it/anvil.rs b/crates/anvil/tests/it/anvil.rs index b5ed0c85312f..65eeac70baf7 100644 --- a/crates/anvil/tests/it/anvil.rs +++ b/crates/anvil/tests/it/anvil.rs @@ -2,11 +2,9 @@ use alloy_consensus::EMPTY_ROOT_HASH; use alloy_eips::BlockNumberOrTag; -use alloy_node_bindings::utils::run_with_tempdir; -use alloy_primitives::{Address, U256}; +use alloy_primitives::Address; use alloy_provider::Provider; use anvil::{spawn, EthereumHardfork, NodeConfig}; -use std::time::Duration; #[tokio::test(flavor = "multi_thread")] async fn test_can_change_mining_mode() { @@ -120,33 +118,3 @@ async fn test_cancun_fields() { assert!(block.header.blob_gas_used.is_some()); assert!(block.header.excess_blob_gas.is_some()); } - -#[tokio::test(flavor = "multi_thread")] -#[cfg(not(windows))] -async fn test_cache_path() { - run_with_tempdir("custom-anvil-cache", |tmp_dir| async move { - let cache_path = tmp_dir.join("cache"); - let (api, _handle) = spawn( - NodeConfig::test() - .with_cache_path(Some(cache_path.clone())) - .with_max_persisted_states(Some(5_usize)) - .with_blocktime(Some(Duration::from_millis(1))), - ) - .await; - - api.anvil_mine(Some(U256::from(1000)), None).await.unwrap(); - - // sleep to ensure the cache is written - tokio::time::sleep(Duration::from_secs(2)).await; - - assert!(cache_path.exists()); - assert!(cache_path.read_dir().unwrap().count() > 0); - - // Clean the directory, this is to prevent an error when temp_dir is dropped. - let _ = std::fs::remove_dir_all(cache_path); - - //sleep to ensure OS file handles are released - tokio::time::sleep(Duration::from_secs(1)).await; - }) - .await; -} From fef20981cbaa9c08e1ef1e3cd8bc57ccbcd1ac4e Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:12:45 +0100 Subject: [PATCH 34/68] feat: add global -j, --threads (#9367) * feat: add global -j, --threads * Update crates/cli/src/opts/global.rs * fix tests after comment update --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: zerosnacks --- Cargo.lock | 1 + crates/cast/bin/cmd/create2.rs | 25 +++++++++++------- crates/cast/tests/cli/main.rs | 5 ++++ crates/cli/Cargo.toml | 3 ++- crates/cli/src/opts/global.rs | 45 ++++++++++++++++++++++---------- crates/config/README.md | 2 +- crates/forge/bin/cmd/test/mod.rs | 18 +------------ crates/forge/tests/cli/cmd.rs | 5 ++++ crates/script/src/lib.rs | 2 +- 9 files changed, 62 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ebc0c4e0bbe1..4a7cb9a73c5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3686,6 +3686,7 @@ dependencies = [ "foundry-wallets", "futures", "indicatif", + "rayon", "regex", "serde", "serde_json", diff --git a/crates/cast/bin/cmd/create2.rs b/crates/cast/bin/cmd/create2.rs index 17523e094577..f46066137dbb 100644 --- a/crates/cast/bin/cmd/create2.rs +++ b/crates/cast/bin/cmd/create2.rs @@ -4,7 +4,6 @@ use eyre::{Result, WrapErr}; use rand::{rngs::StdRng, RngCore, SeedableRng}; use regex::RegexSetBuilder; use std::{ - num::NonZeroUsize, sync::{ atomic::{AtomicBool, Ordering}, Arc, @@ -73,9 +72,9 @@ pub struct Create2Args { #[arg(alias = "ch", long, value_name = "HASH", required_unless_present = "init_code")] init_code_hash: Option, - /// Number of threads to use. Defaults to and caps at the number of logical cores. - #[arg(short, long)] - jobs: Option, + /// Number of threads to use. Specifying 0 defaults to the number of logical cores. + #[arg(global = true, long, short = 'j', visible_alias = "jobs")] + threads: Option, /// Address of the caller. Used for the first 20 bytes of the salt. #[arg(long, value_name = "ADDRESS")] @@ -107,7 +106,7 @@ impl Create2Args { salt, init_code, init_code_hash, - jobs, + threads, caller, seed, no_random, @@ -168,8 +167,8 @@ impl Create2Args { let regex = RegexSetBuilder::new(regexs).case_insensitive(!case_sensitive).build()?; let mut n_threads = std::thread::available_parallelism().map_or(1, |n| n.get()); - if let Some(jobs) = jobs { - n_threads = n_threads.min(jobs.get()); + if let Some(threads) = threads { + n_threads = n_threads.min(threads); } if cfg!(test) { n_threads = n_threads.min(2); @@ -433,8 +432,14 @@ mod tests { #[test] fn j0() { - let e = - Create2Args::try_parse_from(["foundry-cli", "--starts-with=00", "-j0"]).unwrap_err(); - let _ = e.print(); + let args = Create2Args::try_parse_from([ + "foundry-cli", + "--starts-with=00", + "--init-code-hash", + &B256::ZERO.to_string(), + "-j0", + ]) + .unwrap(); + assert_eq!(args.threads, Some(0)); } } diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 2483fa479820..bdc4a6044d0c 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -28,6 +28,11 @@ Options: -h, --help Print help (see a summary with '-h') + -j, --threads + Number of threads to use. Specifying 0 defaults to the number of logical cores + + [aliases: jobs] + -V, --version Print version diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 9fa867db3c8f..8b741937d7c6 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -37,7 +37,9 @@ dotenvy = "0.15" eyre.workspace = true futures.workspace = true indicatif = "0.17" +rayon.workspace = true regex = { workspace = true, default-features = false } +serde_json.workspace = true serde.workspace = true strsim = "0.11" strum = { workspace = true, features = ["derive"] } @@ -45,7 +47,6 @@ tokio = { workspace = true, features = ["macros"] } tracing-subscriber = { workspace = true, features = ["registry", "env-filter"] } tracing.workspace = true yansi.workspace = true -serde_json.workspace = true tracing-tracy = { version = "0.11", optional = true } diff --git a/crates/cli/src/opts/global.rs b/crates/cli/src/opts/global.rs index 99690a530d3f..ad715f24180a 100644 --- a/crates/cli/src/opts/global.rs +++ b/crates/cli/src/opts/global.rs @@ -3,7 +3,7 @@ use foundry_common::shell::{ColorChoice, OutputFormat, OutputMode, Shell, Verbos use serde::{Deserialize, Serialize}; /// Global options. -#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, Parser)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, Parser)] pub struct GlobalOpts { /// Verbosity level of the log messages. /// @@ -16,39 +16,47 @@ pub struct GlobalOpts { /// - 3 (-vvv): Print execution traces for failing tests. /// - 4 (-vvvv): Print execution traces for all tests, and setup traces for failing tests. /// - 5 (-vvvvv): Print execution and setup traces for all tests. - #[clap(short, long, global = true, verbatim_doc_comment, conflicts_with = "quiet", action = ArgAction::Count, help_heading = "Display options")] - pub verbosity: Verbosity, + #[arg(help_heading = "Display options", global = true, short, long, verbatim_doc_comment, conflicts_with = "quiet", action = ArgAction::Count)] + verbosity: Verbosity, /// Do not print log messages. - #[clap(short, long, global = true, alias = "silent", help_heading = "Display options")] + #[arg(help_heading = "Display options", global = true, short, long, alias = "silent")] quiet: bool, /// Format log messages as JSON. - #[clap( - long, - global = true, - alias = "format-json", - conflicts_with_all = &["quiet", "color"], - help_heading = "Display options" - )] + #[arg(help_heading = "Display options", global = true, long, alias = "format-json", conflicts_with_all = &["quiet", "color"])] json: bool, /// The color of the log messages. - #[clap(long, global = true, value_enum, help_heading = "Display options")] + #[arg(help_heading = "Display options", global = true, long, value_enum)] color: Option, + + /// Number of threads to use. Specifying 0 defaults to the number of logical cores. + #[arg(global = true, long, short = 'j', visible_alias = "jobs")] + threads: Option, } impl GlobalOpts { /// Initialize the global options. - pub fn init(self) -> eyre::Result<()> { + pub fn init(&self) -> eyre::Result<()> { // Set the global shell. self.shell().set(); + // Initialize the thread pool only if `threads` was requested to avoid unnecessary overhead. + if self.threads.is_some() { + self.force_init_thread_pool()?; + } + Ok(()) } + /// Initialize the global thread pool. + pub fn force_init_thread_pool(&self) -> eyre::Result<()> { + init_thread_pool(self.threads.unwrap_or(0)) + } + /// Create a new shell instance. - pub fn shell(self) -> Shell { + pub fn shell(&self) -> Shell { let mode = match self.quiet { true => OutputMode::Quiet, false => OutputMode::Normal, @@ -62,3 +70,12 @@ impl GlobalOpts { Shell::new_with(format, mode, color, self.verbosity) } } + +/// Initialize the global thread pool. +pub fn init_thread_pool(threads: usize) -> eyre::Result<()> { + rayon::ThreadPoolBuilder::new() + .thread_name(|i| format!("foundry-{i}")) + .num_threads(threads) + .build_global()?; + Ok(()) +} diff --git a/crates/config/README.md b/crates/config/README.md index 139c2a9f5601..9fcd30ac99dc 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -115,7 +115,7 @@ no_match_contract = "Bar" match_path = "*/Foo*" no_match_path = "*/Bar*" no_match_coverage = "Baz" -# Number of threads to use. Not set or zero specifies the number of logical cores. +# Number of threads to use. Specifying 0 defaults to the number of logical cores. threads = 0 # whether to show test execution progress show_progress = true diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 435c34275850..59434d5eb42b 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -65,7 +65,7 @@ foundry_config::merge_impl_figment_convert!(TestArgs, opts, evm_opts); #[derive(Clone, Debug, Parser)] #[command(next_help_heading = "Test options")] pub struct TestArgs { - /// Include the global options. + // Include global options for users of this struct. #[command(flatten)] pub global: GlobalOpts, @@ -149,11 +149,6 @@ pub struct TestArgs { #[arg(long)] pub fuzz_input_file: Option, - /// Max concurrent threads to use. - /// Default value is the number of available CPUs. - #[arg(long, short = 'j', visible_alias = "jobs")] - pub threads: Option, - /// Show test execution progress. #[arg(long)] pub show_progress: bool, @@ -276,13 +271,6 @@ impl TestArgs { // Merge all configs. let (mut config, mut evm_opts) = self.load_config_and_evm_opts_emit_warnings()?; - // Set number of max threads to execute tests. - // If not specified then the number of threads determined by rayon will be used. - if let Some(test_threads) = config.threads { - trace!(target: "forge::test", "execute tests with {} max threads", test_threads); - rayon::ThreadPoolBuilder::new().num_threads(test_threads).build_global()?; - } - // Explicitly enable isolation for gas reports for more correct gas accounting. if self.gas_report { evm_opts.isolate = true; @@ -898,10 +886,6 @@ impl Provider for TestArgs { dict.insert("show_progress".to_string(), true.into()); } - if let Some(threads) = self.threads { - dict.insert("threads".to_string(), threads.into()); - } - Ok(Map::from([(Config::selected_profile(), dict)])) } } diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 3cd4ae5edb04..e0000e01bee0 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -33,6 +33,11 @@ Options: -h, --help Print help (see a summary with '-h') + -j, --threads + Number of threads to use. Specifying 0 defaults to the number of logical cores + + [aliases: jobs] + -V, --version Print version diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 0f0283b9f01a..aeea4940a0be 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -78,7 +78,7 @@ foundry_config::merge_impl_figment_convert!(ScriptArgs, opts, evm_opts); /// CLI arguments for `forge script`. #[derive(Clone, Debug, Default, Parser)] pub struct ScriptArgs { - /// Include the global options. + // Include global options for users of this struct. #[command(flatten)] pub global: GlobalOpts, From 41b4359973235c37227a1d485cdb71dc56959b8b Mon Sep 17 00:00:00 2001 From: m4rio <92288535+mario-eth@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:55:37 +0200 Subject: [PATCH 35/68] feat: Update to soldeer 0.5.2 (#9373) --- Cargo.lock | 20 +++++++++++--------- Cargo.toml | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a7cb9a73c5d..e6f33e224c89 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1779,9 +1779,9 @@ dependencies = [ [[package]] name = "bon" -version = "2.3.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97493a391b4b18ee918675fb8663e53646fd09321c58b46afa04e8ce2499c869" +checksum = "a636f83af97c6946f3f5cf5c268ec02375bf5efd371110292dfd57961f57a509" dependencies = [ "bon-macros", "rustversion", @@ -1789,14 +1789,16 @@ dependencies = [ [[package]] name = "bon-macros" -version = "2.3.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2af3eac944c12cdf4423eab70d310da0a8e5851a18ffb192c0a5e3f7ae1663" +checksum = "a7eaf1bfaa5b8d512abfd36d0c432591fef139d3de9ee54f1f839ea109d70d33" dependencies = [ "darling", "ident_case", + "prettyplease", "proc-macro2", "quote", + "rustversion", "syn 2.0.87", ] @@ -8510,9 +8512,9 @@ dependencies = [ [[package]] name = "soldeer-commands" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5969c09f89ae6f0e18d5904e5bdbb8842ba948dad0f8202edb7ea510e35654d" +checksum = "a4bd924da31914871820d1404b63a89b100097957f6dc7f3bbb9c094f16d8f4e" dependencies = [ "bon", "clap", @@ -8525,9 +8527,9 @@ dependencies = [ [[package]] name = "soldeer-core" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63aeee0e78b5fba04f005d23a58d20f897720212bd21ad744201cacb9dd34f8" +checksum = "c7a3129568ab6b38132efa9c956b5ae14c09c0a1a1167353e337081d1d7f0c32" dependencies = [ "bon", "chrono", @@ -8546,7 +8548,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "thiserror 1.0.69", + "thiserror 2.0.3", "tokio", "toml_edit", "uuid 1.11.0", diff --git a/Cargo.toml b/Cargo.toml index f5ec94b906bc..03ff020fa732 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -278,7 +278,7 @@ semver = "1" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } similar-asserts = "1.6" -soldeer-commands = "=0.5.1" +soldeer-commands = "=0.5.2" strum = "0.26" tempfile = "3.13" tikv-jemallocator = "0.6" From 2bc7125e913b211b2d6c59ecdc5f1f427440652b Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 22 Nov 2024 05:39:03 +0400 Subject: [PATCH 36/68] fix: `vm.broadcastRawTransaction` (#9378) fix: vm.broadcastRawTransaction --- crates/common/src/transactions.rs | 4 ++++ crates/forge/tests/cli/script.rs | 13 +++++++++---- crates/script/src/broadcast.rs | 7 ++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index b319da0d877b..a05a46eaed23 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -179,6 +179,10 @@ impl TransactionMaybeSigned { Ok(Self::Signed { tx, from }) } + pub fn is_unsigned(&self) -> bool { + matches!(self, Self::Unsigned(_)) + } + pub fn as_unsigned_mut(&mut self) -> Option<&mut WithOtherFields> { match self { Self::Unsigned(tx) => Some(tx), diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 82c61ccbc388..38e702a1b22f 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1,7 +1,7 @@ //! Contains various tests related to `forge script`. use crate::constants::TEMPLATE_CONTRACT; -use alloy_primitives::{hex, Address, Bytes}; +use alloy_primitives::{address, hex, Address, Bytes}; use anvil::{spawn, NodeConfig}; use forge_script_sequence::ScriptSequence; use foundry_test_utils::{ @@ -2039,8 +2039,7 @@ forgetest_async!(can_deploy_library_create2_different_sender, |prj, cmd| { // forgetest_async!(test_broadcast_raw_create2_deployer, |prj, cmd| { - let (_api, handle) = - spawn(NodeConfig::test().with_disable_default_create2_deployer(true)).await; + let (api, handle) = spawn(NodeConfig::test().with_disable_default_create2_deployer(true)).await; foundry_test_utils::util::initialize(prj.root()); prj.add_script( @@ -2051,7 +2050,7 @@ import "forge-std/Script.sol"; contract SimpleScript is Script { function run() external { // send funds to create2 factory deployer - vm.broadcast(); + vm.startBroadcast(); payable(0x3fAB184622Dc19b6109349B94811493BF2a45362).transfer(10000000 gwei); // deploy create2 factory vm.broadcastRawTransaction( @@ -2104,6 +2103,12 @@ ONCHAIN EXECUTION COMPLETE & SUCCESSFUL. "#]]); + + assert!(!api + .get_code(address!("4e59b44847b379578588920cA78FbF26c0B4956C"), Default::default()) + .await + .unwrap() + .is_empty()); }); forgetest_init!(can_get_script_wallets, |prj, cmd| { diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 4058aa6c59b3..51e8baf5b3be 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -213,7 +213,12 @@ impl BundledState { .sequence .sequences() .iter() - .flat_map(|sequence| sequence.transactions().map(|tx| tx.from().expect("missing from"))) + .flat_map(|sequence| { + sequence + .transactions() + .filter(|tx| tx.is_unsigned()) + .map(|tx| tx.from().expect("missing from")) + }) .collect::(); if required_addresses.contains(&Config::DEFAULT_SENDER) { From 76a2cb0dd6d60684fd64a8180500f9d619ec94d2 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 22 Nov 2024 10:53:21 +0400 Subject: [PATCH 37/68] fix(forge test): install missing dependencies before creating `Project` (#9379) * fix(forge test): install missing dependencies before instantiating the project * optimization --- crates/config/src/lib.rs | 3 +++ crates/forge/bin/cmd/test/mod.rs | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 6444802e3bc9..5a159c3925b8 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -904,6 +904,9 @@ impl Config { ) -> Result>, SolcError> { let mut map = BTreeMap::new(); + if self.compilation_restrictions.is_empty() { + return Ok(BTreeMap::new()); + } let graph = Graph::::resolve(paths)?; let (sources, _) = graph.into_sources(); diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 59434d5eb42b..1a409b33a822 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -280,16 +280,15 @@ impl TestArgs { config.invariant.gas_report_samples = 0; } - // Set up the project. - let mut project = config.project()?; - // Install missing dependencies. if install::install_missing_dependencies(&mut config) && config.auto_detect_remappings { // need to re-configure here to also catch additional remappings config = self.load_config(); - project = config.project()?; } + // Set up the project. + let project = config.project()?; + let mut filter = self.filter(&config); trace!(target: "forge::test", ?filter, "using filter"); From 1332b6d6c09264fe4cc3653f9d117ac9fb6c48c7 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:37:30 +0100 Subject: [PATCH 38/68] chore(chisel): replace solang with solar in SolidityHelper (#9376) --- Cargo.lock | 1 + Cargo.toml | 1 + crates/chisel/Cargo.toml | 1 + crates/chisel/src/dispatcher.rs | 14 +- crates/chisel/src/executor.rs | 2 +- crates/chisel/src/solidity_helper.rs | 252 +++++++++++---------------- 6 files changed, 117 insertions(+), 154 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6f33e224c89..9179edf77172 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2067,6 +2067,7 @@ dependencies = [ "serde_json", "serial_test", "solang-parser", + "solar-parse", "strum", "tikv-jemallocator", "time", diff --git a/Cargo.toml b/Cargo.toml index 03ff020fa732..8dc0ecd5f6a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,6 +172,7 @@ foundry-block-explorers = { version = "0.9.0", default-features = false } foundry-compilers = { version = "0.12.3", default-features = false } foundry-fork-db = "0.7.0" solang-parser = "=0.3.3" +solar-parse = { version = "=0.1.0", default-features = false } ## revm revm = { version = "18.0.0", default-features = false } diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index 65b3c974836b..5f098817c39d 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -58,6 +58,7 @@ semver.workspace = true serde_json.workspace = true serde.workspace = true solang-parser.workspace = true +solar-parse.workspace = true strum = { workspace = true, features = ["derive"] } time = { version = "0.3", features = ["formatting"] } tokio = { workspace = true, features = ["full"] } diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index d69de3bf540e..2a6a2fc3f567 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -38,14 +38,16 @@ use strum::IntoEnumIterator; use tracing::debug; use yansi::Paint; -/// Prompt arrow character -pub static PROMPT_ARROW: char = '➜'; -static DEFAULT_PROMPT: &str = "➜ "; +/// Prompt arrow character. +pub const PROMPT_ARROW: char = '➜'; +/// Prompt arrow string. +pub const PROMPT_ARROW_STR: &str = "➜"; +const DEFAULT_PROMPT: &str = "➜ "; /// Command leader character -pub static COMMAND_LEADER: char = '!'; +pub const COMMAND_LEADER: char = '!'; /// Chisel character -pub static CHISEL_CHAR: &str = "⚒️"; +pub const CHISEL_CHAR: &str = "⚒️"; /// Matches Solidity comments static COMMENT_RE: LazyLock = @@ -320,7 +322,7 @@ impl ChiselDispatcher { }, ChiselCommand::Source => match self.format_source() { Ok(formatted_source) => DispatchResult::CommandSuccess(Some( - SolidityHelper::highlight(&formatted_source).into_owned(), + SolidityHelper::new().highlight(&formatted_source).into_owned(), )), Err(_) => { DispatchResult::CommandFailed(String::from("Failed to format session source")) diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index b7f3973b09d5..fc24e0b891b4 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -504,7 +504,7 @@ fn format_event_definition(event_definition: &pt::EventDefinition) -> Result Self { + Self::new() + } } impl SolidityHelper { /// Create a new SolidityHelper. pub fn new() -> Self { - Self::default() + Self { + errored: false, + do_paint: yansi::is_enabled(), + sess: Session::builder().with_silent_emitter(None).build(), + globals: SessionGlobals::new(), + } + } + + /// Returns whether the helper is in an errored state. + pub fn errored(&self) -> bool { + self.errored } /// Set the errored field. @@ -55,54 +68,9 @@ impl SolidityHelper { self } - /// Get styles for a solidity source string - pub fn get_styles(input: &str) -> Vec { - let mut comments = Vec::with_capacity(DEFAULT_COMMENTS); - let mut errors = Vec::with_capacity(5); - let mut out = Lexer::new(input, 0, &mut comments, &mut errors) - .map(|(start, token, end)| (start, token.style(), end)) - .collect::>(); - - // highlight comments too - let comments_iter = comments.into_iter().map(|comment| { - let loc = match comment { - pt::Comment::Line(loc, _) | - pt::Comment::Block(loc, _) | - pt::Comment::DocLine(loc, _) | - pt::Comment::DocBlock(loc, _) => loc, - }; - (loc.start(), Style::new().dim(), loc.end()) - }); - out.extend(comments_iter); - - out - } - - /// Get contiguous styles for a solidity source string - pub fn get_contiguous_styles(input: &str) -> Vec { - let mut styles = Self::get_styles(input); - styles.sort_unstable_by_key(|(start, _, _)| *start); - - let len = input.len(); - // len / 4 is just a random average of whitespaces in the input - let mut out = Vec::with_capacity(styles.len() + len / 4 + 1); - let mut index = 0; - for (start, style, end) in styles { - if index < start { - out.push((index, Style::default(), start)); - } - out.push((start, style, end)); - index = end; - } - if index < len { - out.push((index, Style::default(), len)); - } - out - } - - /// Highlights a solidity source string - pub fn highlight(input: &str) -> Cow<'_, str> { - if !yansi::is_enabled() { + /// Highlights a Solidity source string. + pub fn highlight<'a>(&self, input: &'a str) -> Cow<'a, str> { + if !self.do_paint() { return Cow::Borrowed(input) } @@ -133,52 +101,53 @@ impl SolidityHelper { Cow::Owned(out) } else { - let styles = Self::get_contiguous_styles(input); - let len = styles.len(); - if len == 0 { - Cow::Borrowed(input) - } else { - let mut out = String::with_capacity(input.len() + MAX_ANSI_LEN * len); - for (start, style, end) in styles { - Self::paint_unchecked(&input[start..end], style, &mut out); + let mut out = String::with_capacity(input.len() * 2); + self.with_contiguous_styles(input, |style, range| { + Self::paint_unchecked(&input[range], style, &mut out); + }); + Cow::Owned(out) + } + } + + /// Returns a list of styles and the ranges they should be applied to. + /// + /// Covers the entire source string, including any whitespace. + fn with_contiguous_styles(&self, input: &str, mut f: impl FnMut(Style, Range)) { + self.enter(|sess| { + let len = input.len(); + let mut index = 0; + for token in Lexer::new(sess, input) { + let range = token.span.lo().to_usize()..token.span.hi().to_usize(); + let style = token_style(&token); + if index < range.start { + f(Style::default(), index..range.start); } - Cow::Owned(out) + index = range.end; + f(style, range); } - } + if index < len { + f(Style::default(), index..len); + } + }); } /// Validate that a source snippet is closed (i.e., all braces and parenthesis are matched). - fn validate_closed(input: &str) -> ValidationResult { - let mut bracket_depth = 0usize; - let mut paren_depth = 0usize; - let mut brace_depth = 0usize; - let mut comments = Vec::with_capacity(DEFAULT_COMMENTS); - // returns on any encountered error, so allocate for just one - let mut errors = Vec::with_capacity(1); - for (_, token, _) in Lexer::new(input, 0, &mut comments, &mut errors) { - match token { - Token::OpenBracket => { - bracket_depth += 1; - } - Token::OpenCurlyBrace => { - brace_depth += 1; + fn validate_closed(&self, input: &str) -> ValidationResult { + let mut depth = [0usize; 3]; + self.enter(|sess| { + for token in Lexer::new(sess, input) { + match token.kind { + TokenKind::OpenDelim(delim) => { + depth[delim as usize] += 1; + } + TokenKind::CloseDelim(delim) => { + depth[delim as usize] = depth[delim as usize].saturating_sub(1); + } + _ => {} } - Token::OpenParenthesis => { - paren_depth += 1; - } - Token::CloseBracket => { - bracket_depth = bracket_depth.saturating_sub(1); - } - Token::CloseCurlyBrace => { - brace_depth = brace_depth.saturating_sub(1); - } - Token::CloseParenthesis => { - paren_depth = paren_depth.saturating_sub(1); - } - _ => {} } - } - if (bracket_depth | brace_depth | paren_depth) == 0 { + }); + if depth == [0; 3] { ValidationResult::Valid(None) } else { ValidationResult::Incomplete @@ -186,8 +155,7 @@ impl SolidityHelper { } /// Formats `input` with `style` into `out`, without checking `style.wrapping` or - /// `yansi::is_enabled` - #[inline] + /// `self.do_paint`. fn paint_unchecked(string: &str, style: Style, out: &mut String) { if style == Style::default() { out.push_str(string); @@ -198,17 +166,26 @@ impl SolidityHelper { } } - #[inline] fn paint_unchecked_owned(string: &str, style: Style) -> String { let mut out = String::with_capacity(MAX_ANSI_LEN + string.len()); Self::paint_unchecked(string, style, &mut out); out } + + /// Returns whether to color the output. + fn do_paint(&self) -> bool { + self.do_paint + } + + /// Enters the session. + fn enter(&self, f: impl FnOnce(&Session)) { + self.globals.set(|| self.sess.enter(|| f(&self.sess))); + } } impl Highlighter for SolidityHelper { fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { - Self::highlight(line) + self.highlight(line) } fn highlight_char(&self, line: &str, pos: usize, _forced: bool) -> bool { @@ -220,7 +197,7 @@ impl Highlighter for SolidityHelper { prompt: &'p str, _default: bool, ) -> Cow<'b, str> { - if !yansi::is_enabled() { + if !self.do_paint() { return Cow::Borrowed(prompt) } @@ -241,14 +218,7 @@ impl Highlighter for SolidityHelper { if let Some(i) = out.find(PROMPT_ARROW) { let style = if self.errored { Color::Red.foreground() } else { Color::Green.foreground() }; - - let mut arrow = String::with_capacity(MAX_ANSI_LEN + 4); - - let _ = style.fmt_prefix(&mut arrow); - arrow.push(PROMPT_ARROW); - let _ = style.fmt_suffix(&mut arrow); - - out.replace_range(i..=i + 2, &arrow); + out.replace_range(i..=i + 2, &Self::paint_unchecked_owned(PROMPT_ARROW_STR, style)); } Cow::Owned(out) @@ -257,7 +227,7 @@ impl Highlighter for SolidityHelper { impl Validator for SolidityHelper { fn validate(&self, ctx: &mut ValidationContext<'_>) -> rustyline::Result { - Ok(Self::validate_closed(ctx.input())) + Ok(self.validate_closed(ctx.input())) } } @@ -271,44 +241,32 @@ impl Hinter for SolidityHelper { impl Helper for SolidityHelper {} -/// Trait that assigns a color to a Token kind -pub trait TokenStyle { - /// Returns the style with which the token should be decorated with. - fn style(&self) -> Style; -} +#[allow(non_upper_case_globals)] +#[deny(unreachable_patterns)] +fn token_style(token: &Token) -> Style { + use solar_parse::{ + interface::kw::*, + token::{TokenKind::*, TokenLitKind::*}, + }; -/// [TokenStyle] implementation for [Token] -impl TokenStyle for Token<'_> { - fn style(&self) -> Style { - use Token::*; - match self { - StringLiteral(_, _) => Color::Green.foreground(), - - AddressLiteral(_) | - HexLiteral(_) | - Number(_, _) | - RationalNumber(_, _, _) | - HexNumber(_) | - True | - False => Color::Yellow.foreground(), + match token.kind { + Literal(Str | HexStr | UnicodeStr, _) => Color::Green.foreground(), + Literal(..) => Color::Yellow.foreground(), + Ident( Memory | Storage | Calldata | Public | Private | Internal | External | Constant | Pure | View | Payable | Anonymous | Indexed | Abstract | Virtual | Override | - Modifier | Immutable | Unchecked => Color::Cyan.foreground(), + Modifier | Immutable | Unchecked, + ) => Color::Cyan.foreground(), - Contract | Library | Interface | Function | Pragma | Import | Struct | Event | - Enum | Type | Constructor | As | Is | Using | New | Delete | Do | Continue | - Break | Throw | Emit | Return | Returns | Revert | For | While | If | Else | Try | - Catch | Assembly | Let | Leave | Switch | Case | Default | YulArrow | Arrow => { - Color::Magenta.foreground() - } + Ident(s) if s.is_elementary_type() => Color::Blue.foreground(), + Ident(Mapping) => Color::Blue.foreground(), - Uint(_) | Int(_) | Bytes(_) | Byte | DynamicBytes | Bool | Address | String | - Mapping => Color::Blue.foreground(), + Ident(s) if s.is_used_keyword() || s.is_yul_keyword() => Color::Magenta.foreground(), + Arrow | FatArrow => Color::Magenta.foreground(), - Identifier(_) => Style::default(), + Comment(..) => Color::Primary.dim(), - _ => Style::default(), - } + _ => Color::Primary.foreground(), } } From cf66dea727a6c7f41fa48fbe6dcabe474bfbfd79 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:37:40 +0200 Subject: [PATCH 39/68] fix(chisel): uint/int full word print (#9381) --- crates/chisel/src/executor.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index fc24e0b891b4..09c00f6ad5b3 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -7,7 +7,7 @@ use crate::prelude::{ }; use alloy_dyn_abi::{DynSolType, DynSolValue}; use alloy_json_abi::EventParam; -use alloy_primitives::{hex, Address, U256}; +use alloy_primitives::{hex, Address, B256, U256}; use core::fmt::Debug; use eyre::{Result, WrapErr}; use foundry_compilers::Artifact; @@ -379,7 +379,7 @@ fn format_token(token: DynSolValue) -> String { .collect::() ) .cyan(), - format!("{i:#x}").cyan(), + hex::encode_prefixed(B256::from(i)).cyan(), i.cyan() ) } @@ -397,7 +397,7 @@ fn format_token(token: DynSolValue) -> String { .collect::() ) .cyan(), - format!("{i:#x}").cyan(), + hex::encode_prefixed(B256::from(i)).cyan(), i.cyan() ) } From 37cc284f939a55bc1886e4bb7ba6ca99930fb4ee Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:14:50 +0530 Subject: [PATCH 40/68] fix: flaky test_broadcast_raw_create2_deployer (#9383) --- crates/forge/tests/cli/script.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 38e702a1b22f..df2a59bdc4df 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -2069,6 +2069,7 @@ contract SimpleScript is Script { "--rpc-url", &handle.http_endpoint(), "--broadcast", + "--slow", "SimpleScript", ]); From 8b7d5dfc401aab29a69ff844cfd59c1255d5d106 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:37:31 +0100 Subject: [PATCH 41/68] refactor(forge): rewrite `geiger` with Solar (#9382) --- Cargo.lock | 2 + Cargo.toml | 1 + crates/forge/Cargo.toml | 2 + crates/forge/bin/cmd/geiger.rs | 159 ++++++++++++ crates/forge/bin/cmd/geiger/error.rs | 11 - crates/forge/bin/cmd/geiger/find.rs | 165 ------------ crates/forge/bin/cmd/geiger/mod.rs | 122 --------- crates/forge/bin/cmd/geiger/visitor.rs | 333 ------------------------- crates/forge/bin/main.rs | 3 +- crates/forge/tests/cli/debug.rs | 2 +- crates/forge/tests/cli/geiger.rs | 92 +++++++ crates/forge/tests/cli/main.rs | 1 + crates/test-utils/src/util.rs | 8 +- 13 files changed, 266 insertions(+), 635 deletions(-) create mode 100644 crates/forge/bin/cmd/geiger.rs delete mode 100644 crates/forge/bin/cmd/geiger/error.rs delete mode 100644 crates/forge/bin/cmd/geiger/find.rs delete mode 100644 crates/forge/bin/cmd/geiger/mod.rs delete mode 100644 crates/forge/bin/cmd/geiger/visitor.rs create mode 100644 crates/forge/tests/cli/geiger.rs diff --git a/Cargo.lock b/Cargo.lock index 9179edf77172..15a2386be8b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3408,6 +3408,8 @@ dependencies = [ "similar", "similar-asserts", "solang-parser", + "solar-ast", + "solar-parse", "soldeer-commands", "strum", "svm-rs", diff --git a/Cargo.toml b/Cargo.toml index 8dc0ecd5f6a0..814c43aa556a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,6 +172,7 @@ foundry-block-explorers = { version = "0.9.0", default-features = false } foundry-compilers = { version = "0.12.3", default-features = false } foundry-fork-db = "0.7.0" solang-parser = "=0.3.3" +solar-ast = { version = "=0.1.0", default-features = false } solar-parse = { version = "=0.1.0", default-features = false } ## revm diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index b32a9bd41223..208ea8430ab9 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -89,6 +89,8 @@ semver.workspace = true serde_json.workspace = true similar = { version = "2", features = ["inline"] } solang-parser.workspace = true +solar-ast.workspace = true +solar-parse.workspace = true strum = { workspace = true, features = ["derive"] } thiserror.workspace = true tokio = { workspace = true, features = ["time"] } diff --git a/crates/forge/bin/cmd/geiger.rs b/crates/forge/bin/cmd/geiger.rs new file mode 100644 index 000000000000..9ffddd3c9881 --- /dev/null +++ b/crates/forge/bin/cmd/geiger.rs @@ -0,0 +1,159 @@ +use clap::{Parser, ValueHint}; +use eyre::{Result, WrapErr}; +use foundry_cli::utils::LoadConfig; +use foundry_compilers::{resolver::parse::SolData, Graph}; +use foundry_config::{impl_figment_convert_basic, Config}; +use itertools::Itertools; +use solar_ast::visit::Visit; +use solar_parse::{ast, interface::Session}; +use std::path::{Path, PathBuf}; + +/// CLI arguments for `forge geiger`. +#[derive(Clone, Debug, Parser)] +pub struct GeigerArgs { + /// Paths to files or directories to detect. + #[arg( + conflicts_with = "root", + value_hint = ValueHint::FilePath, + value_name = "PATH", + num_args(1..), + )] + paths: Vec, + + /// The project's root path. + /// + /// By default root of the Git repository, if in one, + /// or the current working directory. + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + root: Option, + + /// Globs to ignore. + #[arg( + long, + value_hint = ValueHint::FilePath, + value_name = "PATH", + num_args(1..), + )] + ignore: Vec, + + #[arg(long, hide = true)] + check: bool, + #[arg(long, hide = true)] + full: bool, +} + +impl_figment_convert_basic!(GeigerArgs); + +impl GeigerArgs { + pub fn sources(&self, config: &Config) -> Result> { + let cwd = std::env::current_dir()?; + + let mut sources: Vec = { + if self.paths.is_empty() { + let paths = config.project_paths(); + Graph::::resolve(&paths)? + .files() + .keys() + .filter(|f| !paths.libraries.iter().any(|lib| f.starts_with(lib))) + .cloned() + .collect() + } else { + self.paths + .iter() + .flat_map(|path| foundry_common::fs::files_with_ext(path, "sol")) + .unique() + .collect() + } + }; + + sources.retain_mut(|path| { + let abs_path = if path.is_absolute() { path.clone() } else { cwd.join(&path) }; + *path = abs_path.strip_prefix(&cwd).unwrap_or(&abs_path).to_path_buf(); + !self.ignore.iter().any(|ignore| { + if ignore.is_absolute() { + abs_path.starts_with(ignore) + } else { + abs_path.starts_with(cwd.join(ignore)) + } + }) + }); + + Ok(sources) + } + + pub fn run(self) -> Result { + if self.check { + sh_warn!("`--check` is deprecated as it's now the default behavior\n")?; + } + if self.full { + sh_warn!("`--full` is deprecated as reports are not generated anymore\n")?; + } + + let config = self.try_load_config_emit_warnings()?; + let sources = self.sources(&config).wrap_err("Failed to resolve files")?; + + if config.ffi { + sh_warn!("FFI enabled\n")?; + } + + let mut sess = Session::builder().with_stderr_emitter().build(); + sess.dcx = sess.dcx.set_flags(|flags| flags.track_diagnostics = false); + let unsafe_cheatcodes = &[ + "ffi".to_string(), + "readFile".to_string(), + "readLine".to_string(), + "writeFile".to_string(), + "writeLine".to_string(), + "removeFile".to_string(), + "closeFile".to_string(), + "setEnv".to_string(), + "deriveKey".to_string(), + ]; + Ok(sess + .enter(|| sources.iter().map(|file| lint_file(&sess, unsafe_cheatcodes, file)).sum())) + } +} + +fn lint_file(sess: &Session, unsafe_cheatcodes: &[String], path: &Path) -> usize { + try_lint_file(sess, unsafe_cheatcodes, path).unwrap_or(0) +} + +fn try_lint_file( + sess: &Session, + unsafe_cheatcodes: &[String], + path: &Path, +) -> solar_parse::interface::Result { + let arena = solar_parse::ast::Arena::new(); + let mut parser = solar_parse::Parser::from_file(sess, &arena, path)?; + let ast = parser.parse_file().map_err(|e| e.emit())?; + let mut visitor = Visitor::new(sess, unsafe_cheatcodes); + visitor.visit_source_unit(&ast); + Ok(visitor.count) +} + +struct Visitor<'a> { + sess: &'a Session, + count: usize, + unsafe_cheatcodes: &'a [String], +} + +impl<'a> Visitor<'a> { + fn new(sess: &'a Session, unsafe_cheatcodes: &'a [String]) -> Self { + Self { sess, count: 0, unsafe_cheatcodes } + } +} + +impl<'ast> Visit<'ast> for Visitor<'_> { + fn visit_expr(&mut self, expr: &'ast ast::Expr<'ast>) { + if let ast::ExprKind::Call(lhs, _args) = &expr.kind { + if let ast::ExprKind::Member(_lhs, member) = &lhs.kind { + if self.unsafe_cheatcodes.iter().any(|c| c.as_str() == member.as_str()) { + let msg = format!("usage of unsafe cheatcode `vm.{member}`"); + self.sess.dcx.err(msg).span(member.span).emit(); + self.count += 1; + } + } + } + self.walk_expr(expr); + } +} diff --git a/crates/forge/bin/cmd/geiger/error.rs b/crates/forge/bin/cmd/geiger/error.rs deleted file mode 100644 index 010fb237ca07..000000000000 --- a/crates/forge/bin/cmd/geiger/error.rs +++ /dev/null @@ -1,11 +0,0 @@ -use forge_fmt::FormatterError; -use foundry_common::errors::FsPathError; - -/// Possible errors when scanning a solidity file -#[derive(Debug, thiserror::Error)] -pub enum ScanFileError { - #[error(transparent)] - Io(#[from] FsPathError), - #[error(transparent)] - ParseSol(#[from] FormatterError), -} diff --git a/crates/forge/bin/cmd/geiger/find.rs b/crates/forge/bin/cmd/geiger/find.rs deleted file mode 100644 index e3cd6541334a..000000000000 --- a/crates/forge/bin/cmd/geiger/find.rs +++ /dev/null @@ -1,165 +0,0 @@ -use super::{error::ScanFileError, visitor::CheatcodeVisitor}; -use eyre::Result; -use forge_fmt::{offset_to_line_column, parse2, FormatterError, Visitable}; -use foundry_common::fs; -use solang_parser::pt::Loc; -use std::{ - fmt, - path::{Path, PathBuf}, -}; -use yansi::Paint; - -/// Scan a single file for `unsafe` cheatcode usage. -pub fn find_cheatcodes_in_file(path: &Path) -> Result { - let contents = fs::read_to_string(path)?; - let cheatcodes = find_cheatcodes_in_string(&contents, Some(path))?; - Ok(SolFileMetrics { contents, cheatcodes, file: path.to_path_buf() }) -} - -/// Scan a string for unsafe cheatcodes. -pub fn find_cheatcodes_in_string( - src: &str, - path: Option<&Path>, -) -> Result { - let mut parsed = parse2(src, path)?; - let mut visitor = CheatcodeVisitor::default(); - parsed.pt.visit(&mut visitor).unwrap(); - Ok(visitor.cheatcodes) -} - -/// Scan result for a single Solidity file. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct SolFileMetrics { - /// The Solidity file - pub file: PathBuf, - - /// The file's contents. - pub contents: String, - - /// The unsafe cheatcodes found. - pub cheatcodes: UnsafeCheatcodes, -} - -/// Formats the metrics for a single file using [`fmt::Display`]. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct SolFileMetricsPrinter<'a, 'b> { - pub metrics: &'a SolFileMetrics, - pub root: &'b Path, -} - -impl fmt::Display for SolFileMetricsPrinter<'_, '_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let SolFileMetricsPrinter { metrics, root } = *self; - - let file = metrics.file.strip_prefix(root).unwrap_or(&metrics.file); - - macro_rules! print_unsafe_fn { - ($($name:literal => $field:ident),*) => {$( - let $field = &metrics.cheatcodes.$field[..]; - if !$field.is_empty() { - writeln!(f, " {} {}", metrics.cheatcodes.$field.len().red(), $name.red())?; - - for &loc in $field { - let content = &metrics.contents[loc.range()]; - let (line, col) = offset_to_line_column(&metrics.contents, loc.start()); - let pos = format!(" --> {}:{}:{}", file.display(), line, col); - writeln!(f,"{}", pos.red())?; - for line in content.lines() { - writeln!(f, " {}", line.red())?; - } - } - } - )*}; - } - - if !metrics.cheatcodes.is_empty() { - writeln!(f, "{} {}", metrics.cheatcodes.len().red(), file.display().red())?; - print_unsafe_fn!( - "ffi" => ffi, - "readFile" => read_file, - "readLine" => read_line, - "writeFile" => write_file, - "writeLine" => write_line, - "removeFile" => remove_file, - "closeFile" => close_file, - "setEnv" => set_env, - "deriveKey" => derive_key - ); - } else { - writeln!(f, "0 {}", file.display())? - } - - Ok(()) - } -} - -/// Unsafe usage metrics collection. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct UnsafeCheatcodes { - pub ffi: Vec, - pub read_file: Vec, - pub read_line: Vec, - pub write_file: Vec, - pub write_line: Vec, - pub remove_file: Vec, - pub close_file: Vec, - pub set_env: Vec, - pub derive_key: Vec, -} - -impl UnsafeCheatcodes { - /// Whether there are any unsafe calls. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// The total number of unsafe calls. - pub fn len(&self) -> usize { - self.ffi.len() + - self.read_file.len() + - self.read_line.len() + - self.write_file.len() + - self.write_line.len() + - self.close_file.len() + - self.set_env.len() + - self.derive_key.len() + - self.remove_file.len() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_find_calls() { - let s = r" - contract A is Test { - function do_ffi() public { - string[] memory inputs = new string[](1); - vm.ffi(inputs); - } - } - "; - - let count = find_cheatcodes_in_string(s, None).unwrap(); - assert_eq!(count.ffi.len(), 1); - assert!(!count.is_empty()); - } - - #[test] - fn can_find_call_in_assignment() { - let s = r" - contract A is Test { - function do_ffi() public { - string[] memory inputs = new string[](1); - bytes stuff = vm.ffi(inputs); - } - } - "; - - let count = find_cheatcodes_in_string(s, None).unwrap(); - assert_eq!(count.ffi.len(), 1); - assert!(!count.is_empty()); - } -} diff --git a/crates/forge/bin/cmd/geiger/mod.rs b/crates/forge/bin/cmd/geiger/mod.rs deleted file mode 100644 index 4167b7882f91..000000000000 --- a/crates/forge/bin/cmd/geiger/mod.rs +++ /dev/null @@ -1,122 +0,0 @@ -use clap::{Parser, ValueHint}; -use eyre::{Result, WrapErr}; -use foundry_cli::utils::LoadConfig; -use foundry_compilers::{resolver::parse::SolData, Graph}; -use foundry_config::{impl_figment_convert_basic, Config}; -use itertools::Itertools; -use rayon::prelude::*; -use std::path::PathBuf; - -mod error; - -mod find; -use find::{find_cheatcodes_in_file, SolFileMetricsPrinter}; - -mod visitor; - -/// CLI arguments for `forge geiger`. -#[derive(Clone, Debug, Parser)] -pub struct GeigerArgs { - /// Paths to files or directories to detect. - #[arg( - conflicts_with = "root", - value_hint = ValueHint::FilePath, - value_name = "PATH", - num_args(1..), - )] - paths: Vec, - - /// The project's root path. - /// - /// By default root of the Git repository, if in one, - /// or the current working directory. - #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] - root: Option, - - /// Run in "check" mode. - /// - /// The exit code of the program will be the number of unsafe cheatcodes found. - #[arg(long)] - pub check: bool, - - /// Globs to ignore. - #[arg( - long, - value_hint = ValueHint::FilePath, - value_name = "PATH", - num_args(1..), - )] - ignore: Vec, - - /// Print a report of all files, even if no unsafe functions are found. - #[arg(long)] - full: bool, -} - -impl_figment_convert_basic!(GeigerArgs); - -impl GeigerArgs { - pub fn sources(&self, config: &Config) -> Result> { - let cwd = std::env::current_dir()?; - - let mut sources: Vec = { - if self.paths.is_empty() { - Graph::::resolve(&config.project_paths())? - .files() - .keys() - .cloned() - .collect() - } else { - self.paths - .iter() - .flat_map(|path| foundry_common::fs::files_with_ext(path, "sol")) - .unique() - .collect() - } - }; - - sources.retain(|path| { - let abs_path = if path.is_absolute() { path.clone() } else { cwd.join(path) }; - !self.ignore.iter().any(|ignore| { - if ignore.is_absolute() { - abs_path.starts_with(ignore) - } else { - abs_path.starts_with(cwd.join(ignore)) - } - }) - }); - - Ok(sources) - } - - pub fn run(self) -> Result { - let config = self.try_load_config_emit_warnings()?; - let sources = self.sources(&config).wrap_err("Failed to resolve files")?; - - if config.ffi { - sh_warn!("FFI enabled\n")?; - } - - let root = config.root.0; - - let sum = sources - .par_iter() - .map(|file| match find_cheatcodes_in_file(file) { - Ok(metrics) => { - let len = metrics.cheatcodes.len(); - let printer = SolFileMetricsPrinter { metrics: &metrics, root: &root }; - if self.full || len == 0 { - let _ = sh_eprint!("{printer}"); - } - len - } - Err(err) => { - let _ = sh_err!("{err}"); - 0 - } - }) - .sum(); - - Ok(sum) - } -} diff --git a/crates/forge/bin/cmd/geiger/visitor.rs b/crates/forge/bin/cmd/geiger/visitor.rs deleted file mode 100644 index 70313089019c..000000000000 --- a/crates/forge/bin/cmd/geiger/visitor.rs +++ /dev/null @@ -1,333 +0,0 @@ -use super::find::UnsafeCheatcodes; -use eyre::Result; -use forge_fmt::{Visitable, Visitor}; -use solang_parser::pt::{ - ContractDefinition, Expression, FunctionDefinition, IdentifierPath, Loc, Parameter, SourceUnit, - Statement, TypeDefinition, VariableDeclaration, VariableDefinition, -}; -use std::convert::Infallible; - -/// a [`forge_fmt::Visitor` that scans for invocations of cheatcodes -#[derive(Default)] -pub struct CheatcodeVisitor { - pub cheatcodes: UnsafeCheatcodes, -} - -impl Visitor for CheatcodeVisitor { - type Error = Infallible; - - fn visit_source_unit(&mut self, source_unit: &mut SourceUnit) -> Result<(), Self::Error> { - source_unit.0.visit(self) - } - - fn visit_contract(&mut self, contract: &mut ContractDefinition) -> Result<(), Self::Error> { - contract.base.visit(self)?; - contract.parts.visit(self) - } - - fn visit_block( - &mut self, - _loc: Loc, - _unchecked: bool, - statements: &mut Vec, - ) -> Result<(), Self::Error> { - statements.visit(self) - } - - fn visit_expr(&mut self, _loc: Loc, expr: &mut Expression) -> Result<(), Self::Error> { - match expr { - Expression::PostIncrement(_, expr) => { - expr.visit(self)?; - } - Expression::PostDecrement(_, expr) => { - expr.visit(self)?; - } - Expression::New(_, expr) => { - expr.visit(self)?; - } - Expression::ArraySubscript(_, expr1, expr2) => { - expr1.visit(self)?; - expr2.visit(self)?; - } - Expression::ArraySlice(_, expr1, expr2, expr3) => { - expr1.visit(self)?; - expr2.visit(self)?; - expr3.visit(self)?; - } - Expression::Parenthesis(_, expr) => { - expr.visit(self)?; - } - Expression::MemberAccess(_, expr, _) => { - expr.visit(self)?; - } - Expression::FunctionCall(loc, lhs, rhs) => { - // all cheatcodes are accessd via .cheatcode - if let Expression::MemberAccess(_, expr, identifier) = &**lhs { - if let Expression::Variable(_) = &**expr { - match identifier.name.as_str() { - "ffi" => self.cheatcodes.ffi.push(*loc), - "readFile" => self.cheatcodes.read_file.push(*loc), - "writeFile" => self.cheatcodes.write_file.push(*loc), - "readLine" => self.cheatcodes.read_line.push(*loc), - "writeLine" => self.cheatcodes.write_line.push(*loc), - "closeFile" => self.cheatcodes.close_file.push(*loc), - "removeFile" => self.cheatcodes.remove_file.push(*loc), - "setEnv" => self.cheatcodes.set_env.push(*loc), - "deriveKey" => self.cheatcodes.derive_key.push(*loc), - _ => {} - } - } - } - rhs.visit(self)?; - } - Expression::FunctionCallBlock(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::NamedFunctionCall(_, lhs, rhs) => { - lhs.visit(self)?; - for arg in rhs.iter_mut() { - arg.expr.visit(self)?; - } - } - Expression::Not(_, expr) => { - expr.visit(self)?; - } - Expression::BitwiseNot(_, expr) => { - expr.visit(self)?; - } - Expression::Delete(_, expr) => { - expr.visit(self)?; - } - Expression::PreIncrement(_, expr) => { - expr.visit(self)?; - } - Expression::PreDecrement(_, expr) => { - expr.visit(self)?; - } - Expression::UnaryPlus(_, expr) => { - expr.visit(self)?; - } - Expression::Negate(_, expr) => { - expr.visit(self)?; - } - Expression::Power(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::Multiply(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::Divide(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::Modulo(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::Add(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::Subtract(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::ShiftLeft(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::ShiftRight(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::BitwiseAnd(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::BitwiseXor(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::BitwiseOr(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::Less(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::More(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::LessEqual(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::MoreEqual(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::Equal(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::NotEqual(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::And(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::Or(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::ConditionalOperator(_, llhs, lhs, rhs) => { - llhs.visit(self)?; - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::Assign(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::AssignOr(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::AssignAnd(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::AssignXor(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::AssignShiftLeft(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::AssignShiftRight(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::AssignAdd(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::AssignSubtract(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::AssignMultiply(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::AssignDivide(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::AssignModulo(_, lhs, rhs) => { - lhs.visit(self)?; - rhs.visit(self)?; - } - Expression::List(_, param) => { - for (_, param) in param.iter_mut() { - param.visit(self)?; - } - } - _ => {} - } - - Ok(()) - } - - fn visit_emit(&mut self, _: Loc, expr: &mut Expression) -> Result<(), Self::Error> { - expr.visit(self) - } - - fn visit_var_definition(&mut self, var: &mut VariableDefinition) -> Result<(), Self::Error> { - var.ty.visit(self)?; - var.initializer.visit(self) - } - - fn visit_var_definition_stmt( - &mut self, - _: Loc, - declaration: &mut VariableDeclaration, - expr: &mut Option, - ) -> Result<(), Self::Error> { - declaration.visit(self)?; - expr.visit(self) - } - - fn visit_var_declaration(&mut self, var: &mut VariableDeclaration) -> Result<(), Self::Error> { - var.ty.visit(self) - } - - fn visit_revert( - &mut self, - _: Loc, - _error: &mut Option, - args: &mut Vec, - ) -> Result<(), Self::Error> { - args.visit(self) - } - - fn visit_if( - &mut self, - _loc: Loc, - cond: &mut Expression, - if_branch: &mut Box, - else_branch: &mut Option>, - _is_frst_stmt: bool, - ) -> Result<(), Self::Error> { - cond.visit(self)?; - if_branch.visit(self)?; - else_branch.visit(self) - } - - fn visit_while( - &mut self, - _loc: Loc, - cond: &mut Expression, - body: &mut Statement, - ) -> Result<(), Self::Error> { - cond.visit(self)?; - body.visit(self) - } - - fn visit_for( - &mut self, - _loc: Loc, - init: &mut Option>, - cond: &mut Option>, - update: &mut Option>, - body: &mut Option>, - ) -> Result<(), Self::Error> { - init.visit(self)?; - cond.visit(self)?; - update.visit(self)?; - body.visit(self) - } - - fn visit_function(&mut self, func: &mut FunctionDefinition) -> Result<(), Self::Error> { - if let Some(ref mut body) = func.body { - body.visit(self)?; - } - Ok(()) - } - - fn visit_parameter(&mut self, parameter: &mut Parameter) -> Result<(), Self::Error> { - parameter.ty.visit(self) - } - - fn visit_type_definition(&mut self, def: &mut TypeDefinition) -> Result<(), Self::Error> { - def.ty.visit(self) - } -} diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index ac0992cf9f65..d60c1639a05a 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -105,9 +105,8 @@ fn run() -> Result<()> { ForgeSubcommand::Inspect(cmd) => cmd.run(), ForgeSubcommand::Tree(cmd) => cmd.run(), ForgeSubcommand::Geiger(cmd) => { - let check = cmd.check; let n = cmd.run()?; - if check && n > 0 { + if n > 0 { std::process::exit(n as i32); } Ok(()) diff --git a/crates/forge/tests/cli/debug.rs b/crates/forge/tests/cli/debug.rs index e8cd084187bb..c217beeb501f 100644 --- a/crates/forge/tests/cli/debug.rs +++ b/crates/forge/tests/cli/debug.rs @@ -3,7 +3,7 @@ use std::path::Path; // Sets up a debuggable test case. // Run with `cargo test-debugger`. -forgetest_async!( +forgetest!( #[ignore = "ran manually"] manual_debug_setup, |prj, cmd| { diff --git a/crates/forge/tests/cli/geiger.rs b/crates/forge/tests/cli/geiger.rs new file mode 100644 index 000000000000..fd2165628474 --- /dev/null +++ b/crates/forge/tests/cli/geiger.rs @@ -0,0 +1,92 @@ +forgetest!(call, |prj, cmd| { + prj.add_source( + "call.sol", + r#" + contract A is Test { + function do_ffi() public { + string[] memory inputs = new string[](1); + vm.ffi(inputs); + } + } + "#, + ) + .unwrap(); + + cmd.arg("geiger").assert_code(1).stderr_eq(str![[r#" +error: usage of unsafe cheatcode `vm.ffi` + [FILE]:7:20 + | +7 | vm.ffi(inputs); + | ^^^ + | + + +"#]]); +}); + +forgetest!(assignment, |prj, cmd| { + prj.add_source( + "assignment.sol", + r#" + contract A is Test { + function do_ffi() public { + string[] memory inputs = new string[](1); + bytes stuff = vm.ffi(inputs); + } + } + "#, + ) + .unwrap(); + + cmd.arg("geiger").assert_code(1).stderr_eq(str![[r#" +error: usage of unsafe cheatcode `vm.ffi` + [FILE]:7:34 + | +7 | bytes stuff = vm.ffi(inputs); + | ^^^ + | + + +"#]]); +}); + +forgetest!(exit_code, |prj, cmd| { + prj.add_source( + "multiple.sol", + r#" + contract A is Test { + function do_ffi() public { + vm.ffi(inputs); + vm.ffi(inputs); + vm.ffi(inputs); + } + } + "#, + ) + .unwrap(); + + cmd.arg("geiger").assert_code(3).stderr_eq(str![[r#" +error: usage of unsafe cheatcode `vm.ffi` + [FILE]:6:20 + | +6 | vm.ffi(inputs); + | ^^^ + | + +error: usage of unsafe cheatcode `vm.ffi` + [FILE]:7:20 + | +7 | vm.ffi(inputs); + | ^^^ + | + +error: usage of unsafe cheatcode `vm.ffi` + [FILE]:8:20 + | +8 | vm.ffi(inputs); + | ^^^ + | + + +"#]]); +}); diff --git a/crates/forge/tests/cli/main.rs b/crates/forge/tests/cli/main.rs index a53a26d2ac5a..5838fa853777 100644 --- a/crates/forge/tests/cli/main.rs +++ b/crates/forge/tests/cli/main.rs @@ -17,6 +17,7 @@ mod create; mod debug; mod doc; mod eip712; +mod geiger; mod multi_script; mod script; mod soldeer; diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 8d7f6cbb5fcc..f887a40ce7a1 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -883,7 +883,7 @@ impl TestCommand { let assert = OutputAssert::new(self.execute()); if self.redact_output { return assert.with_assert(test_assert()); - }; + } assert } @@ -914,6 +914,12 @@ impl TestCommand { self.assert().failure() } + /// Runs the command and asserts that the exit code is `expected`. + #[track_caller] + pub fn assert_code(&mut self, expected: i32) -> OutputAssert { + self.assert().code(expected) + } + /// Runs the command and asserts that it **failed** nothing was printed to stderr. #[track_caller] pub fn assert_empty_stderr(&mut self) { From 398ef4a3d55d8dd769ce86cada5ec845e805188b Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 22 Nov 2024 23:58:04 +0800 Subject: [PATCH 42/68] feat(forge, cast): add `cast --with_local_artifacts`/`forge selectors cache` to trace with local artifacts (#7359) * add RunArgs generate_local_signatures to enable trace with local contracts functions and events * make generate_local_signatures as a helper function * rename generate_local_signatures to cache_local_signatures merge project signatures with exists cached local signatures instead of just override them * extract duplicate method for CachedSignatures * fix cache load path * fix for lint * fix fot lint * remove unnecessary `let` binding * fix for format check * fix for clippy check * fix for clippy check * Move cache in forge selectors, use local artifacts for cast run and send traces * Add test * Review changes: - compile without quiet, fix test - merge local sources with etherscan * Update crates/evm/traces/src/debug/sources.rs Co-authored-by: Arsenii Kulikov --------- Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Arsenii Kulikov --- crates/cast/bin/cmd/call.rs | 16 ++- crates/cast/bin/cmd/run.rs | 8 +- crates/cast/tests/cli/main.rs | 111 +++++++++++++++++- crates/cli/src/utils/cmd.rs | 102 ++++++++++------ crates/evm/traces/src/debug/sources.rs | 8 ++ crates/evm/traces/src/identifier/mod.rs | 2 +- .../evm/traces/src/identifier/signatures.rs | 35 ++++-- crates/forge/bin/cmd/selectors.rs | 26 +++- 8 files changed, 256 insertions(+), 52 deletions(-) diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index aefc5f1c02f5..cdc3bd4bc6e2 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -81,6 +81,10 @@ pub struct CallArgs { #[command(flatten)] eth: EthereumOpts, + + /// Use current project artifacts for trace decoding. + #[arg(long, visible_alias = "la")] + pub with_local_artifacts: bool, } #[derive(Debug, Parser)] @@ -127,6 +131,7 @@ impl CallArgs { decode_internal, labels, data, + with_local_artifacts, .. } = self; @@ -195,7 +200,16 @@ impl CallArgs { ), }; - handle_traces(trace, &config, chain, labels, debug, decode_internal, false).await?; + handle_traces( + trace, + &config, + chain, + labels, + with_local_artifacts, + debug, + decode_internal, + ) + .await?; return Ok(()); } diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 79083fa8db9e..0b85d14feb98 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -10,7 +10,7 @@ use foundry_cli::{ opts::{EtherscanOpts, RpcOpts}, utils::{handle_traces, init_progress, TraceResult}, }; -use foundry_common::{is_known_system_sender, shell, SYSTEM_TRANSACTION_TYPE}; +use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; use foundry_compilers::artifacts::EvmVersion; use foundry_config::{ figment::{ @@ -87,6 +87,10 @@ pub struct RunArgs { /// Enables Alphanet features. #[arg(long, alias = "odyssey")] pub alphanet: bool, + + /// Use current project artifacts for trace decoding. + #[arg(long, visible_alias = "la")] + pub with_local_artifacts: bool, } impl RunArgs { @@ -251,9 +255,9 @@ impl RunArgs { &config, chain, self.label, + self.with_local_artifacts, self.debug, self.decode_internal, - shell::verbosity() > 0, ) .await?; diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index bdc4a6044d0c..332f0f99f57b 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1,10 +1,12 @@ //! Contains various tests for checking cast commands use alloy_chains::NamedChain; +use alloy_network::TransactionResponse; use alloy_primitives::{b256, B256}; +use alloy_rpc_types::{BlockNumberOrTag, Index}; use anvil::{EthereumHardfork, NodeConfig}; use foundry_test_utils::{ - casttest, file, + casttest, file, forgetest_async, rpc::{ next_etherscan_api_key, next_http_rpc_endpoint, next_mainnet_etherscan_api_key, next_rpc_endpoint, next_ws_rpc_endpoint, @@ -1596,3 +1598,110 @@ casttest!(fetch_artifact_from_etherscan, |_prj, cmd| { "#]]); }); + +// tests cast can decode traces when using project artifacts +forgetest_async!(decode_traces_with_project_artifacts, |prj, cmd| { + let (api, handle) = + anvil::spawn(NodeConfig::test().with_disable_default_create2_deployer(true)).await; + + foundry_test_utils::util::initialize(prj.root()); + prj.add_source( + "LocalProjectContract", + r#" +contract LocalProjectContract { + event LocalProjectContractCreated(address owner); + + constructor() { + emit LocalProjectContractCreated(msg.sender); + } +} + "#, + ) + .unwrap(); + prj.add_script( + "LocalProjectScript", + r#" +import "forge-std/Script.sol"; +import {LocalProjectContract} from "../src/LocalProjectContract.sol"; + +contract LocalProjectScript is Script { + function run() public { + vm.startBroadcast(); + new LocalProjectContract(); + vm.stopBroadcast(); + } +} + "#, + ) + .unwrap(); + + cmd.args([ + "script", + "--private-key", + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "--rpc-url", + &handle.http_endpoint(), + "--broadcast", + "LocalProjectScript", + ]); + + cmd.assert_success(); + + let tx_hash = api + .transaction_by_block_number_and_index(BlockNumberOrTag::Latest, Index::from(0)) + .await + .unwrap() + .unwrap() + .tx_hash(); + + // Assert cast with local artifacts from outside the project. + cmd.cast_fuse() + .args(["run", "--la", format!("{tx_hash}").as_str(), "--rpc-url", &handle.http_endpoint()]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Compiling project to generate artifacts +Nothing to compile + +"#]]); + + // Run cast from project dir. + cmd.cast_fuse().set_current_dir(prj.root()); + + // Assert cast without local artifacts cannot decode traces. + cmd.cast_fuse() + .args(["run", format!("{tx_hash}").as_str(), "--rpc-url", &handle.http_endpoint()]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Traces: + [13520] → new @0x5FbDB2315678afecb367f032d93F642f64180aa3 + ├─ emit topic 0: 0xa7263295d3a687d750d1fd377b5df47de69d7db8decc745aaa4bbee44dc1688d + │ data: 0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266 + └─ ← [Return] 62 bytes of code + + +Transaction successfully executed. +[GAS] + +"#]]); + + // Assert cast with local artifacts can decode traces. + cmd.cast_fuse() + .args(["run", "--la", format!("{tx_hash}").as_str(), "--rpc-url", &handle.http_endpoint()]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Compiling project to generate artifacts +No files changed, compilation skipped +Traces: + [13520] → new LocalProjectContract@0x5FbDB2315678afecb367f032d93F642f64180aa3 + ├─ emit LocalProjectContractCreated(owner: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266) + └─ ← [Return] 62 bytes of code + + +Transaction successfully executed. +[GAS] + +"#]]); +}); diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 19e4425b54c7..523c10478fb6 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -1,7 +1,7 @@ use alloy_json_abi::JsonAbi; use alloy_primitives::Address; use eyre::{Result, WrapErr}; -use foundry_common::{fs, TestFunctionExt}; +use foundry_common::{compile::ProjectCompiler, fs, shell, ContractsByArtifact, TestFunctionExt}; use foundry_compilers::{ artifacts::{CompactBytecode, Settings}, cache::{CacheEntry, CompilerCache}, @@ -14,9 +14,9 @@ use foundry_evm::{ executors::{DeployResult, EvmError, RawCallResult}, opts::EvmOpts, traces::{ - debug::DebugTraceIdentifier, + debug::{ContractSources, DebugTraceIdentifier}, decode_trace_arena, - identifier::{EtherscanIdentifier, SignaturesIdentifier}, + identifier::{CachedSignatures, SignaturesIdentifier, TraceIdentifiers}, render_trace_arena_with_bytecodes, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, }, @@ -383,10 +383,25 @@ pub async fn handle_traces( config: &Config, chain: Option, labels: Vec, + with_local_artifacts: bool, debug: bool, decode_internal: bool, - verbose: bool, ) -> Result<()> { + let (known_contracts, mut sources) = if with_local_artifacts { + let _ = sh_println!("Compiling project to generate artifacts"); + let project = config.project()?; + let compiler = ProjectCompiler::new(); + let output = compiler.compile(&project)?; + ( + Some(ContractsByArtifact::new( + output.artifact_ids().map(|(id, artifact)| (id, artifact.clone().into())), + )), + ContractSources::from_project_output(&output, project.root(), None)?, + ) + } else { + (None, ContractSources::default()) + }; + let labels = labels.iter().filter_map(|label_str| { let mut iter = label_str.split(':'); @@ -398,45 +413,44 @@ pub async fn handle_traces( None }); let config_labels = config.labels.clone().into_iter(); - let mut decoder = CallTraceDecoderBuilder::new() + + let mut builder = CallTraceDecoderBuilder::new() .with_labels(labels.chain(config_labels)) .with_signature_identifier(SignaturesIdentifier::new( Config::foundry_cache_dir(), config.offline, - )?) - .build(); + )?); + let mut identifier = TraceIdentifiers::new().with_etherscan(config, chain)?; + if let Some(contracts) = &known_contracts { + builder = builder.with_known_contracts(contracts); + identifier = identifier.with_local(contracts); + } - let mut etherscan_identifier = EtherscanIdentifier::new(config, chain)?; - if let Some(etherscan_identifier) = &mut etherscan_identifier { - for (_, trace) in result.traces.as_deref_mut().unwrap_or_default() { - decoder.identify(trace, etherscan_identifier); - } + let mut decoder = builder.build(); + + for (_, trace) in result.traces.as_deref_mut().unwrap_or_default() { + decoder.identify(trace, &mut identifier); } - if decode_internal { - let sources = if let Some(etherscan_identifier) = ðerscan_identifier { - etherscan_identifier.get_compiled_contracts().await? - } else { - Default::default() - }; + if decode_internal || debug { + if let Some(ref etherscan_identifier) = identifier.etherscan { + sources.merge(etherscan_identifier.get_compiled_contracts().await?); + } + + if debug { + let mut debugger = Debugger::builder() + .traces(result.traces.expect("missing traces")) + .decoder(&decoder) + .sources(sources) + .build(); + debugger.try_run_tui()?; + return Ok(()) + } + decoder.debug_identifier = Some(DebugTraceIdentifier::new(sources)); } - if debug { - let sources = if let Some(etherscan_identifier) = etherscan_identifier { - etherscan_identifier.get_compiled_contracts().await? - } else { - Default::default() - }; - let mut debugger = Debugger::builder() - .traces(result.traces.expect("missing traces")) - .decoder(&decoder) - .sources(sources) - .build(); - debugger.try_run_tui()?; - } else { - print_traces(&mut result, &decoder, verbose).await?; - } + print_traces(&mut result, &decoder, shell::verbosity() > 0).await?; Ok(()) } @@ -464,3 +478,25 @@ pub async fn print_traces( sh_println!("Gas used: {}", result.gas_used)?; Ok(()) } + +/// Traverse the artifacts in the project to generate local signatures and merge them into the cache +/// file. +pub fn cache_local_signatures(output: &ProjectCompileOutput, cache_path: PathBuf) -> Result<()> { + let path = cache_path.join("signatures"); + let mut cached_signatures = CachedSignatures::load(cache_path); + output.artifacts().for_each(|(_, artifact)| { + if let Some(abi) = &artifact.abi { + for func in abi.functions() { + cached_signatures.functions.insert(func.selector().to_string(), func.signature()); + } + for event in abi.events() { + cached_signatures + .events + .insert(event.selector().to_string(), event.full_signature()); + } + } + }); + + fs::write_json_file(&path, &cached_signatures)?; + Ok(()) +} diff --git a/crates/evm/traces/src/debug/sources.rs b/crates/evm/traces/src/debug/sources.rs index 40e540a972d1..dfbfe91af30d 100644 --- a/crates/evm/traces/src/debug/sources.rs +++ b/crates/evm/traces/src/debug/sources.rs @@ -212,6 +212,14 @@ impl ContractSources { Ok(()) } + /// Merges given contract sources. + pub fn merge(&mut self, sources: Self) { + self.sources_by_id.extend(sources.sources_by_id); + for (name, artifacts) in sources.artifacts_by_name { + self.artifacts_by_name.entry(name).or_default().extend(artifacts); + } + } + /// Returns all sources for a contract by name. pub fn get_sources( &self, diff --git a/crates/evm/traces/src/identifier/mod.rs b/crates/evm/traces/src/identifier/mod.rs index 008e5f841843..51f949832659 100644 --- a/crates/evm/traces/src/identifier/mod.rs +++ b/crates/evm/traces/src/identifier/mod.rs @@ -12,7 +12,7 @@ mod etherscan; pub use etherscan::EtherscanIdentifier; mod signatures; -pub use signatures::{SignaturesIdentifier, SingleSignaturesIdentifier}; +pub use signatures::{CachedSignatures, SignaturesIdentifier, SingleSignaturesIdentifier}; /// An address identity pub struct AddressIdentity<'a> { diff --git a/crates/evm/traces/src/identifier/signatures.rs b/crates/evm/traces/src/identifier/signatures.rs index 1e3924aa3f49..2a5ef354a753 100644 --- a/crates/evm/traces/src/identifier/signatures.rs +++ b/crates/evm/traces/src/identifier/signatures.rs @@ -12,11 +12,29 @@ use tokio::sync::RwLock; pub type SingleSignaturesIdentifier = Arc>; #[derive(Debug, Default, Serialize, Deserialize)] -struct CachedSignatures { - events: BTreeMap, - functions: BTreeMap, +pub struct CachedSignatures { + pub events: BTreeMap, + pub functions: BTreeMap, } +impl CachedSignatures { + #[instrument(target = "evm::traces")] + pub fn load(cache_path: PathBuf) -> Self { + let path = cache_path.join("signatures"); + if path.is_file() { + fs::read_json_file(&path) + .map_err( + |err| warn!(target: "evm::traces", ?path, ?err, "failed to read cache file"), + ) + .unwrap_or_default() + } else { + if let Err(err) = std::fs::create_dir_all(cache_path) { + warn!(target: "evm::traces", "could not create signatures cache dir: {:?}", err); + } + Self::default() + } + } +} /// An identifier that tries to identify functions and events using signatures found at /// `https://openchain.xyz` or a local cache. #[derive(Debug)] @@ -42,16 +60,7 @@ impl SignaturesIdentifier { let identifier = if let Some(cache_path) = cache_path { let path = cache_path.join("signatures"); trace!(target: "evm::traces", ?path, "reading signature cache"); - let cached = if path.is_file() { - fs::read_json_file(&path) - .map_err(|err| warn!(target: "evm::traces", ?path, ?err, "failed to read cache file")) - .unwrap_or_default() - } else { - if let Err(err) = std::fs::create_dir_all(cache_path) { - warn!(target: "evm::traces", "could not create signatures cache dir: {:?}", err); - } - CachedSignatures::default() - }; + let cached = CachedSignatures::load(cache_path); Self { cached, cached_path: Some(path), unavailable: HashSet::default(), client } } else { Self { diff --git a/crates/forge/bin/cmd/selectors.rs b/crates/forge/bin/cmd/selectors.rs index ddd6a796870c..f75dabaff449 100644 --- a/crates/forge/bin/cmd/selectors.rs +++ b/crates/forge/bin/cmd/selectors.rs @@ -4,13 +4,14 @@ use comfy_table::Table; use eyre::Result; use foundry_cli::{ opts::{CompilerArgs, CoreBuildArgs, ProjectPathsArgs}, - utils::FoundryPathExt, + utils::{cache_local_signatures, FoundryPathExt}, }; use foundry_common::{ compile::{compile_target, ProjectCompiler}, selectors::{import_selectors, SelectorImportData}, }; use foundry_compilers::{artifacts::output_selection::ContractOutputSelection, info::ContractInfo}; +use foundry_config::Config; use std::fs::canonicalize; /// CLI arguments for `forge selectors`. @@ -67,11 +68,34 @@ pub enum SelectorsSubcommands { #[command(flatten)] project_paths: ProjectPathsArgs, }, + + /// Cache project selectors (enables trace with local contracts functions and events). + #[command(visible_alias = "c")] + Cache { + #[command(flatten)] + project_paths: ProjectPathsArgs, + }, } impl SelectorsSubcommands { pub async fn run(self) -> Result<()> { match self { + Self::Cache { project_paths } => { + sh_println!("Caching selectors for contracts in the project...")?; + let build_args = CoreBuildArgs { + project_paths, + compiler: CompilerArgs { + extra_output: vec![ContractOutputSelection::Abi], + ..Default::default() + }, + ..Default::default() + }; + + // compile the project to get the artifacts/abis + let project = build_args.project()?; + let outcome = ProjectCompiler::new().quiet(true).compile(&project)?; + cache_local_signatures(&outcome, Config::foundry_cache_dir().unwrap())? + } Self::Upload { contract, all, project_paths } => { let build_args = CoreBuildArgs { project_paths: project_paths.clone(), From e5412ad6dc2d7ecdc7541b6c0c8b41df80b511ee Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 23 Nov 2024 15:15:27 +0100 Subject: [PATCH 43/68] chore: use has_library_ancestor (#9387) --- crates/forge/bin/cmd/geiger.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/bin/cmd/geiger.rs b/crates/forge/bin/cmd/geiger.rs index 9ffddd3c9881..6d4c735a9909 100644 --- a/crates/forge/bin/cmd/geiger.rs +++ b/crates/forge/bin/cmd/geiger.rs @@ -54,7 +54,7 @@ impl GeigerArgs { Graph::::resolve(&paths)? .files() .keys() - .filter(|f| !paths.libraries.iter().any(|lib| f.starts_with(lib))) + .filter(|f| !paths.has_library_ancestor(f)) .cloned() .collect() } else { From d14a7b44fc439407d761fccc4c1637216554bbb6 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 23 Nov 2024 16:10:44 +0100 Subject: [PATCH 44/68] chore(evm/traces): replace solang with Solar (#9386) --- Cargo.lock | 2 +- crates/evm/traces/Cargo.toml | 2 +- crates/evm/traces/src/debug/sources.rs | 78 +++++++++++++------------- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15a2386be8b9..0fb6301f5e66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4076,7 +4076,7 @@ dependencies = [ "revm", "revm-inspectors", "serde", - "solang-parser", + "solar-parse", "tempfile", "tokio", "tracing", diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 3423213fbb9e..53bf8b3bb2cb 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -40,7 +40,7 @@ tokio = { workspace = true, features = ["time", "macros"] } tracing.workspace = true tempfile.workspace = true rayon.workspace = true -solang-parser.workspace = true +solar-parse.workspace = true revm.workspace = true [dev-dependencies] diff --git a/crates/evm/traces/src/debug/sources.rs b/crates/evm/traces/src/debug/sources.rs index dfbfe91af30d..ff1911493baa 100644 --- a/crates/evm/traces/src/debug/sources.rs +++ b/crates/evm/traces/src/debug/sources.rs @@ -11,9 +11,13 @@ use foundry_compilers::{ use foundry_evm_core::utils::PcIcMap; use foundry_linking::Linker; use rayon::prelude::*; -use solang_parser::pt::SourceUnitPart; +use solar_parse::{ + interface::{Pos, Session}, + Parser, +}; use std::{ collections::{BTreeMap, HashMap}, + ops::Range, path::{Path, PathBuf}, sync::Arc, }; @@ -25,7 +29,7 @@ pub struct SourceData { pub path: PathBuf, /// Maps contract name to (start, end) of the contract definition in the source code. /// This is useful for determining which contract contains given function definition. - contract_definitions: Vec<(String, usize, usize)>, + contract_definitions: Vec<(String, Range)>, } impl SourceData { @@ -35,26 +39,26 @@ impl SourceData { match language { MultiCompilerLanguage::Vyper(_) => { // Vyper contracts have the same name as the file name. - if let Some(name) = path.file_name().map(|s| s.to_string_lossy().to_string()) { - contract_definitions.push((name, 0, source.len())); + if let Some(name) = path.file_stem().map(|s| s.to_string_lossy().to_string()) { + contract_definitions.push((name, 0..source.len())); } } MultiCompilerLanguage::Solc(_) => { - if let Ok((parsed, _)) = solang_parser::parse(&source, 0) { - for item in parsed.0 { - let SourceUnitPart::ContractDefinition(contract) = item else { - continue; - }; - let Some(name) = contract.name else { - continue; - }; - contract_definitions.push(( - name.name, - name.loc.start(), - contract.loc.end(), - )); + let sess = Session::builder().with_silent_emitter(None).build(); + let _ = sess.enter(|| -> solar_parse::interface::Result<()> { + let arena = solar_parse::ast::Arena::new(); + let filename = path.clone().into(); + let mut parser = + Parser::from_source_code(&sess, &arena, filename, source.to_string())?; + let ast = parser.parse_file().map_err(|e| e.emit())?; + for item in ast.items { + if let solar_parse::ast::ItemKind::Contract(contract) = &item.kind { + let range = item.span.lo().to_usize()..item.span.hi().to_usize(); + contract_definitions.push((contract.name.to_string(), range)); + } } - } + Ok(()) + }); } } @@ -65,8 +69,8 @@ impl SourceData { pub fn find_contract_name(&self, start: usize, end: usize) -> Option<&str> { self.contract_definitions .iter() - .find(|(_, s, e)| start >= *s && end <= *e) - .map(|(name, _, _)| name.as_str()) + .find(|(_, r)| start >= r.start && end <= r.end) + .map(|(name, _)| name.as_str()) } } @@ -182,26 +186,22 @@ impl ContractSources { let mut files: BTreeMap> = BTreeMap::new(); for (build_id, build) in output.builds() { for (source_id, path) in &build.source_id_to_path { - let source_data = if let Some(source_data) = files.get(path) { - source_data.clone() - } else { - let source = Source::read(path).wrap_err_with(|| { - format!("failed to read artifact source file for `{}`", path.display()) - })?; - - let stripped = path.strip_prefix(root).unwrap_or(path).to_path_buf(); - - let source_data = Arc::new(SourceData::new( - source.content.clone(), - build.language.into(), - stripped, - )); - - files.insert(path.clone(), source_data.clone()); - - source_data + let source_data = match files.entry(path.clone()) { + std::collections::btree_map::Entry::Vacant(entry) => { + let source = Source::read(path).wrap_err_with(|| { + format!("failed to read artifact source file for `{}`", path.display()) + })?; + let stripped = path.strip_prefix(root).unwrap_or(path).to_path_buf(); + let source_data = Arc::new(SourceData::new( + source.content.clone(), + build.language.into(), + stripped, + )); + entry.insert(source_data.clone()); + source_data + } + std::collections::btree_map::Entry::Occupied(entry) => entry.get().clone(), }; - self.sources_by_id .entry(build_id.clone()) .or_default() From 4923529c743f25a0f37503a7bcf7c68caa6901f1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 24 Nov 2024 00:51:26 +0000 Subject: [PATCH 45/68] chore(deps): weekly `cargo update` (#9392) Locking 36 packages to latest compatible versions Updating async-compression v0.4.17 -> v0.4.18 Updating bytemuck v1.19.0 -> v1.20.0 Updating const-hex v1.13.1 -> v1.13.2 Adding core-foundation v0.10.0 Updating cpufeatures v0.2.15 -> v0.2.16 Updating h2 v0.4.6 -> v0.4.7 Updating hyper v1.5.0 -> v1.5.1 Updating impl-trait-for-tuples v0.2.2 -> v0.2.3 Updating interprocess v2.2.1 -> v2.2.2 Updating itoa v1.0.11 -> v1.0.13 Updating litemap v0.7.3 -> v0.7.4 Updating op-alloy-consensus v0.6.5 -> v0.6.8 Updating op-alloy-rpc-types v0.6.5 -> v0.6.8 Updating portable-atomic v1.9.0 -> v1.10.0 Updating proc-macro2 v1.0.89 -> v1.0.92 Updating quick-junit v0.5.0 -> v0.5.1 Updating quick-xml v0.36.2 -> v0.37.1 Updating rustix v0.38.40 -> v0.38.41 Updating rustls v0.23.17 -> v0.23.18 Updating rustls-native-certs v0.8.0 -> v0.8.1 Updating scale-info v2.11.5 -> v2.11.6 Updating scale-info-derive v2.11.5 -> v2.11.6 Updating schannel v0.1.26 -> v0.1.27 Adding security-framework v3.0.1 Updating semver-parser v0.10.2 -> v0.10.3 Updating syn v2.0.87 -> v2.0.89 Updating sync_wrapper v1.0.1 -> v1.0.2 Updating unicode-ident v1.0.13 -> v1.0.14 Updating url v2.5.3 -> v2.5.4 Updating wasmtimer v0.4.0 -> v0.4.1 Updating webpki-roots v0.26.6 -> v0.26.7 Updating yoke v0.7.4 -> v0.7.5 Updating yoke-derive v0.7.4 -> v0.7.5 Updating zerofrom v0.1.4 -> v0.1.5 Updating zerofrom-derive v0.1.4 -> v0.1.5 Updating zip v2.2.0 -> v2.2.1 note: pass `--verbose` to see 18 unchanged dependencies behind latest Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> --- Cargo.lock | 365 ++++++++++++++++++++++++++++------------------------- 1 file changed, 192 insertions(+), 173 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0fb6301f5e66..0e6cdd0f96c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -374,7 +374,7 @@ checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -624,7 +624,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -641,7 +641,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", "syn-solidity", "tiny-keccak", ] @@ -659,7 +659,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.87", + "syn 2.0.89", "syn-solidity", ] @@ -752,7 +752,7 @@ dependencies = [ "alloy-transport", "futures", "http 1.1.0", - "rustls 0.23.17", + "rustls 0.23.18", "serde_json", "tokio", "tokio-tungstenite", @@ -909,7 +909,7 @@ dependencies = [ "foundry-evm", "foundry-test-utils", "futures", - "hyper 1.5.0", + "hyper 1.5.1", "itertools 0.13.0", "k256", "op-alloy-consensus", @@ -1151,9 +1151,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb8f1d480b0ea3783ab015936d2a55c87e219676f0c0b7dec61494043f21857" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" dependencies = [ "flate2", "futures-core", @@ -1179,7 +1179,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -1201,7 +1201,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -1212,7 +1212,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -1265,7 +1265,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -1607,7 +1607,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-util", "itoa", "matchit", @@ -1621,7 +1621,7 @@ dependencies = [ "serde_path_to_error", "serde_urlencoded", "sha1", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tokio", "tokio-tungstenite", "tower 0.5.1", @@ -1645,7 +1645,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tower-layer", "tower-service", "tracing", @@ -1799,7 +1799,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -1843,9 +1843,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" [[package]] name = "byteorder" @@ -2181,7 +2181,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -2399,9 +2399,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" +checksum = "487981fa1af147182687064d0a2c336586d337a606595ced9ffb0c685c250c73" dependencies = [ "cfg-if", "cpufeatures", @@ -2435,6 +2435,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -2443,9 +2453,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -2586,7 +2596,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -2597,7 +2607,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -2670,7 +2680,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -2691,7 +2701,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -2701,7 +2711,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -2722,7 +2732,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", "unicode-xid", ] @@ -2836,7 +2846,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -2861,7 +2871,7 @@ checksum = "1b4464d46ce68bfc7cb76389248c7c254def7baca8bece0693b02b83842c4c88" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -2988,7 +2998,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -3123,7 +3133,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.87", + "syn 2.0.89", "toml 0.8.19", "walkdir", ] @@ -3151,7 +3161,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.87", + "syn 2.0.89", "tempfile", "thiserror 1.0.69", "tiny-keccak", @@ -3387,7 +3397,7 @@ dependencies = [ "futures", "globset", "humantime-serde", - "hyper 1.5.0", + "hyper 1.5.1", "indicatif", "inferno", "itertools 0.13.0", @@ -3544,7 +3554,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -4123,7 +4133,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -4282,7 +4292,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -4331,7 +4341,7 @@ dependencies = [ "bytes", "chrono", "futures", - "hyper 1.5.0", + "hyper 1.5.1", "jsonwebtoken", "once_cell", "prost", @@ -4666,9 +4676,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -4816,7 +4826,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -4935,14 +4945,14 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", + "h2 0.4.7", "http 1.1.0", "http-body 1.0.1", "httparse", @@ -4978,10 +4988,10 @@ checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-util", - "rustls 0.23.17", - "rustls-native-certs 0.8.0", + "rustls 0.23.18", + "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -4995,7 +5005,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.5.0", + "hyper 1.5.1", "hyper-util", "pin-project-lite", "tokio", @@ -5010,7 +5020,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-util", "native-tls", "tokio", @@ -5029,7 +5039,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.1", - "hyper 1.5.0", + "hyper 1.5.1", "pin-project-lite", "socket2", "tokio", @@ -5175,7 +5185,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -5269,13 +5279,13 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.89", ] [[package]] @@ -5394,14 +5404,14 @@ dependencies = [ "pretty_assertions", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] name = "interprocess" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f4e4a06d42fab3e85ab1b419ad32b09eab58b901d40c57935ff92db3287a13" +checksum = "894148491d817cb36b6f778017b8ac46b17408d522dd90f539d677ea938362eb" dependencies = [ "doctest-file", "futures-core", @@ -5464,9 +5474,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" [[package]] name = "jiff" @@ -5683,9 +5693,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "lock_api" @@ -5871,7 +5881,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -5962,7 +5972,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -5977,7 +5987,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -6236,7 +6246,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -6284,9 +6294,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "op-alloy-consensus" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff54d1d790eca1f3aedbd666162e9c42eceff90b9f9d24b352ed9c2df1e901a" +checksum = "fce158d886815d419222daa67fcdf949a34f7950653a4498ebeb4963331f70ed" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6295,14 +6305,14 @@ dependencies = [ "alloy-serde", "derive_more", "serde", - "spin", + "thiserror 2.0.3", ] [[package]] name = "op-alloy-rpc-types" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "981b7f8ab11fe85ba3c1723702f000429b8d0c16b5883c93d577895f262cbac6" +checksum = "060ebeaea8c772e396215f69bb86d231ec8b7f36aca0dd6ce367ceaa9a8c33e6" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6376,7 +6386,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -6457,7 +6467,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -6540,7 +6550,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -6599,7 +6609,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -6683,7 +6693,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -6741,7 +6751,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -6774,9 +6784,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "portable-atomic" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "powerfmt" @@ -6842,7 +6852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -6920,14 +6930,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -6940,7 +6950,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", "version_check", "yansi", ] @@ -7004,7 +7014,7 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -7027,7 +7037,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -7085,16 +7095,16 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quick-junit" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ffd2f9a162cfae131bed6d9d1ed60adced33be340a94f96952897d7cb0c240" +checksum = "3ed1a693391a16317257103ad06a88c6529ac640846021da7c435a06fffdacd7" dependencies = [ "chrono", "indexmap 2.6.0", "newtype-uuid", - "quick-xml 0.36.2", + "quick-xml 0.37.1", "strip-ansi-escapes", - "thiserror 1.0.69", + "thiserror 2.0.3", "uuid 1.11.0", ] @@ -7118,9 +7128,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.36.2" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +checksum = "f22f29bdff3987b4d8632ef95fd6424ec7e4e0a57e2f4fc63e489e75357f6a03" dependencies = [ "memchr", ] @@ -7136,7 +7146,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.17", + "rustls 0.23.18", "socket2", "thiserror 2.0.3", "tokio", @@ -7154,7 +7164,7 @@ dependencies = [ "rand", "ring", "rustc-hash", - "rustls 0.23.17", + "rustls 0.23.18", "rustls-pki-types", "slab", "thiserror 2.0.3", @@ -7374,7 +7384,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-rustls 0.27.3", "hyper-tls", "hyper-util", @@ -7388,14 +7398,14 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.17", - "rustls-native-certs 0.8.0", + "rustls 0.23.18", + "rustls-native-certs 0.8.1", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tokio", "tokio-native-tls", "tokio-rustls 0.26.0", @@ -7662,9 +7672,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags 2.6.0", "errno", @@ -7687,9 +7697,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.17" +version = "0.23.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e" +checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" dependencies = [ "log", "once_cell", @@ -7709,20 +7719,19 @@ dependencies = [ "openssl-probe", "rustls-pemfile 1.0.4", "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] name = "rustls-native-certs" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ "openssl-probe", - "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.0.1", ] [[package]] @@ -7849,9 +7858,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.5" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa7ffc1c0ef49b0452c6e2986abf2b07743320641ffd5fc63d552458e3b779b" +checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" dependencies = [ "cfg-if", "derive_more", @@ -7861,14 +7870,14 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.5" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46385cc24172cf615450267463f937c10072516359b3ff1cb24228a4a08bf951" +checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -7882,9 +7891,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -7910,7 +7919,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -8017,7 +8026,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.6.0", - "core-foundation", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -8053,9 +8075,9 @@ dependencies = [ [[package]] name = "semver-parser" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" dependencies = [ "pest", ] @@ -8083,7 +8105,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -8094,7 +8116,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -8138,7 +8160,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -8184,7 +8206,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -8489,7 +8511,7 @@ checksum = "f0cc54b74e214647c1bbfc098d080cc5deac77f8dcb99aca91747276b01a15ad" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -8564,9 +8586,6 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] [[package]] name = "spki" @@ -8656,7 +8675,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -8724,9 +8743,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -8742,7 +8761,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -8753,9 +8772,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] @@ -8768,7 +8787,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -8878,7 +8897,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -8889,7 +8908,7 @@ checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -9024,7 +9043,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -9053,7 +9072,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.17", + "rustls 0.23.18", "rustls-pki-types", "tokio", ] @@ -9090,7 +9109,7 @@ checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", - "rustls 0.23.17", + "rustls 0.23.18", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -9166,17 +9185,17 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.6", + "h2 0.4.7", "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-timeout", "hyper-util", "percent-encoding", "pin-project 1.1.7", "prost", - "rustls-native-certs 0.8.0", + "rustls-native-certs 0.8.1", "rustls-pemfile 2.2.0", "socket2", "tokio", @@ -9299,7 +9318,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -9416,7 +9435,7 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.23.17", + "rustls 0.23.18", "rustls-pki-types", "sha1", "thiserror 1.0.69", @@ -9482,9 +9501,9 @@ checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-linebreak" @@ -9544,9 +9563,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -9715,7 +9734,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", "wasm-bindgen-shared", ] @@ -9749,7 +9768,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9775,9 +9794,9 @@ dependencies = [ [[package]] name = "wasmtimer" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb4f099acbc1043cc752b91615b24b02d7f6fcd975bd781fed9f50b3c3e15bf7" +checksum = "0048ad49a55b9deb3953841fa1fc5858f0efbcb7a18868c899a360269fac1b23" dependencies = [ "futures", "js-sys", @@ -9872,9 +9891,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -9990,7 +10009,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -10001,7 +10020,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -10012,7 +10031,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -10023,7 +10042,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -10285,9 +10304,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -10297,13 +10316,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", "synstructure", ] @@ -10325,27 +10344,27 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] name = "zerofrom" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", "synstructure", ] @@ -10366,7 +10385,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -10388,14 +10407,14 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] name = "zip" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" +checksum = "99d52293fc86ea7cf13971b3bb81eb21683636e7ae24c729cdaf1b7c4157a352" dependencies = [ "arbitrary", "bzip2", @@ -10405,7 +10424,7 @@ dependencies = [ "flate2", "indexmap 2.6.0", "memchr", - "thiserror 1.0.69", + "thiserror 2.0.3", "zopfli", ] From cca72aba47a675380a3c87199c7ed0406e3281c2 Mon Sep 17 00:00:00 2001 From: publicqi <56060664+publicqi@users.noreply.github.com> Date: Mon, 25 Nov 2024 03:07:34 -0800 Subject: [PATCH 46/68] fix: bail incomplete bytecode sequence disassemble (#9390) --- crates/cast/src/lib.rs | 21 ++++++++++++++++++++- crates/evm/core/src/ic.rs | 9 +++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 39e821dc91f1..b5b719e39ae8 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -2017,7 +2017,7 @@ impl SimpleCast { pub fn disassemble(code: &[u8]) -> Result { let mut output = String::new(); - for step in decode_instructions(code) { + for step in decode_instructions(code)? { write!(output, "{:08x}: ", step.pc)?; if let Some(op) = step.op { @@ -2290,4 +2290,23 @@ mod tests { r#"["0x2b5df5f0757397573e8ff34a8b987b21680357de1f6c8d10273aa528a851eaca","0x","0x","0x2838ac1d2d2721ba883169179b48480b2ba4f43d70fcf806956746bd9e83f903","0x","0xe46fff283b0ab96a32a7cc375cecc3ed7b6303a43d64e0a12eceb0bc6bd87549","0x","0x1d818c1c414c665a9c9a0e0c0ef1ef87cacb380b8c1f6223cb2a68a4b2d023f5","0x","0x","0x","0x236e8f61ecde6abfebc6c529441f782f62469d8a2cc47b7aace2c136bd3b1ff0","0x","0x","0x","0x","0x"]"# ) } + + #[test] + fn disassemble_incomplete_sequence() { + let incomplete = &hex!("60"); // PUSH1 + let disassembled = Cast::disassemble(incomplete); + assert!(disassembled.is_err()); + + let complete = &hex!("6000"); // PUSH1 0x00 + let disassembled = Cast::disassemble(complete); + assert!(disassembled.is_ok()); + + let incomplete = &hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // PUSH32 with 31 bytes + let disassembled = Cast::disassemble(incomplete); + assert!(disassembled.is_err()); + + let complete = &hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // PUSH32 with 32 bytes + let disassembled = Cast::disassemble(complete); + assert!(disassembled.is_ok()); + } } diff --git a/crates/evm/core/src/ic.rs b/crates/evm/core/src/ic.rs index 2711f8933543..fcabf2a18b41 100644 --- a/crates/evm/core/src/ic.rs +++ b/crates/evm/core/src/ic.rs @@ -1,4 +1,5 @@ use alloy_primitives::map::HashMap; +use eyre::Result; use revm::interpreter::{ opcode::{PUSH0, PUSH1, PUSH32}, OpCode, @@ -100,7 +101,7 @@ pub struct Instruction<'a> { } /// Decodes raw opcode bytes into [`Instruction`]s. -pub fn decode_instructions(code: &[u8]) -> Vec> { +pub fn decode_instructions(code: &[u8]) -> Result>> { let mut pc = 0; let mut steps = Vec::new(); @@ -108,10 +109,14 @@ pub fn decode_instructions(code: &[u8]) -> Vec> { let op = OpCode::new(code[pc]); let immediate_size = op.map(|op| immediate_size(op, &code[pc + 1..])).unwrap_or(0) as usize; + if pc + 1 + immediate_size > code.len() { + eyre::bail!("incomplete sequence of bytecode"); + } + steps.push(Instruction { op, pc, immediate: &code[pc + 1..pc + 1 + immediate_size] }); pc += 1 + immediate_size; } - steps + Ok(steps) } From 66228e443846127499374d997aa5df9c898d4f5d Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:22:15 +0200 Subject: [PATCH 47/68] fix(forge create): install missing deps if any (#9401) --- crates/forge/bin/cmd/create.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 9fc2629e5988..71823416d713 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -1,3 +1,4 @@ +use crate::cmd::install; use alloy_chains::Chain; use alloy_dyn_abi::{DynSolValue, JsonAbiExt, Specifier}; use alloy_json_abi::{Constructor, JsonAbi}; @@ -98,7 +99,14 @@ pub struct CreateArgs { impl CreateArgs { /// Executes the command to create a contract pub async fn run(mut self) -> Result<()> { - let config = self.try_load_config_emit_warnings()?; + let mut config = self.try_load_config_emit_warnings()?; + + // Install missing dependencies. + if install::install_missing_dependencies(&mut config) && config.auto_detect_remappings { + // need to re-configure here to also catch additional remappings + config = self.load_config(); + } + // Find Project & Compile let project = config.project()?; From eae5fb489d39b4de0a611778b9ce82233399e73e Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:35:49 +0200 Subject: [PATCH 48/68] feat(forge): show additional details of contract to verify (#9403) --- crates/verify/src/verify.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/verify/src/verify.rs b/crates/verify/src/verify.rs index 9c32ee95f5bf..5a8efe4c6598 100644 --- a/crates/verify/src/verify.rs +++ b/crates/verify/src/verify.rs @@ -220,6 +220,17 @@ impl VerifyArgs { let verifier_url = self.verifier.verifier_url.clone(); sh_println!("Start verifying contract `{}` deployed on {chain}", self.address)?; + if let Some(version) = &self.compiler_version { + sh_println!("Compiler version: {version}")?; + } + if let Some(optimizations) = &self.num_of_optimizations { + sh_println!("Optimizations: {optimizations}")? + } + if let Some(args) = &self.constructor_args { + if !args.is_empty() { + sh_println!("Constructor args: {args}")? + } + } self.verifier.verifier.client(&self.etherscan.key())?.verify(self, context).await.map_err(|err| { if let Some(verifier_url) = verifier_url { match Url::parse(&verifier_url) { From de5e89cd117bb30f147c28862c51be6ef239f23f Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:16:57 +0100 Subject: [PATCH 49/68] fix: remove duplicate `gas_limit` / `block_gas_limit` field, declare as alias (#9406) remove duplicate gas_limit field, declare as alias --- crates/common/src/evm.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index 3eca0800e0cd..c4dfccae1641 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -196,11 +196,6 @@ impl Provider for EvmArgs { #[derive(Clone, Debug, Default, Serialize, Parser)] #[command(next_help_heading = "Executor environment config")] pub struct EnvArgs { - /// The block gas limit. - #[arg(long, value_name = "GAS_LIMIT")] - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_limit: Option, - /// EIP-170: Contract code size limit in bytes. Useful to increase this because of tests. By /// default, it is 0x6000 (~25kb). #[arg(long, value_name = "CODE_SIZE")] @@ -253,7 +248,7 @@ pub struct EnvArgs { pub block_prevrandao: Option, /// The block gas limit. - #[arg(long, value_name = "GAS_LIMIT")] + #[arg(long, visible_alias = "gas-limit", value_name = "GAS_LIMIT")] #[serde(skip_serializing_if = "Option::is_none")] pub block_gas_limit: Option, From 672bdf60f01630d849f0bf7ffdb447965a53e4e2 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:33:36 +0200 Subject: [PATCH 50/68] fix(cheatcodes): use calldata in attachDelegation (#9407) --- crates/cheatcodes/assets/cheatcodes.json | 2 +- crates/cheatcodes/spec/src/vm.rs | 2 +- testdata/cheats/Vm.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index d19c776b9916..ed842ddd8aef 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3111,7 +3111,7 @@ "func": { "id": "attachDelegation", "description": "Designate the next call as an EIP-7702 transaction", - "declaration": "function attachDelegation(SignedDelegation memory signedDelegation) external;", + "declaration": "function attachDelegation(SignedDelegation calldata signedDelegation) external;", "visibility": "external", "mutability": "", "signature": "attachDelegation((uint8,bytes32,bytes32,uint64,address))", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index bf0fe9781f8a..e075438258d0 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2040,7 +2040,7 @@ interface Vm { /// Designate the next call as an EIP-7702 transaction #[cheatcode(group = Scripting)] - function attachDelegation(SignedDelegation memory signedDelegation) external; + function attachDelegation(SignedDelegation calldata signedDelegation) external; /// Sign an EIP-7702 authorization and designate the next call as an EIP-7702 transaction #[cheatcode(group = Scripting)] diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 9d1d3efd1420..682b5867197b 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -149,7 +149,7 @@ interface Vm { function assertTrue(bool condition, string calldata error) external pure; function assume(bool condition) external pure; function assumeNoRevert() external pure; - function attachDelegation(SignedDelegation memory signedDelegation) external; + function attachDelegation(SignedDelegation calldata signedDelegation) external; function blobBaseFee(uint256 newBlobBaseFee) external; function blobhashes(bytes32[] calldata hashes) external; function breakpoint(string calldata char) external pure; From 995fd9ea031d902b6dd550c7d8a1cf15379feb82 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:30:14 +0100 Subject: [PATCH 51/68] chore(cheatcodes): enforce `calldata` in declaration (#9408) --- crates/cheatcodes/assets/cheatcodes.json | 18 +++++++++--------- crates/cheatcodes/spec/src/vm.rs | 18 +++++++++--------- crates/macros/src/cheatcodes.rs | 11 +++++++++++ testdata/cheats/Vm.sol | 18 +++++++++--------- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index ed842ddd8aef..d8f8d21df67b 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4615,7 +4615,7 @@ "func": { "id": "eth_getLogs", "description": "Gets all the logs according to specified filter.", - "declaration": "function eth_getLogs(uint256 fromBlock, uint256 toBlock, address target, bytes32[] memory topics) external returns (EthGetLogs[] memory logs);", + "declaration": "function eth_getLogs(uint256 fromBlock, uint256 toBlock, address target, bytes32[] calldata topics) external returns (EthGetLogs[] memory logs);", "visibility": "external", "mutability": "", "signature": "eth_getLogs(uint256,uint256,address,bytes32[])", @@ -5355,7 +5355,7 @@ "func": { "id": "getBroadcast", "description": "Returns the most recent broadcast for the given contract on `chainId` matching `txType`.\nFor example:\nThe most recent deployment can be fetched by passing `txType` as `CREATE` or `CREATE2`.\nThe most recent call can be fetched by passing `txType` as `CALL`.", - "declaration": "function getBroadcast(string memory contractName, uint64 chainId, BroadcastTxType txType) external view returns (BroadcastTxSummary memory);", + "declaration": "function getBroadcast(string calldata contractName, uint64 chainId, BroadcastTxType txType) external view returns (BroadcastTxSummary memory);", "visibility": "external", "mutability": "view", "signature": "getBroadcast(string,uint64,uint8)", @@ -5375,7 +5375,7 @@ "func": { "id": "getBroadcasts_0", "description": "Returns all broadcasts for the given contract on `chainId` with the specified `txType`.\nSorted such that the most recent broadcast is the first element, and the oldest is the last. i.e descending order of BroadcastTxSummary.blockNumber.", - "declaration": "function getBroadcasts(string memory contractName, uint64 chainId, BroadcastTxType txType) external view returns (BroadcastTxSummary[] memory);", + "declaration": "function getBroadcasts(string calldata contractName, uint64 chainId, BroadcastTxType txType) external view returns (BroadcastTxSummary[] memory);", "visibility": "external", "mutability": "view", "signature": "getBroadcasts(string,uint64,uint8)", @@ -5395,7 +5395,7 @@ "func": { "id": "getBroadcasts_1", "description": "Returns all broadcasts for the given contract on `chainId`.\nSorted such that the most recent broadcast is the first element, and the oldest is the last. i.e descending order of BroadcastTxSummary.blockNumber.", - "declaration": "function getBroadcasts(string memory contractName, uint64 chainId) external view returns (BroadcastTxSummary[] memory);", + "declaration": "function getBroadcasts(string calldata contractName, uint64 chainId) external view returns (BroadcastTxSummary[] memory);", "visibility": "external", "mutability": "view", "signature": "getBroadcasts(string,uint64)", @@ -5455,7 +5455,7 @@ "func": { "id": "getDeployment_0", "description": "Returns the most recent deployment for the current `chainId`.", - "declaration": "function getDeployment(string memory contractName) external view returns (address deployedAddress);", + "declaration": "function getDeployment(string calldata contractName) external view returns (address deployedAddress);", "visibility": "external", "mutability": "view", "signature": "getDeployment(string)", @@ -5475,7 +5475,7 @@ "func": { "id": "getDeployment_1", "description": "Returns the most recent deployment for the given contract on `chainId`", - "declaration": "function getDeployment(string memory contractName, uint64 chainId) external view returns (address deployedAddress);", + "declaration": "function getDeployment(string calldata contractName, uint64 chainId) external view returns (address deployedAddress);", "visibility": "external", "mutability": "view", "signature": "getDeployment(string,uint64)", @@ -5495,7 +5495,7 @@ "func": { "id": "getDeployments", "description": "Returns all deployments for the given contract on `chainId`\nSorted in descending order of deployment time i.e descending order of BroadcastTxSummary.blockNumber.\nThe most recent deployment is the first element, and the oldest is the last.", - "declaration": "function getDeployments(string memory contractName, uint64 chainId) external view returns (address[] memory deployedAddresses);", + "declaration": "function getDeployments(string calldata contractName, uint64 chainId) external view returns (address[] memory deployedAddresses);", "visibility": "external", "mutability": "view", "signature": "getDeployments(string,uint64)", @@ -8621,7 +8621,7 @@ "func": { "id": "serializeJsonType_0", "description": "See `serializeJson`.", - "declaration": "function serializeJsonType(string calldata typeDescription, bytes memory value) external pure returns (string memory json);", + "declaration": "function serializeJsonType(string calldata typeDescription, bytes calldata value) external pure returns (string memory json);", "visibility": "external", "mutability": "pure", "signature": "serializeJsonType(string,bytes)", @@ -8641,7 +8641,7 @@ "func": { "id": "serializeJsonType_1", "description": "See `serializeJson`.", - "declaration": "function serializeJsonType(string calldata objectKey, string calldata valueKey, string calldata typeDescription, bytes memory value) external returns (string memory json);", + "declaration": "function serializeJsonType(string calldata objectKey, string calldata valueKey, string calldata typeDescription, bytes calldata value) external returns (string memory json);", "visibility": "external", "mutability": "", "signature": "serializeJsonType(string,string,string,bytes)", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index e075438258d0..6b66d31ddb50 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -808,7 +808,7 @@ interface Vm { /// Gets all the logs according to specified filter. #[cheatcode(group = Evm, safety = Safe)] - function eth_getLogs(uint256 fromBlock, uint256 toBlock, address target, bytes32[] memory topics) + function eth_getLogs(uint256 fromBlock, uint256 toBlock, address target, bytes32[] calldata topics) external returns (EthGetLogs[] memory logs); @@ -1764,27 +1764,27 @@ interface Vm { /// /// The most recent call can be fetched by passing `txType` as `CALL`. #[cheatcode(group = Filesystem)] - function getBroadcast(string memory contractName, uint64 chainId, BroadcastTxType txType) external view returns (BroadcastTxSummary memory); + function getBroadcast(string calldata contractName, uint64 chainId, BroadcastTxType txType) external view returns (BroadcastTxSummary memory); /// Returns all broadcasts for the given contract on `chainId` with the specified `txType`. /// /// Sorted such that the most recent broadcast is the first element, and the oldest is the last. i.e descending order of BroadcastTxSummary.blockNumber. #[cheatcode(group = Filesystem)] - function getBroadcasts(string memory contractName, uint64 chainId, BroadcastTxType txType) external view returns (BroadcastTxSummary[] memory); + function getBroadcasts(string calldata contractName, uint64 chainId, BroadcastTxType txType) external view returns (BroadcastTxSummary[] memory); /// Returns all broadcasts for the given contract on `chainId`. /// /// Sorted such that the most recent broadcast is the first element, and the oldest is the last. i.e descending order of BroadcastTxSummary.blockNumber. #[cheatcode(group = Filesystem)] - function getBroadcasts(string memory contractName, uint64 chainId) external view returns (BroadcastTxSummary[] memory); + function getBroadcasts(string calldata contractName, uint64 chainId) external view returns (BroadcastTxSummary[] memory); /// Returns the most recent deployment for the current `chainId`. #[cheatcode(group = Filesystem)] - function getDeployment(string memory contractName) external view returns (address deployedAddress); + function getDeployment(string calldata contractName) external view returns (address deployedAddress); /// Returns the most recent deployment for the given contract on `chainId` #[cheatcode(group = Filesystem)] - function getDeployment(string memory contractName, uint64 chainId) external view returns (address deployedAddress); + function getDeployment(string calldata contractName, uint64 chainId) external view returns (address deployedAddress); /// Returns all deployments for the given contract on `chainId` /// @@ -1792,7 +1792,7 @@ interface Vm { /// /// The most recent deployment is the first element, and the oldest is the last. #[cheatcode(group = Filesystem)] - function getDeployments(string memory contractName, uint64 chainId) external view returns (address[] memory deployedAddresses); + function getDeployments(string calldata contractName, uint64 chainId) external view returns (address[] memory deployedAddresses); // -------- Foreign Function Interface -------- @@ -2298,13 +2298,13 @@ interface Vm { returns (string memory json); /// See `serializeJson`. #[cheatcode(group = Json)] - function serializeJsonType(string calldata typeDescription, bytes memory value) + function serializeJsonType(string calldata typeDescription, bytes calldata value) external pure returns (string memory json); /// See `serializeJson`. #[cheatcode(group = Json)] - function serializeJsonType(string calldata objectKey, string calldata valueKey, string calldata typeDescription, bytes memory value) + function serializeJsonType(string calldata objectKey, string calldata valueKey, string calldata typeDescription, bytes calldata value) external returns (string memory json); diff --git a/crates/macros/src/cheatcodes.rs b/crates/macros/src/cheatcodes.rs index d9c2d2c91435..4d0f260c294b 100644 --- a/crates/macros/src/cheatcodes.rs +++ b/crates/macros/src/cheatcodes.rs @@ -58,6 +58,17 @@ fn derive_call(name: &Ident, data: &DataStruct, attrs: &[Attribute]) -> Result Date: Tue, 26 Nov 2024 14:04:37 +0100 Subject: [PATCH 52/68] feat: remove ethers (#8826) Co-authored-by: grandizzy --- Cargo.lock | 216 ----------------------------------- Cargo.toml | 3 - README.md | 5 +- crates/forge/Cargo.toml | 2 - crates/forge/bin/cmd/bind.rs | 152 ++---------------------- 5 files changed, 14 insertions(+), 364 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e6cdd0f96c6..2aa261d17725 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1908,38 +1908,6 @@ dependencies = [ "serde", ] -[[package]] -name = "camino" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver 1.0.23", - "serde", - "serde_json", - "thiserror 1.0.69", -] - [[package]] name = "cassowary" version = "0.3.0" @@ -3068,106 +3036,6 @@ dependencies = [ "uuid 0.8.2", ] -[[package]] -name = "ethabi" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" -dependencies = [ - "ethereum-types", - "hex", - "once_cell", - "regex", - "serde", - "serde_json", - "sha3", - "thiserror 1.0.69", - "uint", -] - -[[package]] -name = "ethbloom" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" -dependencies = [ - "crunchy", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "tiny-keccak", -] - -[[package]] -name = "ethereum-types" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" -dependencies = [ - "ethbloom", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "primitive-types", - "scale-info", - "uint", -] - -[[package]] -name = "ethers-contract-abigen" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04ba01fbc2331a38c429eb95d4a570166781f14290ef9fdb144278a90b5a739b" -dependencies = [ - "Inflector", - "const-hex", - "dunce", - "ethers-core", - "eyre", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "serde", - "serde_json", - "syn 2.0.89", - "toml 0.8.19", - "walkdir", -] - -[[package]] -name = "ethers-core" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d80cc6ad30b14a48ab786523af33b37f28a8623fc06afd55324816ef18fb1f" -dependencies = [ - "arrayvec", - "bytes", - "cargo_metadata", - "chrono", - "const-hex", - "elliptic-curve", - "ethabi", - "generic-array", - "k256", - "num_enum", - "once_cell", - "open-fastrlp", - "rand", - "rlp", - "serde", - "serde_json", - "strum", - "syn 2.0.89", - "tempfile", - "thiserror 1.0.69", - "tiny-keccak", - "unicode-xid", -] - [[package]] name = "event-listener" version = "4.0.3" @@ -3375,7 +3243,6 @@ dependencies = [ "comfy-table", "dialoguer", "dunce", - "ethers-contract-abigen", "evm-disassembler", "eyre", "forge-doc", @@ -5259,24 +5126,6 @@ dependencies = [ "parity-scale-codec", ] -[[package]] -name = "impl-rlp" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", -] - -[[package]] -name = "impl-serde" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" -dependencies = [ - "serde", -] - [[package]] name = "impl-trait-for-tuples" version = "0.2.3" @@ -6243,7 +6092,6 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.89", @@ -6326,31 +6174,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "open-fastrlp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", - "ethereum-types", - "open-fastrlp-derive", -] - -[[package]] -name = "open-fastrlp-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" -dependencies = [ - "bytes", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "opener" version = "0.7.2" @@ -6872,9 +6695,6 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", "uint", ] @@ -7554,21 +7374,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ "bytes", - "rlp-derive", "rustc-hex", ] -[[package]] -name = "rlp-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "rpassword" version = "7.3.1" @@ -7856,30 +7664,6 @@ dependencies = [ "regex", ] -[[package]] -name = "scale-info" -version = "2.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" -dependencies = [ - "cfg-if", - "derive_more", - "parity-scale-codec", - "scale-info-derive", -] - -[[package]] -name = "scale-info-derive" -version = "2.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.89", -] - [[package]] name = "scc" version = "2.2.5" diff --git a/Cargo.toml b/Cargo.toml index 814c43aa556a..42b033f260fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -180,9 +180,6 @@ revm = { version = "18.0.0", default-features = false } revm-primitives = { version = "14.0.0", default-features = false } revm-inspectors = { version = "0.11.0", features = ["serde"] } -## ethers -ethers-contract-abigen = { version = "2.0.14", default-features = false } - ## alloy alloy-consensus = { version = "0.6.4", default-features = false } alloy-contract = { version = "0.6.4", default-features = false } diff --git a/README.md b/README.md index ec0884aa2378..bd9325191121 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ If you're experiencing any issues while installing, check out [Getting Help](#ge ### How Fast? -Forge is quite fast at both compiling (leveraging [ethers-solc]) and testing. +Forge is quite fast at both compiling (leveraging [foundry-compilers]) and testing. See the benchmarks below. More benchmarks can be found in the [v0.2.0 announcement post][benchmark-post] and in the [Convex Shutdown Simulation][convex] repository. @@ -127,7 +127,7 @@ If you want to contribute, or follow along with contributor discussion, you can ## Acknowledgements - Foundry is a clean-room rewrite of the testing framework [DappTools](https://github.com/dapphub/dapptools). None of this would have been possible without the DappHub team's work over the years. -- [Matthias Seitz](https://twitter.com/mattsse_): Created [ethers-solc] which is the backbone of our compilation pipeline, as well as countless contributions to ethers, in particular the `abigen` macros. +- [Matthias Seitz](https://twitter.com/mattsse_): Created [ethers-solc] (now [foundry-compilers]) which is the backbone of our compilation pipeline, as well as countless contributions to ethers, in particular the `abigen` macros. - [Rohit Narurkar](https://twitter.com/rohitnarurkar): Created the Rust Solidity version manager [svm-rs](https://github.com/roynalnaruto/svm-rs) which we use to auto-detect and manage multiple Solidity versions. - [Brock Elmore](https://twitter.com/brockjelmore): For extending the VM's cheatcodes and implementing [structured call tracing](https://github.com/foundry-rs/foundry/pull/192), a critical feature for debugging smart contract calls. - All the other [contributors](https://github.com/foundry-rs/foundry/graphs/contributors) to the [ethers-rs](https://github.com/gakonst/ethers-rs) & [foundry](https://github.com/foundry-rs/foundry) repositories and chatrooms. @@ -135,6 +135,7 @@ If you want to contribute, or follow along with contributor discussion, you can [foundry-book]: https://book.getfoundry.sh [foundry-gha]: https://github.com/foundry-rs/foundry-toolchain [ethers-solc]: https://github.com/gakonst/ethers-rs/tree/master/ethers-solc/ +[foundry-compilers]: https://github.com/foundry-rs/foundry-compilers [solmate]: https://github.com/transmissions11/solmate/ [geb]: https://github.com/reflexer-labs/geb [vaults]: https://github.com/rari-capital/vaults diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 208ea8430ab9..b01f2d3e1c27 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -35,8 +35,6 @@ foundry-wallets.workspace = true foundry-linking.workspace = true forge-script-sequence.workspace = true -ethers-contract-abigen = { workspace = true, features = ["providers"] } - revm-inspectors.workspace = true comfy-table.workspace = true diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index ec6b13dfd49a..d541a530d9ce 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -1,9 +1,6 @@ use alloy_primitives::map::HashSet; use clap::{Parser, ValueHint}; -use ethers_contract_abigen::{ - Abigen, ContractFilter, ExcludeContracts, MultiAbigen, SelectContracts, -}; -use eyre::{Result, WrapErr}; +use eyre::Result; use forge_sol_macro_gen::{MultiSolMacroGen, SolMacroGen}; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::{compile::ProjectCompiler, fs::json_files}; @@ -83,15 +80,15 @@ pub struct BindArgs { skip_extra_derives: bool, /// Generate bindings for the `alloy` library, instead of `ethers`. - #[arg(long, conflicts_with = "ethers")] + #[arg(long, hide = true)] alloy: bool, /// Specify the alloy version. - #[arg(long, value_name = "ALLOY_VERSION")] + #[arg(long)] alloy_version: Option, - /// Generate bindings for the `ethers` library, instead of `alloy` (default, deprecated). - #[arg(long)] + /// Generate bindings for the `ethers` library, instead of `alloy` (removed). + #[arg(long, hide = true)] ethers: bool, #[command(flatten)] @@ -100,17 +97,15 @@ pub struct BindArgs { impl BindArgs { pub fn run(self) -> Result<()> { + if self.ethers { + eyre::bail!("`--ethers` bindings have been removed. Use `--alloy` (default) instead."); + } + if !self.skip_build { let project = self.build_args.project()?; let _ = ProjectCompiler::new().compile(&project)?; } - if self.ethers { - sh_warn!( - "`--ethers` bindings are deprecated and will be removed in the future. Consider using `--alloy` (default) instead." - )?; - } - let config = self.try_load_config_emit_warnings()?; let artifacts = config.out; let bindings_root = self.bindings.clone().unwrap_or_else(|| artifacts.join("bindings")); @@ -131,40 +126,7 @@ impl BindArgs { Ok(()) } - /// Returns the filter to use for `MultiAbigen` - fn get_filter(&self) -> Result { - if self.select_all { - return Ok(ContractFilter::All) - } - if !self.select.is_empty() { - return Ok(SelectContracts::default().extend_regex(self.select.clone()).into()) - } - if let Some(skip) = self.build_args.skip.as_ref().filter(|s| !s.is_empty()) { - return Ok(ExcludeContracts::default() - .extend_regex( - skip.clone() - .into_iter() - .map(|s| Regex::new(s.file_pattern())) - .collect::, _>>()?, - ) - .into()) - } - // This excludes all Test/Script and forge-std contracts - Ok(ExcludeContracts::default() - .extend_pattern([ - ".*Test.*", - ".*Script", - "console[2]?", - "CommonBase", - "Components", - "[Ss]td(Chains|Math|Error|Json|Utils|Cheats|Style|Invariant|Assertions|Toml|Storage(Safe)?)", - "[Vv]m.*", - ]) - .extend_names(["IMulticall3"]) - .into()) - } - - fn get_alloy_filter(&self) -> Result { + fn get_filter(&self) -> Result { if self.select_all { // Select all json files return Ok(Filter::All); @@ -190,8 +152,6 @@ impl BindArgs { /// Returns an iterator over the JSON files and the contract name in the `artifacts` directory. fn get_json_files(&self, artifacts: &Path) -> Result> { let filter = self.get_filter()?; - let alloy_filter = self.get_alloy_filter()?; - let is_alloy = !self.ethers; Ok(json_files(artifacts) .filter_map(|path| { // Ignore the build info JSON. @@ -212,35 +172,7 @@ impl BindArgs { Some((name, path)) }) - .filter( - move |(name, _path)| { - if is_alloy { - alloy_filter.is_match(name) - } else { - filter.is_match(name) - } - }, - )) - } - - /// Instantiate the multi-abigen - fn get_multi(&self, artifacts: &Path) -> Result { - let abigens = self - .get_json_files(artifacts)? - .map(|(name, path)| { - trace!(?path, "parsing Abigen from file"); - let abi = Abigen::new(name, path.to_str().unwrap()) - .wrap_err_with(|| format!("failed to parse Abigen from file: {path:?}")); - if !self.skip_extra_derives { - abi?.add_derive("serde::Serialize")?.add_derive("serde::Deserialize") - } else { - abi - } - }) - .collect::, _>>()?; - let multi = MultiAbigen::from_abigens(abigens); - eyre::ensure!(!multi.is_empty(), "No contract artifacts found"); - Ok(multi) + .filter(move |(name, _path)| filter.is_match(name))) } fn get_solmacrogen(&self, artifacts: &Path) -> Result { @@ -264,40 +196,6 @@ impl BindArgs { /// Check that the existing bindings match the expected abigen output fn check_existing_bindings(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { - if self.ethers { - return self.check_ethers(artifacts, bindings_root); - } - - self.check_alloy(artifacts, bindings_root) - } - - fn check_ethers(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { - let bindings = self.get_multi(artifacts)?.build()?; - sh_println!("Checking bindings for {} contracts.", bindings.len())?; - if !self.module { - bindings - .ensure_consistent_crate( - &self.crate_name, - &self.crate_version, - bindings_root, - self.single_file, - !self.skip_cargo_toml, - ) - .map_err(|err| { - if !self.skip_cargo_toml && err.to_string().contains("Cargo.toml") { - err.wrap_err("To skip Cargo.toml consistency check, pass --skip-cargo-toml") - } else { - err - } - })?; - } else { - bindings.ensure_consistent_module(bindings_root, self.single_file)?; - } - sh_println!("OK.")?; - Ok(()) - } - - fn check_alloy(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { let mut bindings = self.get_solmacrogen(artifacts)?; bindings.generate_bindings()?; sh_println!("Checking bindings for {} contracts", bindings.instances.len())?; @@ -316,34 +214,6 @@ impl BindArgs { /// Generate the bindings fn generate_bindings(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { - if self.ethers { - return self.generate_ethers(artifacts, bindings_root); - } - - self.generate_alloy(artifacts, bindings_root) - } - - fn generate_ethers(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { - let mut bindings = self.get_multi(artifacts)?.build()?; - sh_println!("Generating bindings for {} contracts", bindings.len())?; - if !self.module { - trace!(single_file = self.single_file, "generating crate"); - if !self.skip_extra_derives { - bindings = bindings.dependencies([r#"serde = "1""#]) - } - bindings.write_to_crate( - &self.crate_name, - &self.crate_version, - bindings_root, - self.single_file, - ) - } else { - trace!(single_file = self.single_file, "generating module"); - bindings.write_to_module(bindings_root, self.single_file) - } - } - - fn generate_alloy(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { let mut solmacrogen = self.get_solmacrogen(artifacts)?; sh_println!("Generating bindings for {} contracts", solmacrogen.instances.len())?; From 958c713e2fd343c0e84d3f7adda6b8ef9aa42eeb Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:37:29 +0200 Subject: [PATCH 53/68] Revert "feat: remove ethers" (#9411) Revert "feat: remove ethers (#8826)" This reverts commit d7397043e17e8d88a0c21cffa9d300377aed27c5. --- Cargo.lock | 216 +++++++++++++++++++++++++++++++++++ Cargo.toml | 3 + README.md | 5 +- crates/forge/Cargo.toml | 2 + crates/forge/bin/cmd/bind.rs | 152 ++++++++++++++++++++++-- 5 files changed, 364 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2aa261d17725..0e6cdd0f96c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1908,6 +1908,38 @@ dependencies = [ "serde", ] +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.23", + "serde", + "serde_json", + "thiserror 1.0.69", +] + [[package]] name = "cassowary" version = "0.3.0" @@ -3036,6 +3068,106 @@ dependencies = [ "uuid 0.8.2", ] +[[package]] +name = "ethabi" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" +dependencies = [ + "ethereum-types", + "hex", + "once_cell", + "regex", + "serde", + "serde_json", + "sha3", + "thiserror 1.0.69", + "uint", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "primitive-types", + "scale-info", + "uint", +] + +[[package]] +name = "ethers-contract-abigen" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04ba01fbc2331a38c429eb95d4a570166781f14290ef9fdb144278a90b5a739b" +dependencies = [ + "Inflector", + "const-hex", + "dunce", + "ethers-core", + "eyre", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "serde", + "serde_json", + "syn 2.0.89", + "toml 0.8.19", + "walkdir", +] + +[[package]] +name = "ethers-core" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d80cc6ad30b14a48ab786523af33b37f28a8623fc06afd55324816ef18fb1f" +dependencies = [ + "arrayvec", + "bytes", + "cargo_metadata", + "chrono", + "const-hex", + "elliptic-curve", + "ethabi", + "generic-array", + "k256", + "num_enum", + "once_cell", + "open-fastrlp", + "rand", + "rlp", + "serde", + "serde_json", + "strum", + "syn 2.0.89", + "tempfile", + "thiserror 1.0.69", + "tiny-keccak", + "unicode-xid", +] + [[package]] name = "event-listener" version = "4.0.3" @@ -3243,6 +3375,7 @@ dependencies = [ "comfy-table", "dialoguer", "dunce", + "ethers-contract-abigen", "evm-disassembler", "eyre", "forge-doc", @@ -5126,6 +5259,24 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + [[package]] name = "impl-trait-for-tuples" version = "0.2.3" @@ -6092,6 +6243,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.89", @@ -6174,6 +6326,31 @@ dependencies = [ "serde_json", ] +[[package]] +name = "open-fastrlp" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", + "ethereum-types", + "open-fastrlp-derive", +] + +[[package]] +name = "open-fastrlp-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" +dependencies = [ + "bytes", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "opener" version = "0.7.2" @@ -6695,6 +6872,9 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", "uint", ] @@ -7374,9 +7554,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ "bytes", + "rlp-derive", "rustc-hex", ] +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rpassword" version = "7.3.1" @@ -7664,6 +7856,30 @@ dependencies = [ "regex", ] +[[package]] +name = "scale-info" +version = "2.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" +dependencies = [ + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", +] + +[[package]] +name = "scale-info-derive" +version = "2.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "scc" version = "2.2.5" diff --git a/Cargo.toml b/Cargo.toml index 42b033f260fa..814c43aa556a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -180,6 +180,9 @@ revm = { version = "18.0.0", default-features = false } revm-primitives = { version = "14.0.0", default-features = false } revm-inspectors = { version = "0.11.0", features = ["serde"] } +## ethers +ethers-contract-abigen = { version = "2.0.14", default-features = false } + ## alloy alloy-consensus = { version = "0.6.4", default-features = false } alloy-contract = { version = "0.6.4", default-features = false } diff --git a/README.md b/README.md index bd9325191121..ec0884aa2378 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ If you're experiencing any issues while installing, check out [Getting Help](#ge ### How Fast? -Forge is quite fast at both compiling (leveraging [foundry-compilers]) and testing. +Forge is quite fast at both compiling (leveraging [ethers-solc]) and testing. See the benchmarks below. More benchmarks can be found in the [v0.2.0 announcement post][benchmark-post] and in the [Convex Shutdown Simulation][convex] repository. @@ -127,7 +127,7 @@ If you want to contribute, or follow along with contributor discussion, you can ## Acknowledgements - Foundry is a clean-room rewrite of the testing framework [DappTools](https://github.com/dapphub/dapptools). None of this would have been possible without the DappHub team's work over the years. -- [Matthias Seitz](https://twitter.com/mattsse_): Created [ethers-solc] (now [foundry-compilers]) which is the backbone of our compilation pipeline, as well as countless contributions to ethers, in particular the `abigen` macros. +- [Matthias Seitz](https://twitter.com/mattsse_): Created [ethers-solc] which is the backbone of our compilation pipeline, as well as countless contributions to ethers, in particular the `abigen` macros. - [Rohit Narurkar](https://twitter.com/rohitnarurkar): Created the Rust Solidity version manager [svm-rs](https://github.com/roynalnaruto/svm-rs) which we use to auto-detect and manage multiple Solidity versions. - [Brock Elmore](https://twitter.com/brockjelmore): For extending the VM's cheatcodes and implementing [structured call tracing](https://github.com/foundry-rs/foundry/pull/192), a critical feature for debugging smart contract calls. - All the other [contributors](https://github.com/foundry-rs/foundry/graphs/contributors) to the [ethers-rs](https://github.com/gakonst/ethers-rs) & [foundry](https://github.com/foundry-rs/foundry) repositories and chatrooms. @@ -135,7 +135,6 @@ If you want to contribute, or follow along with contributor discussion, you can [foundry-book]: https://book.getfoundry.sh [foundry-gha]: https://github.com/foundry-rs/foundry-toolchain [ethers-solc]: https://github.com/gakonst/ethers-rs/tree/master/ethers-solc/ -[foundry-compilers]: https://github.com/foundry-rs/foundry-compilers [solmate]: https://github.com/transmissions11/solmate/ [geb]: https://github.com/reflexer-labs/geb [vaults]: https://github.com/rari-capital/vaults diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index b01f2d3e1c27..208ea8430ab9 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -35,6 +35,8 @@ foundry-wallets.workspace = true foundry-linking.workspace = true forge-script-sequence.workspace = true +ethers-contract-abigen = { workspace = true, features = ["providers"] } + revm-inspectors.workspace = true comfy-table.workspace = true diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index d541a530d9ce..ec6b13dfd49a 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -1,6 +1,9 @@ use alloy_primitives::map::HashSet; use clap::{Parser, ValueHint}; -use eyre::Result; +use ethers_contract_abigen::{ + Abigen, ContractFilter, ExcludeContracts, MultiAbigen, SelectContracts, +}; +use eyre::{Result, WrapErr}; use forge_sol_macro_gen::{MultiSolMacroGen, SolMacroGen}; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::{compile::ProjectCompiler, fs::json_files}; @@ -80,15 +83,15 @@ pub struct BindArgs { skip_extra_derives: bool, /// Generate bindings for the `alloy` library, instead of `ethers`. - #[arg(long, hide = true)] + #[arg(long, conflicts_with = "ethers")] alloy: bool, /// Specify the alloy version. - #[arg(long)] + #[arg(long, value_name = "ALLOY_VERSION")] alloy_version: Option, - /// Generate bindings for the `ethers` library, instead of `alloy` (removed). - #[arg(long, hide = true)] + /// Generate bindings for the `ethers` library, instead of `alloy` (default, deprecated). + #[arg(long)] ethers: bool, #[command(flatten)] @@ -97,15 +100,17 @@ pub struct BindArgs { impl BindArgs { pub fn run(self) -> Result<()> { - if self.ethers { - eyre::bail!("`--ethers` bindings have been removed. Use `--alloy` (default) instead."); - } - if !self.skip_build { let project = self.build_args.project()?; let _ = ProjectCompiler::new().compile(&project)?; } + if self.ethers { + sh_warn!( + "`--ethers` bindings are deprecated and will be removed in the future. Consider using `--alloy` (default) instead." + )?; + } + let config = self.try_load_config_emit_warnings()?; let artifacts = config.out; let bindings_root = self.bindings.clone().unwrap_or_else(|| artifacts.join("bindings")); @@ -126,7 +131,40 @@ impl BindArgs { Ok(()) } - fn get_filter(&self) -> Result { + /// Returns the filter to use for `MultiAbigen` + fn get_filter(&self) -> Result { + if self.select_all { + return Ok(ContractFilter::All) + } + if !self.select.is_empty() { + return Ok(SelectContracts::default().extend_regex(self.select.clone()).into()) + } + if let Some(skip) = self.build_args.skip.as_ref().filter(|s| !s.is_empty()) { + return Ok(ExcludeContracts::default() + .extend_regex( + skip.clone() + .into_iter() + .map(|s| Regex::new(s.file_pattern())) + .collect::, _>>()?, + ) + .into()) + } + // This excludes all Test/Script and forge-std contracts + Ok(ExcludeContracts::default() + .extend_pattern([ + ".*Test.*", + ".*Script", + "console[2]?", + "CommonBase", + "Components", + "[Ss]td(Chains|Math|Error|Json|Utils|Cheats|Style|Invariant|Assertions|Toml|Storage(Safe)?)", + "[Vv]m.*", + ]) + .extend_names(["IMulticall3"]) + .into()) + } + + fn get_alloy_filter(&self) -> Result { if self.select_all { // Select all json files return Ok(Filter::All); @@ -152,6 +190,8 @@ impl BindArgs { /// Returns an iterator over the JSON files and the contract name in the `artifacts` directory. fn get_json_files(&self, artifacts: &Path) -> Result> { let filter = self.get_filter()?; + let alloy_filter = self.get_alloy_filter()?; + let is_alloy = !self.ethers; Ok(json_files(artifacts) .filter_map(|path| { // Ignore the build info JSON. @@ -172,7 +212,35 @@ impl BindArgs { Some((name, path)) }) - .filter(move |(name, _path)| filter.is_match(name))) + .filter( + move |(name, _path)| { + if is_alloy { + alloy_filter.is_match(name) + } else { + filter.is_match(name) + } + }, + )) + } + + /// Instantiate the multi-abigen + fn get_multi(&self, artifacts: &Path) -> Result { + let abigens = self + .get_json_files(artifacts)? + .map(|(name, path)| { + trace!(?path, "parsing Abigen from file"); + let abi = Abigen::new(name, path.to_str().unwrap()) + .wrap_err_with(|| format!("failed to parse Abigen from file: {path:?}")); + if !self.skip_extra_derives { + abi?.add_derive("serde::Serialize")?.add_derive("serde::Deserialize") + } else { + abi + } + }) + .collect::, _>>()?; + let multi = MultiAbigen::from_abigens(abigens); + eyre::ensure!(!multi.is_empty(), "No contract artifacts found"); + Ok(multi) } fn get_solmacrogen(&self, artifacts: &Path) -> Result { @@ -196,6 +264,40 @@ impl BindArgs { /// Check that the existing bindings match the expected abigen output fn check_existing_bindings(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { + if self.ethers { + return self.check_ethers(artifacts, bindings_root); + } + + self.check_alloy(artifacts, bindings_root) + } + + fn check_ethers(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { + let bindings = self.get_multi(artifacts)?.build()?; + sh_println!("Checking bindings for {} contracts.", bindings.len())?; + if !self.module { + bindings + .ensure_consistent_crate( + &self.crate_name, + &self.crate_version, + bindings_root, + self.single_file, + !self.skip_cargo_toml, + ) + .map_err(|err| { + if !self.skip_cargo_toml && err.to_string().contains("Cargo.toml") { + err.wrap_err("To skip Cargo.toml consistency check, pass --skip-cargo-toml") + } else { + err + } + })?; + } else { + bindings.ensure_consistent_module(bindings_root, self.single_file)?; + } + sh_println!("OK.")?; + Ok(()) + } + + fn check_alloy(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { let mut bindings = self.get_solmacrogen(artifacts)?; bindings.generate_bindings()?; sh_println!("Checking bindings for {} contracts", bindings.instances.len())?; @@ -214,6 +316,34 @@ impl BindArgs { /// Generate the bindings fn generate_bindings(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { + if self.ethers { + return self.generate_ethers(artifacts, bindings_root); + } + + self.generate_alloy(artifacts, bindings_root) + } + + fn generate_ethers(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { + let mut bindings = self.get_multi(artifacts)?.build()?; + sh_println!("Generating bindings for {} contracts", bindings.len())?; + if !self.module { + trace!(single_file = self.single_file, "generating crate"); + if !self.skip_extra_derives { + bindings = bindings.dependencies([r#"serde = "1""#]) + } + bindings.write_to_crate( + &self.crate_name, + &self.crate_version, + bindings_root, + self.single_file, + ) + } else { + trace!(single_file = self.single_file, "generating module"); + bindings.write_to_module(bindings_root, self.single_file) + } + } + + fn generate_alloy(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { let mut solmacrogen = self.get_solmacrogen(artifacts)?; sh_println!("Generating bindings for {} contracts", solmacrogen.instances.len())?; From 0045384f1087897b2665506e95808f022776a5a7 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:32:35 +0100 Subject: [PATCH 54/68] fix: forge script should adhere to `--json` flag (#9404) * adhere to --quiet flag * revert case-specific handling of writing to progress, redundant * handle writing to multiprogress, previously panic * make verification process compatible with --json flag * revert verifaction --json flow, too messy * clean up * revert * handle json correctly for script deployment logs, incl. receipts * avoid incompatible lines with json output * revert unnecessary change * add json and quiet test * address feedback * fix incorrect ordering --- crates/forge/tests/cli/script.rs | 78 +++++++++++++++++++++ crates/script-sequence/src/sequence.rs | 17 ++++- crates/script/src/broadcast.rs | 8 ++- crates/script/src/lib.rs | 9 ++- crates/script/src/multi_sequence.rs | 17 ++++- crates/script/src/progress.rs | 95 +++++++++++++++----------- crates/script/src/receipts.rs | 76 +++++++++++++-------- crates/script/src/simulate.rs | 58 +++++++++------- crates/verify/src/etherscan/mod.rs | 1 - 9 files changed, 257 insertions(+), 102 deletions(-) diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index df2a59bdc4df..d7776eee2e87 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -6,6 +6,7 @@ use anvil::{spawn, NodeConfig}; use forge_script_sequence::ScriptSequence; use foundry_test_utils::{ rpc, + snapbox::IntoData, util::{OTHER_SOLC_VERSION, SOLC_VERSION}, ScriptOutcome, ScriptTester, }; @@ -1821,6 +1822,83 @@ Warning: Script contains a transaction to 0x000000000000000000000000000000000000 "#]]); }); +// Asserts that the script runs with expected non-output using `--quiet` flag +forgetest_async!(adheres_to_quiet_flag, |prj, cmd| { + foundry_test_utils::util::initialize(prj.root()); + prj.add_script( + "Foo", + r#" +import "forge-std/Script.sol"; + +contract SimpleScript is Script { + function run() external returns (bool success) { + vm.startBroadcast(); + (success, ) = address(0).call(""); + } +} + "#, + ) + .unwrap(); + + let (_api, handle) = spawn(NodeConfig::test()).await; + + cmd.args([ + "script", + "SimpleScript", + "--fork-url", + &handle.http_endpoint(), + "--sender", + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "--broadcast", + "--unlocked", + "--non-interactive", + "--quiet", + ]) + .assert_empty_stdout(); +}); + +// Asserts that the script runs with expected non-output using `--quiet` flag +forgetest_async!(adheres_to_json_flag, |prj, cmd| { + foundry_test_utils::util::initialize(prj.root()); + prj.add_script( + "Foo", + r#" +import "forge-std/Script.sol"; + +contract SimpleScript is Script { + function run() external returns (bool success) { + vm.startBroadcast(); + (success, ) = address(0).call(""); + } +} + "#, + ) + .unwrap(); + + let (_api, handle) = spawn(NodeConfig::test()).await; + + cmd.args([ + "script", + "SimpleScript", + "--fork-url", + &handle.http_endpoint(), + "--sender", + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "--broadcast", + "--unlocked", + "--non-interactive", + "--json", + ]) + .assert_success() + .stdout_eq(str![[r#" +{"logs":[],"returns":{"success":{"internal_type":"bool","value":"true"}},"success":true,"raw_logs":[],"traces":[["Deployment",{"arena":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":false,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CREATE","value":"0x0","data":"0x6080604052600c805462ff00ff191662010001179055348015601f575f5ffd5b506101568061002d5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063c040622614610038578063f8ccbf4714610054575b5f5ffd5b610040610067565b604051901515815260200160405180910390f35b600c546100409062010000900460ff1681565b5f7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d5f1c6001600160a01b0316637fb5297f6040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156100c2575f5ffd5b505af11580156100d4573d5f5f3e3d5ffd5b50506040515f925090508181818181805af19150503d805f8114610113576040519150601f19603f3d011682016040523d82523d5f602084013e610118565b606091505b50909291505056fea264697066735822122060ba6332e526de9b6bc731fb4682b44e42845196324ec33068982984d700cdd964736f6c634300081b0033","output":"0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063c040622614610038578063f8ccbf4714610054575b5f5ffd5b610040610067565b604051901515815260200160405180910390f35b600c546100409062010000900460ff1681565b5f7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d5f1c6001600160a01b0316637fb5297f6040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156100c2575f5ffd5b505af11580156100d4573d5f5f3e3d5ffd5b50506040515f925090508181818181805af19150503d805f8114610113576040519150601f19603f3d011682016040523d82523d5f602084013e610118565b606091505b50909291505056fea264697066735822122060ba6332e526de9b6bc731fb4682b44e42845196324ec33068982984d700cdd964736f6c634300081b0033","gas_used":90639,"gas_limit":1073682810,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}],["Execution",{"arena":[{"parent":null,"children":[1,2],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0xc0406226","output":"0x0000000000000000000000000000000000000000000000000000000000000001","gas_used":3214,"gas_limit":1073720760,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[{"Call":0},{"Call":1}]},{"parent":0,"children":[],"idx":1,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x7109709ecfa91a80626ff3989d68f67f5b1dd12d","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x7fb5297f","output":"0x","gas_used":0,"gas_limit":1056940983,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]},{"parent":0,"children":[],"idx":2,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x0000000000000000000000000000000000000000","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1056940820,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}]],"gas_used":24278,"labeled_addresses":{},"returned":"0x0000000000000000000000000000000000000000000000000000000000000001","address":null} +{"chain":31337,"estimated_gas_price":"2.000000001","estimated_total_gas_used":29005,"estimated_amount_required":"0.000058010000029005"} +{"chain":"anvil-hardhat","status":"success","tx_hash":"0x4f78afe915fceb282c7625a68eb350bc0bf78acb59ad893e5c62b710a37f3156","contract_address":null,"block_number":1,"gas_used":21000,"gas_price":1000000001} +{"status":"success","transactions":"[..]/broadcast/Foo.sol/31337/run-latest.json","sensitive":"[..]/cache/Foo.sol/31337/run-latest.json"} + +"#]].is_jsonlines()); +}); + // https://github.com/foundry-rs/foundry/pull/7742 forgetest_async!(unlocked_no_sender, |prj, cmd| { foundry_test_utils::util::initialize(prj.root()); diff --git a/crates/script-sequence/src/sequence.rs b/crates/script-sequence/src/sequence.rs index e34b6d06a865..235f28f2c4ff 100644 --- a/crates/script-sequence/src/sequence.rs +++ b/crates/script-sequence/src/sequence.rs @@ -2,7 +2,7 @@ use crate::transaction::TransactionWithMetadata; use alloy_primitives::{hex, map::HashMap, TxHash}; use alloy_rpc_types::AnyTransactionReceipt; use eyre::{ContextCompat, Result, WrapErr}; -use foundry_common::{fs, TransactionMaybeSigned, SELECTOR_LEN}; +use foundry_common::{fs, shell, TransactionMaybeSigned, SELECTOR_LEN}; use foundry_compilers::ArtifactId; use foundry_config::Config; use serde::{Deserialize, Serialize}; @@ -127,8 +127,19 @@ impl ScriptSequence { } if !silent { - sh_println!("\nTransactions saved to: {}\n", path.display())?; - sh_println!("Sensitive values saved to: {}\n", sensitive_path.display())?; + if shell::is_json() { + sh_println!( + "{}", + serde_json::json!({ + "status": "success", + "transactions": path.display().to_string(), + "sensitive": sensitive_path.display().to_string(), + }) + )?; + } else { + sh_println!("\nTransactions saved to: {}\n", path.display())?; + sh_println!("Sensitive values saved to: {}\n", sensitive_path.display())?; + } } Ok(()) diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 51e8baf5b3be..207754e5c952 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -21,7 +21,7 @@ use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_batch_support, has_different_gas_calc}; use foundry_common::{ provider::{get_http_provider, try_get_http_provider, RetryProvider}, - TransactionMaybeSigned, + shell, TransactionMaybeSigned, }; use foundry_config::Config; use futures::{future::join_all, StreamExt}; @@ -429,8 +429,10 @@ impl BundledState { seq_progress.inner.write().finish(); } - sh_println!("\n\n==========================")?; - sh_println!("\nONCHAIN EXECUTION COMPLETE & SUCCESSFUL.")?; + if !shell::is_json() { + sh_println!("\n\n==========================")?; + sh_println!("\nONCHAIN EXECUTION COMPLETE & SUCCESSFUL.")?; + } Ok(BroadcastedState { args: self.args, diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index aeea4940a0be..517eff552fd0 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -284,7 +284,10 @@ impl ScriptArgs { // Check if there are any missing RPCs and exit early to avoid hard error. if pre_simulation.execution_artifacts.rpc_data.missing_rpc { - sh_println!("\nIf you wish to simulate on-chain transactions pass a RPC URL.")?; + if !shell::is_json() { + sh_println!("\nIf you wish to simulate on-chain transactions pass a RPC URL.")?; + } + return Ok(()); } @@ -298,7 +301,9 @@ impl ScriptArgs { // Exit early in case user didn't provide any broadcast/verify related flags. if !bundled.args.should_broadcast() { - sh_println!("\nSIMULATION COMPLETE. To broadcast these transactions, add --broadcast and wallet configuration(s) to the previous command. See forge script --help for more.")?; + if !shell::is_json() { + sh_println!("\nSIMULATION COMPLETE. To broadcast these transactions, add --broadcast and wallet configuration(s) to the previous command. See forge script --help for more.")?; + } return Ok(()); } diff --git a/crates/script/src/multi_sequence.rs b/crates/script/src/multi_sequence.rs index ec2f03ae9855..e0fd4d1bc7e6 100644 --- a/crates/script/src/multi_sequence.rs +++ b/crates/script/src/multi_sequence.rs @@ -2,7 +2,7 @@ use eyre::{ContextCompat, Result, WrapErr}; use forge_script_sequence::{ now, sig_to_file_name, ScriptSequence, SensitiveScriptSequence, DRY_RUN_DIR, }; -use foundry_common::fs; +use foundry_common::{fs, shell}; use foundry_compilers::ArtifactId; use foundry_config::Config; use serde::{Deserialize, Serialize}; @@ -146,8 +146,19 @@ impl MultiChainSequence { } if !silent { - sh_println!("\nTransactions saved to: {}\n", self.path.display())?; - sh_println!("Sensitive details saved to: {}\n", self.sensitive_path.display())?; + if shell::is_json() { + sh_println!( + "{}", + serde_json::json!({ + "status": "success", + "transactions": self.path.display().to_string(), + "sensitive": self.sensitive_path.display().to_string(), + }) + )?; + } else { + sh_println!("\nTransactions saved to: {}\n", self.path.display())?; + sh_println!("Sensitive details saved to: {}\n", self.sensitive_path.display())?; + } } Ok(()) diff --git a/crates/script/src/progress.rs b/crates/script/src/progress.rs index d9fa53bb3356..bc23fcf67705 100644 --- a/crates/script/src/progress.rs +++ b/crates/script/src/progress.rs @@ -7,7 +7,7 @@ use alloy_primitives::{ use eyre::Result; use forge_script_sequence::ScriptSequence; use foundry_cli::utils::init_progress; -use foundry_common::provider::RetryProvider; +use foundry_common::{provider::RetryProvider, shell}; use futures::StreamExt; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use parking_lot::RwLock; @@ -31,33 +31,42 @@ pub struct SequenceProgressState { impl SequenceProgressState { pub fn new(sequence_idx: usize, sequence: &ScriptSequence, multi: MultiProgress) -> Self { - let mut template = "{spinner:.green}".to_string(); - write!(template, " Sequence #{} on {}", sequence_idx + 1, Chain::from(sequence.chain)) - .unwrap(); - template.push_str("{msg}"); - - let top_spinner = ProgressBar::new_spinner() - .with_style(ProgressStyle::with_template(&template).unwrap().tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈✅")); - let top_spinner = multi.add(top_spinner); - - let txs = multi.insert_after( - &top_spinner, - init_progress(sequence.transactions.len() as u64, "txes").with_prefix(" "), - ); - - let receipts = multi.insert_after( - &txs, - init_progress(sequence.transactions.len() as u64, "receipts").with_prefix(" "), - ); - - top_spinner.enable_steady_tick(Duration::from_millis(100)); - txs.enable_steady_tick(Duration::from_millis(1000)); - receipts.enable_steady_tick(Duration::from_millis(1000)); - - txs.set_position(sequence.receipts.len() as u64); - receipts.set_position(sequence.receipts.len() as u64); - - let mut state = Self { top_spinner, txs, receipts, tx_spinners: Default::default(), multi }; + let mut state = if shell::is_quiet() || shell::is_json() { + let top_spinner = ProgressBar::hidden(); + let txs = ProgressBar::hidden(); + let receipts = ProgressBar::hidden(); + + Self { top_spinner, txs, receipts, tx_spinners: Default::default(), multi } + } else { + let mut template = "{spinner:.green}".to_string(); + write!(template, " Sequence #{} on {}", sequence_idx + 1, Chain::from(sequence.chain)) + .unwrap(); + template.push_str("{msg}"); + + let top_spinner = ProgressBar::new_spinner().with_style( + ProgressStyle::with_template(&template).unwrap().tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈✅"), + ); + let top_spinner = multi.add(top_spinner); + + let txs = multi.insert_after( + &top_spinner, + init_progress(sequence.transactions.len() as u64, "txes").with_prefix(" "), + ); + + let receipts = multi.insert_after( + &txs, + init_progress(sequence.transactions.len() as u64, "receipts").with_prefix(" "), + ); + + top_spinner.enable_steady_tick(Duration::from_millis(100)); + txs.enable_steady_tick(Duration::from_millis(1000)); + receipts.enable_steady_tick(Duration::from_millis(1000)); + + txs.set_position(sequence.receipts.len() as u64); + receipts.set_position(sequence.receipts.len() as u64); + + Self { top_spinner, txs, receipts, tx_spinners: Default::default(), multi } + }; for tx_hash in sequence.pending.iter() { state.tx_sent(*tx_hash); @@ -71,16 +80,21 @@ impl SequenceProgressState { pub fn tx_sent(&mut self, tx_hash: B256) { // Avoid showing more than 10 spinners. if self.tx_spinners.len() < 10 { - let spinner = ProgressBar::new_spinner() - .with_style( - ProgressStyle::with_template(" {spinner:.green} {msg}") - .unwrap() - .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈"), - ) - .with_message(format!("{} {}", "[Pending]".yellow(), tx_hash)); - - let spinner = self.multi.insert_before(&self.txs, spinner); - spinner.enable_steady_tick(Duration::from_millis(100)); + let spinner = if shell::is_quiet() || shell::is_json() { + ProgressBar::hidden() + } else { + let spinner = ProgressBar::new_spinner() + .with_style( + ProgressStyle::with_template(" {spinner:.green} {msg}") + .unwrap() + .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈"), + ) + .with_message(format!("{} {}", "[Pending]".yellow(), tx_hash)); + + let spinner = self.multi.insert_before(&self.txs, spinner); + spinner.enable_steady_tick(Duration::from_millis(100)); + spinner + }; self.tx_spinners.insert(tx_hash, spinner); } @@ -98,7 +112,10 @@ impl SequenceProgressState { /// Same as finish_tx_spinner but also prints a message to stdout above all other progress bars. pub fn finish_tx_spinner_with_msg(&mut self, tx_hash: B256, msg: &str) -> std::io::Result<()> { self.finish_tx_spinner(tx_hash); - self.multi.println(msg)?; + + if !(shell::is_quiet() || shell::is_json()) { + self.multi.println(msg)?; + } Ok(()) } diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index c11fdd71a92c..f073e38e607b 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -3,7 +3,7 @@ use alloy_primitives::{utils::format_units, TxHash, U256}; use alloy_provider::{PendingTransactionBuilder, PendingTransactionError, Provider, WatchTxError}; use alloy_rpc_types::AnyTransactionReceipt; use eyre::Result; -use foundry_common::provider::RetryProvider; +use foundry_common::{provider::RetryProvider, shell}; use std::time::Duration; /// Convenience enum for internal signalling of transaction status @@ -71,31 +71,51 @@ pub async fn check_tx_status( pub fn format_receipt(chain: Chain, receipt: &AnyTransactionReceipt) -> String { let gas_used = receipt.gas_used; let gas_price = receipt.effective_gas_price; - format!( - "\n##### {chain}\n{status} Hash: {tx_hash:?}{caddr}\nBlock: {bn}\n{gas}\n\n", - status = if !receipt.inner.inner.inner.receipt.status.coerce_status() { - "❌ [Failed]" - } else { - "✅ [Success]" - }, - tx_hash = receipt.transaction_hash, - caddr = if let Some(addr) = &receipt.contract_address { - format!("\nContract Address: {}", addr.to_checksum(None)) - } else { - String::new() - }, - bn = receipt.block_number.unwrap_or_default(), - gas = if gas_price == 0 { - format!("Gas Used: {gas_used}") - } else { - let paid = format_units(gas_used.saturating_mul(gas_price), 18) - .unwrap_or_else(|_| "N/A".into()); - let gas_price = format_units(U256::from(gas_price), 9).unwrap_or_else(|_| "N/A".into()); - format!( - "Paid: {} ETH ({gas_used} gas * {} gwei)", - paid.trim_end_matches('0'), - gas_price.trim_end_matches('0').trim_end_matches('.') - ) - }, - ) + let block_number = receipt.block_number.unwrap_or_default(); + let success = receipt.inner.inner.inner.receipt.status.coerce_status(); + + if shell::is_json() { + let _ = sh_println!( + "{}", + serde_json::json!({ + "chain": chain, + "status": if success { + "success" + } else { + "failed" + }, + "tx_hash": receipt.transaction_hash, + "contract_address": receipt.contract_address.map(|addr| addr.to_string()), + "block_number": block_number, + "gas_used": gas_used, + "gas_price": gas_price, + }) + ); + + String::new() + } else { + format!( + "\n##### {chain}\n{status} Hash: {tx_hash:?}{contract_address}\nBlock: {block_number}\n{gas}\n\n", + status = if success { "✅ [Success]" } else { "❌ [Failed]" }, + tx_hash = receipt.transaction_hash, + contract_address = if let Some(addr) = &receipt.contract_address { + format!("\nContract Address: {}", addr.to_checksum(None)) + } else { + String::new() + }, + gas = if gas_price == 0 { + format!("Gas Used: {gas_used}") + } else { + let paid = format_units(gas_used.saturating_mul(gas_price), 18) + .unwrap_or_else(|_| "N/A".into()); + let gas_price = + format_units(U256::from(gas_price), 9).unwrap_or_else(|_| "N/A".into()); + format!( + "Paid: {} ETH ({gas_used} gas * {} gwei)", + paid.trim_end_matches('0'), + gas_price.trim_end_matches('0').trim_end_matches('.') + ) + }, + ) + } } diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 95427225f202..e833073ac00f 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -16,7 +16,7 @@ use eyre::{Context, Result}; use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_different_gas_calc, now}; -use foundry_common::ContractData; +use foundry_common::{shell, ContractData}; use foundry_evm::traces::{decode_trace_arena, render_trace_arena}; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; @@ -151,7 +151,7 @@ impl PreSimulationState { }) .collect::>(); - if self.script_config.evm_opts.verbosity > 3 { + if !shell::is_json() && self.script_config.evm_opts.verbosity > 3 { sh_println!("==========================")?; sh_println!("Simulated On-chain Traces:\n")?; } @@ -220,9 +220,11 @@ impl PreSimulationState { async fn build_runners(&self) -> Result> { let rpcs = self.execution_artifacts.rpc_data.total_rpcs.clone(); - let n = rpcs.len(); - let s = if n != 1 { "s" } else { "" }; - sh_println!("\n## Setting up {n} EVM{s}.")?; + if !shell::is_json() { + let n = rpcs.len(); + let s = if n != 1 { "s" } else { "" }; + sh_println!("\n## Setting up {n} EVM{s}.")?; + } let futs = rpcs.into_iter().map(|rpc| async move { let mut script_config = self.script_config.clone(); @@ -348,24 +350,34 @@ impl FilledTransactionsState { provider_info.gas_price()? }; - sh_println!("\n==========================")?; - sh_println!("\nChain {}", provider_info.chain)?; - - sh_println!( - "\nEstimated gas price: {} gwei", - format_units(per_gas, 9) - .unwrap_or_else(|_| "[Could not calculate]".to_string()) - .trim_end_matches('0') - .trim_end_matches('.') - )?; - sh_println!("\nEstimated total gas used for script: {total_gas}")?; - sh_println!( - "\nEstimated amount required: {} ETH", - format_units(total_gas.saturating_mul(per_gas), 18) - .unwrap_or_else(|_| "[Could not calculate]".to_string()) - .trim_end_matches('0') - )?; - sh_println!("\n==========================")?; + let estimated_gas_price_raw = format_units(per_gas, 9) + .unwrap_or_else(|_| "[Could not calculate]".to_string()); + let estimated_gas_price = + estimated_gas_price_raw.trim_end_matches('0').trim_end_matches('.'); + + let estimated_amount_raw = format_units(total_gas.saturating_mul(per_gas), 18) + .unwrap_or_else(|_| "[Could not calculate]".to_string()); + let estimated_amount = estimated_amount_raw.trim_end_matches('0'); + + if !shell::is_json() { + sh_println!("\n==========================")?; + sh_println!("\nChain {}", provider_info.chain)?; + + sh_println!("\nEstimated gas price: {} gwei", estimated_gas_price)?; + sh_println!("\nEstimated total gas used for script: {total_gas}")?; + sh_println!("\nEstimated amount required: {estimated_amount} ETH",)?; + sh_println!("\n==========================")?; + } else { + sh_println!( + "{}", + serde_json::json!({ + "chain": provider_info.chain, + "estimated_gas_price": estimated_gas_price, + "estimated_total_gas_used": total_gas, + "estimated_amount_required": estimated_amount, + }) + )?; + } } } diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index ca90129d543b..78f39fe96dcc 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -139,7 +139,6 @@ impl VerificationProvider for EtherscanVerificationProvider { retry: RETRY_CHECK_ON_VERIFY, verifier: args.verifier, }; - // return check_args.run().await return self.check(check_args).await } } else { From 31dd1f77fd9156d09836486d97963cec7f555343 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 26 Nov 2024 17:33:30 +0200 Subject: [PATCH 55/68] feat(cast): add decode-event sig data (#9413) --- crates/cast/bin/args.rs | 21 +++++++++++++++------ crates/cast/bin/main.rs | 13 +++++++++---- crates/cast/tests/cli/main.rs | 8 ++++++++ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/crates/cast/bin/args.rs b/crates/cast/bin/args.rs index f010c279e0d0..7078810e4ce9 100644 --- a/crates/cast/bin/args.rs +++ b/crates/cast/bin/args.rs @@ -512,8 +512,8 @@ pub enum CastSubcommand { /// /// Similar to `abi-decode --input`, but function selector MUST be prefixed in `calldata` /// string - #[command(visible_aliases = &["--calldata-decode", "cdd"])] - CalldataDecode { + #[command(visible_aliases = &["calldata-decode", "--calldata-decode", "cdd"])] + DecodeCalldata { /// The function signature in the format `()()`. sig: String, @@ -524,19 +524,28 @@ pub enum CastSubcommand { /// Decode ABI-encoded string. /// /// Similar to `calldata-decode --input`, but the function argument is a `string` - #[command(visible_aliases = &["--string-decode", "sd"])] - StringDecode { + #[command(visible_aliases = &["string-decode", "--string-decode", "sd"])] + DecodeString { /// The ABI-encoded string. data: String, }, + /// Decode event data. + #[command(visible_aliases = &["event-decode", "--event-decode", "ed"])] + DecodeEvent { + /// The event signature. + sig: String, + /// The event data to decode. + data: String, + }, + /// Decode ABI-encoded input or output data. /// /// Defaults to decoding output data. To decode input data pass --input. /// /// When passing `--input`, function selector must NOT be prefixed in `calldata` string - #[command(name = "abi-decode", visible_aliases = &["ad", "--abi-decode"])] - AbiDecode { + #[command(name = "decode-abi", visible_aliases = &["abi-decode", "--abi-decode", "ad"])] + DecodeAbi { /// The function signature in the format `()()`. sig: String, diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index ffce79099c9c..21b1df36d6cd 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate tracing; -use alloy_dyn_abi::DynSolValue; +use alloy_dyn_abi::{DynSolValue, EventExt}; use alloy_primitives::{eip191_hash_message, hex, keccak256, Address, B256}; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, BlockNumberOrTag::Latest}; @@ -189,7 +189,7 @@ async fn main_args(args: CastArgs) -> Result<()> { } // ABI encoding & decoding - CastSubcommand::AbiDecode { sig, calldata, input } => { + CastSubcommand::DecodeAbi { sig, calldata, input } => { let tokens = SimpleCast::abi_decode(&sig, &calldata, input)?; print_tokens(&tokens); } @@ -200,17 +200,22 @@ async fn main_args(args: CastArgs) -> Result<()> { sh_println!("{}", SimpleCast::abi_encode_packed(&sig, &args)?)? } } - CastSubcommand::CalldataDecode { sig, calldata } => { + CastSubcommand::DecodeCalldata { sig, calldata } => { let tokens = SimpleCast::calldata_decode(&sig, &calldata, true)?; print_tokens(&tokens); } CastSubcommand::CalldataEncode { sig, args } => { sh_println!("{}", SimpleCast::calldata_encode(sig, &args)?)?; } - CastSubcommand::StringDecode { data } => { + CastSubcommand::DecodeString { data } => { let tokens = SimpleCast::calldata_decode("Any(string)", &data, true)?; print_tokens(&tokens); } + CastSubcommand::DecodeEvent { sig, data } => { + let event = get_event(sig.as_str())?; + let decoded_event = event.decode_log_parts(None, &hex::decode(data)?, false)?; + print_tokens(&decoded_event.body); + } CastSubcommand::Interface(cmd) => cmd.run().await?, CastSubcommand::CreationCode(cmd) => cmd.run().await?, CastSubcommand::ConstructorArgs(cmd) => cmd.run().await?, diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 332f0f99f57b..92f23a457435 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1474,6 +1474,14 @@ casttest!(string_decode, |_prj, cmd| { "#]]); }); +casttest!(event_decode, |_prj, cmd| { + cmd.args(["decode-event", "MyEvent(uint256,address)", "0x000000000000000000000000000000000000000000000000000000000000004e0000000000000000000000000000000000000000000000000000000000d0004f"]).assert_success().stdout_eq(str![[r#" +78 +0x0000000000000000000000000000000000D0004F + +"#]]); +}); + casttest!(format_units, |_prj, cmd| { cmd.args(["format-units", "1000000", "6"]).assert_success().stdout_eq(str![[r#" 1 From 735b5ebdbbe8fe05c93af930c53e0fba6d3aa4f9 Mon Sep 17 00:00:00 2001 From: wangjingcun Date: Wed, 27 Nov 2024 17:06:47 +0800 Subject: [PATCH 56/68] chore: fix 404 status URL (#9417) Signed-off-by: wangjingcun --- crates/anvil/src/eth/otterscan/api.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 617655444bef..f9a7334e0376 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -46,7 +46,7 @@ pub fn mentions_address(trace: LocalizedTransactionTrace, address: Address) -> O /// Converts the list of traces for a transaction into the expected Otterscan format. /// -/// Follows format specified in the [`ots_traceTransaction`](https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_tracetransaction) spec. +/// Follows format specified in the [`ots_traceTransaction`](https://github.com/otterscan/otterscan/blob/main/docs/custom-jsonrpc.md#ots_tracetransaction) spec. pub fn batch_build_ots_traces(traces: Vec) -> Vec { traces .into_iter() @@ -350,7 +350,7 @@ impl EthApi { /// their `gas_used`. This would be extremely inefficient in a real blockchain RPC, but we can /// get away with that in this context. /// - /// The [original spec](https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_getblockdetails) + /// The [original spec](https://github.com/otterscan/otterscan/blob/main/docs/custom-jsonrpc.md#ots_getblockdetails) /// also mentions we can hardcode `transactions` and `logsBloom` to an empty array to save /// bandwidth, because fields weren't intended to be used in the Otterscan UI at this point. /// @@ -402,7 +402,7 @@ impl EthApi { /// Fetches all receipts for the blocks's transactions, as required by the /// [`ots_getBlockTransactions`] endpoint spec, and returns the final response object. /// - /// [`ots_getBlockTransactions`]: https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_getblockdetails + /// [`ots_getBlockTransactions`]: https://github.com/otterscan/otterscan/blob/main/docs/custom-jsonrpc.md#ots_getblockdetails pub async fn build_ots_block_tx( &self, mut block: AnyRpcBlock, From 2c3114c4d9cbe66a897e634b11b8771a56f91bec Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:06:26 +0100 Subject: [PATCH 57/68] feat: add `--broadcast` flag to forge create, default to dry run mode (#9420) * add --broadcast flag to forge create, default to dry run * nits * fix tests * add dry run tests incl --json * minor fixes, failing test due to minor bytecode difference --- crates/forge/bin/cmd/create.rs | 37 ++++++++- crates/forge/tests/cli/create.rs | 129 +++++++++++++++++++++++++++++-- 2 files changed, 160 insertions(+), 6 deletions(-) diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 71823416d713..6c2fbb0cfecc 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -61,6 +61,10 @@ pub struct CreateArgs { )] constructor_args_path: Option, + /// Broadcast the transaction. + #[arg(long)] + pub broadcast: bool, + /// Verify contract after creation. #[arg(long)] verify: bool, @@ -155,6 +159,10 @@ impl CreateArgs { } else { provider.get_chain_id().await? }; + + // Whether to broadcast the transaction or not + let dry_run = !self.broadcast; + if self.unlocked { // Deploy with unlocked account let sender = self.eth.wallet.from.expect("required"); @@ -167,6 +175,7 @@ impl CreateArgs { sender, config.transaction_timeout, id, + dry_run, ) .await } else { @@ -185,6 +194,7 @@ impl CreateArgs { deployer, config.transaction_timeout, id, + dry_run, ) .await } @@ -260,6 +270,7 @@ impl CreateArgs { deployer_address: Address, timeout: u64, id: ArtifactId, + dry_run: bool, ) -> Result<()> { let bin = bin.into_bytes().unwrap_or_else(|| { panic!("no bytecode found in bin object for {}", self.contract.name) @@ -339,6 +350,30 @@ impl CreateArgs { self.verify_preflight_check(constructor_args.clone(), chain, &id).await?; } + if dry_run { + if !shell::is_json() { + sh_warn!("Dry run enabled, not broadcasting transaction\n")?; + + sh_println!("Contract: {}", self.contract.name)?; + sh_println!( + "Transaction: {}", + serde_json::to_string_pretty(&deployer.tx.clone())? + )?; + sh_println!("ABI: {}\n", serde_json::to_string_pretty(&abi)?)?; + + sh_warn!("To broadcast this transaction, add --broadcast to the previous command. See forge create --help for more.")?; + } else { + let output = json!({ + "contract": self.contract.name, + "transaction": &deployer.tx, + "abi":&abi + }); + sh_println!("{}", serde_json::to_string_pretty(&output)?)?; + } + + return Ok(()); + } + // Deploy the actual contract let (deployed_contract, receipt) = deployer.send_with_receipt().await?; @@ -349,7 +384,7 @@ impl CreateArgs { "deployedTo": address.to_string(), "transactionHash": receipt.transaction_hash }); - sh_println!("{output}")?; + sh_println!("{}", serde_json::to_string_pretty(&output)?)?; } else { sh_println!("Deployer: {deployer_address}")?; sh_println!("Deployed to: {address}")?; diff --git a/crates/forge/tests/cli/create.rs b/crates/forge/tests/cli/create.rs index ebf8c81dbcc1..6a78f83231ef 100644 --- a/crates/forge/tests/cli/create.rs +++ b/crates/forge/tests/cli/create.rs @@ -9,7 +9,9 @@ use anvil::{spawn, NodeConfig}; use foundry_compilers::artifacts::{remappings::Remapping, BytecodeHash}; use foundry_config::Config; use foundry_test_utils::{ - forgetest, forgetest_async, str, + forgetest, forgetest_async, + snapbox::IntoData, + str, util::{OutputExt, TestCommand, TestProject}, }; use std::str::FromStr; @@ -145,6 +147,7 @@ forgetest_async!(can_create_template_contract, |prj, cmd| { let config = Config { bytecode_hash: BytecodeHash::None, ..Default::default() }; prj.write_config(config); + // Dry-run without the `--broadcast` flag cmd.forge_fuse().args([ "create", format!("./src/{TEMPLATE_CONTRACT}.sol:{TEMPLATE_CONTRACT}").as_str(), @@ -154,20 +157,131 @@ forgetest_async!(can_create_template_contract, |prj, cmd| { pk.as_str(), ]); + // Dry-run cmd.assert().stdout_eq(str![[r#" [COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! -Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 -Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 -[TX_HASH] +Contract: Counter +Transaction: { + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": null, + "maxFeePerGas": "0x77359401", + "maxPriorityFeePerGas": "0x1", + "gas": "0x17575", + "input": "[..]", + "nonce": "0x0", + "chainId": "0x7a69" +} +ABI: [ + { + "type": "function", + "name": "increment", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "number", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setNumber", + "inputs": [ + { + "name": "newNumber", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + } +] + "#]]); + // Dry-run with `--json` flag + cmd.arg("--json").assert().stdout_eq( + str![[r#" +{ + "contract": "Counter", + "transaction": { + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": null, + "maxFeePerGas": "0x77359401", + "maxPriorityFeePerGas": "0x1", + "gas": "0x17575", + "input": "[..]", + "nonce": "0x0", + "chainId": "0x7a69" + }, + "abi": [ + { + "type": "function", + "name": "increment", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "number", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setNumber", + "inputs": [ + { + "name": "newNumber", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + } + ] +} + +"#]] + .is_json(), + ); + + cmd.forge_fuse().args([ + "create", + format!("./src/{TEMPLATE_CONTRACT}.sol:{TEMPLATE_CONTRACT}").as_str(), + "--rpc-url", + rpc.as_str(), + "--private-key", + pk.as_str(), + "--broadcast", + ]); + cmd.assert().stdout_eq(str![[r#" No files changed, compilation skipped Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 -Deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 +Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 [TX_HASH] "#]]); @@ -193,6 +307,7 @@ forgetest_async!(can_create_using_unlocked, |prj, cmd| { "--from", format!("{dev:?}").as_str(), "--unlocked", + "--broadcast", ]); cmd.assert().stdout_eq(str![[r#" @@ -204,6 +319,7 @@ Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 [TX_HASH] "#]]); + cmd.assert().stdout_eq(str![[r#" No files changed, compilation skipped Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 @@ -248,6 +364,7 @@ contract ConstructorContract { rpc.as_str(), "--private-key", pk.as_str(), + "--broadcast", "--constructor-args", "My Constructor", ]) @@ -285,6 +402,7 @@ contract TupleArrayConstructorContract { rpc.as_str(), "--private-key", pk.as_str(), + "--broadcast", "--constructor-args", "[(1,2), (2,3), (3,4)]", ]) @@ -335,6 +453,7 @@ contract UniswapV2Swap { rpc.as_str(), "--private-key", pk.as_str(), + "--broadcast", ]) .assert_success() .stdout_eq(str![[r#" From c63aba816b76f9bad103b1275cc662a063919403 Mon Sep 17 00:00:00 2001 From: cl Date: Thu, 28 Nov 2024 03:36:30 +0800 Subject: [PATCH 58/68] feat(`traces`): show state changes in `cast run` and `forge test` on `-vvvvv` (#9013) * Add options for state changes output and json output in cast run command * fix test * add back serde_json in Cargo.lock * format using nightly * rename parameter * update revm-inspectors * supress clippy warning and merge master * add serde_json * disable some stdout print when --json option is used * remove unnecessary check * replace with sh_println * replace with shell::is_json * Show storage for verbosity > 1, add test * Change verbosity to > 4 for both cast and forge test, add test, fix ci --------- Co-authored-by: grandizzy --- Cargo.lock | 1 + crates/cast/bin/cmd/call.rs | 13 ++++-- crates/cast/bin/cmd/run.rs | 7 ++- crates/cast/tests/cli/main.rs | 65 ++++++++++++++++++++++++++- crates/cli/src/opts/global.rs | 2 +- crates/cli/src/utils/cmd.rs | 21 ++++++--- crates/evm/evm/src/executors/trace.rs | 9 ++-- crates/evm/traces/Cargo.toml | 1 + crates/evm/traces/src/lib.rs | 37 ++++++++++++--- crates/forge/bin/cmd/test/mod.rs | 8 ++-- crates/forge/src/multi_runner.rs | 5 ++- crates/forge/tests/cli/cmd.rs | 2 +- crates/forge/tests/cli/test_cmd.rs | 34 ++++++++++++++ crates/verify/src/utils.rs | 1 + 14 files changed, 176 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e6cdd0f96c6..d94d710ac07f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4086,6 +4086,7 @@ dependencies = [ "revm", "revm-inspectors", "serde", + "serde_json", "solar-parse", "tempfile", "tokio", diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index cdc3bd4bc6e2..2d5692efe4c1 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -8,7 +8,7 @@ use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, utils::{self, handle_traces, parse_ether_value, TraceResult}, }; -use foundry_common::ens::NameOrAddress; +use foundry_common::{ens::NameOrAddress, shell}; use foundry_compilers::artifacts::EvmVersion; use foundry_config::{ figment::{ @@ -182,8 +182,15 @@ impl CallArgs { env.cfg.disable_block_gas_limit = true; env.block.gas_limit = U256::MAX; - let mut executor = - TracingExecutor::new(env, fork, evm_version, debug, decode_internal, alphanet); + let mut executor = TracingExecutor::new( + env, + fork, + evm_version, + debug, + decode_internal, + shell::verbosity() > 4, + alphanet, + ); let value = tx.value.unwrap_or_default(); let input = tx.inner.input.into_input().unwrap_or_default(); diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 0b85d14feb98..cfad7263a1e4 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -10,7 +10,7 @@ use foundry_cli::{ opts::{EtherscanOpts, RpcOpts}, utils::{handle_traces, init_progress, TraceResult}, }; -use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; +use foundry_common::{is_known_system_sender, shell, SYSTEM_TRANSACTION_TYPE}; use foundry_compilers::artifacts::EvmVersion; use foundry_config::{ figment::{ @@ -169,6 +169,7 @@ impl RunArgs { evm_version, self.debug, self.decode_internal, + shell::verbosity() > 4, alphanet, ); let mut env = @@ -176,7 +177,9 @@ impl RunArgs { // Set the state to the moment right before the transaction if !self.quick { - sh_println!("Executing previous transactions from the block.")?; + if !shell::is_json() { + sh_println!("Executing previous transactions from the block.")?; + } if let Some(block) = block { let pb = init_progress(block.transactions.len() as u64, "tx"); diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 92f23a457435..aa70fef92db6 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -64,7 +64,7 @@ Display options: - 2 (-vv): Print logs for all tests. - 3 (-vvv): Print execution traces for failing tests. - 4 (-vvvv): Print execution traces for all tests, and setup traces for failing tests. - - 5 (-vvvvv): Print execution and setup traces for all tests. + - 5 (-vvvvv): Print execution and setup traces for all tests, including storage changes. Find more information in the book: http://book.getfoundry.sh/reference/cast/cast.html @@ -1713,3 +1713,66 @@ Transaction successfully executed. "#]]); }); + +// tests cast can decode traces when running with verbosity level > 4 +forgetest_async!(show_state_changes_in_traces, |prj, cmd| { + let (api, handle) = anvil::spawn(NodeConfig::test()).await; + + foundry_test_utils::util::initialize(prj.root()); + // Deploy counter contract. + cmd.args([ + "script", + "--private-key", + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "--rpc-url", + &handle.http_endpoint(), + "--broadcast", + "CounterScript", + ]) + .assert_success(); + + // Send tx to change counter storage value. + cmd.cast_fuse() + .args([ + "send", + "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "setNumber(uint256)", + "111", + "--private-key", + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "--rpc-url", + &handle.http_endpoint(), + ]) + .assert_success(); + + let tx_hash = api + .transaction_by_block_number_and_index(BlockNumberOrTag::Latest, Index::from(0)) + .await + .unwrap() + .unwrap() + .tx_hash(); + + // Assert cast with verbosity displays storage changes. + cmd.cast_fuse() + .args([ + "run", + format!("{tx_hash}").as_str(), + "-vvvvv", + "--rpc-url", + &handle.http_endpoint(), + ]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Traces: + [22287] 0x5FbDB2315678afecb367f032d93F642f64180aa3::setNumber(111) + ├─ storage changes: + │ @ 0: 0 → 111 + └─ ← [Stop] + + +Transaction successfully executed. +[GAS] + +"#]]); +}); diff --git a/crates/cli/src/opts/global.rs b/crates/cli/src/opts/global.rs index ad715f24180a..c820ca2cff7e 100644 --- a/crates/cli/src/opts/global.rs +++ b/crates/cli/src/opts/global.rs @@ -15,7 +15,7 @@ pub struct GlobalOpts { /// - 2 (-vv): Print logs for all tests. /// - 3 (-vvv): Print execution traces for failing tests. /// - 4 (-vvvv): Print execution traces for all tests, and setup traces for failing tests. - /// - 5 (-vvvvv): Print execution and setup traces for all tests. + /// - 5 (-vvvvv): Print execution and setup traces for all tests, including storage changes. #[arg(help_heading = "Display options", global = true, short, long, verbatim_doc_comment, conflicts_with = "quiet", action = ArgAction::Count)] verbosity: Verbosity, diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 523c10478fb6..505634996de8 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -17,8 +17,7 @@ use foundry_evm::{ debug::{ContractSources, DebugTraceIdentifier}, decode_trace_arena, identifier::{CachedSignatures, SignaturesIdentifier, TraceIdentifiers}, - render_trace_arena_with_bytecodes, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, - Traces, + render_trace_arena_inner, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, }, }; use std::{ @@ -450,7 +449,7 @@ pub async fn handle_traces( decoder.debug_identifier = Some(DebugTraceIdentifier::new(sources)); } - print_traces(&mut result, &decoder, shell::verbosity() > 0).await?; + print_traces(&mut result, &decoder, shell::verbosity() > 0, shell::verbosity() > 4).await?; Ok(()) } @@ -459,23 +458,31 @@ pub async fn print_traces( result: &mut TraceResult, decoder: &CallTraceDecoder, verbose: bool, + state_changes: bool, ) -> Result<()> { let traces = result.traces.as_mut().expect("No traces found"); - sh_println!("Traces:")?; + if !shell::is_json() { + sh_println!("Traces:")?; + } + for (_, arena) in traces { decode_trace_arena(arena, decoder).await?; - sh_println!("{}", render_trace_arena_with_bytecodes(arena, verbose))?; + sh_println!("{}", render_trace_arena_inner(arena, verbose, state_changes))?; + } + + if shell::is_json() { + return Ok(()); } - sh_println!()?; + sh_println!()?; if result.success { sh_println!("{}", "Transaction successfully executed.".green())?; } else { sh_err!("Transaction failed.")?; } - sh_println!("Gas used: {}", result.gas_used)?; + Ok(()) } diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index 69c68442b65c..ceea6e67248a 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -18,15 +18,18 @@ impl TracingExecutor { version: Option, debug: bool, decode_internal: bool, + with_state_changes: bool, alphanet: bool, ) -> Self { let db = Backend::spawn(fork); - let trace_mode = - TraceMode::Call.with_debug(debug).with_decode_internal(if decode_internal { + let trace_mode = TraceMode::Call + .with_debug(debug) + .with_decode_internal(if decode_internal { InternalTraceMode::Full } else { InternalTraceMode::None - }); + }) + .with_state_changes(with_state_changes); Self { // configures a bare version of the evm executor: no cheatcode inspector is enabled, // tracing will be enabled only for the targeted transaction diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 53bf8b3bb2cb..f555d619fa22 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -36,6 +36,7 @@ eyre.workspace = true futures.workspace = true itertools.workspace = true serde.workspace = true +serde_json.workspace = true tokio = { workspace = true, features = ["time", "macros"] } tracing.workspace = true tempfile.workspace = true diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 18136c481e7c..a0fa7e1fca49 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -11,7 +11,10 @@ extern crate foundry_common; #[macro_use] extern crate tracing; -use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; +use foundry_common::{ + contracts::{ContractsByAddress, ContractsByArtifact}, + shell, +}; use revm::interpreter::OpCode; use revm_inspectors::tracing::{ types::{DecodedTraceStep, TraceMemberOrder}, @@ -183,15 +186,23 @@ pub async fn decode_trace_arena( /// Render a collection of call traces to a string. pub fn render_trace_arena(arena: &SparsedTraceArena) -> String { - render_trace_arena_with_bytecodes(arena, false) + render_trace_arena_inner(arena, false, false) } -/// Render a collection of call traces to a string optionally including contract creation bytecodes. -pub fn render_trace_arena_with_bytecodes( +/// Render a collection of call traces to a string optionally including contract creation bytecodes +/// and in JSON format. +pub fn render_trace_arena_inner( arena: &SparsedTraceArena, with_bytecodes: bool, + with_storage_changes: bool, ) -> String { - let mut w = TraceWriter::new(Vec::::new()).write_bytecodes(with_bytecodes); + if shell::is_json() { + return serde_json::to_string(&arena.resolve_arena()).expect("Failed to write traces"); + } + + let mut w = TraceWriter::new(Vec::::new()) + .write_bytecodes(with_bytecodes) + .with_storage_changes(with_storage_changes); w.write_arena(&arena.resolve_arena()).expect("Failed to write traces"); String::from_utf8(w.into_writer()).expect("trace writer wrote invalid UTF-8") } @@ -289,6 +300,8 @@ pub enum TraceMode { /// /// Used by debugger. Debug, + /// Debug trace with storage changes. + RecordStateDiff, } impl TraceMode { @@ -308,6 +321,10 @@ impl TraceMode { matches!(self, Self::Jump) } + pub const fn record_state_diff(self) -> bool { + matches!(self, Self::RecordStateDiff) + } + pub const fn is_debug(self) -> bool { matches!(self, Self::Debug) } @@ -324,6 +341,14 @@ impl TraceMode { std::cmp::max(self, mode.into()) } + pub fn with_state_changes(self, yes: bool) -> Self { + if yes { + std::cmp::max(self, Self::RecordStateDiff) + } else { + self + } + } + pub fn with_verbosity(self, verbosiy: u8) -> Self { if verbosiy >= 3 { std::cmp::max(self, Self::Call) @@ -345,7 +370,7 @@ impl TraceMode { StackSnapshotType::None }, record_logs: true, - record_state_diff: false, + record_state_diff: self.record_state_diff(), record_returndata_snapshots: self.is_debug(), record_opcodes_filter: (self.is_jump() || self.is_jump_simple()) .then(|| OpcodeFilter::new().enabled(OpCode::JUMP).enabled(OpCode::JUMPDEST)), diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 1a409b33a822..ad2e52df6725 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -12,7 +12,7 @@ use forge::{ debug::{ContractSources, DebugTraceIdentifier}, decode_trace_arena, folded_stack_trace, identifier::SignaturesIdentifier, - render_trace_arena, CallTraceDecoderBuilder, InternalTraceMode, TraceKind, + CallTraceDecoderBuilder, InternalTraceMode, TraceKind, }, MultiContractRunner, MultiContractRunnerBuilder, TestFilter, TestOptions, TestOptionsBuilder, }; @@ -56,7 +56,7 @@ use summary::TestSummaryReporter; use crate::cmd::test::summary::print_invariant_metrics; pub use filter::FilterArgs; -use forge::result::TestKind; +use forge::{result::TestKind, traces::render_trace_arena_inner}; // Loads project's figment and merges the build cli arguments into it foundry_config::merge_impl_figment_convert!(TestArgs, opts, evm_opts); @@ -652,7 +652,7 @@ impl TestArgs { // - 0..3: nothing // - 3: only display traces for failed tests // - 4: also display the setup trace for failed tests - // - 5..: display all traces for all tests + // - 5..: display all traces for all tests, including storage changes let should_include = match kind { TraceKind::Execution => { (verbosity == 3 && result.status.is_failure()) || verbosity >= 4 @@ -665,7 +665,7 @@ impl TestArgs { if should_include { decode_trace_arena(arena, &decoder).await?; - decoded_traces.push(render_trace_arena(arena)); + decoded_traces.push(render_trace_arena_inner(arena, false, verbosity > 4)); } } diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index a9f2a93eb37a..dfb498c060e6 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -7,7 +7,7 @@ use crate::{ use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes, U256}; use eyre::Result; -use foundry_common::{get_contract_name, ContractsByArtifact, TestFunctionExt}; +use foundry_common::{get_contract_name, shell::verbosity, ContractsByArtifact, TestFunctionExt}; use foundry_compilers::{ artifacts::Libraries, compilers::Compiler, Artifact, ArtifactId, ProjectCompileOutput, }; @@ -249,7 +249,8 @@ impl MultiContractRunner { let trace_mode = TraceMode::default() .with_debug(self.debug) .with_decode_internal(self.decode_internal) - .with_verbosity(self.evm_opts.verbosity); + .with_verbosity(self.evm_opts.verbosity) + .with_state_changes(verbosity() > 4); let executor = ExecutorBuilder::new() .inspectors(|stack| { diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index e0000e01bee0..35f5c2314c16 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -67,7 +67,7 @@ Display options: - 2 (-vv): Print logs for all tests. - 3 (-vvv): Print execution traces for failing tests. - 4 (-vvvv): Print execution traces for all tests, and setup traces for failing tests. - - 5 (-vvvvv): Print execution and setup traces for all tests. + - 5 (-vvvvv): Print execution and setup traces for all tests, including storage changes. Find more information in the book: http://book.getfoundry.sh/reference/forge/forge.html diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 8e064c63c9fd..819c3e9407e0 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -2665,3 +2665,37 @@ Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] "#]]); }); + +// Tests that test traces display state changes when running with verbosity. +forgetest_init!(should_show_state_changes, |prj, cmd| { + cmd.args(["test", "--mt", "test_Increment", "-vvvvv"]).assert_success().stdout_eq(str![[r#" +... +Ran 1 test for test/Counter.t.sol:CounterTest +[PASS] test_Increment() ([GAS]) +Traces: + [87464] CounterTest::setUp() + ├─ [47297] → new Counter@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ └─ ← [Return] 236 bytes of code + ├─ [2387] Counter::setNumber(0) + │ └─ ← [Stop] + └─ ← [Stop] + + [31293] CounterTest::test_Increment() + ├─ [22337] Counter::increment() + │ ├─ storage changes: + │ │ @ 0: 0 → 1 + │ └─ ← [Stop] + ├─ [281] Counter::number() [staticcall] + │ └─ ← [Return] 1 + ├─ [0] VM::assertEq(1, 1) [staticcall] + │ └─ ← [Return] + ├─ storage changes: + │ @ 0: 0 → 1 + └─ ← [Stop] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); +}); diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index a14d6af6df96..56ec035abcea 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -334,6 +334,7 @@ pub async fn get_tracing_executor( Some(fork_config.evm_version), false, false, + false, is_alphanet, ); From 16a013fafb519395dc1aca810dabc3fffb7d02a0 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 28 Nov 2024 08:35:24 +0200 Subject: [PATCH 59/68] feat(cast): decode external lib sigs from cached selectors (#9399) --- crates/cast/tests/cli/main.rs | 91 ++++++++++++++++++++++++++++ crates/cli/src/utils/cmd.rs | 9 +++ crates/evm/traces/src/decoder/mod.rs | 7 ++- 3 files changed, 105 insertions(+), 2 deletions(-) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index aa70fef92db6..d2c70a779e65 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1776,3 +1776,94 @@ Transaction successfully executed. "#]]); }); + +// tests cast can decode external libraries traces with project cached selectors +forgetest_async!(decode_external_libraries_with_cached_selectors, |prj, cmd| { + let (api, handle) = anvil::spawn(NodeConfig::test()).await; + + foundry_test_utils::util::initialize(prj.root()); + prj.add_source( + "ExternalLib", + r#" +import "./CounterInExternalLib.sol"; +library ExternalLib { + function updateCounterInExternalLib(CounterInExternalLib.Info storage counterInfo, uint256 counter) public { + counterInfo.counter = counter + 1; + } +} + "#, + ) + .unwrap(); + prj.add_source( + "CounterInExternalLib", + r#" +import "./ExternalLib.sol"; +contract CounterInExternalLib { + struct Info { + uint256 counter; + } + Info info; + constructor() { + ExternalLib.updateCounterInExternalLib(info, 100); + } +} + "#, + ) + .unwrap(); + prj.add_script( + "CounterInExternalLibScript", + r#" +import "forge-std/Script.sol"; +import {CounterInExternalLib} from "../src/CounterInExternalLib.sol"; +contract CounterInExternalLibScript is Script { + function run() public { + vm.startBroadcast(); + new CounterInExternalLib(); + vm.stopBroadcast(); + } +} + "#, + ) + .unwrap(); + + cmd.args([ + "script", + "--private-key", + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "--rpc-url", + &handle.http_endpoint(), + "--broadcast", + "CounterInExternalLibScript", + ]) + .assert_success(); + + let tx_hash = api + .transaction_by_block_number_and_index(BlockNumberOrTag::Latest, Index::from(0)) + .await + .unwrap() + .unwrap() + .tx_hash(); + + // Cache project selectors. + cmd.forge_fuse().set_current_dir(prj.root()); + cmd.forge_fuse().args(["selectors", "cache"]).assert_success(); + + // Assert cast with local artifacts can decode external lib signature. + cmd.cast_fuse().set_current_dir(prj.root()); + cmd.cast_fuse() + .args(["run", format!("{tx_hash}").as_str(), "--rpc-url", &handle.http_endpoint()]) + .assert_success() + .stdout_eq(str![[r#" +... +Traces: + [37739] → new @0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + ├─ [22411] 0xfAb06527117d29EA121998AC4fAB9Fc88bF5f979::updateCounterInExternalLib(0, 100) [delegatecall] + │ └─ ← [Stop] + └─ ← [Return] 62 bytes of code + + +Transaction successfully executed. +[GAS] + +"#]]); +}); diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 505634996de8..67aa65073ff8 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -501,6 +501,15 @@ pub fn cache_local_signatures(output: &ProjectCompileOutput, cache_path: PathBuf .events .insert(event.selector().to_string(), event.full_signature()); } + // External libraries doesn't have functions included in abi, but `methodIdentifiers`. + if let Some(method_identifiers) = &artifact.method_identifiers { + method_identifiers.iter().for_each(|(signature, selector)| { + cached_signatures + .functions + .entry(format!("0x{selector}")) + .or_insert(signature.to_string()); + }); + } } }); diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index b63c7f1d7209..68ecbac29c7e 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -689,10 +689,13 @@ fn reconstruct_params(event: &Event, decoded: &DecodedEvent) -> Vec let mut unindexed = 0; let mut inputs = vec![]; for input in event.inputs.iter() { - if input.indexed { + // Prevent panic of event `Transfer(from, to)` decoded with a signature + // `Transfer(address indexed from, address indexed to, uint256 indexed tokenId)` by making + // sure the event inputs is not higher than decoded indexed / un-indexed values. + if input.indexed && indexed < decoded.indexed.len() { inputs.push(decoded.indexed[indexed].clone()); indexed += 1; - } else { + } else if unindexed < decoded.body.len() { inputs.push(decoded.body[unindexed].clone()); unindexed += 1; } From 56d0dd8745248e9cd029472eb0a8697d12677246 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:59:27 +0100 Subject: [PATCH 60/68] feat: rewrite inline config using figment (#9414) * feat: rewrite inline config using figment * wip * wip * fix: use same GasLimit type * wip * fixes * tests * test fixes * fmt * test update --- Cargo.lock | 1 + crates/config/Cargo.toml | 5 +- crates/config/src/fuzz.rs | 108 +--------- crates/config/src/inline/conf_parser.rs | 169 --------------- crates/config/src/inline/error.rs | 44 ---- crates/config/src/inline/mod.rs | 185 +++++++++++++---- crates/config/src/inline/natspec.rs | 146 +++++++------ crates/config/src/invariant.rs | 93 +-------- crates/config/src/lib.rs | 194 ++++++++++++++---- crates/config/src/providers/mod.rs | 17 +- crates/config/src/providers/remappings.rs | 2 +- crates/config/src/utils.rs | 78 ------- crates/evm/core/src/opts.rs | 50 +---- crates/forge/bin/cmd/coverage.rs | 6 +- crates/forge/bin/cmd/test/mod.rs | 20 +- crates/forge/src/lib.rs | 239 +++++++--------------- crates/forge/src/runner.rs | 34 +-- crates/forge/tests/cli/alphanet.rs | 15 +- crates/forge/tests/cli/build.rs | 15 +- crates/forge/tests/cli/config.rs | 2 + crates/forge/tests/cli/inline_config.rs | 194 ++++++++++++++++++ crates/forge/tests/cli/main.rs | 1 + crates/forge/tests/it/cheats.rs | 18 +- crates/forge/tests/it/core.rs | 12 +- crates/forge/tests/it/fork.rs | 42 ++-- crates/forge/tests/it/fs.rs | 12 +- crates/forge/tests/it/fuzz.rs | 33 +-- crates/forge/tests/it/inline.rs | 36 +--- crates/forge/tests/it/invariant.rs | 131 +++++++----- crates/forge/tests/it/repros.rs | 38 ++-- crates/forge/tests/it/test_helpers.rs | 190 ++++++++--------- crates/test-utils/src/filter.rs | 1 + 32 files changed, 969 insertions(+), 1162 deletions(-) delete mode 100644 crates/config/src/inline/conf_parser.rs delete mode 100644 crates/config/src/inline/error.rs create mode 100644 crates/forge/tests/cli/inline_config.rs diff --git a/Cargo.lock b/Cargo.lock index d94d710ac07f..09fab74b7f0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3910,6 +3910,7 @@ dependencies = [ "foundry-compilers", "glob", "globset", + "itertools 0.13.0", "mesc", "number_prefix", "path-slash", diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 3e6815d784e6..ba01a1afbf4c 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -27,11 +27,12 @@ dirs-next = "2" dunce.workspace = true eyre.workspace = true figment = { workspace = true, features = ["toml", "env"] } -globset = "0.4" glob = "0.3" +globset = "0.4" Inflector = "0.11" -number_prefix = "0.4" +itertools.workspace = true mesc.workspace = true +number_prefix = "0.4" regex.workspace = true reqwest.workspace = true semver = { workspace = true, features = ["serde"] } diff --git a/crates/config/src/fuzz.rs b/crates/config/src/fuzz.rs index fb04383228a9..ae3d3c796701 100644 --- a/crates/config/src/fuzz.rs +++ b/crates/config/src/fuzz.rs @@ -1,9 +1,5 @@ //! Configuration for fuzz testing. -use crate::inline::{ - parse_config_bool, parse_config_u32, InlineConfigParser, InlineConfigParserError, - INLINE_CONFIG_FUZZ_KEY, -}; use alloy_primitives::U256; use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -53,50 +49,13 @@ impl FuzzConfig { /// Creates fuzz configuration to write failures in `{PROJECT_ROOT}/cache/fuzz` dir. pub fn new(cache_dir: PathBuf) -> Self { Self { - runs: 256, - max_test_rejects: 65536, - seed: None, - dictionary: FuzzDictionaryConfig::default(), - gas_report_samples: 256, failure_persist_dir: Some(cache_dir), failure_persist_file: Some("failures".to_string()), - show_logs: false, + ..Default::default() } } } -impl InlineConfigParser for FuzzConfig { - fn config_key() -> String { - INLINE_CONFIG_FUZZ_KEY.into() - } - - fn try_merge(&self, configs: &[String]) -> Result, InlineConfigParserError> { - let overrides: Vec<(String, String)> = Self::get_config_overrides(configs); - - if overrides.is_empty() { - return Ok(None) - } - - let mut conf_clone = self.clone(); - - for pair in overrides { - let key = pair.0; - let value = pair.1; - match key.as_str() { - "runs" => conf_clone.runs = parse_config_u32(key, value)?, - "max-test-rejects" => conf_clone.max_test_rejects = parse_config_u32(key, value)?, - "dictionary-weight" => { - conf_clone.dictionary.dictionary_weight = parse_config_u32(key, value)? - } - "failure-persist-file" => conf_clone.failure_persist_file = Some(value), - "show-logs" => conf_clone.show_logs = parse_config_bool(key, value)?, - _ => Err(InlineConfigParserError::InvalidConfigProperty(key))?, - } - } - Ok(Some(conf_clone)) - } -} - /// Contains for fuzz testing #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct FuzzDictionaryConfig { @@ -132,68 +91,3 @@ impl Default for FuzzDictionaryConfig { } } } - -#[cfg(test)] -mod tests { - use crate::{inline::InlineConfigParser, FuzzConfig}; - - #[test] - fn unrecognized_property() { - let configs = &["forge-config: default.fuzz.unknownprop = 200".to_string()]; - let base_config = FuzzConfig::default(); - if let Err(e) = base_config.try_merge(configs) { - assert_eq!(e.to_string(), "'unknownprop' is an invalid config property"); - } else { - unreachable!() - } - } - - #[test] - fn successful_merge() { - let configs = &[ - "forge-config: default.fuzz.runs = 42424242".to_string(), - "forge-config: default.fuzz.dictionary-weight = 42".to_string(), - "forge-config: default.fuzz.failure-persist-file = fuzz-failure".to_string(), - ]; - let base_config = FuzzConfig::default(); - let merged: FuzzConfig = base_config.try_merge(configs).expect("No errors").unwrap(); - assert_eq!(merged.runs, 42424242); - assert_eq!(merged.dictionary.dictionary_weight, 42); - assert_eq!(merged.failure_persist_file, Some("fuzz-failure".to_string())); - } - - #[test] - fn merge_is_none() { - let empty_config = &[]; - let base_config = FuzzConfig::default(); - let merged = base_config.try_merge(empty_config).expect("No errors"); - assert!(merged.is_none()); - } - - #[test] - fn merge_is_none_unrelated_property() { - let unrelated_configs = &["forge-config: default.invariant.runs = 2".to_string()]; - let base_config = FuzzConfig::default(); - let merged = base_config.try_merge(unrelated_configs).expect("No errors"); - assert!(merged.is_none()); - } - - #[test] - fn override_detection() { - let configs = &[ - "forge-config: default.fuzz.runs = 42424242".to_string(), - "forge-config: ci.fuzz.runs = 666666".to_string(), - "forge-config: default.invariant.runs = 2".to_string(), - "forge-config: default.fuzz.dictionary-weight = 42".to_string(), - ]; - let variables = FuzzConfig::get_config_overrides(configs); - assert_eq!( - variables, - vec![ - ("runs".into(), "42424242".into()), - ("runs".into(), "666666".into()), - ("dictionary-weight".into(), "42".into()) - ] - ); - } -} diff --git a/crates/config/src/inline/conf_parser.rs b/crates/config/src/inline/conf_parser.rs deleted file mode 100644 index e6944965499d..000000000000 --- a/crates/config/src/inline/conf_parser.rs +++ /dev/null @@ -1,169 +0,0 @@ -use super::{remove_whitespaces, InlineConfigParserError}; -use crate::{inline::INLINE_CONFIG_PREFIX, InlineConfigError, NatSpec}; -use regex::Regex; - -/// This trait is intended to parse configurations from -/// structured text. Foundry users can annotate Solidity test functions, -/// providing special configs just for the execution of a specific test. -/// -/// An example: -/// -/// ```solidity -/// contract MyTest is Test { -/// /// forge-config: default.fuzz.runs = 100 -/// /// forge-config: ci.fuzz.runs = 500 -/// function test_SimpleFuzzTest(uint256 x) public {...} -/// -/// /// forge-config: default.fuzz.runs = 500 -/// /// forge-config: ci.fuzz.runs = 10000 -/// function test_ImportantFuzzTest(uint256 x) public {...} -/// } -/// ``` -pub trait InlineConfigParser -where - Self: Clone + Default + Sized + 'static, -{ - /// Returns a config key that is common to all valid configuration lines - /// for the current impl. This helps to extract correct values out of a text. - /// - /// An example key would be `fuzz` of `invariant`. - fn config_key() -> String; - - /// Tries to override `self` properties with values specified in the `configs` parameter. - /// - /// Returns - /// - `Some(Self)` in case some configurations are merged into self. - /// - `None` in case there are no configurations that can be applied to self. - /// - `Err(InlineConfigParserError)` in case of wrong configuration. - fn try_merge(&self, configs: &[String]) -> Result, InlineConfigParserError>; - - /// Validates and merges the natspec configs for current profile into the current config. - fn merge(&self, natspec: &NatSpec) -> Result, InlineConfigError> { - let config_key = Self::config_key(); - - let configs = natspec - .current_profile_configs() - .filter(|l| l.contains(&config_key)) - .collect::>(); - - self.try_merge(&configs).map_err(|e| { - let line = natspec.debug_context(); - InlineConfigError { line, source: e } - }) - } - - /// Given a list of config lines, returns all available pairs (key, value) matching the current - /// config key. - /// - /// # Examples - /// - /// ```ignore - /// assert_eq!( - /// get_config_overrides(&[ - /// "forge-config: default.invariant.runs = 500", - /// "forge-config: default.invariant.depth = 500", - /// "forge-config: ci.invariant.depth = 500", - /// "forge-config: ci.fuzz.runs = 10", - /// ]), - /// [("runs", "500"), ("depth", "500"), ("depth", "500")] - /// ); - /// ``` - fn get_config_overrides(config_lines: &[String]) -> Vec<(String, String)> { - let mut result: Vec<(String, String)> = vec![]; - let config_key = Self::config_key(); - let profile = ".*"; - let prefix = format!("^{INLINE_CONFIG_PREFIX}:{profile}{config_key}\\."); - let re = Regex::new(&prefix).unwrap(); - - config_lines - .iter() - .map(|l| remove_whitespaces(l)) - .filter(|l| re.is_match(l)) - .map(|l| re.replace(&l, "").to_string()) - .for_each(|line| { - let key_value = line.split('=').collect::>(); // i.e. "['runs', '500']" - if let Some(key) = key_value.first() { - if let Some(value) = key_value.last() { - result.push((key.to_string(), value.to_string())); - } - } - }); - - result - } -} - -/// Checks if all configuration lines specified in `natspec` use a valid profile. -/// -/// i.e. Given available profiles -/// ```rust -/// let _profiles = vec!["ci", "default"]; -/// ``` -/// A configuration like `forge-config: ciii.invariant.depth = 1` would result -/// in an error. -pub fn validate_profiles(natspec: &NatSpec, profiles: &[String]) -> Result<(), InlineConfigError> { - for config in natspec.config_lines() { - if !profiles.iter().any(|p| config.starts_with(&format!("{INLINE_CONFIG_PREFIX}:{p}."))) { - let err_line: String = natspec.debug_context(); - let profiles = format!("{profiles:?}"); - Err(InlineConfigError { - source: InlineConfigParserError::InvalidProfile(config, profiles), - line: err_line, - })? - } - } - Ok(()) -} - -/// Tries to parse a `u32` from `value`. The `key` argument is used to give details -/// in the case of an error. -pub fn parse_config_u32(key: String, value: String) -> Result { - value.parse().map_err(|_| InlineConfigParserError::ParseInt(key, value)) -} - -/// Tries to parse a `bool` from `value`. The `key` argument is used to give details -/// in the case of an error. -pub fn parse_config_bool(key: String, value: String) -> Result { - value.parse().map_err(|_| InlineConfigParserError::ParseBool(key, value)) -} - -#[cfg(test)] -mod tests { - use crate::{inline::conf_parser::validate_profiles, NatSpec}; - - #[test] - fn can_reject_invalid_profiles() { - let profiles = ["ci".to_string(), "default".to_string()]; - let natspec = NatSpec { - contract: Default::default(), - function: Default::default(), - line: Default::default(), - docs: r" - forge-config: ciii.invariant.depth = 1 - forge-config: default.invariant.depth = 1 - " - .into(), - }; - - let result = validate_profiles(&natspec, &profiles); - assert!(result.is_err()); - } - - #[test] - fn can_accept_valid_profiles() { - let profiles = ["ci".to_string(), "default".to_string()]; - let natspec = NatSpec { - contract: Default::default(), - function: Default::default(), - line: Default::default(), - docs: r" - forge-config: ci.invariant.depth = 1 - forge-config: default.invariant.depth = 1 - " - .into(), - }; - - let result = validate_profiles(&natspec, &profiles); - assert!(result.is_ok()); - } -} diff --git a/crates/config/src/inline/error.rs b/crates/config/src/inline/error.rs deleted file mode 100644 index ddcb6a61bdb8..000000000000 --- a/crates/config/src/inline/error.rs +++ /dev/null @@ -1,44 +0,0 @@ -/// Errors returned by the [`InlineConfigParser`](crate::InlineConfigParser) trait. -#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] -pub enum InlineConfigParserError { - /// An invalid configuration property has been provided. - /// The property cannot be mapped to the configuration object - #[error("'{0}' is an invalid config property")] - InvalidConfigProperty(String), - /// An invalid profile has been provided - #[error("'{0}' specifies an invalid profile. Available profiles are: {1}")] - InvalidProfile(String, String), - /// An error occurred while trying to parse an integer configuration value - #[error("Invalid config value for key '{0}'. Unable to parse '{1}' into an integer value")] - ParseInt(String, String), - /// An error occurred while trying to parse a boolean configuration value - #[error("Invalid config value for key '{0}'. Unable to parse '{1}' into a boolean value")] - ParseBool(String, String), -} - -/// Wrapper error struct that catches config parsing errors, enriching them with context information -/// reporting the misconfigured line. -#[derive(Debug, thiserror::Error)] -#[error("Inline config error detected at {line}")] -pub struct InlineConfigError { - /// Specifies the misconfigured line. This is something of the form - /// `dir/TestContract.t.sol:FuzzContract:10:12:111` - pub line: String, - /// The inner error - pub source: InlineConfigParserError, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_format_inline_config_errors() { - let source = InlineConfigParserError::ParseBool("key".into(), "invalid-bool-value".into()); - let line = "dir/TestContract.t.sol:FuzzContract".to_string(); - let error = InlineConfigError { line: line.clone(), source }; - - let expected = format!("Inline config error detected at {line}"); - assert_eq!(error.to_string(), expected); - } -} diff --git a/crates/config/src/inline/mod.rs b/crates/config/src/inline/mod.rs index 8b5616a21afb..30b1c820ec9b 100644 --- a/crates/config/src/inline/mod.rs +++ b/crates/config/src/inline/mod.rs @@ -1,61 +1,168 @@ use crate::Config; use alloy_primitives::map::HashMap; -use std::sync::LazyLock; -mod conf_parser; -pub use conf_parser::*; - -mod error; -pub use error::*; +use figment::{ + value::{Dict, Map, Value}, + Figment, Profile, Provider, +}; +use itertools::Itertools; mod natspec; pub use natspec::*; -pub const INLINE_CONFIG_FUZZ_KEY: &str = "fuzz"; -pub const INLINE_CONFIG_INVARIANT_KEY: &str = "invariant"; -const INLINE_CONFIG_PREFIX: &str = "forge-config"; +const INLINE_CONFIG_PREFIX: &str = "forge-config:"; + +type DataMap = Map; -static INLINE_CONFIG_PREFIX_SELECTED_PROFILE: LazyLock = LazyLock::new(|| { - let selected_profile = Config::selected_profile().to_string(); - format!("{INLINE_CONFIG_PREFIX}:{selected_profile}.") -}); +/// Errors returned when parsing inline config. +#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] +pub enum InlineConfigErrorKind { + /// Failed to parse inline config as TOML. + #[error(transparent)] + Parse(#[from] toml::de::Error), + /// An invalid profile has been provided. + #[error("invalid profile `{0}`; valid profiles: {1}")] + InvalidProfile(String, String), +} + +/// Wrapper error struct that catches config parsing errors, enriching them with context information +/// reporting the misconfigured line. +#[derive(Debug, thiserror::Error)] +#[error("Inline config error at {location}: {kind}")] +pub struct InlineConfigError { + /// The span of the error in the format: + /// `dir/TestContract.t.sol:FuzzContract:10:12:111` + pub location: String, + /// The inner error + pub kind: InlineConfigErrorKind, +} /// Represents per-test configurations, declared inline /// as structured comments in Solidity test files. This allows /// to create configs directly bound to a solidity test. #[derive(Clone, Debug, Default)] -pub struct InlineConfig { - /// Contract-level configurations, used for functions that do not have a specific - /// configuration. - contract_level: HashMap, - /// Maps a (test-contract, test-function) pair - /// to a specific configuration provided by the user. - fn_level: HashMap<(String, String), T>, +pub struct InlineConfig { + /// Contract-level configuration. + contract_level: HashMap, + /// Function-level configuration. + fn_level: HashMap<(String, String), DataMap>, } -impl InlineConfig { - /// Returns an inline configuration, if any, for a test function. - /// Configuration is identified by the pair "contract", "function". - pub fn get(&self, contract_id: &str, fn_name: &str) -> Option<&T> { - let key = (contract_id.to_string(), fn_name.to_string()); - self.fn_level.get(&key).or_else(|| self.contract_level.get(contract_id)) +impl InlineConfig { + /// Creates a new, empty [`InlineConfig`]. + pub fn new() -> Self { + Self::default() + } + + /// Inserts a new [`NatSpec`] into the [`InlineConfig`]. + pub fn insert(&mut self, natspec: &NatSpec) -> Result<(), InlineConfigError> { + let map = if let Some(function) = &natspec.function { + self.fn_level.entry((natspec.contract.clone(), function.clone())).or_default() + } else { + self.contract_level.entry(natspec.contract.clone()).or_default() + }; + let joined = natspec + .config_values() + .map(|s| { + // Replace `-` with `_` for backwards compatibility with the old parser. + if let Some(idx) = s.find('=') { + s[..idx].replace('-', "_") + &s[idx..] + } else { + s.to_string() + } + }) + .format("\n") + .to_string(); + let data = toml::from_str::(&joined).map_err(|e| InlineConfigError { + location: natspec.location_string(), + kind: InlineConfigErrorKind::Parse(e), + })?; + extend_data_map(map, &data); + Ok(()) } - pub fn insert_contract(&mut self, contract_id: impl Into, config: T) { - self.contract_level.insert(contract_id.into(), config); + /// Returns a [`figment::Provider`] for this [`InlineConfig`] at the given contract and function + /// level. + pub fn provide<'a>(&'a self, contract: &'a str, function: &'a str) -> InlineConfigProvider<'a> { + InlineConfigProvider { inline: self, contract, function } } - /// Inserts an inline configuration, for a test function. - /// Configuration is identified by the pair "contract", "function". - pub fn insert_fn(&mut self, contract_id: C, fn_name: F, config: T) - where - C: Into, - F: Into, - { - let key = (contract_id.into(), fn_name.into()); - self.fn_level.insert(key, config); + /// Merges the inline configuration at the given contract and function level with the provided + /// base configuration. + pub fn merge(&self, contract: &str, function: &str, base: &Config) -> Figment { + Figment::from(base).merge(self.provide(contract, function)) + } + + /// Returns `true` if a configuration is present at the given contract and function level. + pub fn contains(&self, contract: &str, function: &str) -> bool { + // Order swapped to avoid allocation in `get_function` since order doesn't matter here. + self.get_contract(contract) + .filter(|map| !map.is_empty()) + .or_else(|| self.get_function(contract, function)) + .is_some_and(|map| !map.is_empty()) + } + + fn get_contract(&self, contract: &str) -> Option<&DataMap> { + self.contract_level.get(contract) + } + + fn get_function(&self, contract: &str, function: &str) -> Option<&DataMap> { + let key = (contract.to_string(), function.to_string()); + self.fn_level.get(&key) } } -pub(crate) fn remove_whitespaces(s: &str) -> String { - s.chars().filter(|c| !c.is_whitespace()).collect() +/// [`figment::Provider`] for [`InlineConfig`] at a given contract and function level. +/// +/// Created by [`InlineConfig::provide`]. +#[derive(Clone, Debug)] +pub struct InlineConfigProvider<'a> { + inline: &'a InlineConfig, + contract: &'a str, + function: &'a str, +} + +impl Provider for InlineConfigProvider<'_> { + fn metadata(&self) -> figment::Metadata { + figment::Metadata::named("inline config") + } + + fn data(&self) -> figment::Result { + let mut map = DataMap::new(); + if let Some(new) = self.inline.get_contract(self.contract) { + extend_data_map(&mut map, new); + } + if let Some(new) = self.inline.get_function(self.contract, self.function) { + extend_data_map(&mut map, new); + } + Ok(map) + } +} + +fn extend_data_map(map: &mut DataMap, new: &DataMap) { + for (profile, data) in new { + extend_dict(map.entry(profile.clone()).or_default(), data); + } +} + +fn extend_dict(dict: &mut Dict, new: &Dict) { + for (k, v) in new { + match dict.entry(k.clone()) { + std::collections::btree_map::Entry::Vacant(entry) => { + entry.insert(v.clone()); + } + std::collections::btree_map::Entry::Occupied(entry) => { + extend_value(entry.into_mut(), v); + } + } + } +} + +fn extend_value(value: &mut Value, new: &Value) { + match (value, new) { + (Value::Dict(tag, dict), Value::Dict(new_tag, new_dict)) => { + *tag = *new_tag; + extend_dict(dict, new_dict); + } + (value, new) => *value = new.clone(), + } } diff --git a/crates/config/src/inline/natspec.rs b/crates/config/src/inline/natspec.rs index 6dd6b696c015..5774d9e193f1 100644 --- a/crates/config/src/inline/natspec.rs +++ b/crates/config/src/inline/natspec.rs @@ -1,8 +1,10 @@ -use super::{remove_whitespaces, INLINE_CONFIG_PREFIX, INLINE_CONFIG_PREFIX_SELECTED_PROFILE}; +use super::{InlineConfigError, InlineConfigErrorKind, INLINE_CONFIG_PREFIX}; +use figment::Profile; use foundry_compilers::{ artifacts::{ast::NodeType, Node}, ProjectCompileOutput, }; +use itertools::Itertools; use serde_json::Value; use solang_parser::{helpers::CodeLocation, pt}; use std::{collections::BTreeMap, path::Path}; @@ -10,15 +12,13 @@ use std::{collections::BTreeMap, path::Path}; /// Convenient struct to hold in-line per-test configurations #[derive(Clone, Debug, PartialEq, Eq)] pub struct NatSpec { - /// The parent contract of the natspec + /// The parent contract of the natspec. pub contract: String, - /// The function annotated with the natspec. None if the natspec is contract-level + /// The function annotated with the natspec. None if the natspec is contract-level. pub function: Option, - /// The line the natspec appears, in the form - /// `row:col:length` i.e. `10:21:122` + /// The line the natspec appears, in the form `row:col:length`, i.e. `10:21:122`. pub line: String, - /// The actual natspec comment, without slashes or block - /// punctuation + /// The actual natspec comment, without slashes or block punctuation. pub docs: String, } @@ -56,29 +56,52 @@ impl NatSpec { natspecs } - /// Returns a string describing the natspec - /// context, for debugging purposes 🐞 - /// i.e. `test/Counter.t.sol:CounterTest:testFuzz_SetNumber` - pub fn debug_context(&self) -> String { - format!("{}:{}", self.contract, self.function.as_deref().unwrap_or_default()) + /// Checks if all configuration lines use a valid profile. + /// + /// i.e. Given available profiles + /// ```rust + /// let _profiles = vec!["ci", "default"]; + /// ``` + /// A configuration like `forge-config: ciii.invariant.depth = 1` would result + /// in an error. + pub fn validate_profiles(&self, profiles: &[Profile]) -> eyre::Result<()> { + for config in self.config_values() { + if !profiles.iter().any(|p| { + config + .strip_prefix(p.as_str().as_str()) + .is_some_and(|rest| rest.trim_start().starts_with('.')) + }) { + Err(InlineConfigError { + location: self.location_string(), + kind: InlineConfigErrorKind::InvalidProfile( + config.to_string(), + profiles.iter().format(", ").to_string(), + ), + })? + } + } + Ok(()) } - /// Returns a list of configuration lines that match the current profile - pub fn current_profile_configs(&self) -> impl Iterator + '_ { - self.config_lines_with_prefix(INLINE_CONFIG_PREFIX_SELECTED_PROFILE.as_str()) + /// Returns the path of the contract. + pub fn path(&self) -> &str { + match self.contract.split_once(':') { + Some((path, _)) => path, + None => self.contract.as_str(), + } } - /// Returns a list of configuration lines that match a specific string prefix - pub fn config_lines_with_prefix<'a>( - &'a self, - prefix: &'a str, - ) -> impl Iterator + 'a { - self.config_lines().filter(move |l| l.starts_with(prefix)) + /// Returns the location of the natspec as a string. + pub fn location_string(&self) -> String { + format!("{}:{}", self.path(), self.line) } - /// Returns a list of all the configuration lines available in the natspec - pub fn config_lines(&self) -> impl Iterator + '_ { - self.docs.lines().filter(|line| line.contains(INLINE_CONFIG_PREFIX)).map(remove_whitespaces) + /// Returns a list of all the configuration values available in the natspec. + pub fn config_values(&self) -> impl Iterator { + self.docs.lines().filter_map(|line| { + line.find(INLINE_CONFIG_PREFIX) + .map(|idx| line[idx + INLINE_CONFIG_PREFIX.len()..].trim()) + }) } } @@ -258,6 +281,42 @@ mod tests { use super::*; use serde_json::json; + #[test] + fn can_reject_invalid_profiles() { + let profiles = ["ci".into(), "default".into()]; + let natspec = NatSpec { + contract: Default::default(), + function: Default::default(), + line: Default::default(), + docs: r" + forge-config: ciii.invariant.depth = 1 + forge-config: default.invariant.depth = 1 + " + .into(), + }; + + let result = natspec.validate_profiles(&profiles); + assert!(result.is_err()); + } + + #[test] + fn can_accept_valid_profiles() { + let profiles = ["ci".into(), "default".into()]; + let natspec = NatSpec { + contract: Default::default(), + function: Default::default(), + line: Default::default(), + docs: r" + forge-config: ci.invariant.depth = 1 + forge-config: default.invariant.depth = 1 + " + .into(), + }; + + let result = natspec.validate_profiles(&profiles); + assert!(result.is_ok()); + } + #[test] fn parse_solang() { let src = " @@ -355,42 +414,13 @@ contract FuzzInlineConf is DSTest { #[test] fn config_lines() { let natspec = natspec(); - let config_lines = natspec.config_lines(); - assert_eq!( - config_lines.collect::>(), - vec![ - "forge-config:default.fuzz.runs=600".to_string(), - "forge-config:ci.fuzz.runs=500".to_string(), - "forge-config:default.invariant.runs=1".to_string() - ] - ) - } - - #[test] - fn current_profile_configs() { - let natspec = natspec(); - let config_lines = natspec.current_profile_configs(); - - assert_eq!( - config_lines.collect::>(), - vec![ - "forge-config:default.fuzz.runs=600".to_string(), - "forge-config:default.invariant.runs=1".to_string() - ] - ); - } - - #[test] - fn config_lines_with_prefix() { - use super::INLINE_CONFIG_PREFIX; - let natspec = natspec(); - let prefix = format!("{INLINE_CONFIG_PREFIX}:default"); - let config_lines = natspec.config_lines_with_prefix(&prefix); + let config_lines = natspec.config_values(); assert_eq!( config_lines.collect::>(), - vec![ - "forge-config:default.fuzz.runs=600".to_string(), - "forge-config:default.invariant.runs=1".to_string() + [ + "default.fuzz.runs = 600".to_string(), + "ci.fuzz.runs = 500".to_string(), + "default.invariant.runs = 1".to_string() ] ) } diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index 70e9a2b85847..97f189b363d1 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -1,12 +1,6 @@ //! Configuration for invariant testing -use crate::{ - fuzz::FuzzDictionaryConfig, - inline::{ - parse_config_bool, parse_config_u32, InlineConfigParser, InlineConfigParserError, - INLINE_CONFIG_INVARIANT_KEY, - }, -}; +use crate::fuzz::FuzzDictionaryConfig; use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -80,88 +74,3 @@ impl InvariantConfig { .join(contract_name.split(':').last().unwrap()) } } - -impl InlineConfigParser for InvariantConfig { - fn config_key() -> String { - INLINE_CONFIG_INVARIANT_KEY.into() - } - - fn try_merge(&self, configs: &[String]) -> Result, InlineConfigParserError> { - let overrides: Vec<(String, String)> = Self::get_config_overrides(configs); - - if overrides.is_empty() { - return Ok(None) - } - - let mut conf_clone = self.clone(); - - for pair in overrides { - let key = pair.0; - let value = pair.1; - match key.as_str() { - "runs" => conf_clone.runs = parse_config_u32(key, value)?, - "depth" => conf_clone.depth = parse_config_u32(key, value)?, - "fail-on-revert" => conf_clone.fail_on_revert = parse_config_bool(key, value)?, - "call-override" => conf_clone.call_override = parse_config_bool(key, value)?, - "failure-persist-dir" => { - conf_clone.failure_persist_dir = Some(PathBuf::from(value)) - } - "shrink-run-limit" => conf_clone.shrink_run_limit = parse_config_u32(key, value)?, - "show-metrics" => conf_clone.show_metrics = parse_config_bool(key, value)?, - _ => Err(InlineConfigParserError::InvalidConfigProperty(key.to_string()))?, - } - } - Ok(Some(conf_clone)) - } -} - -#[cfg(test)] -mod tests { - use crate::{inline::InlineConfigParser, InvariantConfig}; - - #[test] - fn unrecognized_property() { - let configs = &["forge-config: default.invariant.unknownprop = 200".to_string()]; - let base_config = InvariantConfig::default(); - if let Err(e) = base_config.try_merge(configs) { - assert_eq!(e.to_string(), "'unknownprop' is an invalid config property"); - } else { - unreachable!() - } - } - - #[test] - fn successful_merge() { - let configs = &["forge-config: default.invariant.runs = 42424242".to_string()]; - let base_config = InvariantConfig::default(); - let merged: InvariantConfig = base_config.try_merge(configs).expect("No errors").unwrap(); - assert_eq!(merged.runs, 42424242); - } - - #[test] - fn merge_is_none() { - let empty_config = &[]; - let base_config = InvariantConfig::default(); - let merged = base_config.try_merge(empty_config).expect("No errors"); - assert!(merged.is_none()); - } - - #[test] - fn can_merge_unrelated_properties_into_config() { - let unrelated_configs = &["forge-config: default.fuzz.runs = 2".to_string()]; - let base_config = InvariantConfig::default(); - let merged = base_config.try_merge(unrelated_configs).expect("No errors"); - assert!(merged.is_none()); - } - - #[test] - fn override_detection() { - let configs = &[ - "forge-config: default.fuzz.runs = 42424242".to_string(), - "forge-config: ci.fuzz.runs = 666666".to_string(), - "forge-config: default.invariant.runs = 2".to_string(), - ]; - let variables = InvariantConfig::get_config_overrides(configs); - assert_eq!(variables, vec![("runs".into(), "2".into())]); - } -} diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 5a159c3925b8..72be43ab18a9 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -108,7 +108,7 @@ mod invariant; pub use invariant::InvariantConfig; mod inline; -pub use inline::{validate_profiles, InlineConfig, InlineConfigError, InlineConfigParser, NatSpec}; +pub use inline::{InlineConfig, InlineConfigError, NatSpec}; pub mod soldeer; use soldeer::{SoldeerConfig, SoldeerDependencyConfig}; @@ -163,6 +163,11 @@ pub struct Config { /// set to the extracting Figment's selected `Profile`. #[serde(skip)] pub profile: Profile, + /// The list of all profiles defined in the config. + /// + /// See `profile`. + #[serde(skip)] + pub profiles: Vec, /// path of the source contracts dir, like `src` or `contracts` pub src: PathBuf, /// path of the test dir @@ -481,7 +486,7 @@ pub struct Config { /// Use EOF-enabled solc for compilation. pub eof: bool, - /// Warnings gathered when loading the Config. See [`WarningsProvider`] for more information + /// Warnings gathered when loading the Config. See [`WarningsProvider`] for more information. #[serde(rename = "__warnings", default, skip_serializing)] pub warnings: Vec, @@ -516,7 +521,7 @@ pub const DEPRECATIONS: &[(&str, &str)] = &[("cancun", "evm_version = Cancun")]; impl Config { /// The default profile: "default" - pub const DEFAULT_PROFILE: Profile = Profile::const_new("default"); + pub const DEFAULT_PROFILE: Profile = Profile::Default; /// The hardhat profile: "hardhat" pub const HARDHAT_PROFILE: Profile = Profile::const_new("hardhat"); @@ -569,7 +574,7 @@ impl Config { /// See [`figment`](Self::figment) for more details. #[track_caller] pub fn load_with_providers(providers: FigmentProviders) -> Self { - Self::default().to_figment(providers).extract().unwrap() + Self::from_provider(Self::default().to_figment(providers)) } /// Returns the current `Config` @@ -620,19 +625,47 @@ impl Config { /// let config = Config::try_from(figment); /// ``` pub fn try_from(provider: T) -> Result { - let figment = Figment::from(provider); + Self::try_from_figment(Figment::from(provider)) + } + + fn try_from_figment(figment: Figment) -> Result { let mut config = figment.extract::().map_err(ExtractConfigError::new)?; config.profile = figment.profile().clone(); + + // The `"profile"` profile contains all the profiles as keys. + let mut add_profile = |profile: &Profile| { + if !config.profiles.contains(profile) { + config.profiles.push(profile.clone()); + } + }; + let figment = figment.select(Self::PROFILE_SECTION); + if let Ok(data) = figment.data() { + if let Some(profiles) = data.get(&Profile::new(Self::PROFILE_SECTION)) { + for profile in profiles.keys() { + add_profile(&Profile::new(profile)); + } + } + } + add_profile(&Self::DEFAULT_PROFILE); + add_profile(&config.profile); + Ok(config) } /// Returns the populated [Figment] using the requested [FigmentProviders] preset. /// - /// This will merge various providers, such as env,toml,remappings into the figment. - pub fn to_figment(self, providers: FigmentProviders) -> Figment { - let mut c = self; + /// This will merge various providers, such as env,toml,remappings into the figment if + /// requested. + pub fn to_figment(&self, providers: FigmentProviders) -> Figment { + // Note that `Figment::from` here is a method on `Figment` rather than the `From` impl below + + if providers.is_none() { + return Figment::from(self); + } + + let root = self.root.0.as_path(); let profile = Self::selected_profile(); - let mut figment = Figment::default().merge(DappHardhatDirProvider(&c.root.0)); + let mut figment = Figment::default().merge(DappHardhatDirProvider(root)); // merge global foundry.toml file if let Some(global_toml) = Self::foundry_dir_toml().filter(|p| p.exists()) { @@ -645,7 +678,7 @@ impl Config { // merge local foundry.toml file figment = Self::merge_toml_provider( figment, - TomlFileProvider::new(Some("FOUNDRY_CONFIG"), c.root.0.join(Self::FILE_NAME)).cached(), + TomlFileProvider::new(Some("FOUNDRY_CONFIG"), root.join(Self::FILE_NAME)).cached(), profile.clone(), ); @@ -692,17 +725,17 @@ impl Config { lib_paths: figment .extract_inner::>("libs") .map(Cow::Owned) - .unwrap_or_else(|_| Cow::Borrowed(&c.libs)), - root: &c.root.0, + .unwrap_or_else(|_| Cow::Borrowed(&self.libs)), + root, remappings: figment.extract_inner::>("remappings"), }; figment = figment.merge(remappings); } // normalize defaults - figment = c.normalize_defaults(figment); + figment = self.normalize_defaults(figment); - Figment::from(c).merge(figment).select(profile) + Figment::from(self).merge(figment).select(profile) } /// The config supports relative paths and tracks the root path separately see @@ -1722,6 +1755,19 @@ impl Config { /// /// If the `FOUNDRY_PROFILE` env variable is not set, this returns the `DEFAULT_PROFILE`. pub fn selected_profile() -> Profile { + // Can't cache in tests because the env var can change. + #[cfg(test)] + { + Self::force_selected_profile() + } + #[cfg(not(test))] + { + static CACHE: std::sync::OnceLock = std::sync::OnceLock::new(); + CACHE.get_or_init(Self::force_selected_profile).clone() + } + } + + fn force_selected_profile() -> Profile { Profile::from_env_or("FOUNDRY_PROFILE", Self::DEFAULT_PROFILE) } @@ -2017,19 +2063,20 @@ impl Config { /// This normalizes the default `evm_version` if a `solc` was provided in the config. /// /// See also - fn normalize_defaults(&mut self, figment: Figment) -> Figment { + fn normalize_defaults(&self, mut figment: Figment) -> Figment { + // TODO: add a warning if evm_version is provided but incompatible + if figment.contains("evm_version") { + return figment; + } + + // Normalize `evm_version` based on the provided solc version. if let Ok(solc) = figment.extract_inner::("solc") { - // check if evm_version is set - // TODO: add a warning if evm_version is provided but incompatible - if figment.find_value("evm_version").is_err() { - if let Some(version) = solc - .try_version() - .ok() - .and_then(|version| self.evm_version.normalize_version_solc(&version)) - { - // normalize evm_version based on the provided solc version - self.evm_version = version; - } + if let Some(version) = solc + .try_version() + .ok() + .and_then(|version| self.evm_version.normalize_version_solc(&version)) + { + figment = figment.merge(("evm_version", version)); } } @@ -2039,36 +2086,53 @@ impl Config { impl From for Figment { fn from(c: Config) -> Self { + (&c).into() + } +} +impl From<&Config> for Figment { + fn from(c: &Config) -> Self { c.to_figment(FigmentProviders::All) } } -/// Determines what providers should be used when loading the [Figment] for a [Config] +/// Determines what providers should be used when loading the [`Figment`] for a [`Config`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum FigmentProviders { - /// Include all providers + /// Include all providers. #[default] All, - /// Only include necessary providers that are useful for cast commands + /// Only include necessary providers that are useful for cast commands. /// - /// This will exclude more expensive providers such as remappings + /// This will exclude more expensive providers such as remappings. Cast, - /// Only include necessary providers that are useful for anvil + /// Only include necessary providers that are useful for anvil. /// - /// This will exclude more expensive providers such as remappings + /// This will exclude more expensive providers such as remappings. Anvil, + /// Don't include any providers. + None, } impl FigmentProviders { - /// Returns true if all providers should be included + /// Returns true if all providers should be included. pub const fn is_all(&self) -> bool { matches!(self, Self::All) } - /// Returns true if this is the cast preset + /// Returns true if this is the cast preset. pub const fn is_cast(&self) -> bool { matches!(self, Self::Cast) } + + /// Returns true if this is the anvil preset. + pub const fn is_anvil(&self) -> bool { + matches!(self, Self::Anvil) + } + + /// Returns true if no providers should be included. + pub const fn is_none(&self) -> bool { + matches!(self, Self::None) + } } /// Wrapper type for `regex::Regex` that implements `PartialEq` @@ -2154,6 +2218,20 @@ impl AsRef for RootPath { } } +impl std::ops::Deref for RootPath { + type Target = PathBuf; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for RootPath { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + /// Parses a config profile /// /// All `Profile` date is ignored by serde, however the `Config::to_string_pretty` includes it and @@ -2202,11 +2280,9 @@ impl Default for Config { fn default() -> Self { Self { profile: Self::DEFAULT_PROFILE, + profiles: vec![Self::DEFAULT_PROFILE], fs_permissions: FsPermissions::new([PathPermission::read("out")]), - #[cfg(not(feature = "isolate-by-default"))] - isolate: false, - #[cfg(feature = "isolate-by-default")] - isolate: true, + isolate: cfg!(feature = "isolate-by-default"), root: Default::default(), src: "src".into(), test: "test".into(), @@ -2322,11 +2398,12 @@ impl Default for Config { } } -/// Wrapper for the config's `gas_limit` value necessary because toml-rs can't handle larger number because integers are stored signed: +/// Wrapper for the config's `gas_limit` value necessary because toml-rs can't handle larger number +/// because integers are stored signed: /// /// Due to this limitation this type will be serialized/deserialized as String if it's larger than /// `i64` -#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize)] pub struct GasLimit(#[serde(deserialize_with = "crate::deserialize_u64_or_max")] pub u64); impl From for GasLimit { @@ -3052,9 +3129,39 @@ mod tests { #[test] fn test_figment_is_default() { figment::Jail::expect_with(|_| { - let mut default: Config = Config::figment().extract().unwrap(); - default.profile = Config::default().profile; - assert_eq!(default, Config::default()); + let mut default: Config = Config::figment().extract()?; + let default2 = Config::default(); + default.profile = default2.profile.clone(); + default.profiles = default2.profiles.clone(); + assert_eq!(default, default2); + Ok(()) + }); + } + + #[test] + fn figment_profiles() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r" + [foo.baz] + libs = ['node_modules', 'lib'] + + [profile.default] + libs = ['node_modules', 'lib'] + + [profile.ci] + libs = ['node_modules', 'lib'] + + [profile.local] + libs = ['node_modules', 'lib'] + ", + )?; + + let config = crate::Config::load(); + let expected: &[figment::Profile] = &["ci".into(), "default".into(), "local".into()]; + assert_eq!(config.profiles, expected); + Ok(()) }); } @@ -3163,7 +3270,6 @@ mod tests { jail.set_env("FOUNDRY_PROFILE", "custom"); let config = Config::load(); - assert_eq!(config.src, PathBuf::from("customsrc")); assert_eq!(config.test, PathBuf::from("defaulttest")); assert_eq!(config.libs, vec![PathBuf::from("lib"), PathBuf::from("node_modules")]); diff --git a/crates/config/src/providers/mod.rs b/crates/config/src/providers/mod.rs index 1f9f5c88ea89..9bd8a014f969 100644 --- a/crates/config/src/providers/mod.rs +++ b/crates/config/src/providers/mod.rs @@ -17,7 +17,7 @@ pub struct WarningsProvider

{ old_warnings: Result, Error>, } -impl

WarningsProvider

{ +impl WarningsProvider

{ const WARNINGS_KEY: &'static str = "__warnings"; /// Creates a new warnings provider. @@ -41,9 +41,7 @@ impl

WarningsProvider

{ }; Self::new(provider, figment.profile().clone(), old_warnings) } -} -impl WarningsProvider

{ /// Collects all warnings. pub fn collect_warnings(&self) -> Result, Error> { let data = self.provider.data().unwrap_or_default(); @@ -103,12 +101,10 @@ impl Provider for WarningsProvider

{ } fn data(&self) -> Result, Error> { + let warnings = self.collect_warnings()?; Ok(Map::from([( self.profile.clone(), - Dict::from([( - Self::WARNINGS_KEY.to_string(), - Value::serialize(self.collect_warnings()?)?, - )]), + Dict::from([(Self::WARNINGS_KEY.to_string(), Value::serialize(warnings)?)]), )])) } @@ -138,8 +134,9 @@ impl Provider for FallbackProfileProvider

{ } fn data(&self) -> Result, Error> { - if let Some(fallback) = self.provider.data()?.get(&self.fallback) { - let mut inner = self.provider.data()?.remove(&self.profile).unwrap_or_default(); + let data = self.provider.data()?; + if let Some(fallback) = data.get(&self.fallback) { + let mut inner = data.get(&self.profile).cloned().unwrap_or_default(); for (k, v) in fallback.iter() { if !inner.contains_key(k) { inner.insert(k.to_owned(), v.clone()); @@ -147,7 +144,7 @@ impl Provider for FallbackProfileProvider

{ } Ok(self.profile.collect(inner)) } else { - self.provider.data() + Ok(data) } } diff --git a/crates/config/src/providers/remappings.rs b/crates/config/src/providers/remappings.rs index 623234f94798..343fde697859 100644 --- a/crates/config/src/providers/remappings.rs +++ b/crates/config/src/providers/remappings.rs @@ -112,7 +112,7 @@ pub struct RemappingsProvider<'a> { pub lib_paths: Cow<'a, Vec>, /// the root path used to turn an absolute `Remapping`, as we're getting it from /// `Remapping::find_many` into a relative one. - pub root: &'a PathBuf, + pub root: &'a Path, /// This contains either: /// - previously set remappings /// - a `MissingField` error, which means previous provider didn't set the "remappings" field diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index 2117834f428a..e07d7dfbcb09 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -14,7 +14,6 @@ use std::{ path::{Path, PathBuf}, str::FromStr, }; -use toml_edit::{DocumentMut, Item}; /// Loads the config for the current project workspace pub fn load_config() -> Config { @@ -186,45 +185,6 @@ pub(crate) fn get_dir_remapping(dir: impl AsRef) -> Option { } } -/// Returns all available `profile` keys in a given `.toml` file -/// -/// i.e. The toml below would return would return `["default", "ci", "local"]` -/// ```toml -/// [profile.default] -/// ... -/// [profile.ci] -/// ... -/// [profile.local] -/// ``` -pub fn get_available_profiles(toml_path: impl AsRef) -> eyre::Result> { - let mut result = vec![Config::DEFAULT_PROFILE.to_string()]; - - if !toml_path.as_ref().exists() { - return Ok(result) - } - - let doc = read_toml(toml_path)?; - - if let Some(Item::Table(profiles)) = doc.as_table().get(Config::PROFILE_SECTION) { - for (profile, _) in profiles { - let p = profile.to_string(); - if !result.contains(&p) { - result.push(p); - } - } - } - - Ok(result) -} - -/// Returns a [`toml_edit::Document`] loaded from the provided `path`. -/// Can raise an error in case of I/O or parsing errors. -fn read_toml(path: impl AsRef) -> eyre::Result { - let path = path.as_ref().to_owned(); - let doc: DocumentMut = std::fs::read_to_string(path)?.parse()?; - Ok(doc) -} - /// Deserialize stringified percent. The value must be between 0 and 100 inclusive. pub(crate) fn deserialize_stringified_percent<'de, D>(deserializer: D) -> Result where @@ -319,41 +279,3 @@ pub fn evm_spec_id(evm_version: &EvmVersion, alphanet: bool) -> SpecId { EvmVersion::Prague => SpecId::OSAKA, // Osaka enables EOF } } - -#[cfg(test)] -mod tests { - use crate::get_available_profiles; - use std::path::Path; - - #[test] - fn get_profiles_from_toml() { - figment::Jail::expect_with(|jail| { - jail.create_file( - "foundry.toml", - r" - [foo.baz] - libs = ['node_modules', 'lib'] - - [profile.default] - libs = ['node_modules', 'lib'] - - [profile.ci] - libs = ['node_modules', 'lib'] - - [profile.local] - libs = ['node_modules', 'lib'] - ", - )?; - - let path = Path::new("./foundry.toml"); - let profiles = get_available_profiles(path).unwrap(); - - assert_eq!( - profiles, - vec!["default".to_string(), "ci".to_string(), "local".to_string()] - ); - - Ok(()) - }); - } -} diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 9849fd1cef9c..6f4448ae482c 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -4,9 +4,9 @@ use alloy_primitives::{Address, B256, U256}; use alloy_provider::{network::AnyRpcBlock, Provider}; use eyre::WrapErr; use foundry_common::{provider::ProviderBuilder, ALCHEMY_FREE_TIER_CUPS}; -use foundry_config::{Chain, Config}; +use foundry_config::{Chain, Config, GasLimit}; use revm::primitives::{BlockEnv, CfgEnv, TxEnv}; -use serde::{Deserialize, Deserializer, Serialize}; +use serde::{Deserialize, Serialize}; use url::Url; #[derive(Clone, Debug, Default, Serialize, Deserialize)] @@ -166,7 +166,7 @@ impl EvmOpts { /// Returns the gas limit to use pub fn gas_limit(&self) -> u64 { - self.env.block_gas_limit.unwrap_or(self.env.gas_limit) + self.env.block_gas_limit.unwrap_or(self.env.gas_limit).0 } /// Returns the configured chain id, which will be @@ -225,8 +225,7 @@ impl EvmOpts { #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct Env { /// The block gas limit. - #[serde(deserialize_with = "string_or_number")] - pub gas_limit: u64, + pub gas_limit: GasLimit, /// The `CHAINID` opcode value. pub chain_id: Option, @@ -260,47 +259,10 @@ pub struct Env { pub block_prevrandao: B256, /// the block.gaslimit value during EVM execution - #[serde( - default, - skip_serializing_if = "Option::is_none", - deserialize_with = "string_or_number_opt" - )] - pub block_gas_limit: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub block_gas_limit: Option, /// EIP-170: Contract code size limit in bytes. Useful to increase this because of tests. #[serde(default, skip_serializing_if = "Option::is_none")] pub code_size_limit: Option, } - -#[derive(Deserialize)] -#[serde(untagged)] -enum Gas { - Number(u64), - Text(String), -} - -fn string_or_number<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - use serde::de::Error; - match Gas::deserialize(deserializer)? { - Gas::Number(num) => Ok(num), - Gas::Text(s) => s.parse().map_err(D::Error::custom), - } -} - -fn string_or_number_opt<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - use serde::de::Error; - - match Option::::deserialize(deserializer)? { - Some(gas) => match gas { - Gas::Number(num) => Ok(Some(num)), - Gas::Text(s) => s.parse().map(Some).map_err(D::Error::custom), - }, - _ => Ok(None), - } -} diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 7e60a5451efc..4ca111a56740 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -230,11 +230,7 @@ impl CoverageArgs { .evm_spec(config.evm_spec_id()) .sender(evm_opts.sender) .with_fork(evm_opts.get_fork(&config, env.clone())) - .with_test_options(TestOptions { - fuzz: config.fuzz.clone(), - invariant: config.invariant.clone(), - ..Default::default() - }) + .with_test_options(TestOptions::new(output, config.clone())?) .set_coverage(true) .build(&root, output, env, evm_opts)?; diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index ad2e52df6725..6e19c96d08d7 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -14,7 +14,7 @@ use forge::{ identifier::SignaturesIdentifier, CallTraceDecoderBuilder, InternalTraceMode, TraceKind, }, - MultiContractRunner, MultiContractRunnerBuilder, TestFilter, TestOptions, TestOptionsBuilder, + MultiContractRunner, MultiContractRunnerBuilder, TestFilter, TestOptions, }; use foundry_cli::{ opts::{CoreBuildArgs, GlobalOpts}, @@ -34,7 +34,7 @@ use foundry_config::{ Metadata, Profile, Provider, }, filter::GlobMatcher, - get_available_profiles, Config, + Config, }; use foundry_debugger::Debugger; use foundry_evm::traces::identifier::TraceIdentifiers; @@ -301,25 +301,20 @@ impl TestArgs { // Create test options from general project settings and compiler output. let project_root = &project.paths.root; - let toml = config.get_config_path(); - let profiles = get_available_profiles(toml)?; // Remove the snapshots directory if it exists. // This is to ensure that we don't have any stale snapshots. // If `FORGE_SNAPSHOT_CHECK` is set, we don't remove the snapshots directory as it is // required for comparison. - if std::env::var("FORGE_SNAPSHOT_CHECK").is_err() { + if std::env::var_os("FORGE_SNAPSHOT_CHECK").is_none() { let snapshot_dir = project_root.join(&config.snapshots); if snapshot_dir.exists() { let _ = fs::remove_dir_all(project_root.join(&config.snapshots)); } } - let test_options: TestOptions = TestOptionsBuilder::default() - .fuzz(config.fuzz.clone()) - .invariant(config.invariant.clone()) - .profiles(profiles) - .build(&output, project_root)?; + let config = Arc::new(config); + let test_options = TestOptions::new(&output, config.clone())?; let should_debug = self.debug.is_some(); let should_draw = self.flamegraph || self.flamechart; @@ -347,7 +342,6 @@ impl TestArgs { }; // Prepare the test builder. - let config = Arc::new(config); let runner = MultiContractRunnerBuilder::new(config.clone()) .set_debug(should_debug) .set_decode_internal(decode_internal) @@ -1067,9 +1061,9 @@ contract FooBarTest is DSTest { &prj.root().to_string_lossy(), ]); let outcome = args.run().await.unwrap(); - let gas_report = outcome.gas_report.unwrap(); + let gas_report = outcome.gas_report.as_ref().unwrap(); - assert_eq!(gas_report.contracts.len(), 3); + assert_eq!(gas_report.contracts.len(), 3, "{}", outcome.summary(Default::default())); let call_cnts = gas_report .contracts .values() diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 0bec55153099..257760c4e94b 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -7,15 +7,16 @@ extern crate foundry_common; #[macro_use] extern crate tracing; +use alloy_primitives::U256; +use eyre::Result; use foundry_compilers::ProjectCompileOutput; use foundry_config::{ - validate_profiles, Config, FuzzConfig, InlineConfig, InlineConfigError, InlineConfigParser, - InvariantConfig, NatSpec, + figment::Figment, Config, FuzzConfig, InlineConfig, InvariantConfig, NatSpec, }; use proptest::test_runner::{ FailurePersistence, FileFailurePersistence, RngAlgorithm, TestRng, TestRunner, }; -use std::path::Path; +use std::sync::Arc; pub mod coverage; @@ -34,200 +35,108 @@ pub mod result; pub use foundry_common::traits::TestFilter; pub use foundry_evm::*; -/// Metadata on how to run fuzz/invariant tests +/// Test configuration. #[derive(Clone, Debug, Default)] pub struct TestOptions { - /// The base "fuzz" test configuration. To be used as a fallback in case - /// no more specific configs are found for a given run. - pub fuzz: FuzzConfig, - /// The base "invariant" test configuration. To be used as a fallback in case - /// no more specific configs are found for a given run. - pub invariant: InvariantConfig, - /// Contains per-test specific "fuzz" configurations. - pub inline_fuzz: InlineConfig, - /// Contains per-test specific "invariant" configurations. - pub inline_invariant: InlineConfig, + /// The base configuration. + pub config: Arc, + /// Per-test configuration. Merged onto `base_config`. + pub inline: InlineConfig, } impl TestOptions { /// Tries to create a new instance by detecting inline configurations from the project compile /// output. - pub fn new( - output: &ProjectCompileOutput, - root: &Path, - profiles: Vec, - base_fuzz: FuzzConfig, - base_invariant: InvariantConfig, - ) -> Result { - let natspecs: Vec = NatSpec::parse(output, root); - let mut inline_invariant = InlineConfig::::default(); - let mut inline_fuzz = InlineConfig::::default(); - - // Validate all natspecs + pub fn new(output: &ProjectCompileOutput, base_config: Arc) -> eyre::Result { + let natspecs: Vec = NatSpec::parse(output, &base_config.root); + let profiles = &base_config.profiles; + let mut inline = InlineConfig::new(); for natspec in &natspecs { - validate_profiles(natspec, &profiles)?; + inline.insert(natspec)?; + // Validate after parsing as TOML. + natspec.validate_profiles(profiles)?; } + Ok(Self { config: base_config, inline }) + } - // Firstly, apply contract-level configurations - for natspec in natspecs.iter().filter(|n| n.function.is_none()) { - if let Some(fuzz) = base_fuzz.merge(natspec)? { - inline_fuzz.insert_contract(&natspec.contract, fuzz); - } - - if let Some(invariant) = base_invariant.merge(natspec)? { - inline_invariant.insert_contract(&natspec.contract, invariant); - } - } - - for (natspec, f) in natspecs.iter().filter_map(|n| n.function.as_ref().map(|f| (n, f))) { - // Apply in-line configurations for the current profile - let c = &natspec.contract; - - // We might already have inserted contract-level configs above, so respect data already - // present in inline configs. - let base_fuzz = inline_fuzz.get(c, f).unwrap_or(&base_fuzz); - let base_invariant = inline_invariant.get(c, f).unwrap_or(&base_invariant); - - if let Some(fuzz) = base_fuzz.merge(natspec)? { - inline_fuzz.insert_fn(c, f, fuzz); - } - - if let Some(invariant) = base_invariant.merge(natspec)? { - inline_invariant.insert_fn(c, f, invariant); - } - } + /// Creates a new instance without parsing inline configuration. + pub fn new_unparsed(base_config: Arc) -> Self { + Self { config: base_config, inline: InlineConfig::new() } + } - Ok(Self { fuzz: base_fuzz, invariant: base_invariant, inline_fuzz, inline_invariant }) + /// Returns the [`Figment`] for the configuration. + pub fn figment(&self, contract: &str, function: &str) -> Result { + Ok(self.inline.merge(contract, function, &self.config)) } /// Returns a "fuzz" test runner instance. Parameters are used to select tight scoped fuzz /// configs that apply for a contract-function pair. A fallback configuration is applied /// if no specific setup is found for a given input. /// - /// - `contract_id` is the id of the test contract, expressed as a relative path from the - /// project root. - /// - `test_fn` is the name of the test function declared inside the test contract. - pub fn fuzz_runner(&self, contract_id: &str, test_fn: &str) -> TestRunner { - let fuzz_config = self.fuzz_config(contract_id, test_fn).clone(); - let failure_persist_path = fuzz_config + /// - `contract` is the id of the test contract, expressed as a relative path from the project + /// root. + /// - `function` is the name of the test function declared inside the test contract. + pub fn fuzz_runner(&self, contract: &str, function: &str) -> Result<(FuzzConfig, TestRunner)> { + let config: FuzzConfig = self.figment(contract, function)?.extract_inner("fuzz")?; + let failure_persist_path = config .failure_persist_dir + .as_ref() .unwrap() - .join(fuzz_config.failure_persist_file.unwrap()) + .join(config.failure_persist_file.as_ref().unwrap()) .into_os_string() .into_string() .unwrap(); - self.fuzzer_with_cases( - fuzz_config.runs, - fuzz_config.max_test_rejects, + let runner = fuzzer_with_cases( + config.seed, + config.runs, + config.max_test_rejects, Some(Box::new(FileFailurePersistence::Direct(failure_persist_path.leak()))), - ) + ); + Ok((config, runner)) } /// Returns an "invariant" test runner instance. Parameters are used to select tight scoped fuzz /// configs that apply for a contract-function pair. A fallback configuration is applied /// if no specific setup is found for a given input. /// - /// - `contract_id` is the id of the test contract, expressed as a relative path from the - /// project root. - /// - `test_fn` is the name of the test function declared inside the test contract. - pub fn invariant_runner(&self, contract_id: &str, test_fn: &str) -> TestRunner { - let invariant = self.invariant_config(contract_id, test_fn); - self.fuzzer_with_cases(invariant.runs, invariant.max_assume_rejects, None) - } - - /// Returns a "fuzz" configuration setup. Parameters are used to select tight scoped fuzz - /// configs that apply for a contract-function pair. A fallback configuration is applied - /// if no specific setup is found for a given input. - /// - /// - `contract_id` is the id of the test contract, expressed as a relative path from the - /// project root. - /// - `test_fn` is the name of the test function declared inside the test contract. - pub fn fuzz_config(&self, contract_id: &str, test_fn: &str) -> &FuzzConfig { - self.inline_fuzz.get(contract_id, test_fn).unwrap_or(&self.fuzz) - } - - /// Returns an "invariant" configuration setup. Parameters are used to select tight scoped - /// invariant configs that apply for a contract-function pair. A fallback configuration is - /// applied if no specific setup is found for a given input. - /// - /// - `contract_id` is the id of the test contract, expressed as a relative path from the - /// project root. - /// - `test_fn` is the name of the test function declared inside the test contract. - pub fn invariant_config(&self, contract_id: &str, test_fn: &str) -> &InvariantConfig { - self.inline_invariant.get(contract_id, test_fn).unwrap_or(&self.invariant) - } - - pub fn fuzzer_with_cases( + /// - `contract` is the id of the test contract, expressed as a relative path from the project + /// root. + /// - `function` is the name of the test function declared inside the test contract. + pub fn invariant_runner( &self, - cases: u32, - max_global_rejects: u32, - file_failure_persistence: Option>, - ) -> TestRunner { - let config = proptest::test_runner::Config { - failure_persistence: file_failure_persistence, - cases, - max_global_rejects, - // Disable proptest shrink: for fuzz tests we provide single counterexample, - // for invariant tests we shrink outside proptest. - max_shrink_iters: 0, - ..Default::default() - }; - - if let Some(seed) = &self.fuzz.seed { - trace!(target: "forge::test", %seed, "building deterministic fuzzer"); - let rng = TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()); - TestRunner::new_with_rng(config, rng) - } else { - trace!(target: "forge::test", "building stochastic fuzzer"); - TestRunner::new(config) - } + contract: &str, + function: &str, + ) -> Result<(InvariantConfig, TestRunner)> { + let figment = self.figment(contract, function)?; + let config: InvariantConfig = figment.extract_inner("invariant")?; + let seed: Option = figment.extract_inner("fuzz.seed").ok(); + let runner = fuzzer_with_cases(seed, config.runs, config.max_assume_rejects, None); + Ok((config, runner)) } } -/// Builder utility to create a [`TestOptions`] instance. -#[derive(Default)] -#[must_use = "builders do nothing unless you call `build` on them"] -pub struct TestOptionsBuilder { - fuzz: Option, - invariant: Option, - profiles: Option>, -} - -impl TestOptionsBuilder { - /// Sets a [`FuzzConfig`] to be used as base "fuzz" configuration. - pub fn fuzz(mut self, conf: FuzzConfig) -> Self { - self.fuzz = Some(conf); - self - } - - /// Sets a [`InvariantConfig`] to be used as base "invariant" configuration. - pub fn invariant(mut self, conf: InvariantConfig) -> Self { - self.invariant = Some(conf); - self - } - - /// Sets available configuration profiles. Profiles are useful to validate existing in-line - /// configurations. This argument is necessary in case a `compile_output`is provided. - pub fn profiles(mut self, p: Vec) -> Self { - self.profiles = Some(p); - self - } - - /// Creates an instance of [`TestOptions`]. This takes care of creating "fuzz" and - /// "invariant" fallbacks, and extracting all inline test configs, if available. - /// - /// `root` is a reference to the user's project root dir. This is essential - /// to determine the base path of generated contract identifiers. This is to provide correct - /// matchers for inline test configs. - pub fn build( - self, - output: &ProjectCompileOutput, - root: &Path, - ) -> Result { - let profiles: Vec = - self.profiles.unwrap_or_else(|| vec![Config::selected_profile().into()]); - let base_fuzz = self.fuzz.unwrap_or_default(); - let base_invariant = self.invariant.unwrap_or_default(); - TestOptions::new(output, root, profiles, base_fuzz, base_invariant) +fn fuzzer_with_cases( + seed: Option, + cases: u32, + max_global_rejects: u32, + file_failure_persistence: Option>, +) -> TestRunner { + let config = proptest::test_runner::Config { + failure_persistence: file_failure_persistence, + cases, + max_global_rejects, + // Disable proptest shrink: for fuzz tests we provide single counterexample, + // for invariant tests we shrink outside proptest. + max_shrink_iters: 0, + ..Default::default() + }; + + if let Some(seed) = seed { + trace!(target: "forge::test", %seed, "building deterministic fuzzer"); + let rng = TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()); + TestRunner::new_with_rng(config, rng) + } else { + trace!(target: "forge::test", "building stochastic fuzzer"); + TestRunner::new(config) } } diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 4b66a482c27c..fc2b89cb0815 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -341,24 +341,26 @@ impl ContractRunner<'_> { self.run_unit_test(func, should_fail, setup) } TestFunctionKind::FuzzTest { should_fail } => { - let runner = test_options.fuzz_runner(self.name, &func.name); - let fuzz_config = test_options.fuzz_config(self.name, &func.name); - - self.run_fuzz_test(func, should_fail, runner, setup, fuzz_config.clone()) + match test_options.fuzz_runner(self.name, &func.name) { + Ok((fuzz_config, runner)) => { + self.run_fuzz_test(func, should_fail, runner, setup, fuzz_config) + } + Err(err) => TestResult::fail(err.to_string()), + } } TestFunctionKind::InvariantTest => { - let runner = test_options.invariant_runner(self.name, &func.name); - let invariant_config = test_options.invariant_config(self.name, &func.name); - - self.run_invariant_test( - runner, - setup, - invariant_config.clone(), - func, - call_after_invariant, - &known_contracts, - identified_contracts.as_ref().unwrap(), - ) + match test_options.invariant_runner(self.name, &func.name) { + Ok((invariant_config, runner)) => self.run_invariant_test( + runner, + setup, + invariant_config, + func, + call_after_invariant, + &known_contracts, + identified_contracts.as_ref().unwrap(), + ), + Err(err) => TestResult::fail(err.to_string()), + } } _ => unreachable!(), }; diff --git a/crates/forge/tests/cli/alphanet.rs b/crates/forge/tests/cli/alphanet.rs index 6e41551ac890..49b8c01fc7d2 100644 --- a/crates/forge/tests/cli/alphanet.rs +++ b/crates/forge/tests/cli/alphanet.rs @@ -1,6 +1,10 @@ // Ensure we can run basic counter tests with EOF support. -#[cfg(target_os = "linux")] forgetest_init!(test_eof_flag, |prj, cmd| { + if !has_docker() { + println!("skipping because no docker is available"); + return; + } + cmd.forge_fuse().args(["test", "--eof"]).assert_success().stdout_eq(str![[r#" [COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] @@ -17,3 +21,12 @@ Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests) "#]]); }); + +fn has_docker() -> bool { + if !cfg!(target_os = "linux") { + return false; + } + + // `images` will also check for the daemon. + std::process::Command::new("docker").arg("images").output().is_ok_and(|o| o.status.success()) +} diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 9585b216b159..ca54ae0d0352 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -140,6 +140,11 @@ Compiler run successful! // tests build output is as expected forgetest_init!(build_sizes_no_forge_std, |prj, cmd| { + prj.write_config(Config { + solc: Some(foundry_config::SolcReq::Version(semver::Version::new(0, 8, 27))), + ..Default::default() + }); + cmd.args(["build", "--sizes"]).assert_success().stdout_eq(str![ r#" ... @@ -154,12 +159,12 @@ forgetest_init!(build_sizes_no_forge_std, |prj, cmd| { str![[r#" { "Counter": { - "runtime_size": 247, - "init_size": 277, - "runtime_margin": 24329, - "init_margin": 48875 + "runtime_size": 236, + "init_size": 263, + "runtime_margin": 24340, + "init_margin": 48889 } -} +} "#]] .is_json(), ); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index db87a85ba0d1..72aacff49eb6 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -29,6 +29,8 @@ forgetest!(can_extract_config_values, |prj, cmd| { // explicitly set all values let input = Config { profile: Config::DEFAULT_PROFILE, + // `profiles` is not serialized. + profiles: vec![], root: Default::default(), src: "test-src".into(), test: "test-test".into(), diff --git a/crates/forge/tests/cli/inline_config.rs b/crates/forge/tests/cli/inline_config.rs new file mode 100644 index 000000000000..de585a48ce17 --- /dev/null +++ b/crates/forge/tests/cli/inline_config.rs @@ -0,0 +1,194 @@ +forgetest!(runs, |prj, cmd| { + prj.add_test( + "inline.sol", + " + contract Inline { + /** forge-config: default.fuzz.runs = 2 */ + function test1(bool) public {} + + \t///\t forge-config:\tdefault.fuzz.runs=\t3 \t + + function test2(bool) public {} + } + ", + ) + .unwrap(); + + cmd.arg("test").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 2 tests for test/inline.sol:Inline +[PASS] test1(bool) (runs: 2, [AVG_GAS]) +[PASS] test2(bool) (runs: 3, [AVG_GAS]) +Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests) + +"#]]); + + // Make sure inline config is parsed in coverage too. + cmd.forge_fuse().arg("coverage").assert_success().stdout_eq(str![[r#" +... +Ran 2 tests for test/inline.sol:Inline +[PASS] test1(bool) (runs: 2, [AVG_GAS]) +[PASS] test2(bool) (runs: 3, [AVG_GAS]) +Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests) +| File | % Lines | % Statements | % Branches | % Funcs | +|-------|---------------|---------------|---------------|---------------| +| Total | 100.00% (0/0) | 100.00% (0/0) | 100.00% (0/0) | 100.00% (0/0) | + +"#]]); +}); + +forgetest!(invalid_profile, |prj, cmd| { + prj.add_test( + "inline.sol", + " + /** forge-config: unknown.fuzz.runs = 2 */ + contract Inline { + function test(bool) public {} + } + ", + ) + .unwrap(); + + cmd.arg("test").assert_failure().stderr_eq(str![[r#" +Error: Inline config error at test/inline.sol:0:0:0: invalid profile `unknown.fuzz.runs = 2`; valid profiles: default + +"#]]); +}); + +// TODO: Uncomment once this done for normal config too. +/* +forgetest!(invalid_key, |prj, cmd| { + prj.add_test( + "inline.sol", + " + /** forge-config: default.fuzzz.runs = 2 */ + contract Inline { + function test(bool) public {} + } + ", + ) + .unwrap(); + + cmd.arg("test").assert_failure().stderr_eq(str![[]]).stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/inline.sol:Inline +[FAIL: failed to get inline configuration: unknown config section `default`] test(bool) ([GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/inline.sol:Inline +[FAIL: failed to get inline configuration: unknown config section `default`] test(bool) ([GAS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +"#]]); +}); + +forgetest!(invalid_key_2, |prj, cmd| { + prj.add_test( + "inline.sol", + " +/** forge-config: default.fuzz.runss = 2 */ + contract Inline { + function test(bool) public {} + } + ", + ) + .unwrap(); + + cmd.arg("test").assert_failure().stderr_eq(str![[]]).stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/inline.sol:Inline +[FAIL: failed to get inline configuration: unknown config section `default`] test(bool) ([GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/inline.sol:Inline +[FAIL: failed to get inline configuration: unknown config section `default`] test(bool) ([GAS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +"#]]); +}); +*/ + +forgetest!(invalid_value, |prj, cmd| { + prj.add_test( + "inline.sol", + " + /** forge-config: default.fuzz.runs = [2] */ + contract Inline { + function test(bool) public {} + } + ", + ) + .unwrap(); + + cmd.arg("test").assert_failure().stderr_eq(str![[]]).stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/inline.sol:Inline +[FAIL: invalid type: found sequence, expected u32 for key "default.runs.fuzz" in inline config] test(bool) ([GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/inline.sol:Inline +[FAIL: invalid type: found sequence, expected u32 for key "default.runs.fuzz" in inline config] test(bool) ([GAS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +"#]]); +}); + +forgetest!(invalid_value_2, |prj, cmd| { + prj.add_test( + "inline.sol", + " + /** forge-config: default.fuzz.runs = '2' */ + contract Inline { + function test(bool) public {} + } + ", + ) + .unwrap(); + + cmd.arg("test").assert_failure().stderr_eq(str![[]]).stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/inline.sol:Inline +[FAIL: invalid type: found string "2", expected u32 for key "default.runs.fuzz" in inline config] test(bool) ([GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/inline.sol:Inline +[FAIL: invalid type: found string "2", expected u32 for key "default.runs.fuzz" in inline config] test(bool) ([GAS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +"#]]); +}); diff --git a/crates/forge/tests/cli/main.rs b/crates/forge/tests/cli/main.rs index 5838fa853777..d59dbc6bedd6 100644 --- a/crates/forge/tests/cli/main.rs +++ b/crates/forge/tests/cli/main.rs @@ -18,6 +18,7 @@ mod debug; mod doc; mod eip712; mod geiger; +mod inline_config; mod multi_script; mod script; mod soldeer; diff --git a/crates/forge/tests/it/cheats.rs b/crates/forge/tests/it/cheats.rs index 871cda045fa7..11fcdbcfd5c1 100644 --- a/crates/forge/tests/it/cheats.rs +++ b/crates/forge/tests/it/cheats.rs @@ -27,9 +27,9 @@ async fn test_cheats_local(test_data: &ForgeTestData) { filter = filter.exclude_contracts("(LastCallGasDefaultTest|MockFunctionTest|WithSeed)"); } - let mut config = test_data.config.clone(); - config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write("./")]); - let runner = test_data.runner_with_config(config); + let runner = test_data.runner_with(|config| { + config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write("./")]); + }); TestConfig::with_filter(runner, filter).run().await; } @@ -38,9 +38,9 @@ async fn test_cheats_local(test_data: &ForgeTestData) { async fn test_cheats_local_isolated(test_data: &ForgeTestData) { let filter = Filter::new(".*", ".*(Isolated)", &format!(".*cheats{RE_PATH_SEPARATOR}*")); - let mut config = test_data.config.clone(); - config.isolate = true; - let runner = test_data.runner_with_config(config); + let runner = test_data.runner_with(|config| { + config.isolate = true; + }); TestConfig::with_filter(runner, filter).run().await; } @@ -49,9 +49,9 @@ async fn test_cheats_local_isolated(test_data: &ForgeTestData) { async fn test_cheats_local_with_seed(test_data: &ForgeTestData) { let filter = Filter::new(".*", ".*(WithSeed)", &format!(".*cheats{RE_PATH_SEPARATOR}*")); - let mut config = test_data.config.clone(); - config.fuzz.seed = Some(U256::from(100)); - let runner = test_data.runner_with_config(config); + let runner = test_data.runner_with(|config| { + config.fuzz.seed = Some(U256::from(100)); + }); TestConfig::with_filter(runner, filter).run().await; } diff --git a/crates/forge/tests/it/core.rs b/crates/forge/tests/it/core.rs index c8a599195441..a2b4916d3e7c 100644 --- a/crates/forge/tests/it/core.rs +++ b/crates/forge/tests/it/core.rs @@ -758,9 +758,9 @@ async fn test_trace() { #[tokio::test(flavor = "multi_thread")] async fn test_assertions_revert_false() { let filter = Filter::new(".*", ".*NoAssertionsRevertTest", ".*"); - let mut config = TEST_DATA_DEFAULT.config.clone(); - config.assertions_revert = false; - let mut runner = TEST_DATA_DEFAULT.runner_with_config(config); + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.assertions_revert = false; + }); let results = runner.test_collect(&filter); assert_multiple( @@ -784,9 +784,9 @@ async fn test_assertions_revert_false() { #[tokio::test(flavor = "multi_thread")] async fn test_legacy_assertions() { let filter = Filter::new(".*", ".*LegacyAssertions", ".*"); - let mut config = TEST_DATA_DEFAULT.config.clone(); - config.legacy_assertions = true; - let mut runner = TEST_DATA_DEFAULT.runner_with_config(config); + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.legacy_assertions = true; + }); let results = runner.test_collect(&filter); assert_multiple( diff --git a/crates/forge/tests/it/fork.rs b/crates/forge/tests/it/fork.rs index 8dc637528ddd..5974a12ed644 100644 --- a/crates/forge/tests/it/fork.rs +++ b/crates/forge/tests/it/fork.rs @@ -35,9 +35,9 @@ async fn test_cheats_fork_revert() { /// Executes all non-reverting fork cheatcodes #[tokio::test(flavor = "multi_thread")] async fn test_cheats_fork() { - let mut config = TEST_DATA_PARIS.config.clone(); - config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - let runner = TEST_DATA_PARIS.runner_with_config(config); + let runner = TEST_DATA_PARIS.runner_with(|config| { + config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); + }); let filter = Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) .exclude_tests(".*Revert"); TestConfig::with_filter(runner, filter).run().await; @@ -46,9 +46,9 @@ async fn test_cheats_fork() { /// Executes eth_getLogs cheatcode #[tokio::test(flavor = "multi_thread")] async fn test_get_logs_fork() { - let mut config = TEST_DATA_DEFAULT.config.clone(); - config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - let runner = TEST_DATA_DEFAULT.runner_with_config(config); + let runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); + }); let filter = Filter::new("testEthGetLogs", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) .exclude_tests(".*Revert"); TestConfig::with_filter(runner, filter).run().await; @@ -57,9 +57,9 @@ async fn test_get_logs_fork() { /// Executes rpc cheatcode #[tokio::test(flavor = "multi_thread")] async fn test_rpc_fork() { - let mut config = TEST_DATA_DEFAULT.config.clone(); - config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - let runner = TEST_DATA_DEFAULT.runner_with_config(config); + let runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); + }); let filter = Filter::new("testRpc", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) .exclude_tests(".*Revert"); TestConfig::with_filter(runner, filter).run().await; @@ -102,25 +102,25 @@ async fn test_create_same_fork() { /// Test that `no_storage_caching` config is properly applied #[tokio::test(flavor = "multi_thread")] async fn test_storage_caching_config() { - // no_storage_caching set to true: storage should not be cached - let mut config = TEST_DATA_DEFAULT.config.clone(); - config.no_storage_caching = true; - let runner = TEST_DATA_DEFAULT.runner_with_config(config); let filter = Filter::new("testStorageCaching", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) .exclude_tests(".*Revert"); - TestConfig::with_filter(runner, filter).run().await; + + let runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.no_storage_caching = true; + }); + + // no_storage_caching set to true: storage should not be cached + TestConfig::with_filter(runner, filter.clone()).run().await; let cache_dir = Config::foundry_block_cache_dir(Chain::mainnet(), 19800000).unwrap(); let _ = fs::remove_file(cache_dir); - // no_storage_caching set to false: storage should be cached - let mut config = TEST_DATA_DEFAULT.config.clone(); - config.no_storage_caching = false; - let runner = TEST_DATA_DEFAULT.runner_with_config(config); - let filter = - Filter::new("testStorageCaching", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) - .exclude_tests(".*Revert"); + let runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.no_storage_caching = false; + }); TestConfig::with_filter(runner, filter).run().await; + + // no_storage_caching set to false: storage should be cached let cache_dir = Config::foundry_block_cache_dir(Chain::mainnet(), 19800000).unwrap(); assert!(cache_dir.exists()); diff --git a/crates/forge/tests/it/fs.rs b/crates/forge/tests/it/fs.rs index 5bb0b59fb24b..5733ec5849b9 100644 --- a/crates/forge/tests/it/fs.rs +++ b/crates/forge/tests/it/fs.rs @@ -6,18 +6,18 @@ use foundry_test_utils::Filter; #[tokio::test(flavor = "multi_thread")] async fn test_fs_disabled() { - let mut config = TEST_DATA_DEFAULT.config.clone(); - config.fs_permissions = FsPermissions::new(vec![PathPermission::none("./")]); - let runner = TEST_DATA_DEFAULT.runner_with_config(config); + let runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.fs_permissions = FsPermissions::new(vec![PathPermission::none("./")]); + }); let filter = Filter::new(".*", ".*", ".*fs/Disabled"); TestConfig::with_filter(runner, filter).run().await; } #[tokio::test(flavor = "multi_thread")] async fn test_fs_default() { - let mut config = TEST_DATA_DEFAULT.config.clone(); - config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - let runner = TEST_DATA_DEFAULT.runner_with_config(config); + let runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); + }); let filter = Filter::new(".*", ".*", ".*fs/Default"); TestConfig::with_filter(runner, filter).run().await; } diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index 8972c9bd98f1..eaa627b9652f 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -82,11 +82,12 @@ async fn test_successful_fuzz_cases() { #[ignore] async fn test_fuzz_collection() { let filter = Filter::new(".*", ".*", ".*fuzz/FuzzCollection.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.invariant.depth = 100; - runner.test_options.invariant.runs = 1000; - runner.test_options.fuzz.runs = 1000; - runner.test_options.fuzz.seed = Some(U256::from(6u32)); + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.invariant.depth = 100; + config.invariant.runs = 1000; + config.fuzz.runs = 1000; + config.fuzz.seed = Some(U256::from(6u32)); + }); let results = runner.test_collect(&filter); assert_multiple( @@ -111,11 +112,14 @@ async fn test_fuzz_collection() { #[tokio::test(flavor = "multi_thread")] async fn test_persist_fuzz_failure() { let filter = Filter::new(".*", ".*", ".*fuzz/FuzzFailurePersist.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.fuzz.runs = 1000; - macro_rules! get_failure_result { - () => { + macro_rules! run_fail { + () => { run_fail!(|config| {}) }; + (|$config:ident| $e:expr) => {{ + let mut runner = TEST_DATA_DEFAULT.runner_with(|$config| { + $config.fuzz.runs = 1000; + $e + }); runner .test_collect(&filter) .get("default/fuzz/FuzzFailurePersist.t.sol:FuzzFailurePersistTest") @@ -125,11 +129,11 @@ async fn test_persist_fuzz_failure() { .unwrap() .counterexample .clone() - }; + }}; } // record initial counterexample calldata - let initial_counterexample = get_failure_result!(); + let initial_counterexample = run_fail!(); let initial_calldata = match initial_counterexample { Some(CounterExample::Single(counterexample)) => counterexample.calldata, _ => Bytes::new(), @@ -137,7 +141,7 @@ async fn test_persist_fuzz_failure() { // run several times and compare counterexamples calldata for i in 0..10 { - let new_calldata = match get_failure_result!() { + let new_calldata = match run_fail!() { Some(CounterExample::Single(counterexample)) => counterexample.calldata, _ => Bytes::new(), }; @@ -146,8 +150,9 @@ async fn test_persist_fuzz_failure() { } // write new failure in different file - runner.test_options.fuzz.failure_persist_file = Some("failure1".to_string()); - let new_calldata = match get_failure_result!() { + let new_calldata = match run_fail!(|config| { + config.fuzz.failure_persist_file = Some("failure1".to_string()); + }) { Some(CounterExample::Single(counterexample)) => counterexample.calldata, _ => Bytes::new(), }; diff --git a/crates/forge/tests/it/inline.rs b/crates/forge/tests/it/inline.rs index 4448f982dcd3..eab7f9ec1bb1 100644 --- a/crates/forge/tests/it/inline.rs +++ b/crates/forge/tests/it/inline.rs @@ -1,15 +1,13 @@ //! Inline configuration tests. -use crate::test_helpers::{ForgeTestData, ForgeTestProfile, TEST_DATA_DEFAULT}; -use forge::{result::TestKind, TestOptionsBuilder}; -use foundry_config::{FuzzConfig, InvariantConfig}; +use crate::test_helpers::TEST_DATA_DEFAULT; +use forge::result::TestKind; use foundry_test_utils::Filter; #[tokio::test(flavor = "multi_thread")] async fn inline_config_run_fuzz() { let filter = Filter::new(".*", ".*", ".*inline/FuzzInlineConf.t.sol"); - // Fresh runner to make sure there's no persisted failure from previous tests. - let mut runner = ForgeTestData::new(ForgeTestProfile::Default).runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); let result = runner.test_collect(&filter); let results = result .into_iter() @@ -70,31 +68,3 @@ async fn inline_config_run_invariant() { _ => unreachable!(), } } - -#[test] -fn build_test_options() { - let root = &TEST_DATA_DEFAULT.project.paths.root; - let profiles = vec!["default".to_string(), "ci".to_string()]; - let build_result = TestOptionsBuilder::default() - .fuzz(FuzzConfig::default()) - .invariant(InvariantConfig::default()) - .profiles(profiles) - .build(&TEST_DATA_DEFAULT.output, root); - - assert!(build_result.is_ok()); -} - -#[test] -fn build_test_options_just_one_valid_profile() { - let root = &TEST_DATA_DEFAULT.project.root(); - let valid_profiles = vec!["profile-sheldon-cooper".to_string()]; - let build_result = TestOptionsBuilder::default() - .fuzz(FuzzConfig::default()) - .invariant(InvariantConfig::default()) - .profiles(valid_profiles) - .build(&TEST_DATA_DEFAULT.output, root); - - // We expect an error, since COMPILED contains in-line - // per-test configs for "default" and "ci" profiles - assert!(build_result.is_err()); -} diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 3e09cd465a33..2f4da4054245 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -48,8 +48,9 @@ async fn test_invariant_with_alias() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_filters() { - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.invariant.runs = 10; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.invariant.runs = 10; + }); // Contracts filter tests. assert_multiple( @@ -173,9 +174,10 @@ async fn test_invariant_filters() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_override() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantReentrancy.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.invariant.fail_on_revert = false; - runner.test_options.invariant.call_override = true; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.invariant.fail_on_revert = false; + config.invariant.call_override = true; + }); let results = runner.test_collect(&filter); assert_multiple( &results, @@ -189,10 +191,11 @@ async fn test_invariant_override() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_fail_on_revert() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantHandlerFailure.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.invariant.fail_on_revert = true; - runner.test_options.invariant.runs = 1; - runner.test_options.invariant.depth = 10; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.invariant.fail_on_revert = true; + config.invariant.runs = 1; + config.invariant.depth = 10; + }); let results = runner.test_collect(&filter); assert_multiple( &results, @@ -213,9 +216,13 @@ async fn test_invariant_fail_on_revert() { #[ignore] async fn test_invariant_storage() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/storage/InvariantStorageTest.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.invariant.depth = 100 + (50 * cfg!(windows) as u32); - runner.test_options.fuzz.seed = Some(U256::from(6u32)); + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.invariant.depth = 100; + if cfg!(windows) { + config.invariant.depth += 50; + } + config.fuzz.seed = Some(U256::from(6u32)); + }); let results = runner.test_collect(&filter); assert_multiple( &results, @@ -254,8 +261,9 @@ async fn test_invariant_inner_contract() { #[cfg_attr(windows, ignore = "for some reason there's different rng")] async fn test_invariant_shrink() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantInnerContract.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.fuzz.seed = Some(U256::from(119u32)); + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.fuzz.seed = Some(U256::from(119u32)); + }); match get_counterexample!(runner, &filter) { CounterExample::Single(_) => panic!("CounterExample should be a sequence."), @@ -300,10 +308,11 @@ async fn test_invariant_require_shrink() { async fn check_shrink_sequence(test_pattern: &str, expected_len: usize) { let filter = Filter::new(test_pattern, ".*", ".*fuzz/invariant/common/InvariantShrinkWithAssert.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.fuzz.seed = Some(U256::from(100u32)); - runner.test_options.invariant.runs = 1; - runner.test_options.invariant.depth = 15; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.fuzz.seed = Some(U256::from(100u32)); + config.invariant.runs = 1; + config.invariant.depth = 15; + }); match get_counterexample!(runner, &filter) { CounterExample::Single(_) => panic!("CounterExample should be a sequence."), @@ -318,10 +327,11 @@ async fn check_shrink_sequence(test_pattern: &str, expected_len: usize) { async fn test_shrink_big_sequence() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantShrinkBigSequence.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.fuzz.seed = Some(U256::from(119u32)); - runner.test_options.invariant.runs = 1; - runner.test_options.invariant.depth = 1000; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.fuzz.seed = Some(U256::from(119u32)); + config.invariant.runs = 1; + config.invariant.depth = 1000; + }); let initial_counterexample = runner .test_collect(&filter) @@ -390,11 +400,12 @@ async fn test_shrink_big_sequence() { async fn test_shrink_fail_on_revert() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.fuzz.seed = Some(U256::from(119u32)); - runner.test_options.invariant.fail_on_revert = true; - runner.test_options.invariant.runs = 1; - runner.test_options.invariant.depth = 200; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.fuzz.seed = Some(U256::from(119u32)); + config.invariant.fail_on_revert = true; + config.invariant.runs = 1; + config.invariant.depth = 200; + }); match get_counterexample!(runner, &filter) { CounterExample::Single(_) => panic!("CounterExample should be a sequence."), @@ -408,8 +419,9 @@ async fn test_shrink_fail_on_revert() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_preserve_state() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantPreserveState.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.invariant.fail_on_revert = true; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.invariant.fail_on_revert = true; + }); let results = runner.test_collect(&filter); assert_multiple( &results, @@ -452,9 +464,10 @@ async fn test_invariant_with_address_fixture() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_assume_does_not_revert() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantAssume.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - // Should not treat vm.assume as revert. - runner.test_options.invariant.fail_on_revert = true; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + // Should not treat vm.assume as revert. + config.invariant.fail_on_revert = true; + }); let results = runner.test_collect(&filter); assert_multiple( &results, @@ -468,10 +481,11 @@ async fn test_invariant_assume_does_not_revert() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_assume_respects_restrictions() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantAssume.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.invariant.runs = 1; - runner.test_options.invariant.depth = 10; - runner.test_options.invariant.max_assume_rejects = 1; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.invariant.runs = 1; + config.invariant.depth = 10; + config.invariant.max_assume_rejects = 1; + }); let results = runner.test_collect(&filter); assert_multiple( &results, @@ -491,8 +505,9 @@ async fn test_invariant_assume_respects_restrictions() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_decode_custom_error() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantCustomError.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.invariant.fail_on_revert = true; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.invariant.fail_on_revert = true; + }); let results = runner.test_collect(&filter); assert_multiple( &results, @@ -512,8 +527,9 @@ async fn test_invariant_decode_custom_error() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_fuzzed_selected_targets() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/target/FuzzedTargetContracts.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.invariant.fail_on_revert = true; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.invariant.fail_on_revert = true; + }); let results = runner.test_collect(&filter); assert_multiple( &results, @@ -539,9 +555,10 @@ async fn test_invariant_fuzzed_selected_targets() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_fixtures() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantFixtures.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.invariant.runs = 1; - runner.test_options.invariant.depth = 100; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.invariant.runs = 1; + config.invariant.depth = 100; + }); let results = runner.test_collect(&filter); assert_multiple( &results, @@ -592,8 +609,9 @@ async fn test_invariant_scrape_values() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_roll_fork_handler() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantRollFork.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.fuzz.seed = Some(U256::from(119u32)); + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.fuzz.seed = Some(U256::from(119u32)); + }); let results = runner.test_collect(&filter); assert_multiple( &results, @@ -625,8 +643,9 @@ async fn test_invariant_roll_fork_handler() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_excluded_senders() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantExcludedSenders.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.invariant.fail_on_revert = true; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.invariant.fail_on_revert = true; + }); let results = runner.test_collect(&filter); assert_multiple( &results, @@ -670,10 +689,11 @@ async fn test_invariant_after_invariant() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_selectors_weight() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantSelectorsWeight.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.fuzz.seed = Some(U256::from(119u32)); - runner.test_options.invariant.runs = 1; - runner.test_options.invariant.depth = 10; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.fuzz.seed = Some(U256::from(119u32)); + config.invariant.runs = 1; + config.invariant.depth = 10; + }); let results = runner.test_collect(&filter); assert_multiple( &results, @@ -688,10 +708,11 @@ async fn test_invariant_selectors_weight() { async fn test_no_reverts_in_counterexample() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantSequenceNoReverts.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.invariant.fail_on_revert = false; - // Use original counterexample to test sequence len. - runner.test_options.invariant.shrink_run_limit = 0; + let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.invariant.fail_on_revert = false; + // Use original counterexample to test sequence len. + config.invariant.shrink_run_limit = 0; + }); match get_counterexample!(runner, &filter) { CounterExample::Single(_) => panic!("CounterExample should be a sequence."), diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 53185cf97856..69c3a0fb35f7 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -1,11 +1,6 @@ //! Regression tests for previous issues. -use std::sync::Arc; - -use crate::{ - config::*, - test_helpers::{ForgeTestData, TEST_DATA_DEFAULT}, -}; +use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt}; use alloy_json_abi::Event; use alloy_primitives::{address, b256, Address, U256}; @@ -19,6 +14,7 @@ use foundry_evm::{ traces::{CallKind, CallTraceDecoder, DecodedCallData, TraceKind}, }; use foundry_test_utils::Filter; +use std::sync::Arc; /// Creates a test that runs `testdata/repros/Issue{issue}.t.sol`. macro_rules! test_repro { @@ -33,7 +29,7 @@ macro_rules! test_repro { #[tokio::test(flavor = "multi_thread")] $(#[$attr])* async fn [< issue_ $issue_number >]() { - repro_config($issue_number, $should_fail, $sender.into(), &*TEST_DATA_DEFAULT).await.run().await; + repro_config($issue_number, $should_fail, $sender.into()).await.run().await; } } }; @@ -42,7 +38,7 @@ macro_rules! test_repro { #[tokio::test(flavor = "multi_thread")] $(#[$attr])* async fn [< issue_ $issue_number >]() { - let mut $res = repro_config($issue_number, $should_fail, $sender.into(), &*TEST_DATA_DEFAULT).await.test(); + let mut $res = repro_config($issue_number, $should_fail, $sender.into()).await.test(); $e } } @@ -52,7 +48,7 @@ macro_rules! test_repro { #[tokio::test(flavor = "multi_thread")] $(#[$attr])* async fn [< issue_ $issue_number >]() { - let mut $config = repro_config($issue_number, false, None, &*TEST_DATA_DEFAULT).await; + let mut $config = repro_config($issue_number, false, None).await; $e $config.run().await; } @@ -60,23 +56,19 @@ macro_rules! test_repro { }; } -async fn repro_config( - issue: usize, - should_fail: bool, - sender: Option

, - test_data: &ForgeTestData, -) -> TestConfig { +async fn repro_config(issue: usize, should_fail: bool, sender: Option
) -> TestConfig { foundry_test_utils::init_tracing(); let filter = Filter::path(&format!(".*repros/Issue{issue}.t.sol")); - let mut config = test_data.config.clone(); - config.fs_permissions = - FsPermissions::new(vec![PathPermission::read("./fixtures"), PathPermission::read("out")]); - if let Some(sender) = sender { - config.sender = sender; - } - - let runner = TEST_DATA_DEFAULT.runner_with_config(config); + let runner = TEST_DATA_DEFAULT.runner_with(|config| { + config.fs_permissions = FsPermissions::new(vec![ + PathPermission::read("./fixtures"), + PathPermission::read("out"), + ]); + if let Some(sender) = sender { + config.sender = sender; + } + }); TestConfig::with_filter(runner, filter).set_should_fail(should_fail) } diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 5e540d8c67aa..298bbae2971d 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -4,7 +4,6 @@ use alloy_chains::NamedChain; use alloy_primitives::U256; use forge::{ revm::primitives::SpecId, MultiContractRunner, MultiContractRunnerBuilder, TestOptions, - TestOptionsBuilder, }; use foundry_compilers::{ artifacts::{EvmVersion, Libraries, Settings}, @@ -15,10 +14,7 @@ use foundry_config::{ fs_permissions::PathPermission, Config, FsPermissions, FuzzConfig, FuzzDictionaryConfig, InvariantConfig, RpcEndpoint, RpcEndpoints, }; -use foundry_evm::{ - constants::CALLER, - opts::{Env, EvmOpts}, -}; +use foundry_evm::{constants::CALLER, opts::EvmOpts}; use foundry_test_utils::{fd_lock, init_tracing, rpc::next_rpc_endpoint}; use std::{ env, fmt, @@ -74,69 +70,6 @@ impl ForgeTestProfile { SolcConfig { settings } } - pub fn project(&self) -> Project { - self.config().project().expect("Failed to build project") - } - - pub fn test_opts(&self, output: &ProjectCompileOutput) -> TestOptions { - TestOptionsBuilder::default() - .fuzz(FuzzConfig { - runs: 256, - max_test_rejects: 65536, - seed: None, - dictionary: FuzzDictionaryConfig { - include_storage: true, - include_push_bytes: true, - dictionary_weight: 40, - max_fuzz_dictionary_addresses: 10_000, - max_fuzz_dictionary_values: 10_000, - }, - gas_report_samples: 256, - failure_persist_dir: Some(tempfile::tempdir().unwrap().into_path()), - failure_persist_file: Some("testfailure".to_string()), - show_logs: false, - }) - .invariant(InvariantConfig { - runs: 256, - depth: 15, - fail_on_revert: false, - call_override: false, - dictionary: FuzzDictionaryConfig { - dictionary_weight: 80, - include_storage: true, - include_push_bytes: true, - max_fuzz_dictionary_addresses: 10_000, - max_fuzz_dictionary_values: 10_000, - }, - shrink_run_limit: 5000, - max_assume_rejects: 65536, - gas_report_samples: 256, - failure_persist_dir: Some(tempfile::tempdir().unwrap().into_path()), - show_metrics: false, - }) - .build(output, Path::new(self.project().root())) - .expect("Config loaded") - } - - pub fn evm_opts(&self) -> EvmOpts { - EvmOpts { - env: Env { - gas_limit: u64::MAX, - chain_id: None, - tx_origin: CALLER, - block_number: 1, - block_timestamp: 1, - ..Default::default() - }, - sender: CALLER, - initial_balance: U256::MAX, - ffi: true, - verbosity: 3, - memory_limit: 1 << 26, - ..Default::default() - } - } - /// Build [Config] for test profile. /// /// Project source files are read from testdata/{profile_name} @@ -155,11 +88,66 @@ impl ForgeTestProfile { "fork/Fork.t.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4".to_string(), ]; + config.prompt_timeout = 0; + + config.gas_limit = u64::MAX.into(); + config.chain = None; + config.tx_origin = CALLER; + config.block_number = 1; + config.block_timestamp = 1; + + config.sender = CALLER; + config.initial_balance = U256::MAX; + config.ffi = true; + config.verbosity = 3; + config.memory_limit = 1 << 26; + if self.is_paris() { config.evm_version = EvmVersion::Paris; } - config + config.fuzz = FuzzConfig { + runs: 256, + max_test_rejects: 65536, + seed: None, + dictionary: FuzzDictionaryConfig { + include_storage: true, + include_push_bytes: true, + dictionary_weight: 40, + max_fuzz_dictionary_addresses: 10_000, + max_fuzz_dictionary_values: 10_000, + }, + gas_report_samples: 256, + failure_persist_dir: Some(tempfile::tempdir().unwrap().into_path()), + failure_persist_file: Some("testfailure".to_string()), + show_logs: false, + }; + config.invariant = InvariantConfig { + runs: 256, + depth: 15, + fail_on_revert: false, + call_override: false, + dictionary: FuzzDictionaryConfig { + dictionary_weight: 80, + include_storage: true, + include_push_bytes: true, + max_fuzz_dictionary_addresses: 10_000, + max_fuzz_dictionary_values: 10_000, + }, + shrink_run_limit: 5000, + max_assume_rejects: 65536, + gas_report_samples: 256, + failure_persist_dir: Some( + tempfile::Builder::new() + .prefix(&format!("foundry-{self}")) + .tempdir() + .unwrap() + .into_path(), + ), + show_metrics: false, + }; + + config.sanitized() } } @@ -167,9 +155,7 @@ impl ForgeTestProfile { pub struct ForgeTestData { pub project: Project, pub output: ProjectCompileOutput, - pub test_opts: TestOptions, - pub evm_opts: EvmOpts, - pub config: Config, + pub config: Arc, pub profile: ForgeTestProfile, } @@ -179,67 +165,63 @@ impl ForgeTestData { /// Uses [get_compiled] to lazily compile the project. pub fn new(profile: ForgeTestProfile) -> Self { init_tracing(); - - let mut project = profile.project(); + let config = Arc::new(profile.config()); + let mut project = config.project().unwrap(); let output = get_compiled(&mut project); - let test_opts = profile.test_opts(&output); - let config = profile.config(); - let evm_opts = profile.evm_opts(); - - Self { project, output, test_opts, evm_opts, config, profile } + Self { project, output, config, profile } } /// Builds a base runner pub fn base_runner(&self) -> MultiContractRunnerBuilder { init_tracing(); - let mut runner = MultiContractRunnerBuilder::new(Arc::new(self.config.clone())) - .sender(self.evm_opts.sender) - .with_test_options(self.test_opts.clone()); + let config = self.config.clone(); + let mut runner = MultiContractRunnerBuilder::new(config.clone()) + .sender(self.config.sender) + .with_test_options(TestOptions::new_unparsed(config)); if self.profile.is_paris() { runner = runner.evm_spec(SpecId::MERGE); } - runner } /// Builds a non-tracing runner pub fn runner(&self) -> MultiContractRunner { - let mut config = self.config.clone(); - config.fs_permissions = - FsPermissions::new(vec![PathPermission::read_write(manifest_root())]); - self.runner_with_config(config) + self.runner_with(|_| {}) } /// Builds a non-tracing runner - pub fn runner_with_config(&self, mut config: Config) -> MultiContractRunner { + pub fn runner_with(&self, modify: impl FnOnce(&mut Config)) -> MultiContractRunner { + let mut config = (*self.config).clone(); + modify(&mut config); + self.runner_with_config(config) + } + + fn runner_with_config(&self, mut config: Config) -> MultiContractRunner { config.rpc_endpoints = rpc_endpoints(); config.allow_paths.push(manifest_root().to_path_buf()); - // no prompt testing - config.prompt_timeout = 0; - - let root = self.project.root(); - let mut opts = self.evm_opts.clone(); - - if config.isolate { - opts.isolate = true; + if config.fs_permissions.is_empty() { + config.fs_permissions = + FsPermissions::new(vec![PathPermission::read_write(manifest_root())]); } - let sender = config.sender; + let opts = config_evm_opts(&config); let mut builder = self.base_runner(); - builder.config = Arc::new(config); + let config = Arc::new(config); + let root = self.project.root(); + builder.config = config.clone(); builder .enable_isolation(opts.isolate) - .sender(sender) - .with_test_options(self.test_opts.clone()) + .sender(config.sender) + .with_test_options(TestOptions::new(&self.output, config.clone()).unwrap()) .build(root, &self.output, opts.local_evm_env(), opts) .unwrap() } /// Builds a tracing runner pub fn tracing_runner(&self) -> MultiContractRunner { - let mut opts = self.evm_opts.clone(); + let mut opts = config_evm_opts(&self.config); opts.verbosity = 5; self.base_runner() .build(self.project.root(), &self.output, opts.local_evm_env(), opts) @@ -248,7 +230,7 @@ impl ForgeTestData { /// Builds a runner that runs against forked state pub async fn forked_runner(&self, rpc: &str) -> MultiContractRunner { - let mut opts = self.evm_opts.clone(); + let mut opts = config_evm_opts(&self.config); opts.env.chain_id = None; // clear chain id so the correct one gets fetched from the RPC opts.fork_url = Some(rpc.to_string()); @@ -369,3 +351,7 @@ pub fn rpc_endpoints() -> RpcEndpoints { ("rpcEnvAlias", RpcEndpoint::Env("${RPC_ENV_ALIAS}".into())), ]) } + +fn config_evm_opts(config: &Config) -> EvmOpts { + config.to_figment(foundry_config::FigmentProviders::None).extract().unwrap() +} diff --git a/crates/test-utils/src/filter.rs b/crates/test-utils/src/filter.rs index 003b0170fca8..1ba905d27d8c 100644 --- a/crates/test-utils/src/filter.rs +++ b/crates/test-utils/src/filter.rs @@ -2,6 +2,7 @@ use foundry_common::TestFilter; use regex::Regex; use std::path::Path; +#[derive(Clone, Debug)] pub struct Filter { test_regex: Regex, contract_regex: Regex, From 20905ef9491f86c45415bf8ec764fbda31b83f54 Mon Sep 17 00:00:00 2001 From: Delweng Date: Fri, 29 Nov 2024 03:56:28 +0800 Subject: [PATCH 61/68] chore: rename the arg name of EvmOpts from evm_opts to evm_args (#9424) * script: evm_opts -> evm_args Signed-off-by: jsvisa * forge: evm_opts -> evm_args Signed-off-by: jsvisa * chisel: evm_opts -> evm_args Signed-off-by: jsvisa * forge: evm_opts -> evm_args Signed-off-by: jsvisa --------- Signed-off-by: jsvisa --- crates/chisel/bin/main.rs | 4 ++-- crates/forge/bin/cmd/config.rs | 4 ++-- crates/forge/bin/cmd/debug.rs | 6 +++--- crates/forge/bin/cmd/mod.rs | 2 +- crates/forge/bin/cmd/test/mod.rs | 6 +++--- crates/script/src/execute.rs | 2 +- crates/script/src/lib.rs | 10 +++++----- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/crates/chisel/bin/main.rs b/crates/chisel/bin/main.rs index acc2c6f17263..ca3fc1ff593e 100644 --- a/crates/chisel/bin/main.rs +++ b/crates/chisel/bin/main.rs @@ -35,7 +35,7 @@ extern crate foundry_common; static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; // Loads project's figment and merges the build cli arguments into it -foundry_config::merge_impl_figment_convert!(Chisel, opts, evm_opts); +foundry_config::merge_impl_figment_convert!(Chisel, opts, evm_args); const VERSION_MESSAGE: &str = concat!( env!("CARGO_PKG_VERSION"), @@ -76,7 +76,7 @@ pub struct Chisel { pub opts: CoreBuildArgs, #[command(flatten)] - pub evm_opts: EvmArgs, + pub evm_args: EvmArgs, } /// Chisel binary subcommands diff --git a/crates/forge/bin/cmd/config.rs b/crates/forge/bin/cmd/config.rs index 36f4d1731f4b..0aa1fdb6322a 100644 --- a/crates/forge/bin/cmd/config.rs +++ b/crates/forge/bin/cmd/config.rs @@ -5,7 +5,7 @@ use foundry_cli::utils::LoadConfig; use foundry_common::{evm::EvmArgs, shell}; use foundry_config::fix::fix_tomls; -foundry_config::impl_figment_convert!(ConfigArgs, opts, evm_opts); +foundry_config::impl_figment_convert!(ConfigArgs, opts, evm_args); /// CLI arguments for `forge config`. #[derive(Clone, Debug, Parser)] @@ -23,7 +23,7 @@ pub struct ConfigArgs { opts: BuildArgs, #[command(flatten)] - evm_opts: EvmArgs, + evm_args: EvmArgs, } impl ConfigArgs { diff --git a/crates/forge/bin/cmd/debug.rs b/crates/forge/bin/cmd/debug.rs index 421478bd5762..5ccfc13d57d0 100644 --- a/crates/forge/bin/cmd/debug.rs +++ b/crates/forge/bin/cmd/debug.rs @@ -6,7 +6,7 @@ use foundry_common::evm::EvmArgs; use std::path::PathBuf; // Loads project's figment and merges the build cli arguments into it -foundry_config::impl_figment_convert!(DebugArgs, opts, evm_opts); +foundry_config::impl_figment_convert!(DebugArgs, opts, evm_args); /// CLI arguments for `forge debug`. #[derive(Clone, Debug, Parser)] @@ -46,7 +46,7 @@ pub struct DebugArgs { pub opts: CoreBuildArgs, #[command(flatten)] - pub evm_opts: EvmArgs, + pub evm_args: EvmArgs, } impl DebugArgs { @@ -58,7 +58,7 @@ impl DebugArgs { sig: self.sig, gas_estimate_multiplier: 130, opts: self.opts, - evm_opts: self.evm_opts, + evm_args: self.evm_args, debug: true, dump: self.dump, retry: RETRY_VERIFY_ON_CREATE, diff --git a/crates/forge/bin/cmd/mod.rs b/crates/forge/bin/cmd/mod.rs index f2de1d6321d5..427b25fb0c50 100644 --- a/crates/forge/bin/cmd/mod.rs +++ b/crates/forge/bin/cmd/mod.rs @@ -24,7 +24,7 @@ //! #[derive(Clone, Debug, Parser)] //! pub struct MyArgs { //! #[command(flatten)] -//! evm_opts: EvmArgs, +//! evm_args: EvmArgs, //! #[command(flatten)] //! opts: BuildArgs, //! } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 6e19c96d08d7..982a8f0a3d26 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -59,7 +59,7 @@ pub use filter::FilterArgs; use forge::{result::TestKind, traces::render_trace_arena_inner}; // Loads project's figment and merges the build cli arguments into it -foundry_config::merge_impl_figment_convert!(TestArgs, opts, evm_opts); +foundry_config::merge_impl_figment_convert!(TestArgs, opts, evm_args); /// CLI arguments for `forge test`. #[derive(Clone, Debug, Parser)] @@ -162,7 +162,7 @@ pub struct TestArgs { pub rerun: bool, #[command(flatten)] - evm_opts: EvmArgs, + evm_args: EvmArgs, #[command(flatten)] opts: CoreBuildArgs, @@ -1001,7 +1001,7 @@ mod tests { fn extract_chain() { let test = |arg: &str, expected: Chain| { let args = TestArgs::parse_from(["foundry-cli", arg]); - assert_eq!(args.evm_opts.env.chain, Some(expected)); + assert_eq!(args.evm_args.env.chain, Some(expected)); let (config, evm_opts) = args.load_config_and_evm_opts().unwrap(); assert_eq!(config.chain, Some(expected)); assert_eq!(evm_opts.env.chain_id, Some(expected.id())); diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 51ab0141456b..3e6cc30a1242 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -189,7 +189,7 @@ impl PreExecutionState { if let Some(txs) = transactions { // If the user passed a `--sender` don't check anything. if self.build_data.predeploy_libraries.libraries_count() > 0 && - self.args.evm_opts.sender.is_none() + self.args.evm_args.sender.is_none() { for tx in txs.iter() { if tx.transaction.to().is_none() { diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 517eff552fd0..6ccc818410a4 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -73,7 +73,7 @@ mod transaction; mod verify; // Loads project's figment and merges the build cli arguments into it -foundry_config::merge_impl_figment_convert!(ScriptArgs, opts, evm_opts); +foundry_config::merge_impl_figment_convert!(ScriptArgs, opts, evm_args); /// CLI arguments for `forge script`. #[derive(Clone, Debug, Default, Parser)] @@ -210,7 +210,7 @@ pub struct ScriptArgs { pub wallets: MultiWalletOpts, #[command(flatten)] - pub evm_opts: EvmArgs, + pub evm_args: EvmArgs, #[command(flatten)] pub verifier: forge_verify::VerifierArgs, @@ -222,7 +222,7 @@ pub struct ScriptArgs { impl ScriptArgs { pub async fn preprocess(self) -> Result { let script_wallets = - Wallets::new(self.wallets.get_multi_wallet().await?, self.evm_opts.sender); + Wallets::new(self.wallets.get_multi_wallet().await?, self.evm_args.sender); let (config, mut evm_opts) = self.load_config_and_evm_opts_emit_warnings()?; @@ -411,7 +411,7 @@ impl ScriptArgs { } let mut prompt_user = false; - let max_size = match self.evm_opts.env.code_size_limit { + let max_size = match self.evm_args.env.code_size_limit { Some(size) => size, None => CONTRACT_MAX_SIZE, }; @@ -723,7 +723,7 @@ mod tests { "--code-size-limit", "50000", ]); - assert_eq!(args.evm_opts.env.code_size_limit, Some(50000)); + assert_eq!(args.evm_args.env.code_size_limit, Some(50000)); } #[test] From 27cabbd6c905b1273a5ed3ba7c10acce90833d76 Mon Sep 17 00:00:00 2001 From: Voronor <129545215+voronor@users.noreply.github.com> Date: Thu, 28 Nov 2024 20:57:46 +0100 Subject: [PATCH 62/68] Fix conditional syntax issue in macOS libusb check (#9384) This pull request addresses a minor but important syntax issue in the conditional statement used to check for the presence of libusb on macOS. --- foundryup/install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foundryup/install b/foundryup/install index 1a8bc8c6ed5a..22870f939a93 100755 --- a/foundryup/install +++ b/foundryup/install @@ -54,7 +54,7 @@ if [[ ":$PATH:" != *":${FOUNDRY_BIN_DIR}:"* ]]; then fi # Warn MacOS users that they may need to manually install libusb via Homebrew: -if [[ "$OSTYPE" =~ ^darwin ]] && [[ ! -f /usr/local/opt/libusb/lib/libusb-1.0.0.dylib && ! -f /opt/homebrew/opt/libusb/lib/libusb-1.0.0.dylib ]]; then +if [[ "$OSTYPE" =~ ^darwin ]] && [[ ! -f /usr/local/opt/libusb/lib/libusb-1.0.0.dylib ]] && [[ ! -f /opt/homebrew/opt/libusb/lib/libusb-1.0.0.dylib ]]; then echo && echo "warning: libusb not found. You may need to install it manually on MacOS via Homebrew (brew install libusb)." fi From 2e9f53632a787323318e4575d7a0325ef3e7cc84 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Fri, 29 Nov 2024 01:21:24 -0500 Subject: [PATCH 63/68] feat: add timeouts to fuzz testing (#9394) * feat: add timeouts to fuzz testing Adds --fuzz-timeout-secs to fuzz tests which will cause a property test to timeout after a certain number of seconds. Also adds --fuzz-allow-timeouts so that timeouts are optionally not considered to be failures. * simplify timeout implementation * use u32 for timeout * switch back to failing for timeouts * clippy * Nits: - move logic to interrupt invariant test in depth loop - add and reuse start_timer fn and TEST_TIMEOUT constant - add fuzz and invariant tests - fix failing test * Fix fmt * Changes after review: introduce FuzzTestTimer --------- Co-authored-by: grandizzy --- crates/config/src/fuzz.rs | 3 ++ crates/config/src/invariant.rs | 4 ++ crates/evm/core/src/constants.rs | 3 ++ crates/evm/evm/src/executors/fuzz/mod.rs | 34 +++++++++---- crates/evm/evm/src/executors/invariant/mod.rs | 15 +++++- crates/evm/evm/src/executors/mod.rs | 22 +++++++- crates/forge/bin/cmd/test/mod.rs | 7 +++ crates/forge/tests/it/fuzz.rs | 35 +++++++++++++ crates/forge/tests/it/invariant.rs | 50 +++++++++++++++++++ crates/forge/tests/it/test_helpers.rs | 2 + 10 files changed, 162 insertions(+), 13 deletions(-) diff --git a/crates/config/src/fuzz.rs b/crates/config/src/fuzz.rs index ae3d3c796701..26e1c080cbc8 100644 --- a/crates/config/src/fuzz.rs +++ b/crates/config/src/fuzz.rs @@ -28,6 +28,8 @@ pub struct FuzzConfig { pub failure_persist_file: Option, /// show `console.log` in fuzz test, defaults to `false` pub show_logs: bool, + /// Optional timeout (in seconds) for each property test + pub timeout: Option, } impl Default for FuzzConfig { @@ -41,6 +43,7 @@ impl Default for FuzzConfig { failure_persist_dir: None, failure_persist_file: None, show_logs: false, + timeout: None, } } } diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index 97f189b363d1..334cf3b87b99 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -30,6 +30,8 @@ pub struct InvariantConfig { pub failure_persist_dir: Option, /// Whether to collect and display fuzzed selectors metrics. pub show_metrics: bool, + /// Optional timeout (in seconds) for each invariant test. + pub timeout: Option, } impl Default for InvariantConfig { @@ -45,6 +47,7 @@ impl Default for InvariantConfig { gas_report_samples: 256, failure_persist_dir: None, show_metrics: false, + timeout: None, } } } @@ -63,6 +66,7 @@ impl InvariantConfig { gas_report_samples: 256, failure_persist_dir: Some(cache_dir), show_metrics: false, + timeout: None, } } diff --git a/crates/evm/core/src/constants.rs b/crates/evm/core/src/constants.rs index 70c7441d20a7..cebbdcb87c79 100644 --- a/crates/evm/core/src/constants.rs +++ b/crates/evm/core/src/constants.rs @@ -37,6 +37,9 @@ pub const MAGIC_ASSUME: &[u8] = b"FOUNDRY::ASSUME"; /// Magic return value returned by the `skip` cheatcode. Optionally appended with a reason. pub const MAGIC_SKIP: &[u8] = b"FOUNDRY::SKIP"; +/// Test timeout return value. +pub const TEST_TIMEOUT: &str = "FOUNDRY::TEST_TIMEOUT"; + /// The address that deploys the default CREATE2 deployer contract. pub const DEFAULT_CREATE2_DEPLOYER_DEPLOYER: Address = address!("3fAB184622Dc19b6109349B94811493BF2a45362"); diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 2bbe80a63ead..0d79f8fa4961 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -1,4 +1,4 @@ -use crate::executors::{Executor, RawCallResult}; +use crate::executors::{Executor, FuzzTestTimer, RawCallResult}; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; use alloy_primitives::{map::HashMap, Address, Bytes, Log, U256}; @@ -6,7 +6,7 @@ use eyre::Result; use foundry_common::evm::Breakpoints; use foundry_config::FuzzConfig; use foundry_evm_core::{ - constants::MAGIC_ASSUME, + constants::{MAGIC_ASSUME, TEST_TIMEOUT}, decode::{RevertDecoder, SkipReason}, }; use foundry_evm_coverage::HitMaps; @@ -98,7 +98,15 @@ impl FuzzedExecutor { let max_traces_to_collect = std::cmp::max(1, self.config.gas_report_samples) as usize; let show_logs = self.config.show_logs; + // Start timer for this fuzz test. + let timer = FuzzTestTimer::new(self.config.timeout); + let run_result = self.runner.clone().run(&strategy, |calldata| { + // Check if the timeout has been reached. + if timer.is_timed_out() { + return Err(TestCaseError::fail(TEST_TIMEOUT)); + } + let fuzz_res = self.single_fuzz(address, should_fail, calldata)?; // If running with progress then increment current run. @@ -193,17 +201,21 @@ impl FuzzedExecutor { } Err(TestError::Fail(reason, _)) => { let reason = reason.to_string(); - result.reason = (!reason.is_empty()).then_some(reason); - - let args = if let Some(data) = calldata.get(4..) { - func.abi_decode_input(data, false).unwrap_or_default() + if reason == TEST_TIMEOUT { + // If the reason is a timeout, we consider the fuzz test successful. + result.success = true; } else { - vec![] - }; + result.reason = (!reason.is_empty()).then_some(reason); + let args = if let Some(data) = calldata.get(4..) { + func.abi_decode_input(data, false).unwrap_or_default() + } else { + vec![] + }; - result.counterexample = Some(CounterExample::Single( - BaseCounterExample::from_fuzz_call(calldata, args, call.traces), - )); + result.counterexample = Some(CounterExample::Single( + BaseCounterExample::from_fuzz_call(calldata, args, call.traces), + )); + } } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index f98dd21114cb..d5fdb5668f5e 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -10,6 +10,7 @@ use foundry_config::InvariantConfig; use foundry_evm_core::{ constants::{ CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME, + TEST_TIMEOUT, }, precompiles::PRECOMPILES, }; @@ -49,7 +50,7 @@ pub use result::InvariantFuzzTestResult; use serde::{Deserialize, Serialize}; mod shrink; -use crate::executors::EvmError; +use crate::executors::{EvmError, FuzzTestTimer}; pub use shrink::check_sequence; sol! { @@ -332,6 +333,9 @@ impl<'a> InvariantExecutor<'a> { let (invariant_test, invariant_strategy) = self.prepare_test(&invariant_contract, fuzz_fixtures)?; + // Start timer for this invariant test. + let timer = FuzzTestTimer::new(self.config.timeout); + let _ = self.runner.run(&invariant_strategy, |first_input| { // Create current invariant run data. let mut current_run = InvariantTestRun::new( @@ -347,6 +351,15 @@ impl<'a> InvariantExecutor<'a> { } while current_run.depth < self.config.depth { + // Check if the timeout has been reached. + if timer.is_timed_out() { + // Since we never record a revert here the test is still considered + // successful even though it timed out. We *want* + // this behavior for now, so that's ok, but + // future developers should be aware of this. + return Err(TestCaseError::fail(TEST_TIMEOUT)); + } + let tx = current_run.inputs.last().ok_or_else(|| { TestCaseError::fail("No input generated to call fuzzed target.") })?; diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index b5b31b812298..2ccfad9e2a58 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -35,7 +35,10 @@ use revm::{ ResultAndState, SignedAuthorization, SpecId, TxEnv, TxKind, }, }; -use std::borrow::Cow; +use std::{ + borrow::Cow, + time::{Duration, Instant}, +}; mod builder; pub use builder::ExecutorBuilder; @@ -952,3 +955,20 @@ fn convert_executed_result( chisel_state, }) } + +/// Timer for a fuzz test. +pub struct FuzzTestTimer { + /// Inner fuzz test timer - (test start time, test duration). + inner: Option<(Instant, Duration)>, +} + +impl FuzzTestTimer { + pub fn new(timeout: Option) -> Self { + Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) } + } + + /// Whether the current fuzz test timed out and should be stopped. + pub fn is_timed_out(&self) -> bool { + self.inner.is_some_and(|(start, duration)| start.elapsed() > duration) + } +} diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 982a8f0a3d26..18ab08d6d152 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -145,6 +145,10 @@ pub struct TestArgs { #[arg(long, env = "FOUNDRY_FUZZ_RUNS", value_name = "RUNS")] pub fuzz_runs: Option, + /// Timeout for each fuzz run in seconds. + #[arg(long, env = "FOUNDRY_FUZZ_TIMEOUT", value_name = "TIMEOUT")] + pub fuzz_timeout: Option, + /// File to rerun fuzz failures from. #[arg(long)] pub fuzz_input_file: Option, @@ -864,6 +868,9 @@ impl Provider for TestArgs { if let Some(fuzz_runs) = self.fuzz_runs { fuzz_dict.insert("runs".to_string(), fuzz_runs.into()); } + if let Some(fuzz_timeout) = self.fuzz_timeout { + fuzz_dict.insert("timeout".to_string(), fuzz_timeout.into()); + } if let Some(fuzz_input_file) = self.fuzz_input_file.clone() { fuzz_dict.insert("failure_persist_file".to_string(), fuzz_input_file.into()); } diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index eaa627b9652f..8b49d4accad3 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -240,3 +240,38 @@ contract InlineMaxRejectsTest is Test { ... "#]]); }); + +// Tests that test timeout config is properly applied. +// If test doesn't timeout after one second, then test will fail with `rejected too many inputs`. +forgetest_init!(test_fuzz_timeout, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "Contract.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract FuzzTimeoutTest is Test { + /// forge-config: default.fuzz.max-test-rejects = 10000 + /// forge-config: default.fuzz.timeout = 1 + function test_fuzz_bound(uint256 a) public pure { + vm.assume(a == 0); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/Contract.t.sol:FuzzTimeoutTest +[PASS] test_fuzz_bound(uint256) (runs: [..], [AVG_GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); +}); diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 2f4da4054245..76afd5b36a77 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -949,3 +949,53 @@ Ran 2 tests for test/SelectorMetricsTest.t.sol:CounterTest ... "#]]); }); + +// Tests that invariant exists with success after configured timeout. +forgetest_init!(should_apply_configured_timeout, |prj, cmd| { + // Add initial test that breaks invariant. + prj.add_test( + "TimeoutTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract TimeoutHandler is Test { + uint256 public count; + + function increment() public { + count++; + } +} + +contract TimeoutTest is Test { + TimeoutHandler handler; + + function setUp() public { + handler = new TimeoutHandler(); + } + + /// forge-config: default.invariant.runs = 10000 + /// forge-config: default.invariant.depth = 20000 + /// forge-config: default.invariant.timeout = 1 + function invariant_counter_timeout() public view { + // Invariant will fail if more than 10000 increments. + // Make sure test timeouts after one second and remaining runs are canceled. + require(handler.count() < 10000); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test", "--mt", "invariant_counter_timeout"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/TimeoutTest.t.sol:TimeoutTest +[PASS] invariant_counter_timeout() (runs: 0, calls: 0, reverts: 0) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); +}); diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 298bbae2971d..54985b9b6154 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -121,6 +121,7 @@ impl ForgeTestProfile { failure_persist_dir: Some(tempfile::tempdir().unwrap().into_path()), failure_persist_file: Some("testfailure".to_string()), show_logs: false, + timeout: None, }; config.invariant = InvariantConfig { runs: 256, @@ -145,6 +146,7 @@ impl ForgeTestProfile { .into_path(), ), show_metrics: false, + timeout: None, }; config.sanitized() From 0d76df57a28236908084f21c965b20e30ed9dfdd Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 29 Nov 2024 09:52:07 +0200 Subject: [PATCH 64/68] feat(`cast`): `decode-error` with sig, local cache and openchain api (#9428) * feat(cast): Add custom error decoding support * Review changes * Changes after review: decode with Openchain too, add test * Review changes: nit, handle incomplete selectors --- crates/cast/bin/args.rs | 10 ++++ crates/cast/bin/main.rs | 27 ++++++++- crates/cast/tests/cli/main.rs | 56 ++++++++++++++++++- crates/cli/src/utils/cmd.rs | 3 + crates/common/src/abi.rs | 7 ++- crates/common/src/selectors.rs | 12 ++-- .../evm/traces/src/identifier/signatures.rs | 21 ++++++- 7 files changed, 124 insertions(+), 12 deletions(-) diff --git a/crates/cast/bin/args.rs b/crates/cast/bin/args.rs index 7078810e4ce9..e4cf32639d4b 100644 --- a/crates/cast/bin/args.rs +++ b/crates/cast/bin/args.rs @@ -539,6 +539,16 @@ pub enum CastSubcommand { data: String, }, + /// Decode custom error data. + #[command(visible_aliases = &["error-decode", "--error-decode", "erd"])] + DecodeError { + /// The error signature. If none provided then tries to decode from local cache or `https://api.openchain.xyz`. + #[arg(long, visible_alias = "error-sig")] + sig: Option, + /// The error data to decode. + data: String, + }, + /// Decode ABI-encoded input or output data. /// /// Defaults to decoding output data. To decode input data pass --input. diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 21b1df36d6cd..cacddb8344f5 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate tracing; -use alloy_dyn_abi::{DynSolValue, EventExt}; +use alloy_dyn_abi::{DynSolValue, ErrorExt, EventExt}; use alloy_primitives::{eip191_hash_message, hex, keccak256, Address, B256}; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, BlockNumberOrTag::Latest}; @@ -11,7 +11,7 @@ use clap_complete::generate; use eyre::Result; use foundry_cli::{handler, utils}; use foundry_common::{ - abi::get_event, + abi::{get_error, get_event}, ens::{namehash, ProviderEnsExt}, fmt::{format_tokens, format_tokens_raw, format_uint_exp}, fs, @@ -30,6 +30,7 @@ pub mod cmd; pub mod tx; use args::{Cast as CastArgs, CastSubcommand, ToBaseArgs}; +use cast::traces::identifier::SignaturesIdentifier; #[macro_use] extern crate foundry_common; @@ -216,6 +217,28 @@ async fn main_args(args: CastArgs) -> Result<()> { let decoded_event = event.decode_log_parts(None, &hex::decode(data)?, false)?; print_tokens(&decoded_event.body); } + CastSubcommand::DecodeError { sig, data } => { + let error = if let Some(err_sig) = sig { + get_error(err_sig.as_str())? + } else { + let data = data.strip_prefix("0x").unwrap_or(data.as_str()); + let selector = data.get(..8).unwrap_or_default(); + let identified_error = + SignaturesIdentifier::new(Config::foundry_cache_dir(), false)? + .write() + .await + .identify_error(&hex::decode(selector)?) + .await; + if let Some(error) = identified_error { + let _ = sh_println!("{}", error.signature()); + error + } else { + eyre::bail!("No matching error signature found for selector `{selector}`") + } + }; + let decoded_error = error.decode_error(&hex::decode(data)?)?; + print_tokens(&decoded_error.body); + } CastSubcommand::Interface(cmd) => cmd.run().await?, CastSubcommand::CreationCode(cmd) => cmd.run().await?, CastSubcommand::ConstructorArgs(cmd) => cmd.run().await?, diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index d2c70a779e65..0cd43766e3e1 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -6,7 +6,7 @@ use alloy_primitives::{b256, B256}; use alloy_rpc_types::{BlockNumberOrTag, Index}; use anvil::{EthereumHardfork, NodeConfig}; use foundry_test_utils::{ - casttest, file, forgetest_async, + casttest, file, forgetest, forgetest_async, rpc::{ next_etherscan_api_key, next_http_rpc_endpoint, next_mainnet_etherscan_api_key, next_rpc_endpoint, next_ws_rpc_endpoint, @@ -1482,6 +1482,60 @@ casttest!(event_decode, |_prj, cmd| { "#]]); }); +// tests cast can decode traces with provided signature +casttest!(error_decode_with_sig, |_prj, cmd| { + cmd.args(["decode-error", "--sig", "AnotherValueTooHigh(uint256,address)", "0x7191bc6200000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000D0004F"]).assert_success().stdout_eq(str![[r#" +101 +0x0000000000000000000000000000000000D0004F + +"#]]); + + cmd.args(["--json"]).assert_success().stdout_eq(str![[r#" +[ + "101", + "0x0000000000000000000000000000000000D0004F" +] + +"#]]); +}); + +// tests cast can decode traces with Openchain API +casttest!(error_decode_with_openchain, |_prj, cmd| { + cmd.args(["decode-error", "0x7a0e198500000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000064"]).assert_success().stdout_eq(str![[r#" +ValueTooHigh(uint256,uint256) +101 +100 + +"#]]); +}); + +// tests cast can decode traces when using local sig identifiers cache +forgetest!(error_decode_with_cache, |prj, cmd| { + foundry_test_utils::util::initialize(prj.root()); + prj.add_source( + "LocalProjectContract", + r#" +contract ContractWithCustomError { + error AnotherValueTooHigh(uint256, address); +} + "#, + ) + .unwrap(); + // Store selectors in local cache. + cmd.forge_fuse().args(["selectors", "cache"]).assert_success(); + + // Assert cast can decode custom error with local cache. + cmd.cast_fuse() + .args(["decode-error", "0x7191bc6200000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000D0004F"]) + .assert_success() + .stdout_eq(str![[r#" +AnotherValueTooHigh(uint256,address) +101 +0x0000000000000000000000000000000000D0004F + +"#]]); +}); + casttest!(format_units, |_prj, cmd| { cmd.args(["format-units", "1000000", "6"]).assert_success().stdout_eq(str![[r#" 1 diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 67aa65073ff8..4cf24221bbee 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -501,6 +501,9 @@ pub fn cache_local_signatures(output: &ProjectCompileOutput, cache_path: PathBuf .events .insert(event.selector().to_string(), event.full_signature()); } + for error in abi.errors() { + cached_signatures.errors.insert(error.selector().to_string(), error.signature()); + } // External libraries doesn't have functions included in abi, but `methodIdentifiers`. if let Some(method_identifiers) = &artifact.method_identifiers { method_identifiers.iter().for_each(|(signature, selector)| { diff --git a/crates/common/src/abi.rs b/crates/common/src/abi.rs index de9b36219ecd..fa9f241719fd 100644 --- a/crates/common/src/abi.rs +++ b/crates/common/src/abi.rs @@ -1,7 +1,7 @@ //! ABI related helper functions. use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt, JsonAbiExt}; -use alloy_json_abi::{Event, Function, Param}; +use alloy_json_abi::{Error, Event, Function, Param}; use alloy_primitives::{hex, Address, LogData}; use eyre::{Context, ContextCompat, Result}; use foundry_block_explorers::{contract::ContractMetadata, errors::EtherscanError, Client}; @@ -85,6 +85,11 @@ pub fn get_event(sig: &str) -> Result { Event::parse(sig).wrap_err("could not parse event signature") } +/// Given an error signature string, it tries to parse it as a `Error` +pub fn get_error(sig: &str) -> Result { + Error::parse(sig).wrap_err("could not parse event signature") +} + /// Given an event without indexed parameters and a rawlog, it tries to return the event with the /// proper indexed parameters. Otherwise, it returns the original event. pub fn get_indexed_event(mut event: Event, raw_log: &LogData) -> Event { diff --git a/crates/common/src/selectors.rs b/crates/common/src/selectors.rs index cd4e2ffd0882..cb59e1f32e37 100644 --- a/crates/common/src/selectors.rs +++ b/crates/common/src/selectors.rs @@ -140,7 +140,7 @@ impl OpenChainClient { .ok_or_else(|| eyre::eyre!("No signature found")) } - /// Decodes the given function or event selectors using OpenChain + /// Decodes the given function, error or event selectors using OpenChain. pub async fn decode_selectors( &self, selector_type: SelectorType, @@ -164,8 +164,8 @@ impl OpenChainClient { self.ensure_not_spurious()?; let expected_len = match selector_type { - SelectorType::Function => 10, // 0x + hex(4bytes) - SelectorType::Event => 66, // 0x + hex(32bytes) + SelectorType::Function | SelectorType::Error => 10, // 0x + hex(4bytes) + SelectorType::Event => 66, // 0x + hex(32bytes) }; if let Some(s) = selectors.iter().find(|s| s.len() != expected_len) { eyre::bail!( @@ -193,7 +193,7 @@ impl OpenChainClient { let url = format!( "{SELECTOR_LOOKUP_URL}?{ltype}={selectors_str}", ltype = match selector_type { - SelectorType::Function => "function", + SelectorType::Function | SelectorType::Error => "function", SelectorType::Event => "event", }, selectors_str = selectors.join(",") @@ -212,7 +212,7 @@ impl OpenChainClient { } let decoded = match selector_type { - SelectorType::Function => api_response.result.function, + SelectorType::Function | SelectorType::Error => api_response.result.function, SelectorType::Event => api_response.result.event, }; @@ -391,6 +391,8 @@ pub enum SelectorType { Function, /// An event selector. Event, + /// An custom error selector. + Error, } /// Decodes the given function or event selector using OpenChain. diff --git a/crates/evm/traces/src/identifier/signatures.rs b/crates/evm/traces/src/identifier/signatures.rs index 2a5ef354a753..801f9da373d5 100644 --- a/crates/evm/traces/src/identifier/signatures.rs +++ b/crates/evm/traces/src/identifier/signatures.rs @@ -1,7 +1,7 @@ -use alloy_json_abi::{Event, Function}; +use alloy_json_abi::{Error, Event, Function}; use alloy_primitives::{hex, map::HashSet}; use foundry_common::{ - abi::{get_event, get_func}, + abi::{get_error, get_event, get_func}, fs, selectors::{OpenChainClient, SelectorType}, }; @@ -13,6 +13,7 @@ pub type SingleSignaturesIdentifier = Arc>; #[derive(Debug, Default, Serialize, Deserialize)] pub struct CachedSignatures { + pub errors: BTreeMap, pub events: BTreeMap, pub functions: BTreeMap, } @@ -39,7 +40,7 @@ impl CachedSignatures { /// `https://openchain.xyz` or a local cache. #[derive(Debug)] pub struct SignaturesIdentifier { - /// Cached selectors for functions and events. + /// Cached selectors for functions, events and custom errors. cached: CachedSignatures, /// Location where to save `CachedSignatures`. cached_path: Option, @@ -101,6 +102,7 @@ impl SignaturesIdentifier { let cache = match selector_type { SelectorType::Function => &mut self.cached.functions, SelectorType::Event => &mut self.cached.events, + SelectorType::Error => &mut self.cached.errors, }; let hex_identifiers: Vec = @@ -157,6 +159,19 @@ impl SignaturesIdentifier { pub async fn identify_event(&mut self, identifier: &[u8]) -> Option { self.identify_events(&[identifier]).await.pop().unwrap() } + + /// Identifies `Error`s from its cache or `https://api.openchain.xyz`. + pub async fn identify_errors( + &mut self, + identifiers: impl IntoIterator>, + ) -> Vec> { + self.identify(SelectorType::Error, identifiers, get_error).await + } + + /// Identifies `Error` from its cache or `https://api.openchain.xyz`. + pub async fn identify_error(&mut self, identifier: &[u8]) -> Option { + self.identify_errors(&[identifier]).await.pop().unwrap() + } } impl Drop for SignaturesIdentifier { From 0f7268f46d2db7502cd0a75c8cfba34f06f8fd6e Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 29 Nov 2024 11:38:40 +0200 Subject: [PATCH 65/68] feat(`cast`): `decode-event` with local and openchain API (#9431) --- crates/cast/bin/args.rs | 5 ++-- crates/cast/bin/main.rs | 25 +++++++++++++++++-- crates/cast/tests/cli/main.rs | 47 +++++++++++++++++++++++++++++------ 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/crates/cast/bin/args.rs b/crates/cast/bin/args.rs index e4cf32639d4b..fb7fb0757890 100644 --- a/crates/cast/bin/args.rs +++ b/crates/cast/bin/args.rs @@ -533,8 +533,9 @@ pub enum CastSubcommand { /// Decode event data. #[command(visible_aliases = &["event-decode", "--event-decode", "ed"])] DecodeEvent { - /// The event signature. - sig: String, + /// The event signature. If none provided then tries to decode from local cache or `https://api.openchain.xyz`. + #[arg(long, visible_alias = "event-sig")] + sig: Option, /// The event data to decode. data: String, }, diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index cacddb8344f5..fcb5a20eb1ad 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -213,8 +213,29 @@ async fn main_args(args: CastArgs) -> Result<()> { print_tokens(&tokens); } CastSubcommand::DecodeEvent { sig, data } => { - let event = get_event(sig.as_str())?; - let decoded_event = event.decode_log_parts(None, &hex::decode(data)?, false)?; + let decoded_event = if let Some(event_sig) = sig { + get_event(event_sig.as_str())?.decode_log_parts(None, &hex::decode(data)?, false)? + } else { + let data = data.strip_prefix("0x").unwrap_or(data.as_str()); + let selector = data.get(..64).unwrap_or_default(); + let identified_event = + SignaturesIdentifier::new(Config::foundry_cache_dir(), false)? + .write() + .await + .identify_event(&hex::decode(selector)?) + .await; + if let Some(event) = identified_event { + let _ = sh_println!("{}", event.signature()); + let data = data.get(64..).unwrap_or_default(); + get_event(event.signature().as_str())?.decode_log_parts( + None, + &hex::decode(data)?, + false, + )? + } else { + eyre::bail!("No matching event signature found for selector `{selector}`") + } + }; print_tokens(&decoded_event.body); } CastSubcommand::DecodeError { sig, data } => { diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 0cd43766e3e1..f3d04b09456b 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1474,15 +1474,35 @@ casttest!(string_decode, |_prj, cmd| { "#]]); }); -casttest!(event_decode, |_prj, cmd| { - cmd.args(["decode-event", "MyEvent(uint256,address)", "0x000000000000000000000000000000000000000000000000000000000000004e0000000000000000000000000000000000000000000000000000000000d0004f"]).assert_success().stdout_eq(str![[r#" +// tests cast can decode event with provided signature +casttest!(event_decode_with_sig, |_prj, cmd| { + cmd.args(["decode-event", "--sig", "MyEvent(uint256,address)", "0x000000000000000000000000000000000000000000000000000000000000004e0000000000000000000000000000000000000000000000000000000000d0004f"]).assert_success().stdout_eq(str![[r#" 78 0x0000000000000000000000000000000000D0004F +"#]]); + + cmd.args(["--json"]).assert_success().stdout_eq(str![[r#" +[ + "78", + "0x0000000000000000000000000000000000D0004F" +] + "#]]); }); -// tests cast can decode traces with provided signature +// tests cast can decode event with Openchain API +casttest!(event_decode_with_openchain, |prj, cmd| { + prj.clear_cache(); + cmd.args(["decode-event", "0xe27c4c1372396a3d15a9922f74f9dfc7c72b1ad6d63868470787249c356454c1000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000dd00000004e"]).assert_success().stdout_eq(str![[r#" +BaseCurrencySet(address,uint256) +0x000000000000000000000000000000000000004e +15187004358734 [1.518e13] + +"#]]); +}); + +// tests cast can decode error with provided signature casttest!(error_decode_with_sig, |_prj, cmd| { cmd.args(["decode-error", "--sig", "AnotherValueTooHigh(uint256,address)", "0x7191bc6200000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000D0004F"]).assert_success().stdout_eq(str![[r#" 101 @@ -1499,8 +1519,9 @@ casttest!(error_decode_with_sig, |_prj, cmd| { "#]]); }); -// tests cast can decode traces with Openchain API -casttest!(error_decode_with_openchain, |_prj, cmd| { +// tests cast can decode error with Openchain API +casttest!(error_decode_with_openchain, |prj, cmd| { + prj.clear_cache(); cmd.args(["decode-error", "0x7a0e198500000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000064"]).assert_success().stdout_eq(str![[r#" ValueTooHigh(uint256,uint256) 101 @@ -1509,14 +1530,16 @@ ValueTooHigh(uint256,uint256) "#]]); }); -// tests cast can decode traces when using local sig identifiers cache -forgetest!(error_decode_with_cache, |prj, cmd| { +// tests cast can decode error and event when using local sig identifiers cache +forgetest!(error_event_decode_with_cache, |prj, cmd| { + prj.clear_cache(); foundry_test_utils::util::initialize(prj.root()); prj.add_source( "LocalProjectContract", r#" contract ContractWithCustomError { error AnotherValueTooHigh(uint256, address); + event MyUniqueEventWithinLocalProject(uint256 a, address b); } "#, ) @@ -1533,6 +1556,16 @@ AnotherValueTooHigh(uint256,address) 101 0x0000000000000000000000000000000000D0004F +"#]]); + // Assert cast can decode event with local cache. + cmd.cast_fuse() + .args(["decode-event", "0xbd3699995dcc867b64dbb607be2c33be38df9134bef1178df13bfb9446e73104000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000dd00000004e"]) + .assert_success() + .stdout_eq(str![[r#" +MyUniqueEventWithinLocalProject(uint256,address) +78 +0x00000000000000000000000000000DD00000004e + "#]]); }); From fbbcc8c4521bae19dfeac451d51db97c0912e512 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Fri, 29 Nov 2024 11:15:21 +0100 Subject: [PATCH 66/68] chore: use alloy-chains' `is_arbitrum` (#9432) * use alloy-chains' is_arbitrum * clean up --- crates/anvil/src/eth/backend/mem/mod.rs | 11 +++--- crates/cli/src/utils/cmd.rs | 47 ++++++++++--------------- 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 83718ad821aa..fa79332e7406 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -3013,11 +3013,8 @@ pub fn prove_storage(storage: &HashMap, keys: &[B256]) -> Vec bool { - matches!( - NamedChain::try_from(chain_id), - Ok(NamedChain::Arbitrum | - NamedChain::ArbitrumTestnet | - NamedChain::ArbitrumGoerli | - NamedChain::ArbitrumNova) - ) + if let Ok(chain) = NamedChain::try_from(chain_id) { + return chain.is_arbitrum() + } + false } diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 4cf24221bbee..0d2febf93d07 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -159,27 +159,24 @@ pub fn init_progress(len: u64, label: &str) -> indicatif::ProgressBar { /// True if the network calculates gas costs differently. pub fn has_different_gas_calc(chain_id: u64) -> bool { if let Some(chain) = Chain::from(chain_id).named() { - return matches!( - chain, - NamedChain::Acala | - NamedChain::AcalaMandalaTestnet | - NamedChain::AcalaTestnet | - NamedChain::Arbitrum | - NamedChain::ArbitrumGoerli | - NamedChain::ArbitrumSepolia | - NamedChain::ArbitrumTestnet | - NamedChain::Etherlink | - NamedChain::EtherlinkTestnet | - NamedChain::Karura | - NamedChain::KaruraTestnet | - NamedChain::Mantle | - NamedChain::MantleSepolia | - NamedChain::MantleTestnet | - NamedChain::Moonbase | - NamedChain::Moonbeam | - NamedChain::MoonbeamDev | - NamedChain::Moonriver - ); + return chain.is_arbitrum() || + matches!( + chain, + NamedChain::Acala | + NamedChain::AcalaMandalaTestnet | + NamedChain::AcalaTestnet | + NamedChain::Etherlink | + NamedChain::EtherlinkTestnet | + NamedChain::Karura | + NamedChain::KaruraTestnet | + NamedChain::Mantle | + NamedChain::MantleSepolia | + NamedChain::MantleTestnet | + NamedChain::Moonbase | + NamedChain::Moonbeam | + NamedChain::MoonbeamDev | + NamedChain::Moonriver + ); } false } @@ -187,13 +184,7 @@ pub fn has_different_gas_calc(chain_id: u64) -> bool { /// True if it supports broadcasting in batches. pub fn has_batch_support(chain_id: u64) -> bool { if let Some(chain) = Chain::from(chain_id).named() { - return !matches!( - chain, - NamedChain::Arbitrum | - NamedChain::ArbitrumTestnet | - NamedChain::ArbitrumGoerli | - NamedChain::ArbitrumSepolia - ); + return !chain.is_arbitrum(); } true } From af0fee2031ed4273c1b697775650de1efb2a2d4e Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 29 Nov 2024 18:39:51 +0530 Subject: [PATCH 67/68] feat: rpc_headers in cast and config (#9429) * feat: specify `rpc_headers` in cast and config * test --- Cargo.lock | 18 ++++++++++++++++++ Cargo.toml | 1 + crates/cast/Cargo.toml | 1 + crates/cast/bin/cmd/run.rs | 8 +++----- crates/cli/src/opts/ethereum.rs | 7 +++++++ crates/cli/src/utils/mod.rs | 10 ++++++++++ crates/config/src/lib.rs | 10 ++++++++++ crates/forge/tests/cli/config.rs | 1 + 8 files changed, 51 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09fab74b7f0d..b83fd212c944 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -262,6 +262,23 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-node-bindings" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9805d126f24be459b958973c0569c73e1aadd27d4535eee82b2b6764aa03616" +dependencies = [ + "alloy-genesis", + "alloy-primitives", + "k256", + "rand", + "serde_json", + "tempfile", + "thiserror 1.0.69", + "tracing", + "url", +] + [[package]] name = "alloy-primitives" version = "0.8.12" @@ -1957,6 +1974,7 @@ dependencies = [ "alloy-json-abi", "alloy-json-rpc", "alloy-network", + "alloy-node-bindings", "alloy-primitives", "alloy-provider", "alloy-rlp", diff --git a/Cargo.toml b/Cargo.toml index 814c43aa556a..b07d99cc9f92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -205,6 +205,7 @@ alloy-transport = { version = "0.6.4", default-features = false } alloy-transport-http = { version = "0.6.4", default-features = false } alloy-transport-ipc = { version = "0.6.4", default-features = false } alloy-transport-ws = { version = "0.6.4", default-features = false } +alloy-node-bindings = { version = "0.6.4", default-features = false } ## alloy-core alloy-dyn-abi = "0.8.11" diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 0887649e58dd..f6011831accb 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -94,6 +94,7 @@ tikv-jemallocator = { workspace = true, optional = true } [dev-dependencies] anvil.workspace = true foundry-test-utils.workspace = true +alloy-node-bindings.workspace = true async-trait.workspace = true divan.workspace = true diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index cfad7263a1e4..bb5c505b11c3 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -107,11 +107,9 @@ impl RunArgs { let compute_units_per_second = if self.no_rate_limit { Some(u64::MAX) } else { self.compute_units_per_second }; - let provider = foundry_common::provider::ProviderBuilder::new( - &config.get_rpc_url_or_localhost_http()?, - ) - .compute_units_per_second_opt(compute_units_per_second) - .build()?; + let provider = foundry_cli::utils::get_provider_builder(&config)? + .compute_units_per_second_opt(compute_units_per_second) + .build()?; let tx_hash = self.tx_hash.parse().wrap_err("invalid tx hash")?; let tx = provider diff --git a/crates/cli/src/opts/ethereum.rs b/crates/cli/src/opts/ethereum.rs index b858d998fafb..4b15b8551940 100644 --- a/crates/cli/src/opts/ethereum.rs +++ b/crates/cli/src/opts/ethereum.rs @@ -48,6 +48,10 @@ pub struct RpcOpts { /// Default value: 45 #[arg(long, env = "ETH_RPC_TIMEOUT")] pub rpc_timeout: Option, + + /// Specify custom headers for RPC requests. + #[arg(long, alias = "headers", env = "ETH_RPC_HEADERS", value_delimiter(','))] + pub rpc_headers: Option>, } impl_figment_convert_cast!(RpcOpts); @@ -95,6 +99,9 @@ impl RpcOpts { if let Some(rpc_timeout) = self.rpc_timeout { dict.insert("eth_rpc_timeout".into(), rpc_timeout.into()); } + if let Some(headers) = &self.rpc_headers { + dict.insert("eth_rpc_headers".into(), headers.clone().into()); + } dict } } diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 2d8471e62685..f833924f57fe 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -39,6 +39,8 @@ pub const STATIC_FUZZ_SEED: [u8; 32] = [ 0x5d, 0x64, 0x0b, 0x19, 0xad, 0xf0, 0xe3, 0x57, 0xb8, 0xd4, 0xbe, 0x7d, 0x49, 0xee, 0x70, 0xe6, ]; +const DEFAULT_USER_AGENT: &str = concat!("foundry/", env!("CARGO_PKG_VERSION")); + /// Useful extensions to [`std::path::Path`]. pub trait FoundryPathExt { /// Returns true if the [`Path`] ends with `.t.sol` @@ -110,6 +112,14 @@ pub fn get_provider_builder(config: &Config) -> Result { builder = builder.timeout(Duration::from_secs(rpc_timeout)); } + if let Some(mut rpc_headers) = config.eth_rpc_headers.clone() { + if !rpc_headers.iter().any(|h| h.starts_with("User-Agent:")) { + rpc_headers.push(format!("User-Agent:{DEFAULT_USER_AGENT}")); + } + + builder = builder.headers(rpc_headers); + } + Ok(builder) } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 72be43ab18a9..bcdeb04a9952 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -253,6 +253,15 @@ pub struct Config { pub eth_rpc_jwt: Option, /// Timeout that should be used for any rpc calls pub eth_rpc_timeout: Option, + /// Headers that should be used for any rpc calls + /// + /// # Example + /// + /// rpc_headers = ["x-custom-header:value", "x-another-header:another-value"] + /// + /// You can also the ETH_RPC_HEADERS env variable like so: + /// `ETH_RPC_HEADERS="x-custom-header:value x-another-header:another-value"` + pub eth_rpc_headers: Option>, /// etherscan API key, or alias for an `EtherscanConfig` in `etherscan` table pub etherscan_api_key: Option, /// Multiple etherscan api configs and their aliases @@ -2347,6 +2356,7 @@ impl Default for Config { eth_rpc_url: None, eth_rpc_jwt: None, eth_rpc_timeout: None, + eth_rpc_headers: None, etherscan_api_key: None, verbosity: 0, remappings: vec![], diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 72aacff49eb6..aa38a0b77c65 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -109,6 +109,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { eth_rpc_url: Some("localhost".to_string()), eth_rpc_jwt: None, eth_rpc_timeout: None, + eth_rpc_headers: None, etherscan_api_key: None, etherscan: Default::default(), verbosity: 4, From 4527475bc8be4044a8daa1dddecb4086403c5b76 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 29 Nov 2024 21:10:40 +0530 Subject: [PATCH 68/68] fix: set user-agent header in runtime transport (#9434) --- crates/cli/src/utils/mod.rs | 8 +------- crates/common/src/constants.rs | 3 +++ crates/common/src/provider/runtime_transport.rs | 10 +++++++++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index f833924f57fe..9f8475f63dab 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -39,8 +39,6 @@ pub const STATIC_FUZZ_SEED: [u8; 32] = [ 0x5d, 0x64, 0x0b, 0x19, 0xad, 0xf0, 0xe3, 0x57, 0xb8, 0xd4, 0xbe, 0x7d, 0x49, 0xee, 0x70, 0xe6, ]; -const DEFAULT_USER_AGENT: &str = concat!("foundry/", env!("CARGO_PKG_VERSION")); - /// Useful extensions to [`std::path::Path`]. pub trait FoundryPathExt { /// Returns true if the [`Path`] ends with `.t.sol` @@ -112,11 +110,7 @@ pub fn get_provider_builder(config: &Config) -> Result { builder = builder.timeout(Duration::from_secs(rpc_timeout)); } - if let Some(mut rpc_headers) = config.eth_rpc_headers.clone() { - if !rpc_headers.iter().any(|h| h.starts_with("User-Agent:")) { - rpc_headers.push(format!("User-Agent:{DEFAULT_USER_AGENT}")); - } - + if let Some(rpc_headers) = config.eth_rpc_headers.clone() { builder = builder.headers(rpc_headers); } diff --git a/crates/common/src/constants.rs b/crates/common/src/constants.rs index 0ba0514c2b87..4ff3eb8d70fb 100644 --- a/crates/common/src/constants.rs +++ b/crates/common/src/constants.rs @@ -40,6 +40,9 @@ pub const OPTIMISM_SYSTEM_ADDRESS: Address = address!("deaddeaddeaddeaddeaddeadd /// Transaction identifier of System transaction types pub const SYSTEM_TRANSACTION_TYPE: u8 = 126; +/// Default user agent set as the header for requests that don't specify one. +pub const DEFAULT_USER_AGENT: &str = concat!("foundry/", env!("CARGO_PKG_VERSION")); + /// Returns whether the sender is a known L2 system sender that is the first tx in every block. /// /// Transactions from these senders usually don't have a any fee information. diff --git a/crates/common/src/provider/runtime_transport.rs b/crates/common/src/provider/runtime_transport.rs index a95969be5a40..563cec313850 100644 --- a/crates/common/src/provider/runtime_transport.rs +++ b/crates/common/src/provider/runtime_transport.rs @@ -1,7 +1,7 @@ //! Runtime transport that connects on first request, which can take either of an HTTP, //! WebSocket, or IPC transport and supports retries based on CUPS logic. -use crate::REQUEST_TIMEOUT; +use crate::{DEFAULT_USER_AGENT, REQUEST_TIMEOUT}; use alloy_json_rpc::{RequestPacket, ResponsePacket}; use alloy_pubsub::{PubSubConnect, PubSubFrontend}; use alloy_rpc_types::engine::{Claims, JwtSecret}; @@ -176,6 +176,14 @@ impl RuntimeTransport { ); } + if !headers.iter().any(|(k, _v)| k.as_str().starts_with("User-Agent:")) { + headers.insert( + reqwest::header::USER_AGENT, + HeaderValue::from_str(DEFAULT_USER_AGENT) + .expect("User-Agent should be valid string"), + ); + } + client_builder = client_builder.default_headers(headers); let client =